From 04f33896eeb827b04a2d1c5c63674e3f8238fd80 Mon Sep 17 00:00:00 2001 From: Manav Bhatia Date: Wed, 29 Jan 2020 18:23:03 -0600 Subject: [PATCH] Topology optimization with homogenized level set (#58) * -- Added two classes for subelement refinement that look for element intersection with level set boundary and add new elements to the mesh inside each subelement. * -- Bug-fixes for sub element refinement. * -- Modifying the behavior of if_elem_has_positive_phi_region() in level set elem intersection. * -- Bug-fix for stress assembly. * removing commented code from level set nonlinear implicit assembly class. * -- Bug-fixes for sub element refinement. * -- Modified example 5 to use the sub-element refinement class. * -- Added methods to compute topology sensitivity callable from nonlinear implicit assembly. -- Modifications in elem operation classes to support this. * -- Added nanoFlann KD-tree based geometry filter initialization * -- Added iteration counter to function evaluation. * -- Minor fix * -- refactored topology optimization examples by moving each model to an independent file and making the driver functions in the example templated so that the models can be easily switched out. * -- Added reference volume calculation to the topology models. * -- Added structural null-space vector to SIMP topology optimization so that AMG solvers can be used. -- Added problem_type in SIM topology optimization example so that objective/constraint function can be modified using user options. * -- Added null space vector object to level-set topology optimization example -- Added problem_type to level-set topology optimization example. * -- Removed unnecessary output call from FunctionEvaluation::evaluate * Removing errors introduced during merging of master. * Added a dof-constraint for the hanging node created with element intersection. * -- Bug-fix for sub-element hanging node constraint. * -- Added ability to specify element subdomain ids that will be excluded in the global assembly routines. * Added level-set interface point search and normal computation at a point on level-set function. * -- Minor fixes. * -- Added traction shifted boundary condition to all topology optimization examples -- Added method in level-set topology optimization example to mark shifted boundaries for application of traction boundary condition. -- Added traction boundary condition in structural analysis. Currently only implemented for 2D inplane structural analysis. -- Added a shifted boundary application of traction boundary condition. Currently only implemented for 2D inplane structural analysis. -- Added option for computation of second order spatial derivative for side integration. -- Damped the Newton-step for identification of point on shifted boundary. -- Added method in level-set element intersection class to identify the sides of the element on material. -- Bug-fixes in sub-element mesh refinement class. * -- Minor fixes. * -- added methods to compute sensitivity of shifted boundary traction. -- added supporting methods in level-set boundary velocity object and mesh function. * -- Added sensitivity of shifted boundary traction residual for 2D elements. * -- Bug-fix for volume sensitivity in compliance minimization part of example 5 * -- bug-fix * -- Bug-fixes for example 5. * Minor edit in example 5. * Minor fix in structural element 2D. * Added damping factor to boundary point search. * -- Changed Node to Point in plot utility. * The level set boundary point search can lack convergence in regions of high curvature when the searched point moves from one element to another since the gradient of the level-set function can be very different between the two adjacent elements. In this case the point is searched starting from the most recent unconverged point. This is allowed only once. * -- bug-fix for sensitivity of surface normal on level set boundary. * -- bug-fixes in topology optimization examples. * -- Bug-fixes for 2d structural elem. * -- Added methods to remove side and volume loads in physics discipline. * -- Bug-fixes in example 5. * -- Bug-fix in sensitivity of boundary normal computed in level set boundary velocity class -- Added a Hessian approximation in interface point search to improve robustness of point search when the search point ends up in a different element with jump in slope (in regions of high curvature). * -- Added the ability to obtain nearest intersection points on an element based on the intersection computed on an element. * Removing shifted boundary initializations from example 5. * -- Removed _assembly as a member of the ElemBase class. * -- Added classes to compute homogenized element volume fraction from level set function. * added example 7 for homogenized level-set based topology optimization * various bug-fixes for homogenized level-set-based topology optimization. * Fixes for memory errors in example 7. * -- Level-set-based Homogenized volume fraction sensitivity initialization now uses the geometric filter information to improve the computational efficiency. * -- Bug-fix in heaviside function based homogenization -- Modified the element modulus function in example 7 to use a penalty parameter similar to SIMP. * -- Updated example 7 for homogenized level-set-based topology optimization with different penalization of modulus of elasticity for static analysis and stress analysis. -- Updated smoothing width for approximate Heaviside to 0.1. -- Minor fixes for compatibility with libMesh changes to parallel communicator API. * integrating homogenized level-set topology optimization example as example 8 * reintegrated commits lost during rebase. Nastran example is enabled only if NastranIO is enabled. * commiting files missed in previous commit * -- Minor changes to the topology example titles. * adding fix for timpi * -- Bug-fixes for compilation of nastranIO with conditional compilation of mast with pynastran -- Modified upper/lower limit of level-set in example 8 to +/- 10 * removing pynastran_io.cpp and pynastran_io.h from repository since these are created by cython during each compilation. * undoing the previous change of removing the pynastran_io* files in src/mesh. * -- Fixes for backwards compatibility to libMesh. * -- Fix for lib mesh backwards compatibility. * -- More fixes for backwards compatibility. --- CMakeLists.txt | 4 + cmake/FindlibMesh.cmake | 34 +- doc/tutorials.dox | 1 + examples/structural/CMakeLists.txt | 5 +- examples/structural/base/bracket_2d_model.h | 646 ++++ examples/structural/base/eyebar_2d_model.h | 550 +++ examples/structural/base/inplane_2d_model.h | 506 +++ .../structural/base/level_set_nucleation.h | 143 + examples/structural/base/truss_2d_model.h | 547 +++ examples/structural/example_5/example_5.cpp | 1827 +++------- examples/structural/example_6/example_6.cpp | 1151 +----- examples/structural/example_7/CMakeLists.txt | 20 +- examples/structural/example_8/CMakeLists.txt | 16 + examples/structural/example_8/example_8.cpp | 1651 +++++++++ src/CMakeLists.txt | 2 + src/base/assembly_base.cpp | 11 +- src/base/assembly_base.h | 7 + src/base/assembly_elem_operation.cpp | 52 + src/base/assembly_elem_operation.h | 11 + src/base/boundary_condition_base.h | 5 +- .../eigenproblem_assembly_elem_operations.h | 9 + src/base/elem_base.cpp | 2 - src/base/elem_base.h | 13 - src/base/mast_config.h.in | 2 + src/base/mesh_field_function.cpp | 32 + src/base/mesh_field_function.h | 11 +- src/base/nonlinear_implicit_assembly.cpp | 140 +- ...linear_implicit_assembly_elem_operations.h | 9 + src/base/output_assembly_elem_operations.h | 9 + src/base/physics_discipline_base.cpp | 34 + src/base/physics_discipline_base.h | 15 +- src/elasticity/bending_structural_element.cpp | 3 +- src/elasticity/bending_structural_element.h | 1 - src/elasticity/compliance_output.cpp | 42 +- src/elasticity/compliance_output.h | 9 + ...uid_structure_assembly_elem_operations.cpp | 2 +- src/elasticity/smooth_ramp_stress_output.cpp | 1 + src/elasticity/solid_element_3d.cpp | 7 +- src/elasticity/solid_element_3d.h | 50 +- src/elasticity/stress_assembly.cpp | 4 + src/elasticity/stress_output_base.cpp | 30 +- src/elasticity/stress_output_base.h | 10 + ..._buckling_eigenproblem_elem_operations.cpp | 2 +- src/elasticity/structural_element_1d.cpp | 7 +- src/elasticity/structural_element_1d.h | 51 +- src/elasticity/structural_element_2d.cpp | 3127 ++++++++++++----- src/elasticity/structural_element_2d.h | 97 +- src/elasticity/structural_element_base.cpp | 52 +- src/elasticity/structural_element_base.h | 48 +- ...structural_modal_eigenproblem_assembly.cpp | 61 +- .../structural_modal_eigenproblem_assembly.h | 10 + .../structural_nonlinear_assembly.cpp | 45 +- .../structural_nonlinear_assembly.h | 9 + .../structural_transient_assembly.cpp | 2 +- src/fluid/conservative_fluid_element_base.cpp | 21 +- src/fluid/conservative_fluid_element_base.h | 1 - .../conservative_fluid_transient_assembly.cpp | 2 +- ...ncy_domain_linearized_complex_assembly.cpp | 2 +- ...ain_linearized_conservative_fluid_elem.cpp | 7 +- ...omain_linearized_conservative_fluid_elem.h | 1 - src/fluid/integrated_force_output.cpp | 2 +- src/fluid/integrated_force_output.h | 11 + .../heat_conduction_elem_base.cpp | 27 +- .../heat_conduction_elem_base.h | 1 - .../heat_conduction_nonlinear_assembly.cpp | 38 +- .../heat_conduction_nonlinear_assembly.h | 9 + .../heat_conduction_transient_assembly.cpp | 2 +- src/level_set/CMakeLists.txt | 12 +- src/level_set/filter_base.cpp | 227 +- src/level_set/filter_base.h | 1 + ...heaviside_elem_homogenization_function.cpp | 175 + .../heaviside_elem_homogenization_function.h | 58 + .../homogenized_density_function_base.cpp | 131 + .../homogenized_density_function_base.h | 102 + ...tersected_elem_homogenization_function.cpp | 118 + ...intersected_elem_homogenization_function.h | 58 + src/level_set/level_set_boundary_velocity.cpp | 355 +- src/level_set/level_set_boundary_velocity.h | 66 +- src/level_set/level_set_elem_base.cpp | 83 +- src/level_set/level_set_elem_base.h | 23 +- src/level_set/level_set_intersected_elem.cpp | 4 +- src/level_set/level_set_intersected_elem.h | 1 + src/level_set/level_set_intersection.cpp | 180 +- src/level_set/level_set_intersection.h | 62 +- .../level_set_nonlinear_implicit_assembly.h | 20 - src/level_set/level_set_perimeter_output.cpp | 25 +- src/level_set/level_set_perimeter_output.h | 19 +- .../level_set_transient_assembly.cpp | 2 +- src/level_set/level_set_volume_output.cpp | 14 +- src/level_set/level_set_volume_output.h | 15 +- src/level_set/sub_cell_fe.cpp | 1 + src/level_set/sub_elem_mesh_refinement.cpp | 727 ++++ src/level_set/sub_elem_mesh_refinement.h | 122 + src/level_set/sub_elem_node_map.cpp | 59 + src/level_set/sub_elem_node_map.h | 89 + src/mesh/fe_base.h | 2 + src/mesh/geom_elem.cpp | 4 +- src/mesh/geom_elem.h | 1 + src/mesh/nastran_io.cpp | 4 + src/mesh/nastran_io.h | 4 + src/optimization/function_evaluation.cpp | 8 +- src/optimization/function_evaluation.h | 3 + .../first_order_newmark_transient_solver.cpp | 9 + .../first_order_newmark_transient_solver.h | 9 + .../second_order_newmark_transient_solver.cpp | 9 + .../second_order_newmark_transient_solver.h | 8 + ...first_order_transient_sensitivity_solver.h | 7 + src/utility/plot.cpp | 6 +- src/utility/plot.h | 2 +- tests/fluid/base/fluid_elem_initialization.h | 1 - 110 files changed, 10665 insertions(+), 3420 deletions(-) create mode 100644 examples/structural/base/bracket_2d_model.h create mode 100644 examples/structural/base/eyebar_2d_model.h create mode 100644 examples/structural/base/inplane_2d_model.h create mode 100644 examples/structural/base/level_set_nucleation.h create mode 100644 examples/structural/base/truss_2d_model.h create mode 100644 examples/structural/example_8/CMakeLists.txt create mode 100644 examples/structural/example_8/example_8.cpp create mode 100644 src/level_set/heaviside_elem_homogenization_function.cpp create mode 100644 src/level_set/heaviside_elem_homogenization_function.h create mode 100644 src/level_set/homogenized_density_function_base.cpp create mode 100644 src/level_set/homogenized_density_function_base.h create mode 100644 src/level_set/intersected_elem_homogenization_function.cpp create mode 100644 src/level_set/intersected_elem_homogenization_function.h create mode 100644 src/level_set/sub_elem_mesh_refinement.cpp create mode 100644 src/level_set/sub_elem_mesh_refinement.h create mode 100644 src/level_set/sub_elem_node_map.cpp create mode 100644 src/level_set/sub_elem_node_map.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef5cad9b..1993421c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,10 @@ if (ENABLE_NASTRANIO) else() message(STATUS " Numpy package found") endif() + + set (MAST_ENABLE_NASTRANIO 1) +else() + set (MAST_ENABLE_NASTRANIO 0) endif() # THIRD PARTY/CONTRIB diff --git a/cmake/FindlibMesh.cmake b/cmake/FindlibMesh.cmake index 9ffa67b3..1f6e1db1 100644 --- a/cmake/FindlibMesh.cmake +++ b/cmake/FindlibMesh.cmake @@ -19,12 +19,35 @@ find_library(libMesh_dbg_LIBRARY NAMES mesh_dbg HINTS ${libMesh_DIR}/lib) +find_library(timpi_opt_LIBRARY + NAMES timpi_opt + HINTS ${libMesh_DIR}/lib) + +find_library(timpi_dbg_LIBRARY + NAMES timpi_dbg + HINTS ${libMesh_DIR}/lib) + +if (NOT timpi_opt_LIBRARY) + find_library(timpi_opt_LIBRARY + NAMES mesh_opt + HINTS ${libMesh_DIR}/lib) +endif() + +if (NOT timpi_dbg_LIBRARY) + find_library(timpi_dbg_LIBRARY + NAMES mesh_dbg + HINTS ${libMesh_DIR}/lib) +endif() + # If debug library is not available then set it to the optimized library if(NOT libMesh_dbg_LIBRARY) message("-- WARN: Did not find libmesh_dbg using libmesh_opt for debug version.") find_library(libMesh_dbg_LIBRARY NAMES mesh_opt HINTS ${libMesh_DIR}/lib) + find_library(timpi_dbg_LIBRARY + NAMES timpi_opt + HINTS ${libMesh_DIR}/lib) endif() @@ -52,15 +75,24 @@ endif() # Set variables. include(FindPackageHandleStandardArgs) find_package_handle_standard_args(libMesh - REQUIRED_VARS libMesh_dbg_LIBRARY libMesh_opt_LIBRARY libMesh_INCLUDE_DIR + REQUIRED_VARS + libMesh_dbg_LIBRARY + libMesh_opt_LIBRARY + timpi_dbg_LIBRARY + timpi_opt_LIBRARY + libMesh_INCLUDE_DIR VERSION_VAR libMesh_VERSION) mark_as_advanced(libMesh_INCLUDE_DIR libMesh_dbg_LIBRARY libMesh_opt_LIBRARY + timpi_dbg_LIBRARY + timpi_opt_LIBRARY libMesh_VERSION libMesh_FOUND) set(libMesh_dbg_LIBRARIES ${libMesh_dbg_LIBRARY}) set(libMesh_opt_LIBRARIES ${libMesh_opt_LIBRARY}) +set(timpi_dbg_LIBRARIES ${timpi_dbg_LIBRARY}) +set(timpi_opt_LIBRARIES ${timpi_opt_LIBRARY}) set(libMesh_INCLUDE_DIRS ${libMesh_INCLUDE_DIR}) diff --git a/doc/tutorials.dox b/doc/tutorials.dox index 634b55da..5a4756ad 100644 --- a/doc/tutorials.dox +++ b/doc/tutorials.dox @@ -12,6 +12,7 @@ - \subpage structural_example_4 - \subpage structural_example_5 - \subpage structural_example_6 + - \subpage structural_example_8 - \subpage structural_example_7 - \subpage fluid_example_1 - \subpage fsi_example_1 diff --git a/examples/structural/CMakeLists.txt b/examples/structural/CMakeLists.txt index e00b0049..ece23aa2 100644 --- a/examples/structural/CMakeLists.txt +++ b/examples/structural/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(example_1) # bar extension add_subdirectory(example_2) # continuation solver add_subdirectory(example_3) # thermoelastic plate bending add_subdirectory(example_4) # tie-constraints -add_subdirectory(example_5) # 2D topology optimization -add_subdirectory(example_6) # 2D SIMP topology optimization +add_subdirectory(example_5) # topology optimization +add_subdirectory(example_6) # SIMP topology optimization add_subdirectory(example_7) # NastranIO input for Nastran BDF mesh +add_subdirectory(example_8) # Homogenized level-set topology optimization diff --git a/examples/structural/base/bracket_2d_model.h b/examples/structural/base/bracket_2d_model.h new file mode 100644 index 00000000..42df44c5 --- /dev/null +++ b/examples/structural/base/bracket_2d_model.h @@ -0,0 +1,646 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast_topology_2d_bracket_model__ +#define __mast_topology_2d_bracket_model__ + +// MAST includes +#include "base/mast_data_types.h" +#include "examples/base/input_wrapper.h" +#include "examples/structural/base/level_set_nucleation.h" +#include "base/boundary_condition_base.h" +#include "base/field_function_base.h" +#include "base/physics_discipline_base.h" +#include "boundary_condition/dirichlet_boundary_condition.h" +#include "level_set/level_set_parameter.h" + +// libMesh includes +#include "libmesh/system.h" +#include "libmesh/unstructured_mesh.h" +#include "libmesh/fe_type.h" +#include "libmesh/string_to_enum.h" +#include "libmesh/mesh_generation.h" +#include "libmesh/elem.h" +#include "libmesh/node.h" + + + +namespace MAST { + +// Forward declerations +class DisciplineBase; +class BoundaryConditionBase; +class FunctionBase; +class Parameter; + +namespace Examples { +struct Bracket2DModel { + + template + static Real reference_volume(Opt& opt); + + template + static void init_analysis_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void init_level_set_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void init_analysis_dirichlet_conditions(Opt& opt); + + template + static void init_indicator_dirichlet_conditions(Opt& opt); + + template + static void init_structural_loads(Opt& opt); + + template + static MAST::BoundaryConditionBase& + init_structural_shifted_boudnary_load(Opt& opt, unsigned int bid); + + template + static void init_indicator_loads(Opt& opt); + + template + static void init_level_set_dvs(Opt& opt); + + template + static void initialize_level_set_solution(Opt& opt); + + template + static void init_simp_dvs(Opt& opt); + + template + static void _delete_elems_from_bracket_mesh(Opt& opt, libMesh::MeshBase &mesh); + + class BracketLoad: + public MAST::FieldFunction { + public: + BracketLoad(const std::string& nm, Real p, Real l1, Real fraction): + MAST::FieldFunction(nm), _p(p), _l1(l1), _frac(fraction) { } + ~BracketLoad() {} + void operator() (const libMesh::Point& p, const Real t, Real& v) const { + if (fabs(p(0) >= _l1*(1.-_frac))) v = _p; + else v = 0.; + } + void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { + v = 0.; + } + protected: + Real _p, _l1, _frac; + }; +}; + +} +} + + +template +Real +MAST::Examples::Bracket2DModel:: +reference_volume(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + return length * height; +} + + + +template +void +MAST::Examples::Bracket2DModel:: +init_analysis_mesh(Opt& opt, + libMesh::UnstructuredMesh& mesh) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_divs = opt._input("nx_divs", "number of elements along x-axis", 20), + ny_divs = opt._input("ny_divs", "number of elements along y-axis", 20); + + if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error(); + + std::string + t = opt._input("elem_type", "type of geometric element in the mesh", "quad4"); + + libMesh::ElemType + e_type = libMesh::Utility::string_to_enum(t); + + // + // if high order FE is used, libMesh requires atleast a second order + // geometric element. + // + if (opt._fetype.order > 1 && e_type == libMesh::QUAD4) + e_type = libMesh::QUAD9; + else if (opt._fetype.order > 1 && e_type == libMesh::TRI3) + e_type = libMesh::TRI6; + + // + // initialize the mesh with one element + // + libMesh::MeshTools::Generation::build_square(mesh, + nx_divs, ny_divs, + 0, length, + 0, height, + e_type); + + _delete_elems_from_bracket_mesh(opt, mesh); +} + + +template +void +MAST::Examples::Bracket2DModel:: +init_level_set_mesh(Opt& opt, + libMesh::UnstructuredMesh& mesh) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_divs = opt._input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), + ny_divs = opt._input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); + + if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error(); + + libMesh::ElemType + e_type = libMesh::QUAD4; + + // initialize the mesh with one element + libMesh::MeshTools::Generation::build_square(mesh, + nx_divs, ny_divs, + 0, length, + 0, height, + e_type); + + _delete_elems_from_bracket_mesh(opt, mesh); +} + + + +template +void +MAST::Examples::Bracket2DModel:: +init_analysis_dirichlet_conditions(Opt& opt) { + + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // bottom boundary + dirichlet->init(0, opt._sys_init->vars()); + opt._discipline->add_dirichlet_bc(0, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._discipline->init_system_dirichlet_bc(*opt._sys); +} + + + +template +void +MAST::Examples::Bracket2DModel:: +init_indicator_dirichlet_conditions(Opt& opt) { + + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // bottom boundary + dirichlet->init(0, opt._indicator_sys_init->vars()); + opt._indicator_discipline->add_dirichlet_bc(0, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._indicator_discipline->init_system_dirichlet_bc(*opt._indicator_sys); + opt._dirichlet_bc_ids.insert(0); +} + + + +template +void +MAST::Examples::Bracket2DModel::init_structural_loads(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.125), + p_val = opt._input("pressure", "pressure on side of domain", 5.e7); + + BracketLoad + *press_f = new BracketLoad( "pressure", p_val, length, frac); + + // + // initialize the load + // + MAST::BoundaryConditionBase + *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); + + p_load->add(*press_f); + opt._discipline->add_side_load(5, *p_load); + opt._boundary_conditions.insert(p_load); + + opt._field_functions.insert(press_f); +} + + +template +MAST::BoundaryConditionBase& +MAST::Examples::Bracket2DModel::init_structural_shifted_boudnary_load(Opt& opt, + unsigned int bid) { + + class ZeroTraction: public MAST::FieldFunction { + public: + ZeroTraction(): MAST::FieldFunction("traction") {} + virtual ~ZeroTraction() {} + virtual void operator() (const libMesh::Point& pt, const Real t, RealVectorX& v) const {v.setZero(3);} + virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& pt, const Real t, RealVectorX& v) const + {v.setZero(3);} + }; + + ZeroTraction + *trac_f = new ZeroTraction; + + MAST::BoundaryConditionBase + *load = new MAST::BoundaryConditionBase(MAST::SURFACE_TRACTION_SHIFTED_BOUNDARY); + + load->add(*opt._level_set_vel); + load->add(*trac_f); + opt._discipline->add_side_load(bid, *load); + opt._boundary_conditions.insert(load); + + opt._field_functions.insert(trac_f); + return *load; +} + + +template +void +MAST::Examples::Bracket2DModel::init_indicator_loads(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.125); + + BracketLoad + *flux_f = new BracketLoad("heat_flux", -2.e6, length, frac); + + // + // initialize the load + // + MAST::BoundaryConditionBase + *f_load = new MAST::BoundaryConditionBase(MAST::HEAT_FLUX); + + f_load->add(*flux_f); + opt._indicator_discipline->add_side_load(5, *f_load); + opt._boundary_conditions.insert(f_load); + + opt._field_functions.insert(flux_f); +} + + + +template +void +MAST::Examples::Bracket2DModel::init_level_set_dvs(Opt& opt) { + + libmesh_assert(opt._initialized); + // + // this assumes that level set is defined using lagrange shape functions + // + libmesh_assert_equal_to(opt._level_set_fetype.family, libMesh::LAGRANGE); + + Real + tol = 1.e-12, + l_frac = 0.4,//_input("length_fraction", "fraction of length along x-axis that is in the bracket", 0.4), + h_frac = 0.4,//_input( "height_fraction", "fraction of length along y-axis that is in the bracket", 0.4), + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3), + x_lim = length * l_frac, + y_lim = height * (1.-h_frac), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.125), + filter_radius = opt._input("filter_radius", "radius of geometric filter for level set field", 0.015); + + unsigned int + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._level_set_mesh->is_replicated()); + + std::vector local_phi(opt._level_set_sys->solution->size()); + opt._level_set_sys->solution->localize(local_phi); + + // + // iterate over all the node values + // + libMesh::MeshBase::const_node_iterator + it = opt._level_set_mesh->nodes_begin(), + end = opt._level_set_mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._level_set_mesh->n_nodes()); + n_vars = 0; + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(0, 0, 0); + + if ((n(1)-filter_radius) <= y_lim && + (n(0)+filter_radius) >= length*(1.-frac)) { + + // + // set value at the constrained points to a small positive number + // material here + // + if (dof_id >= opt._level_set_sys->solution->first_local_index() && + dof_id < opt._level_set_sys->solution->last_local_index()) + opt._level_set_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + // + // on the boundary, set everything to be zero, so that there + // is always a boundary there that the optimizer can move + // + if (n(0) < tol || // left boundary + std::fabs(n(0) - length) < tol || // right boundary + std::fabs(n(1) - height) < tol || // top boundary + (n(0) >= x_lim && n(1) <= y_lim)) { + + if (dof_id >= opt._level_set_sys->solution->first_local_index() && + dof_id < opt._level_set_sys->solution->last_local_index()) + opt._level_set_sys->solution->set(dof_id, -1.0); + val = -1.0; + } + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + + opt._level_set_sys->solution->close(); +} + + +template +void +MAST::Examples::Bracket2DModel::init_simp_dvs(Opt& opt) { + + libmesh_assert(opt._initialized); + + // + // this assumes that density variable has a constant value per element + // + libmesh_assert_equal_to(opt._density_fetype.family, libMesh::LAGRANGE); + + Real + tol = 1.e-12, + l_frac = 0.4,//_input("length_fraction", "fraction of length along x-axis that is in the bracket", 0.4), + h_frac = 0.4,//_input( "height_fraction", "fraction of length along y-axis that is in the bracket", 0.4), + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3), + x_lim = length * l_frac, + y_lim = height * (1.-h_frac), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.125), + filter_radius = opt._input("filter_radius", "radius of geometric filter for level set field", 0.015); + + unsigned int + sys_num = opt._density_sys->number(), + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._mesh->is_replicated()); + + std::vector local_phi(opt._density_sys->solution->size()); + opt._density_sys->solution->localize(local_phi); + + // iterate over all the element values + libMesh::MeshBase::const_node_iterator + it = opt._mesh->nodes_begin(), + end = opt._mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._mesh->n_elem()); + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(sys_num, 0, 0); + + if ((n(1)-filter_radius) <= y_lim && (n(0)+filter_radius) >= length*(1.-frac)) { + + // + // set value at the constrained points to a small positive number + // material here + // + if (dof_id >= opt._density_sys->solution->first_local_index() && + dof_id < opt._density_sys->solution->last_local_index()) + opt._density_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + // + // on the boundary, set everything to be zero, so that there + // is always a boundary there that the optimizer can move + // + if (n(0) < tol || // left boundary + std::fabs(n(0) - length) < tol || // right boundary + std::fabs(n(1) - height) < tol || // top boundary + (n(0) >= x_lim && n(1) <= y_lim)) { + + if (dof_id >= opt._density_sys->solution->first_local_index() && + dof_id < opt._density_sys->solution->last_local_index()) + opt._density_sys->solution->set(dof_id, opt._rho_min); + val = opt._rho_min; + } + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + opt._density_sys->solution->close(); + +} + + + +template +void +MAST::Examples::Bracket2DModel:: +_delete_elems_from_bracket_mesh(Opt& opt, + libMesh::MeshBase &mesh) { + + Real + tol = 1.e-12, + x = -1., + y = -1., + l_frac = 0.4, + w_frac = 0.4, + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3), + x_lim = length * l_frac, + y_lim = height * (1.-w_frac); + + // + // now, remove elements that are outside of the L-bracket domain + // + libMesh::MeshBase::element_iterator + e_it = mesh.elements_begin(), + e_end = mesh.elements_end(); + + for ( ; e_it!=e_end; e_it++) { + + libMesh::Elem* elem = *e_it; + x = length; + y = 0.; + for (unsigned int i=0; in_nodes(); i++) { + const libMesh::Node& n = elem->node_ref(i); + if (x > n(0)) x = n(0); + if (y < n(1)) y = n(1); + } + + // + // delete element if the lowest x,y locations are outside of the bracket + // domain + // + if (x >= x_lim && y<= y_lim) + mesh.delete_elem(elem); + } + + mesh.prepare_for_use(); + + // + // add the two additional boundaries to the boundary info so that + // we can apply loads on them + // + bool + facing_right = false, + facing_down = false; + + e_it = mesh.elements_begin(); + e_end = mesh.elements_end(); + + for ( ; e_it != e_end; e_it++) { + + libMesh::Elem* elem = *e_it; + + if (!elem->on_boundary()) continue; + + for (unsigned int i=0; in_sides(); i++) { + + if (elem->neighbor_ptr(i)) continue; + + std::unique_ptr s(elem->side_ptr(i).release()); + + const libMesh::Point p = s->centroid(); + + facing_right = true; + facing_down = true; + for (unsigned int j=0; jn_nodes(); j++) { + const libMesh::Node& n = s->node_ref(j); + + if (n(0) < x_lim || n(1) > y_lim) { + facing_right = false; + facing_down = false; + } + else if (std::fabs(n(0) - p(0)) > tol) + facing_right = false; + else if (std::fabs(n(1) - p(1)) > tol) + facing_down = false; + } + + if (facing_right) mesh.boundary_info->add_side(elem, i, 4); + if (facing_down) mesh.boundary_info->add_side(elem, i, 5); + } + } + + mesh.boundary_info->sideset_name(4) = "facing_right"; + mesh.boundary_info->sideset_name(5) = "facing_down"; +} + + + +template +void +MAST::Examples::Bracket2DModel::initialize_level_set_solution(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_h = opt._input("initial_level_set_n_holes_in_x", + "number of holes along x-direction for initial level-set field", 6), + ny_h = opt._input("initial_level_set_n_holes_in_y", + "number of holes along y-direction for initial level-set field", 6), + nx_m = opt._input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), + ny_m = opt._input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); + + MAST::Examples::LevelSetNucleationFunction + phi(0., 0., length, height, nx_m, ny_m, nx_h, ny_h); + + opt._level_set_sys_init->initialize_solution(phi); +} + + +#endif // __mast_topology_2d_bracket_model__ diff --git a/examples/structural/base/eyebar_2d_model.h b/examples/structural/base/eyebar_2d_model.h new file mode 100644 index 00000000..9c602a6e --- /dev/null +++ b/examples/structural/base/eyebar_2d_model.h @@ -0,0 +1,550 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast_topology_2d_eyebar_model__ +#define __mast_topology_2d_eyebar_model__ + +// MAST includes +#include "base/mast_data_types.h" +#include "base/boundary_condition_base.h" +#include "base/field_function_base.h" +#include "base/physics_discipline_base.h" +#include "examples/base/input_wrapper.h" +#include "examples/structural/base/level_set_nucleation.h" +#include "examples/fluid/meshing/cylinder.h" +#include "boundary_condition/dirichlet_boundary_condition.h" +#include "level_set/level_set_parameter.h" + +// libMesh includes +#include "libmesh/system.h" +#include "libmesh/unstructured_mesh.h" +#include "libmesh/fe_type.h" +#include "libmesh/string_to_enum.h" +#include "libmesh/mesh_generation.h" +#include "libmesh/elem.h" +#include "libmesh/node.h" + + + + +namespace MAST { + +// Forward declerations +class DisciplineBase; +class BoundaryConditionBase; +class FunctionBase; +class Parameter; + +namespace Examples { + +struct Eyebar2DModel { + + template + static Real reference_volume(Opt& opt); + + template + static void init_analysis_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void init_level_set_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void + init_analysis_dirichlet_conditions(Opt& opt); + + template + static void + init_indicator_dirichlet_conditions(Opt& opt); + + + template + static MAST::BoundaryConditionBase& + init_structural_shifted_boudnary_load(Opt& opt, unsigned int bid); + + template + static void + init_structural_loads(Opt& opt); + + template + static void + init_indicator_loads(Opt& opt); + + template + static void + init_level_set_dvs(Opt& opt); + + template + static void + initialize_level_set_solution(Opt& opt); + + template + static void + init_simp_dvs(Opt& opt); + + class EyebarLoad: + public MAST::FieldFunction { + public: + EyebarLoad(): + MAST::FieldFunction("pressure") { } + ~EyebarLoad() {} + void operator() (const libMesh::Point& p, const Real t, Real& v) const { + if (p(0) <= 0.) v = (-std::pow(p(1), 2) + std::pow(1.5, 2))*1.e6; + else v = 0.; + } + void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { + v = 0.; + } + }; +}; + +} +} + + +template +Real +MAST::Examples::Eyebar2DModel:: +reference_volume(Opt& opt) { + + return 16.*8. - acos(-1.) * 1.5*1.5; +} + + + + +template +void +MAST::Examples::Eyebar2DModel:: +init_analysis_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh) { + + // + // identify the element type from the input file or from the order + // of the element + // + unsigned int + n_radial_divs = opt._input("n_radial_divs", "number of elements along radial direction", 20), + n_quarter_divs = opt._input("n_quarter_divs", "number of elements along height", 20); + + Real + //length = 16., + height = 8., + radius = 1.5, + h_ratio = opt._input("h_ratio", "ratio of radial element size at cylinder and at edge", 2); + + std::string + t = opt._input("elem_type", "type of geometric element in the mesh", "quad4"); + + libMesh::ElemType + e_type = libMesh::Utility::string_to_enum(t); + + // + // if high order FE is used, libMesh requires atleast a second order + // geometric element. + // + if (opt._fetype.order > 1 && e_type == libMesh::QUAD4) + e_type = libMesh::QUAD9; + else if (opt._fetype.order > 1 && e_type == libMesh::TRI3) + e_type = libMesh::TRI6; + + MAST::Examples::CylinderMesh2D cylinder; + cylinder.mesh(radius, height/2., + n_radial_divs, n_quarter_divs, h_ratio, + mesh, e_type, + true, height, n_quarter_divs*2); + + // + // add the boundary ids for Dirichlet conditions + // + libMesh::MeshBase::const_element_iterator + e_it = mesh.elements_begin(), + e_end = mesh.elements_end(); + + Real + tol = radius * 1.e-8; + + for (; e_it != e_end; e_it++) { + + libMesh::Elem* elem = *e_it; + + std::unique_ptr edge(elem->side_ptr(1)); + libMesh::Point p = edge->centroid(); + + if (std::fabs(p(0)-height*1.5) < tol && + std::fabs(p(1)) <= 1.) // on the right edge + mesh.boundary_info->add_side(elem, 1, 0); + + // check for the circumference of the circle where load will be + // applied + edge.reset(elem->side_ptr(3).release()); + p = edge->centroid(); + + if ((std::fabs(p.norm()-radius) < 1.e-2) && + p(0) < 0.) // left semi-circle + mesh.boundary_info->add_side(elem, 3, 5); + } + + mesh.boundary_info->sideset_name(0) = "dirichlet"; + mesh.boundary_info->sideset_name(5) = "load"; + + +} + + +template +void +MAST::Examples::Eyebar2DModel:: +init_level_set_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh) { + + Real + //length = 16., + height = 8., + radius = 1.5, + h_ratio = opt._input("h_ratio", "ratio of radial element size at cylinder and at edge", 2); + + unsigned int + n_radial_divs = opt._input("level_set_n_radial_divs", "number of elements along radial direction", 10), + n_quarter_divs = opt._input("level_set_n_quarter_divs", "number of elements along height", 10); + + libMesh::ElemType + e_type = libMesh::QUAD4; + + MAST::Examples::CylinderMesh2D cylinder; + cylinder.mesh(radius, height/2, + n_radial_divs, n_quarter_divs, h_ratio, + mesh, e_type, + true, height, n_quarter_divs*2); +} + + + +template +void +MAST::Examples::Eyebar2DModel::init_analysis_dirichlet_conditions(Opt& opt) { + + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary + dirichlet->init(0, opt._sys_init->vars()); + opt._discipline->add_dirichlet_bc(0, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._discipline->init_system_dirichlet_bc(*opt._sys); + opt._dirichlet_bc_ids.insert(0); +} + + + +template +void +MAST::Examples::Eyebar2DModel::init_indicator_dirichlet_conditions(Opt& opt) { + + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary + dirichlet->init(0, opt._indicator_sys_init->vars()); + opt._indicator_discipline->add_dirichlet_bc(0, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._indicator_discipline->init_system_dirichlet_bc(*opt._indicator_sys); +} + + + + +template +MAST::BoundaryConditionBase& +MAST::Examples::Eyebar2DModel::init_structural_shifted_boudnary_load(Opt& opt, + unsigned int bid) { + + class ZeroTraction: public MAST::FieldFunction { + public: + ZeroTraction(): MAST::FieldFunction("traction") {} + virtual ~ZeroTraction() {} + virtual void operator() (const libMesh::Point& pt, const Real t, RealVectorX& v) const + {v.setZero(3);} + virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& pt, const Real t, RealVectorX& v) const + {v.setZero(3);} + }; + + ZeroTraction + *trac_f = new ZeroTraction; + + MAST::BoundaryConditionBase + *load = new MAST::BoundaryConditionBase(MAST::SURFACE_TRACTION_SHIFTED_BOUNDARY); + + load->add(*opt._level_set_vel); + load->add(*trac_f); + opt._discipline->add_side_load(bid, *load); + opt._boundary_conditions.insert(load); + + opt._field_functions.insert(trac_f); + return *load; +} + + +template +void +MAST::Examples::Eyebar2DModel::init_structural_loads(Opt& opt) { + + EyebarLoad + *press_f = new EyebarLoad(); + + // initialize the load + MAST::BoundaryConditionBase + *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); + + p_load->add(*press_f); + opt._discipline->add_side_load(5, *p_load); + opt._boundary_conditions.insert(p_load); + + opt._field_functions.insert(press_f); +} + + +template +void +MAST::Examples::Eyebar2DModel::init_indicator_loads(Opt& opt) { + +} + + + +template +void +MAST::Examples::Eyebar2DModel::init_level_set_dvs(Opt& opt) { + + libmesh_assert(opt._initialized); + // + // this assumes that level set is defined using lagrange shape functions + // + libmesh_assert_equal_to(opt._level_set_fetype.family, libMesh::LAGRANGE); + + Real + tol = 1.e-6, + height = 8., + filterradius = opt._input("filterradius", "radius of geometric filter for level set field", 0.015); + + unsigned int + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._level_set_mesh->is_replicated()); + + std::vector local_phi(opt._level_set_sys->solution->size()); + opt._level_set_sys->solution->localize(local_phi); + + // + // iterate over all the node values + // + libMesh::MeshBase::const_node_iterator + it = opt._level_set_mesh->nodes_begin(), + end = opt._level_set_mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._level_set_mesh->n_nodes()); + n_vars = 0; + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(0, 0, 0); + + if (((n.norm() <= 1.5+filterradius) && n(0) <= 0.) || // circle + (std::fabs(n(0)-height*1.5) < filterradius && // right edge + std::fabs(n(1)) <= 1.+filterradius)) { // dirichlet constraint + + // + // set value at the constrained points to a small positive number + // material here + // + if (dof_id >= opt._level_set_sys->solution->first_local_index() && + dof_id < opt._level_set_sys->solution->last_local_index()) + opt._level_set_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + // + // on the boundary, set everything to be zero, so that there + // is always a boundary there that the optimizer can move + // + if (std::fabs(n(0)+height*0.5) < tol || // left boundary + std::fabs(n(1)-height*0.5) < tol || // top boundary + std::fabs(n(1)+height*0.5) < tol || // bottom boundary + std::fabs(n(0)-height*1.5) < tol) { // right boundary + + if (dof_id >= opt._level_set_sys->solution->first_local_index() && + dof_id < opt._level_set_sys->solution->last_local_index()) + opt._level_set_sys->solution->set(dof_id, -1.); + val = -1.; + } + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + opt._level_set_sys->solution->close(); +} + + +template +void +MAST::Examples::Eyebar2DModel::init_simp_dvs(Opt& opt) { + + libmesh_assert(opt._initialized); + + // + // this assumes that density variable has a constant value per element + // + libmesh_assert_equal_to(opt._density_fetype.family, libMesh::LAGRANGE); + + Real + tol = 1.e-6, + height = 8., + filterradius = opt._input("filterradius", "radius of geometric filter for level set field", 0.015); + + unsigned int + sys_num = opt._density_sys->number(), + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._mesh->is_replicated()); + + std::vector local_phi(opt._density_sys->solution->size()); + opt._density_sys->solution->localize(local_phi); + + // iterate over all the element values + // iterate over all the element values + libMesh::MeshBase::const_node_iterator + it = opt._mesh->nodes_begin(), + end = opt._mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._mesh->n_elem()); + n_vars = 0; + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(sys_num, 0, 0); + + + + if (((n.norm() <= 1.5+filterradius) && n(0) <= 0.) || // circle + (std::fabs(n(0)-height*1.5) < filterradius && // right edge + std::fabs(n(1)) <= 1.+filterradius)) { // dirichlet constraint + + // + // set value at the constrained points to material + // + if (dof_id >= opt._density_sys->solution->first_local_index() && + dof_id < opt._density_sys->solution->last_local_index()) + opt._density_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + // + // on the boundary, set everything to be zero, so that there + // is always a boundary there that the optimizer can move + // + if (std::fabs(n(0)+height*0.5) < tol || // left boundary + std::fabs(n(1)-height*0.5) < tol || // top boundary + std::fabs(n(1)+height*0.5) < tol || // bottom boundary + std::fabs(n(0)-height*1.5) < tol) { // right boundary + + if (dof_id >= opt._density_sys->solution->first_local_index() && + dof_id < opt._density_sys->solution->last_local_index()) + opt._density_sys->solution->set(dof_id, opt._rho_min); + val = opt._rho_min; + } + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + opt._density_sys->solution->close(); +} + + + +template +void +MAST::Examples::Eyebar2DModel::initialize_level_set_solution(Opt& opt) { + + Real + height = 8., + length = 16.; + + unsigned int + nx_h = opt._input("initial_level_set_n_holes_in_x", + "number of holes along x-direction for initial level-set field", 6), + ny_h = opt._input("initial_level_set_n_holes_in_y", + "number of holes along y-direction for initial level-set field", 6), + nx_m = opt._input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), + ny_m = opt._input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); + + MAST::Examples::LevelSetNucleationFunction + phi(-0.5*height, -0.5*height, length, height, nx_m, ny_m, nx_h, ny_h); + + opt._level_set_sys_init->initialize_solution(phi); +} + + +#endif // __mast_topology_2d_eyebar_model__ diff --git a/examples/structural/base/inplane_2d_model.h b/examples/structural/base/inplane_2d_model.h new file mode 100644 index 00000000..a609628a --- /dev/null +++ b/examples/structural/base/inplane_2d_model.h @@ -0,0 +1,506 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast_topology_2d_inplane_model__ +#define __mast_topology_2d_inplane_model__ + +// MAST includes +#include "base/mast_data_types.h" +#include "examples/base/input_wrapper.h" +#include "base/boundary_condition_base.h" +#include "base/field_function_base.h" +#include "base/physics_discipline_base.h" +#include "boundary_condition/dirichlet_boundary_condition.h" +#include "level_set/level_set_parameter.h" + +// libMesh includes +#include "libmesh/system.h" +#include "libmesh/unstructured_mesh.h" +#include "libmesh/fe_type.h" +#include "libmesh/string_to_enum.h" +#include "libmesh/mesh_generation.h" +#include "libmesh/elem.h" +#include "libmesh/node.h" + + + + +namespace MAST { + +// Forward declerations +class DisciplineBase; +class BoundaryConditionBase; +class FunctionBase; +class Parameter; + +namespace Examples { + + +struct Inplane2DModel { + + template + static Real reference_volume(Opt& opt); + + template + static void init_analysis_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void init_level_set_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void + init_analysis_dirichlet_conditions(Opt& opt); + + template + static void + init_indicator_dirichlet_conditions(Opt& opt); + + + template + static MAST::BoundaryConditionBase& + init_structural_shifted_boudnary_load(Opt& opt, unsigned int bid); + + template + static void + init_structural_loads(Opt& opt); + + template + static void + init_indicator_loads(Opt& opt); + + template + static void + init_level_set_dvs(Opt& opt); + + template + static void + initialize_level_set_solution(Opt& opt); + + template + static void + init_simp_dvs(Opt& opt); + + class FluxLoad: + public MAST::FieldFunction { + public: + FluxLoad(const std::string& nm, Real p, Real l1, Real fraction): + MAST::FieldFunction(nm), _p(p), _l1(l1), _frac(fraction) { } + ~FluxLoad() {} + void operator() (const libMesh::Point& p, const Real t, Real& v) const { + if (fabs(p(0)-_l1*0.5) <= 0.5*_frac*_l1) v = _p; + else v = 0.; + } + void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { + v = 0.; + } + protected: + Real _p, _l1, _frac; + }; + +}; + +} +} + + +template +Real +MAST::Examples::Inplane2DModel:: +reference_volume(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + return length * height; +} + + + +template +void +MAST::Examples::Inplane2DModel:: +init_analysis_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_divs = opt._input("nx_divs", "number of elements along x-axis", 20), + ny_divs = opt._input("ny_divs", "number of elements along y-axis", 20); + + std::string + t = opt._input("elem_type", "type of geometric element in the mesh", "quad4"); + + libMesh::ElemType + e_type = libMesh::Utility::string_to_enum(t); + + // + // if high order FE is used, libMesh requires atleast a second order + // geometric element. + // + if (opt._fetype.order > 1 && e_type == libMesh::QUAD4) + e_type = libMesh::QUAD9; + else if (opt._fetype.order > 1 && e_type == libMesh::TRI3) + e_type = libMesh::TRI6; + + // + // initialize the mesh with one element + // + libMesh::MeshTools::Generation::build_square(mesh, + nx_divs, ny_divs, + 0, length, + 0, height, + e_type); +} + + +template +void +MAST::Examples::Inplane2DModel:: +init_level_set_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_divs = opt._input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), + ny_divs = opt._input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); + + if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error(); + + libMesh::ElemType + e_type = libMesh::QUAD4; + + // initialize the mesh with one element + libMesh::MeshTools::Generation::build_square(mesh, + nx_divs, ny_divs, + 0, length, + 0, height, + e_type); +} + + + +template +void +MAST::Examples::Inplane2DModel::init_analysis_dirichlet_conditions(Opt& opt) { + + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary + dirichlet->init(1, opt._sys_init->vars()); + opt._discipline->add_dirichlet_bc(1, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + dirichlet = new MAST::DirichletBoundaryCondition; // right boundary + dirichlet->init(3, opt._sys_init->vars()); + opt._discipline->add_dirichlet_bc(3, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._discipline->init_system_dirichlet_bc(*opt._sys); + opt._dirichlet_bc_ids.insert(1); + opt._dirichlet_bc_ids.insert(3); +} + + + +template +void +MAST::Examples::Inplane2DModel::init_indicator_dirichlet_conditions(Opt& opt) { + + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary + dirichlet->init(1, opt._indicator_sys_init->vars()); + opt._indicator_discipline->add_dirichlet_bc(1, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + dirichlet = new MAST::DirichletBoundaryCondition; // left boundary + dirichlet->init(3, opt._indicator_sys_init->vars()); + opt._indicator_discipline->add_dirichlet_bc(3, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._indicator_discipline->init_system_dirichlet_bc(*opt._indicator_sys); +} + + +template +MAST::BoundaryConditionBase& +MAST::Examples::Inplane2DModel::init_structural_shifted_boudnary_load(Opt& opt, + unsigned int bid) { + + class ZeroTraction: public MAST::FieldFunction { + public: + ZeroTraction(): MAST::FieldFunction("traction") {} + virtual ~ZeroTraction() {} + virtual void operator() (const libMesh::Point& pt, const Real t, RealVectorX& v) const + {v.setZero(3);} + virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& pt, const Real t, RealVectorX& v) const + {v.setZero(3);} + }; + + ZeroTraction + *trac_f = new ZeroTraction; + + MAST::BoundaryConditionBase + *load = new MAST::BoundaryConditionBase(MAST::SURFACE_TRACTION_SHIFTED_BOUNDARY); + + load->add(*opt._level_set_vel); + load->add(*trac_f); + opt._discipline->add_side_load(bid, *load); + opt._boundary_conditions.insert(load); + + opt._field_functions.insert(trac_f); + return *load; +} + + +template +void +MAST::Examples::Inplane2DModel::init_structural_loads(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2), + p_val = opt._input("pressure", "pressure on side of domain", 2.e4); + + FluxLoad + *press_f = new FluxLoad( "pressure", p_val, length, frac); + + // initialize the load + MAST::BoundaryConditionBase + *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); + + p_load->add(*press_f); + opt._discipline->add_side_load(2, *p_load); + opt._boundary_conditions.insert(p_load); + + opt._field_functions.insert(press_f); +} + + +template +void +MAST::Examples::Inplane2DModel::init_indicator_loads(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2); + + FluxLoad + *flux_f = new FluxLoad("heat_flux", -2.e6, length, frac); + + // initialize the load + MAST::BoundaryConditionBase + *f_load = new MAST::BoundaryConditionBase(MAST::HEAT_FLUX); + + f_load->add(*flux_f); + opt._indicator_discipline->add_side_load(2, *f_load); + opt._boundary_conditions.insert(f_load); + + opt._field_functions.insert(flux_f); +} + + + +template +void +MAST::Examples::Inplane2DModel::init_level_set_dvs(Opt& opt) { + + // + // this assumes that level set is defined using lagrange shape functions + // + libmesh_assert_equal_to(opt._level_set_fetype.family, libMesh::LAGRANGE); + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2), + filter_radius = opt._input("filter_radius", "radius of geometric filter for level set field", 0.015); + + unsigned int + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._level_set_mesh->is_replicated()); + + std::vector local_phi(opt._level_set_sys->solution->size()); + opt._level_set_sys->solution->localize(local_phi); + + // iterate over all the node values + libMesh::MeshBase::const_node_iterator + it = opt._level_set_mesh->nodes_begin(), + end = opt._level_set_mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._level_set_mesh->n_nodes()); + n_vars = 0; + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(0, 0, 0); + + // only if node is not on the upper edge + if ((n(1)+filter_radius >= height) && + (n(0)-filter_radius <= length*.5*(1.+frac)) && + (n(0)+filter_radius >= length*.5*(1.-frac))) { + + // set value at the material points to a small positive number + if (dof_id >= opt._level_set_sys->solution->first_local_index() && + dof_id < opt._level_set_sys->solution->last_local_index()) + opt._level_set_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + opt._level_set_sys->solution->close(); +} + + +template +void +MAST::Examples::Inplane2DModel::init_simp_dvs(Opt& opt) { + + // + // this assumes that density variable has a constant value per element + // + libmesh_assert_equal_to(opt._density_fetype.family, libMesh::LAGRANGE); + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2), + filter_radius = opt._input("filter_radius", "radius of geometric filter for level set field", 0.015); + + unsigned int + sys_num = opt._density_sys->number(), + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._mesh->is_replicated()); + + std::vector local_phi(opt._density_sys->solution->size()); + opt._density_sys->solution->localize(local_phi); + + // iterate over all the element values + libMesh::MeshBase::const_node_iterator + it = opt._mesh->nodes_begin(), + end = opt._mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._mesh->n_elem()); + n_vars = 0; + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(sys_num, 0, 0); + + // only if node is not on the upper edge + if ((n(1)+filter_radius >= height) && + (n(0)-filter_radius <= length*.5*(1.+frac)) && + (n(0)+filter_radius >= length*.5*(1.-frac))) { + + // set value at the material points to a small positive number + if (dof_id >= opt._density_sys->solution->first_local_index() && + dof_id < opt._density_sys->solution->last_local_index()) + opt._density_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + opt._density_sys->solution->close(); +} + + +template +void +MAST::Examples::Inplane2DModel::initialize_level_set_solution(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_h = opt._input("initial_level_set_n_holes_in_x", + "number of holes along x-direction for initial level-set field", 6), + ny_h = opt._input("initial_level_set_n_holes_in_y", + "number of holes along y-direction for initial level-set field", 6), + nx_m = opt._input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), + ny_m = opt._input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); + + MAST::Examples::LevelSetNucleationFunction + phi(0., 0., length, height, nx_m, ny_m, nx_h, ny_h); + + opt._level_set_sys_init->initialize_solution(phi); +} + +#endif // __mast_topology_2d_inplane_model__ diff --git a/examples/structural/base/level_set_nucleation.h b/examples/structural/base/level_set_nucleation.h new file mode 100644 index 00000000..83eaf26d --- /dev/null +++ b/examples/structural/base/level_set_nucleation.h @@ -0,0 +1,143 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast_topology_level_set_nucleation__ +#define __mast_topology_level_set_nucleation__ + +// MAST includes +#include "base/mast_data_types.h" + + +namespace MAST { + +namespace Examples { + +class LevelSetNucleationFunction: +public MAST::FieldFunction { + +public: + LevelSetNucleationFunction(Real x0, + Real y0, + Real l1, + Real l2, + Real nx_mesh, + Real ny_mesh, + Real nx_holes, + Real ny_holes): + MAST::FieldFunction("Phi"), + _x0 (x0), + _y0 (y0), + _l1 (l1), + _l2 (l2), + _nx_mesh (nx_mesh), + _ny_mesh (ny_mesh), + _nx_holes (nx_holes), + _ny_holes (ny_holes), + _pi (acos(-1.)) { + + Real + dx = _l1/(1.*_nx_holes); + + for (unsigned int i=0; i<_nx_holes; i++) + _x_axis_hole_locations.insert(_x0+(i+.5)*dx); + + // + // now, along the y-axis + // + dx = _l2/(1.*_ny_holes); + for (unsigned int i=0; i<_ny_holes; i++) + _y_axis_hole_locations.insert(_y0+(i+0.5)*dx); + } + + virtual ~LevelSetNucleationFunction() {} + + virtual void operator()(const libMesh::Point& p, + const Real t, + RealVectorX& v) const { + + libmesh_assert_less_equal(t, 1); + libmesh_assert_equal_to(v.size(), 1); + + // + // the libMesh solution projection routine for Lagrange elements + // will query the function value at the nodes. So, we figure + // out which nodes should have zero values set to them. + // if there is one hole in any direction, it will be in the + // center of the domain. If there are more than 1, then two of + // the holes will be on the boundary and others will fill the + // interior evenly. + // + const Real + dx_mesh = _l1/(1.*_nx_holes), + dy_mesh = _l2/(1.*_ny_holes); + + std::set::const_iterator + x_it_low = _x_axis_hole_locations.lower_bound(p(0)-dx_mesh), + y_it_low = _y_axis_hole_locations.lower_bound(p(1)-dy_mesh); + + unsigned int + n = 0; + // + // see if the x-location needs a hole + // + for ( ; x_it_low != _x_axis_hole_locations.end(); x_it_low++) { + if (std::fabs(*x_it_low - p(0)) <= dx_mesh*0.25) { + n++; + break; + } + } + + // + // now check the y-location + // + for ( ; y_it_low != _y_axis_hole_locations.end(); y_it_low++) { + if (std::fabs(*y_it_low - p(1)) <= dy_mesh*0.25) { + n++; + break; + } + } + + if (n == 2) + v(0) = -1.e0; + else + v(0) = 1.e0; + } + + +protected: + Real + _x0, + _y0, + _l1, + _l2, + _nx_mesh, + _ny_mesh, + _nx_holes, + _ny_holes, + _pi; + std::set _x_axis_hole_locations; + std::set _y_axis_hole_locations; +}; + + +} +} + + +#endif // __mast_topology_level_set_nucleation__ diff --git a/examples/structural/base/truss_2d_model.h b/examples/structural/base/truss_2d_model.h new file mode 100644 index 00000000..9d7912eb --- /dev/null +++ b/examples/structural/base/truss_2d_model.h @@ -0,0 +1,547 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast_topology_2d_truss_model__ +#define __mast_topology_2d_truss_model__ + +// MAST includes +#include "base/mast_data_types.h" +#include "examples/base/input_wrapper.h" +#include "examples/structural/base/level_set_nucleation.h" +#include "base/boundary_condition_base.h" +#include "base/field_function_base.h" +#include "base/physics_discipline_base.h" +#include "boundary_condition/dirichlet_boundary_condition.h" +#include "level_set/level_set_parameter.h" + +// libMesh includes +#include "libmesh/system.h" +#include "libmesh/unstructured_mesh.h" +#include "libmesh/fe_type.h" +#include "libmesh/string_to_enum.h" +#include "libmesh/mesh_generation.h" +#include "libmesh/elem.h" +#include "libmesh/node.h" + + + + +namespace MAST { + +// Forward declerations +class DisciplineBase; +class BoundaryConditionBase; +class FunctionBase; +class Parameter; + +namespace Examples { + +struct Truss2DModel { + + template + static Real reference_volume(Opt& opt); + + template + static void init_analysis_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void init_level_set_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh); + + template + static void + init_analysis_dirichlet_conditions(Opt& opt); + + template + static void + init_indicator_dirichlet_conditions(Opt& opt); + + + template + static MAST::BoundaryConditionBase& + init_structural_shifted_boudnary_load(Opt& opt, unsigned int bid); + + template + static void + init_structural_loads(Opt& opt); + + template + static void + init_indicator_loads(Opt& opt); + + template + static void + init_level_set_dvs(Opt& opt); + + template + static void + initialize_level_set_solution(Opt& opt); + + template + static void + init_simp_dvs(Opt& opt); + + class FluxLoad: + public MAST::FieldFunction { + public: + FluxLoad(const std::string& nm, Real p, Real l1, Real fraction): + MAST::FieldFunction(nm), _p(p), _l1(l1), _frac(fraction) { } + ~FluxLoad() {} + void operator() (const libMesh::Point& p, const Real t, Real& v) const { + if (fabs(p(0)-_l1*0.5) <= 0.5*_frac*_l1) v = _p; + else v = 0.; + } + void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { + v = 0.; + } + protected: + Real _p, _l1, _frac; + }; + +}; + +} +} + + + +template +Real +MAST::Examples::Truss2DModel:: +reference_volume(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + return length * height; +} + + + +template +void +MAST::Examples::Truss2DModel:: +init_analysis_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_divs = opt._input("nx_divs", "number of elements along x-axis", 20), + ny_divs = opt._input("ny_divs", "number of elements along y-axis", 20); + + std::string + t = opt._input("elem_type", "type of geometric element in the mesh", "quad4"); + + libMesh::ElemType + e_type = libMesh::Utility::string_to_enum(t); + + // + // if high order FE is used, libMesh requires atleast a second order + // geometric element. + // + if (opt._fetype.order > 1 && e_type == libMesh::QUAD4) + e_type = libMesh::QUAD9; + else if (opt._fetype.order > 1 && e_type == libMesh::TRI3) + e_type = libMesh::TRI6; + + // + // initialize the mesh with one element + // + libMesh::MeshTools::Generation::build_square(mesh, + nx_divs, ny_divs, + 0, length, + 0, height, + e_type); + + Real + dirichletlength_fraction = + opt._input("truss_dirichletlength_fraction", + "length fraction of the truss boundary where dirichlet condition is applied", + 0.05); + + // identify the boundaries for dirichlet condition + libMesh::MeshBase::const_element_iterator + e_it = opt._mesh->elements_begin(), + e_end = opt._mesh->elements_end(); + + for ( ; e_it != e_end; e_it++) { + + const libMesh::Elem* e = *e_it; + + if ((*e->node_ptr(0))(1) < 1.e-8 && + e->centroid()(0) <= length*dirichletlength_fraction) + opt._mesh->boundary_info->add_side(e, 0, 6); + else if ((*e->node_ptr(1))(1) < 1.e-8 && + e->centroid()(0) >= length*(1.-dirichletlength_fraction)) + opt._mesh->boundary_info->add_side(e, 0, 7); + + if ((*e->node_ptr(0))(0) < 1.e-8 && + (*e->node_ptr(0))(1) < 1.e-8 && + e->centroid()(0) <= length*dirichletlength_fraction) + opt._mesh->boundary_info->add_side(e, 0, 8); + } + + opt._mesh->boundary_info->sideset_name(6) = "left_dirichlet"; + opt._mesh->boundary_info->sideset_name(7) = "right_dirichlet"; +} + + +template +void +MAST::Examples::Truss2DModel:: +init_level_set_mesh(Opt& opt, libMesh::UnstructuredMesh& mesh) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_divs = opt._input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), + ny_divs = opt._input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); + + if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error(); + + libMesh::ElemType + e_type = libMesh::QUAD4; + + // initialize the mesh with one element + libMesh::MeshTools::Generation::build_square(mesh, + nx_divs, ny_divs, + 0, length, + 0, height, + e_type); +} + + + +template +void +MAST::Examples::Truss2DModel::init_analysis_dirichlet_conditions(Opt& opt) { + + std::vector vars = {1, 2, 3, 4, 5}; + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // left support + dirichlet->init(6, vars); + opt._discipline->add_dirichlet_bc(6, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + dirichlet = new MAST::DirichletBoundaryCondition; // right support + dirichlet->init(7, vars); + opt._discipline->add_dirichlet_bc(7, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + vars = {0}; + dirichlet = new MAST::DirichletBoundaryCondition; // left support + dirichlet->init(8, vars); + opt._discipline->add_dirichlet_bc(8, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._discipline->init_system_dirichlet_bc(*opt._sys); + opt._dirichlet_bc_ids.insert(6); + opt._dirichlet_bc_ids.insert(7); + opt._dirichlet_bc_ids.insert(8); +} + + + +template +void +MAST::Examples::Truss2DModel::init_indicator_dirichlet_conditions(Opt& opt) { + + MAST::DirichletBoundaryCondition + *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary + dirichlet->init(6, opt._indicator_sys_init->vars()); + opt._indicator_discipline->add_dirichlet_bc(6, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + dirichlet = new MAST::DirichletBoundaryCondition; // left boundary + dirichlet->init(7, opt._indicator_sys_init->vars()); + opt._indicator_discipline->add_dirichlet_bc(7, *dirichlet); + opt._boundary_conditions.insert(dirichlet); + + opt._indicator_discipline->init_system_dirichlet_bc(*opt._indicator_sys); +} + + + +template +MAST::BoundaryConditionBase& +MAST::Examples::Truss2DModel::init_structural_shifted_boudnary_load(Opt& opt, + unsigned int bid) { + + class ZeroTraction: public MAST::FieldFunction { + public: + ZeroTraction(): MAST::FieldFunction("traction") {} + virtual ~ZeroTraction() {} + virtual void operator() (const libMesh::Point& pt, const Real t, RealVectorX& v) const + {v.setZero(3);} + virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& pt, const Real t, RealVectorX& v) const + {v.setZero(3);} + }; + + ZeroTraction + *trac_f = new ZeroTraction; + + MAST::BoundaryConditionBase + *load = new MAST::BoundaryConditionBase(MAST::SURFACE_TRACTION_SHIFTED_BOUNDARY); + + load->add(*opt._level_set_vel); + load->add(*trac_f); + opt._discipline->add_side_load(bid, *load); + opt._boundary_conditions.insert(load); + + opt._field_functions.insert(trac_f); + return *load; +} + + +template +void +MAST::Examples::Truss2DModel::init_structural_loads(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2), + p_val = opt._input("pressure", "pressure on side of domain", 2.e4); + + FluxLoad + *press_f = new FluxLoad( "pressure", p_val, length, frac); + + // initialize the load + MAST::BoundaryConditionBase + *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); + + p_load->add(*press_f); + opt._discipline->add_side_load(2, *p_load); + opt._boundary_conditions.insert(p_load); + + opt._field_functions.insert(press_f); +} + + +template +void +MAST::Examples::Truss2DModel::init_indicator_loads(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2); + + FluxLoad + *flux_f = new FluxLoad("heat_flux", -2.e6, length, frac); + + // initialize the load + MAST::BoundaryConditionBase + *f_load = new MAST::BoundaryConditionBase(MAST::HEAT_FLUX); + + f_load->add(*flux_f); + opt._indicator_discipline->add_side_load(2, *f_load); + opt._boundary_conditions.insert(f_load); + + opt._field_functions.insert(flux_f); +} + + + +template +void +MAST::Examples::Truss2DModel::init_level_set_dvs(Opt& opt) { + + // + // this assumes that level set is defined using lagrange shape functions + // + libmesh_assert_equal_to(opt._level_set_fetype.family, libMesh::LAGRANGE); + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2), + filter_radius = opt._input("filter_radius", "radius of geometric filter for level set field", 0.015); + + unsigned int + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._level_set_mesh->is_replicated()); + + std::vector local_phi(opt._level_set_sys->solution->size()); + opt._level_set_sys->solution->localize(local_phi); + + // iterate over all the node values + libMesh::MeshBase::const_node_iterator + it = opt._level_set_mesh->nodes_begin(), + end = opt._level_set_mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._level_set_mesh->n_nodes()); + n_vars = 0; + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(0, 0, 0); + + // only if node is not on the upper edge + if ((n(1)-filter_radius <= 0.) && + (n(0)-filter_radius <= length*.5*(1.+frac)) && + (n(0)+filter_radius >= length*.5*(1.-frac))) { + + // set value at the material points to a small positive number + if (dof_id >= opt._level_set_sys->solution->first_local_index() && + dof_id < opt._level_set_sys->solution->last_local_index()) + opt._level_set_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + opt._level_set_sys->solution->close(); +} + + + +template +void +MAST::Examples::Truss2DModel::initialize_level_set_solution(Opt& opt) { + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + height = opt._input("height", "length of domain along y-axis", 0.3); + + unsigned int + nx_h = opt._input("initial_level_set_n_holes_in_x", + "number of holes along x-direction for initial level-set field", 6), + ny_h = opt._input("initial_level_set_n_holes_in_y", + "number of holes along y-direction for initial level-set field", 6), + nx_m = opt._input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), + ny_m = opt._input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); + + MAST::Examples::LevelSetNucleationFunction + phi(0., 0., length, height, nx_m, ny_m, nx_h, ny_h); + + opt._level_set_sys_init->initialize_solution(phi); +} + + +template +void +MAST::Examples::Truss2DModel::init_simp_dvs(Opt& opt) { + + // + // this assumes that density variable has a constant value per element + // + libmesh_assert_equal_to(opt._density_fetype.family, libMesh::LAGRANGE); + + Real + length = opt._input("length", "length of domain along x-axis", 0.3), + frac = opt._input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2), + filter_radius = opt._input("filter_radius", "radius of geometric filter for level set field", 0.015); + + unsigned int + sys_num = opt._density_sys->number(), + dof_id = 0, + n_vars = 0; + + Real + val = 0.; + + // + // all ranks will have DVs defined for all variables. So, we should be + // operating on a replicated mesh + // + libmesh_assert(opt._mesh->is_replicated()); + + std::vector local_phi(opt._density_sys->solution->size()); + opt._density_sys->solution->localize(local_phi); + + // iterate over all the element values + libMesh::MeshBase::const_node_iterator + it = opt._mesh->nodes_begin(), + end = opt._mesh->nodes_end(); + + // + // maximum number of dvs is the number of nodes on the level set function + // mesh. We will evaluate the actual number of dvs + // + opt._dv_params.reserve(opt._mesh->n_elem()); + n_vars = 0; + + for ( ; it!=end; it++) { + + const libMesh::Node& n = **it; + + dof_id = n.dof_number(sys_num, 0, 0); + + // only if node is not on the upper edge + if ((n(1)-filter_radius <= 0.) && + (n(0)-filter_radius <= length*.5*(1.+frac)) && + (n(0)+filter_radius >= length*.5*(1.-frac))) { + + // set value at the material points to a small positive number + if (dof_id >= opt._density_sys->solution->first_local_index() && + dof_id < opt._density_sys->solution->last_local_index()) + opt._density_sys->solution->set(dof_id, 1.e0); + } + else { + + std::ostringstream oss; + oss << "dv_" << n_vars; + val = local_phi[dof_id]; + + opt._dv_params.push_back(std::pair()); + opt._dv_params[n_vars].first = dof_id; + opt._dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); + opt._dv_params[n_vars].second->set_as_topology_parameter(true); + opt._dv_dof_ids.insert(dof_id); + + n_vars++; + } + } + + opt.set_n_vars(n_vars); + opt._density_sys->solution->close(); +} + + +#endif // __mast_topology_2d_truss_model__ diff --git a/examples/structural/example_5/example_5.cpp b/examples/structural/example_5/example_5.cpp index 22c78892..b661858e 100644 --- a/examples/structural/example_5/example_5.cpp +++ b/examples/structural/example_5/example_5.cpp @@ -22,13 +22,10 @@ // MAST includes #include "examples/base/input_wrapper.h" -#include "examples/fluid/meshing/cylinder.h" #include "level_set/level_set_discipline.h" #include "level_set/level_set_system_initialization.h" #include "level_set/level_set_eigenproblem_assembly.h" -#include "level_set/level_set_transient_assembly.h" #include "level_set/level_set_nonlinear_implicit_assembly.h" -#include "level_set/level_set_reinitialization_transient_assembly.h" #include "level_set/level_set_volume_output.h" #include "level_set/level_set_perimeter_output.h" #include "level_set/level_set_boundary_velocity.h" @@ -37,6 +34,7 @@ #include "level_set/level_set_intersection.h" #include "level_set/filter_base.h" #include "level_set/level_set_parameter.h" +#include "level_set/sub_elem_mesh_refinement.h" #include "elasticity/structural_nonlinear_assembly.h" #include "elasticity/structural_modal_eigenproblem_assembly.h" #include "elasticity/ks_stress_output.h" @@ -44,6 +42,7 @@ #include "elasticity/level_set_stress_assembly.h" #include "elasticity/compliance_output.h" #include "elasticity/structural_system_initialization.h" +#include "elasticity/structural_near_null_vector_space.h" #include "heat_conduction/heat_conduction_system_initialization.h" #include "heat_conduction/heat_conduction_nonlinear_assembly.h" #include "base/constant_field_function.h" @@ -57,6 +56,10 @@ #include "optimization/gcmma_optimization_interface.h" #include "optimization/npsol_optimization_interface.h" #include "optimization/function_evaluation.h" +#include "examples/structural/base/bracket_2d_model.h" +#include "examples/structural/base/inplane_2d_model.h" +#include "examples/structural/base/truss_2d_model.h" +#include "examples/structural/base/eyebar_2d_model.h" // libMesh includes @@ -64,7 +67,6 @@ #include "libmesh/serial_mesh.h" #include "libmesh/equation_systems.h" #include "libmesh/string_to_enum.h" -#include "libmesh/mesh_generation.h" #include "libmesh/dof_map.h" #include "libmesh/exodusII_io.h" #include "libmesh/petsc_nonlinear_solver.h" @@ -91,7 +93,7 @@ _optim_con(int* mode, int* nstate); // -// BEGIN_TRANSLATE 2D Level-set topology optimization +// BEGIN_TRANSLATE Level-set topology optimization // // \tableofcontents // @@ -147,16 +149,17 @@ public MAST::AssemblyBase::ElemParameterDependence { }; -class TopologyOptimizationLevelSet2D: +template +class TopologyOptimizationLevelSet: public MAST::FunctionEvaluation { -protected: +public: bool _initialized; MAST::Examples::GetPotWrapper& _input; - - Real _length; - Real _height; + + std::string _problem; + Real _volume; Real _obj_scaling; Real _stress_penalty; Real _perimeter_penalty; @@ -168,6 +171,8 @@ public MAST::FunctionEvaluation { libMesh::UnstructuredMesh* _mesh; libMesh::UnstructuredMesh* _level_set_mesh; + MAST::SubElemMeshRefinement* _mesh_refinement; + libMesh::EquationSystems* _eq_sys; libMesh::EquationSystems* _level_set_eq_sys; @@ -180,15 +185,17 @@ public MAST::FunctionEvaluation { MAST::LevelSetSystemInitialization* _level_set_sys_init_on_str_mesh; MAST::LevelSetSystemInitialization* _level_set_sys_init; MAST::HeatConductionSystemInitialization* _indicator_sys_init; - + + MAST::StructuralNearNullVectorSpace* _nsp; + MAST::PhysicsDisciplineBase* _discipline; MAST::PhysicsDisciplineBase* _indicator_discipline; MAST::LevelSetDiscipline* _level_set_discipline; MAST::FilterBase* _filter; - MAST::MaterialPropertyCardBase* _m_card; - MAST::ElementPropertyCardBase* _p_card; + MAST::MaterialPropertyCardBase *_m_card1, *_m_card2; + MAST::ElementPropertyCardBase *_p_card1, *_p_card2; PhiMeshFunction* _level_set_function; MAST::LevelSetBoundaryVelocity* _level_set_vel; @@ -197,311 +204,19 @@ public MAST::FunctionEvaluation { libMesh::FEType _fetype; libMesh::FEType _level_set_fetype; + MAST::BoundaryConditionBase* _shifted_boundary_load; std::vector _params_for_sensitivity; std::map _parameters; std::set _field_functions; std::set _boundary_conditions; std::set _dv_dof_ids; + std::set _dirichlet_bc_ids; std::vector> _dv_params; -public: - - // \section ex_5_init_mesh Mesh Generation - // This creates the mesh for the specified problem type. - // - void _init_mesh() { - - // The mesh is created using classes written in MAST. The particular - // mesh to be used can be selected using the input parameter - // ` mesh=val `, where `val` can be one of the following: - // - `inplane` inplane structure with load on top and left and right boundaries constrained - // - `bracket` L-bracket - // - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, bracket}", "inplane"); - - if (s == "inplane" || s == "truss") - _init_mesh_inplane(); - else if (s == "bracket") - _init_mesh_bracket(); - else if (s == "eye_bar") - _init_mesh_eye_bar(); - else - libmesh_error(); - } - - // - // \subsection ex_5_inplane_mesh Inplane problem - // - void _init_mesh_inplane() { - - _mesh = new libMesh::SerialMesh(this->comm()); - - // - // identify the element type from the input file or from the order - // of the element - // - unsigned int - nx_divs = _input("nx_divs", "number of elements along x-axis", 20), - ny_divs = _input("ny_divs", "number of elements along y-axis", 20); - - _length = _input("length", "length of domain along x-axis", 0.3), - _height = _input("height", "length of domain along y-axis", 0.3); - - std::string - t = _input("elem_type", "type of geometric element in the mesh", "quad4"); - - libMesh::ElemType - e_type = libMesh::Utility::string_to_enum(t); - - // - // if high order FE is used, libMesh requires atleast a second order - // geometric element. - // - if (_fetype.order > 1 && e_type == libMesh::QUAD4) - e_type = libMesh::QUAD9; - else if (_fetype.order > 1 && e_type == libMesh::TRI3) - e_type = libMesh::TRI6; - - // - // initialize the mesh with one element - // - libMesh::MeshTools::Generation::build_square(*_mesh, - nx_divs, ny_divs, - 0, _length, - 0, _height, - e_type); - - // - // mesh on which the level-set function is defined - // - _level_set_mesh = new libMesh::SerialMesh(this->comm()); - - nx_divs = _input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10); - ny_divs = _input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); - e_type = libMesh::QUAD4; - - // initialize the mesh with one element - libMesh::MeshTools::Generation::build_square(*_level_set_mesh, - nx_divs, ny_divs, - 0, _length, - 0, _height, - e_type); - } - - // - // \subsection ex_5_bracket_mesh Bracket - // - void _init_mesh_bracket() { - - { - unsigned int - nx_divs = _input("nx_divs", "number of elements along x-axis", 20), - ny_divs = _input("ny_divs", "number of elements along y-axis", 20); - - if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error(); - } - - { - unsigned int - nx_divs = _input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), - ny_divs = _input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); - - if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error(); - - } - - _init_mesh_inplane(); - _delete_elems_from_bracket_mesh(*_mesh); - _delete_elems_from_bracket_mesh(*_level_set_mesh); - } - - void _delete_elems_from_bracket_mesh(libMesh::MeshBase &mesh) { - - Real - tol = 1.e-12, - x = -1., - y = -1., - length = _input("length", "length of domain along x-axis", 0.3), - width = _input( "height", "length of domain along y-axis", 0.3), - l_frac = 0.4, - w_frac = 0.4, - x_lim = length * l_frac, - y_lim = width * (1.-w_frac); - - // - // now, remove elements that are outside of the L-bracket domain - // - libMesh::MeshBase::element_iterator - e_it = mesh.elements_begin(), - e_end = mesh.elements_end(); - - for ( ; e_it!=e_end; e_it++) { - - libMesh::Elem* elem = *e_it; - x = length; - y = 0.; - for (unsigned int i=0; in_nodes(); i++) { - const libMesh::Node& n = elem->node_ref(i); - if (x > n(0)) x = n(0); - if (y < n(1)) y = n(1); - } - - // - // delete element if the lowest x,y locations are outside of the bracket - // domain - // - if (x >= x_lim && y<= y_lim) - mesh.delete_elem(elem); - } - - mesh.prepare_for_use(); - - // - // add the two additional boundaries to the boundary info so that - // we can apply loads on them - // - bool - facing_right = false, - facing_down = false; - - e_it = mesh.elements_begin(); - e_end = mesh.elements_end(); - - for ( ; e_it != e_end; e_it++) { - - libMesh::Elem* elem = *e_it; - - if (!elem->on_boundary()) continue; - - for (unsigned int i=0; in_sides(); i++) { - - if (elem->neighbor_ptr(i)) continue; - - std::unique_ptr s(elem->side_ptr(i).release()); - - const libMesh::Point p = s->centroid(); - - facing_right = true; - facing_down = true; - for (unsigned int j=0; jn_nodes(); j++) { - const libMesh::Node& n = s->node_ref(j); - - if (n(0) < x_lim || n(1) > y_lim) { - facing_right = false; - facing_down = false; - } - else if (std::fabs(n(0) - p(0)) > tol) - facing_right = false; - else if (std::fabs(n(1) - p(1)) > tol) - facing_down = false; - } - - if (facing_right) mesh.boundary_info->add_side(elem, i, 4); - if (facing_down) mesh.boundary_info->add_side(elem, i, 5); - } - } - - mesh.boundary_info->sideset_name(4) = "facing_right"; - mesh.boundary_info->sideset_name(5) = "facing_down"; - } - // - // \subsection ex_5_eyebar_mesh Eyebar - // - void _init_mesh_eye_bar() { - - _mesh = new libMesh::SerialMesh(this->comm()); - - // - // identify the element type from the input file or from the order - // of the element - // - unsigned int - n_radial_divs = _input("n_radial_divs", "number of elements along radial direction", 20), - n_quarter_divs = _input("n_quarter_divs", "number of elements along height", 20); - - Real - radius = 1.5, - h_ratio = _input("h_ratio", "ratio of radial element size at cylinder and at edge", 2); - _height = 8.; - _length = _height*2; - - std::string - t = _input("elem_type", "type of geometric element in the mesh", "quad4"); - - libMesh::ElemType - e_type = libMesh::Utility::string_to_enum(t); - - // - // if high order FE is used, libMesh requires atleast a second order - // geometric element. - // - if (_fetype.order > 1 && e_type == libMesh::QUAD4) - e_type = libMesh::QUAD9; - else if (_fetype.order > 1 && e_type == libMesh::TRI3) - e_type = libMesh::TRI6; - - MAST::Examples::CylinderMesh2D cylinder; - cylinder.mesh(radius, _height/2., - n_radial_divs, n_quarter_divs, h_ratio, - *_mesh, e_type, - true, _height, n_quarter_divs*2); - - // - // add the boundary ids for Dirichlet conditions - // - libMesh::MeshBase::const_element_iterator - e_it = _mesh->elements_begin(), - e_end = _mesh->elements_end(); - - Real - tol = radius * 1.e-8; - - for (; e_it != e_end; e_it++) { - - libMesh::Elem* elem = *e_it; - - std::unique_ptr edge(elem->side_ptr(1)); - libMesh::Point p = edge->centroid(); - - if (std::fabs(p(0)-_height*1.5) < tol && - std::fabs(p(1)) <= 1.) // on the right edge - _mesh->boundary_info->add_side(elem, 1, 0); - - // check for the circumference of the circle where load will be - // applied - edge.reset(elem->side_ptr(3).release()); - p = edge->centroid(); - - if ((std::fabs(p.norm()-radius) < 1.e-2) && - p(0) < 0.) // left semi-circle - _mesh->boundary_info->add_side(elem, 3, 5); - } - - _mesh->boundary_info->sideset_name(0) = "dirichlet"; - _mesh->boundary_info->sideset_name(5) = "load"; - - // mesh on which the level-set function is defined - _level_set_mesh = new libMesh::SerialMesh(this->comm()); - - n_radial_divs = _input("level_set_n_radial_divs", "number of elements along radial direction", 10), - n_quarter_divs = _input("level_set_n_quarter_divs", "number of elements along height", 10); - e_type = libMesh::QUAD4; - - // - // initialize the mesh with one element - // - cylinder.mesh(radius, _height/2, - n_radial_divs, n_quarter_divs, h_ratio, - *_level_set_mesh, e_type, - true, _height, n_quarter_divs*2); - } - - // - // \section ex_5_system_discipline System and Discipline + // \section ex_5_system_discipline System and Discipline // void _init_system_and_discipline() { @@ -521,6 +236,9 @@ public MAST::FunctionEvaluation { // _sys = &(_eq_sys->add_system("structural")); _sys->set_eigenproblem_type(libMesh::GHEP); + + _mesh_refinement = new MAST::SubElemMeshRefinement(*_mesh, *_sys); + _sys->attach_constraint_object(*_mesh_refinement); // // initialize the system to the right set of variables @@ -567,980 +285,169 @@ public MAST::FunctionEvaluation { _indicator_discipline = new MAST::PhysicsDisciplineBase(*_eq_sys); } - - void _init_eq_sys() { - - _eq_sys->init(); - _sys->eigen_solver->set_position_of_spectrum(libMesh::LARGEST_MAGNITUDE); - _sys->set_exchange_A_and_B(true); - - _level_set_eq_sys->init(); - } - - - // - // variables added to the mesh - // - void _init_fetype() { - - // FEType to initialize the system. Get the order and type of element. - std::string - order_str = _input("fe_order", "order of finite element shape basis functions", "first"), - family_str = _input("fe_family", "family of finite element shape functions", "lagrange"); - - libMesh::Order - o = libMesh::Utility::string_to_enum(order_str); - libMesh::FEFamily - fe = libMesh::Utility::string_to_enum(family_str); - _fetype = libMesh::FEType(o, fe); - } - - - - // - // \section ex_5_dirichlet Dirichlet Constraints - // - void _init_dirichlet_conditions() { - - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, truss, bracket, eye_bar}", "inplane"); - - if (s == "inplane") - _init_dirichlet_conditions_inplane(); - else if (s == "truss") - _init_dirichlet_conditions_truss(); - else if (s == "bracket") - _init_dirichlet_conditions_bracket(); - else if (s == "eye_bar") - _init_dirichlet_conditions_eye_bar(); - else - libmesh_error(); - } - - // - // \subsection ex_5_inplane_dirichlet Inplane - // - void _init_dirichlet_conditions_inplane() { - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(1, _sys_init->vars()); - _discipline->add_dirichlet_bc(1, *dirichlet); - - dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(3, _sys_init->vars()); - _discipline->add_dirichlet_bc(3, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for indicator system - /////////////////////////////////////////////////////////////////////// - dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(1, _indicator_sys_init->vars()); - _indicator_discipline->add_dirichlet_bc(1, *dirichlet); - _boundary_conditions.insert(dirichlet); - - dirichlet = new MAST::DirichletBoundaryCondition; // left boundary - dirichlet->init(3, _indicator_sys_init->vars()); - _indicator_discipline->add_dirichlet_bc(3, *dirichlet); - _boundary_conditions.insert(dirichlet); - - _indicator_discipline->init_system_dirichlet_bc(*_indicator_sys); - } - - // - // \subsection ex_5_truss_dirichlet Truss - // - void _init_dirichlet_conditions_truss() { - - Real - dirichlet_length_fraction = _input("truss_dirichlet_length_fraction", "length fraction of the truss boundary where dirichlet condition is applied", 0.05); - - // identify the boundaries for dirichlet condition - libMesh::MeshBase::const_element_iterator - e_it = _mesh->elements_begin(), - e_end = _mesh->elements_end(); - - for ( ; e_it != e_end; e_it++) { - - const libMesh::Elem* e = *e_it; - - if ((*e->node_ptr(0))(1) < 1.e-8 && - e->centroid()(0) <= _length*dirichlet_length_fraction) - _mesh->boundary_info->add_side(e, 0, 6); - else if ((*e->node_ptr(1))(1) < 1.e-8 && - e->centroid()(0) >= _length*(1.-dirichlet_length_fraction)) - _mesh->boundary_info->add_side(e, 0, 7); - - if ((*e->node_ptr(0))(0) < 1.e-8 && - (*e->node_ptr(0))(1) < 1.e-8 && - e->centroid()(0) <= _length*dirichlet_length_fraction) - _mesh->boundary_info->add_side(e, 0, 8); - } - - _mesh->boundary_info->sideset_name(6) = "left_dirichlet"; - _mesh->boundary_info->sideset_name(7) = "right_dirichlet"; - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - std::vector vars = {1, 2, 3, 4, 5}; - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // left support - dirichlet->init(6, vars); - _discipline->add_dirichlet_bc(6, *dirichlet); - - dirichlet = new MAST::DirichletBoundaryCondition; // right support - dirichlet->init(7, vars); - _discipline->add_dirichlet_bc(7, *dirichlet); - - vars = {0}; - dirichlet = new MAST::DirichletBoundaryCondition; // left support - dirichlet->init(8, vars); - _discipline->add_dirichlet_bc(8, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for indicator system - /////////////////////////////////////////////////////////////////////// - dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(6, _indicator_sys_init->vars()); - _indicator_discipline->add_dirichlet_bc(6, *dirichlet); - _boundary_conditions.insert(dirichlet); - - dirichlet = new MAST::DirichletBoundaryCondition; // left boundary - dirichlet->init(7, _indicator_sys_init->vars()); - _indicator_discipline->add_dirichlet_bc(7, *dirichlet); - _boundary_conditions.insert(dirichlet); - - _indicator_discipline->init_system_dirichlet_bc(*_indicator_sys); - } - - - // - // \subsection ex_5_bracket_dirichlet Bracket - // - void _init_dirichlet_conditions_bracket() { - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // bottom boundary - dirichlet->init(0, _sys_init->vars()); - _discipline->add_dirichlet_bc(0, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for indicator system - /////////////////////////////////////////////////////////////////////// - dirichlet = new MAST::DirichletBoundaryCondition; // bottom boundary - dirichlet->init(0, _indicator_sys_init->vars()); - _indicator_discipline->add_dirichlet_bc(0, *dirichlet); - _boundary_conditions.insert(dirichlet); - - _indicator_discipline->init_system_dirichlet_bc(*_indicator_sys); - } - - - // - // \subsection ex_5_eyebar_dirichlet Eyebar - // - void _init_dirichlet_conditions_eye_bar() { - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(0, _sys_init->vars()); - _discipline->add_dirichlet_bc(0, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for indicator system - /////////////////////////////////////////////////////////////////////// - dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(0, _indicator_sys_init->vars()); - _indicator_discipline->add_dirichlet_bc(0, *dirichlet); - _boundary_conditions.insert(dirichlet); - - _indicator_discipline->init_system_dirichlet_bc(*_indicator_sys); - } - - - - // - // \section ex_5_loading Loading - // - // - void _init_loads() { - - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, truss, bracket, eye_bar}", "inplane"); - - if (s == "inplane" || s == "truss") - _init_loads_inplane(); - else if (s == "bracket") - _init_loads_bracket(); - else if (s == "eye_bar") - _init_loads_eye_bar(); - else - libmesh_error(); - } - - - // \subsection ex_5_inplane_loading Inplane - // - class FluxLoad: - public MAST::FieldFunction { - public: - FluxLoad(const std::string& nm, Real p, Real l1, Real fraction): - MAST::FieldFunction(nm), _p(p), _l1(l1), _frac(fraction) { } - virtual ~FluxLoad() {} - virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { - if (fabs(p(0)-_l1*0.5) <= 0.5*_frac*_l1) v = _p; - else v = 0.; - } - virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { - v = 0.; - } - protected: - Real _p, _l1, _frac; - }; - - - void _init_loads_inplane() { - - Real - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.2), - p_val = _input("pressure", "pressure on side of domain", 2.e4); - - FluxLoad - *press_f = new FluxLoad( "pressure", p_val, _length, frac), - *flux_f = new FluxLoad("heat_flux", -2.e6, _length, frac); - - // initialize the load - MAST::BoundaryConditionBase - *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE), - *f_load = new MAST::BoundaryConditionBase(MAST::HEAT_FLUX); - - p_load->add(*press_f); - _discipline->add_side_load(2, *p_load); - - f_load->add(*flux_f); - _indicator_discipline->add_side_load(2, *f_load); - - _field_functions.insert(press_f); - _field_functions.insert(flux_f); - } - - - // - // \subsection ex_5_bracket_loading Bracket - // - class BracketLoad: - public MAST::FieldFunction { - public: - BracketLoad(const std::string& nm, Real p, Real l1, Real fraction): - MAST::FieldFunction(nm), _p(p), _l1(l1), _frac(fraction) { } - virtual ~BracketLoad() {} - virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { - if (fabs(p(0) >= _l1*(1.-_frac))) v = _p; - else v = 0.; - } - virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { - v = 0.; - } - protected: - Real _p, _l1, _frac; - }; - - - - void _init_loads_bracket() { - - Real - length = _input("length", "length of domain along x-axis", 0.3), - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.125), - p_val = _input("pressure", "pressure on side of domain", 5.e7); - - BracketLoad - *press_f = new BracketLoad( "pressure", p_val, length, frac), - *flux_f = new BracketLoad("heat_flux", -2.e6, length, frac); - - // - // initialize the load - // - MAST::BoundaryConditionBase - *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE), - *f_load = new MAST::BoundaryConditionBase(MAST::HEAT_FLUX); - - p_load->add(*press_f); - _discipline->add_side_load(5, *p_load); - - f_load->add(*flux_f); - _indicator_discipline->add_side_load(5, *f_load); - - _field_functions.insert(press_f); - _field_functions.insert(flux_f); - } - - - // - // \subsection ex_5_eyebar_loading Eyebar - // - class EyebarLoad: - public MAST::FieldFunction { - public: - EyebarLoad(): - MAST::FieldFunction("pressure") { } - virtual ~EyebarLoad() {} - virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { - if (p(0) <= 0.) v = (-std::pow(p(1), 2) + std::pow(1.5, 2))*1.e6; - else v = 0.; - } - virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { - v = 0.; - } - }; - - - - void _init_loads_eye_bar() { - - EyebarLoad - *press_f = new EyebarLoad(); - - // initialize the load - MAST::BoundaryConditionBase - *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); - - p_load->add(*press_f); - _discipline->add_side_load(5, *p_load); - - _field_functions.insert(press_f); - } - - - // - // \section ex_5_properties Properties - // - // - // - // \subsection ex_5_material_properties Material Properties - // - - void _init_material() { - - Real - Eval = _input("E", "modulus of elasticity", 72.e9), - rhoval = _input("rho", "material density", 2700.), - nu_val = _input("nu", "Poisson's ratio", 0.33), - kappa_val = _input("kappa", "shear correction factor", 5./6.), - kval = _input("k", "thermal conductivity", 1.e-2), - cpval = _input("cp", "thermal capacitance", 864.); - - - MAST::Parameter - *E = new MAST::Parameter("E", Eval), - *rho = new MAST::Parameter("rho", rhoval), - *nu = new MAST::Parameter("nu", nu_val), - *kappa = new MAST::Parameter("kappa", kappa_val), - *k = new MAST::Parameter("k", kval), - *cp = new MAST::Parameter("cp", cpval); - - MAST::ConstantFieldFunction - *E_f = new MAST::ConstantFieldFunction( "E", *E), - *rho_f = new MAST::ConstantFieldFunction( "rho", *rho), - *nu_f = new MAST::ConstantFieldFunction( "nu", *nu), - *kappa_f = new MAST::ConstantFieldFunction("kappa", *kappa), - *k_f = new MAST::ConstantFieldFunction( "k_th", *k), - *cp_f = new MAST::ConstantFieldFunction( "cp", *cp); - - _parameters[ E->name()] = E; - _parameters[ rho->name()] = rho; - _parameters[ nu->name()] = nu; - _parameters[kappa->name()] = kappa; - _parameters[ k->name()] = k; - _parameters[ cp->name()] = cp; - _field_functions.insert(E_f); - _field_functions.insert(rho_f); - _field_functions.insert(nu_f); - _field_functions.insert(kappa_f); - _field_functions.insert(k_f); - _field_functions.insert(cp_f); - - _m_card = new MAST::IsotropicMaterialPropertyCard; - _m_card->add(*E_f); - _m_card->add(*rho_f); - _m_card->add(*nu_f); - _m_card->add(*kappa_f); - _m_card->add(*k_f); - _m_card->add(*cp_f); - } - - - // - // \subsection ex_5_section_properties Section Properties - // - - void _init_section_property(){ - - - - Real - th_v = _input("th", "thickness of 2D element", 0.001); - - MAST::Parameter - *th = new MAST::Parameter("th", th_v), - *zero = new MAST::Parameter("zero", 0.); - - MAST::ConstantFieldFunction - *th_f = new MAST::ConstantFieldFunction("h", *th), - *hoff_f = new MAST::ConstantFieldFunction("off", *zero); - - - _parameters[th->name()] = th; - _parameters[zero->name()] = zero; - _field_functions.insert(th_f); - _field_functions.insert(hoff_f); - - MAST::Solid2DSectionElementPropertyCard - *p_card = new MAST::Solid2DSectionElementPropertyCard; - - _p_card = p_card; - - // set nonlinear strain if requested - bool - nonlinear = _input("if_nonlinear", "flag to turn on/off nonlinear strain", false); - if (nonlinear) p_card->set_strain(MAST::NONLINEAR_STRAIN); - - p_card->add(*th_f); - p_card->add(*hoff_f); - p_card->set_material(*_m_card); - _discipline->set_property_for_subdomain(0, *p_card); - _indicator_discipline->set_property_for_subdomain(0, *p_card); - } - - - // - // \section ex_5_initial_solution Initial Level Set - // - // - class Phi: - public MAST::FieldFunction { - - public: - Phi(Real x0, - Real y0, - Real l1, - Real l2, - Real nx_mesh, - Real ny_mesh, - Real nx_holes, - Real ny_holes): - MAST::FieldFunction("Phi"), - _x0 (x0), - _y0 (y0), - _l1 (l1), - _l2 (l2), - _nx_mesh (nx_mesh), - _ny_mesh (ny_mesh), - _nx_holes (nx_holes), - _ny_holes (ny_holes), - _pi (acos(-1.)) { - - Real - dx = _l1/(1.*_nx_holes); - - for (unsigned int i=0; i<_nx_holes; i++) - _x_axis_hole_locations.insert(_x0+(i+.5)*dx); - - // - // now, along the y-axis - // - dx = _l2/(1.*_ny_holes); - for (unsigned int i=0; i<_ny_holes; i++) - _y_axis_hole_locations.insert(_y0+(i+0.5)*dx); - } - virtual ~Phi() {} - virtual void operator()(const libMesh::Point& p, - const Real t, - RealVectorX& v) const { - - libmesh_assert_less_equal(t, 1); - libmesh_assert_equal_to(v.size(), 1); - - // - // the libMesh solution projection routine for Lagrange elements - // will query the function value at the nodes. So, we figure - // out which nodes should have zero values set to them. - // if there is one hole in any direction, it will be in the - // center of the domain. If there are more than 1, then two of - // the holes will be on the boundary and others will fill the - // interior evenly. - // - const Real - dx_mesh = _l1/(1.*_nx_holes), - dy_mesh = _l2/(1.*_ny_holes); - - std::set::const_iterator - x_it_low = _x_axis_hole_locations.lower_bound(p(0)-dx_mesh), - y_it_low = _y_axis_hole_locations.lower_bound(p(1)-dy_mesh); - - unsigned int - n = 0; - // - // see if the x-location needs a hole - // - for ( ; x_it_low != _x_axis_hole_locations.end(); x_it_low++) { - if (std::fabs(*x_it_low - p(0)) <= dx_mesh*0.25) { - n++; - break; - } - } - - // - // now check the y-location - // - for ( ; y_it_low != _y_axis_hole_locations.end(); y_it_low++) { - if (std::fabs(*y_it_low - p(1)) <= dy_mesh*0.25) { - n++; - break; - } - } - - if (n == 2) - v(0) = -1.e0; - else - v(0) = 1.e0; - } - protected: - Real - _x0, - _y0, - _l1, - _l2, - _nx_mesh, - _ny_mesh, - _nx_holes, - _ny_holes, - _pi; - std::set _x_axis_hole_locations; - std::set _y_axis_hole_locations; - }; - - - void initialize_solution() { - - // - // initialize solution of the level set problem - // - unsigned int - nx_h = _input("initial_level_set_n_holes_in_x", - "number of holes along x-direction for initial level-set field", 6), - ny_h = _input("initial_level_set_n_holes_in_y", - "number of holes along y-direction for initial level-set field", 6), - nx_m = _input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10), - ny_m = _input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10); - - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, truss, bracket, eyebar}", "inplane"); - - std::unique_ptr phi; - - if (s == "inplane" || s == "truss" || s == "bracket") - phi.reset(new Phi(0., 0., _length, _height, nx_m, ny_m, nx_h, ny_h)); - else if (s == "eye_bar") - phi.reset(new Phi(-0.5*_height, -0.5*_height, _length, _height, nx_m, ny_m, nx_h, ny_h)); - else - libmesh_error(); - - _level_set_sys_init->initialize_solution(*phi); - } - - - void _init_phi_dvs() { - - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, truss, bracket, eye_bar}", "inplane"); - - if (s == "inplane" || s == "truss") - _init_phi_dvs_inplane(); - else if (s == "bracket") - _init_phi_dvs_bracket(); - else if (s == "eye_bar") - _init_phi_dvs_eye_bar(); - else - libmesh_error(); - - Real - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - _filter = new MAST::FilterBase(*_level_set_sys, filter_radius, _dv_dof_ids); - libMesh::NumericVector& vec = _level_set_sys->add_vector("base_values"); - vec = *_level_set_sys->solution; - vec.close(); - } - - // - // \subsection ex_5_inplane_initial_level_set Inplane - // - void _init_phi_dvs_inplane() { - - // - // this assumes that level set is defined using lagrange shape functions - // - libmesh_assert_equal_to(_level_set_fetype.family, libMesh::LAGRANGE); - - Real - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.2), - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - - unsigned int - dof_id = 0; - - Real - val = 0.; - - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_level_set_mesh->is_replicated()); - - std::vector local_phi(_level_set_sys->solution->size()); - _level_set_sys->solution->localize(local_phi); - - // iterate over all the node values - libMesh::MeshBase::const_node_iterator - it = _level_set_mesh->nodes_begin(), - end = _level_set_mesh->nodes_end(); - - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_level_set_mesh->n_nodes()); - _n_vars = 0; - - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(0, 0, 0); - - // only if node is not on the upper edge - if ((n(1)+filter_radius >= _height) && - (n(0)-filter_radius <= _length*.5*(1.+frac)) && - (n(0)+filter_radius >= _length*.5*(1.-frac))) { - - // set value at the material points to a small positive number - if (dof_id >= _level_set_sys->solution->first_local_index() && - dof_id < _level_set_sys->solution->last_local_index()) - _level_set_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } - - _level_set_sys->solution->close(); - } - - // - // \subsection ex_5_truss_initial_level_set Truss - // - void _init_phi_dvs_truss() { - - // - // this assumes that level set is defined using lagrange shape functions - // - libmesh_assert_equal_to(_level_set_fetype.family, libMesh::LAGRANGE); - - Real - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.2), - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - - unsigned int - dof_id = 0; - - Real - val = 0.; - - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_level_set_mesh->is_replicated()); - - std::vector local_phi(_level_set_sys->solution->size()); - _level_set_sys->solution->localize(local_phi); - - // iterate over all the node values - libMesh::MeshBase::const_node_iterator - it = _level_set_mesh->nodes_begin(), - end = _level_set_mesh->nodes_end(); - - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_level_set_mesh->n_nodes()); - _n_vars = 0; - - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(0, 0, 0); - - // only if node is not on the upper edge - if ((n(1)-filter_radius <= 0.) && - (n(0)-filter_radius <= _length*.5*(1.+frac)) && - (n(0)+filter_radius >= _length*.5*(1.-frac))) { - - // set value at the material points to a small positive number - if (dof_id >= _level_set_sys->solution->first_local_index() && - dof_id < _level_set_sys->solution->last_local_index()) - _level_set_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } + + void _init_eq_sys() { + + _eq_sys->init(); + _sys->eigen_solver->set_position_of_spectrum(libMesh::LARGEST_MAGNITUDE); + _sys->set_exchange_A_and_B(true); - _level_set_sys->solution->close(); + _level_set_eq_sys->init(); } + // - // \subsection ex_5_bracket_initial_level_set Bracket + // variables added to the mesh // - void _init_phi_dvs_bracket() { - - libmesh_assert(_initialized); - // - // this assumes that level set is defined using lagrange shape functions - // - libmesh_assert_equal_to(_level_set_fetype.family, libMesh::LAGRANGE); + void _init_fetype() { - Real - tol = 1.e-12, - length = _input("length", "length of domain along x-axis", 0.3), - height = _input("height", "length of domain along y-axis", 0.3), - l_frac = 0.4,//_input("length_fraction", "fraction of length along x-axis that is in the bracket", 0.4), - h_frac = 0.4,//_input( "height_fraction", "fraction of length along y-axis that is in the bracket", 0.4), - x_lim = length * l_frac, - y_lim = height * (1.-h_frac), - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.125), - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); + // FEType to initialize the system. Get the order and type of element. + std::string + order_str = _input("fe_order", "order of finite element shape basis functions", "first"), + family_str = _input("fe_family", "family of finite element shape functions", "lagrange"); - unsigned int - dof_id = 0; + libMesh::Order + o = libMesh::Utility::string_to_enum(order_str); + libMesh::FEFamily + fe = libMesh::Utility::string_to_enum(family_str); + _fetype = libMesh::FEType(o, fe); + } + + + // + // \section ex_5_properties Properties + // + // + // + // \subsection ex_5_material_properties Material Properties + // + + void _init_material() { Real - val = 0.; - - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_level_set_mesh->is_replicated()); - - std::vector local_phi(_level_set_sys->solution->size()); - _level_set_sys->solution->localize(local_phi); + Eval = _input("E", "modulus of elasticity", 72.e9), + rhoval = _input("rho", "material density", 2700.), + nu_val = _input("nu", "Poisson's ratio", 0.33), + kappa_val = _input("kappa", "shear correction factor", 5./6.), + kval = _input("k", "thermal conductivity", 1.e-2), + cpval = _input("cp", "thermal capacitance", 864.); - // - // iterate over all the node values - // - libMesh::MeshBase::const_node_iterator - it = _level_set_mesh->nodes_begin(), - end = _level_set_mesh->nodes_end(); - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_level_set_mesh->n_nodes()); - _n_vars = 0; + MAST::Parameter + *E = new MAST::Parameter("E", Eval), + *E_v = new MAST::Parameter("E_v", 0.), + *rho = new MAST::Parameter("rho", rhoval), + *nu = new MAST::Parameter("nu", nu_val), + *kappa = new MAST::Parameter("kappa", kappa_val), + *k = new MAST::Parameter("k", kval), + *cp = new MAST::Parameter("cp", cpval); - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(0, 0, 0); - - if ((n(1)-filter_radius) <= y_lim && (n(0)+filter_radius) >= length*(1.-frac)) { - - // - // set value at the constrained points to a small positive number - // material here - // - if (dof_id >= _level_set_sys->solution->first_local_index() && - dof_id < _level_set_sys->solution->last_local_index()) - _level_set_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - // - // on the boundary, set everything to be zero, so that there - // is always a boundary there that the optimizer can move - // - if (n(0) < tol || // left boundary - std::fabs(n(0) - length) < tol || // right boundary - std::fabs(n(1) - height) < tol || // top boundary - (n(0) >= x_lim && n(1) <= y_lim)) { - - if (dof_id >= _level_set_sys->solution->first_local_index() && - dof_id < _level_set_sys->solution->last_local_index()) - _level_set_sys->solution->set(dof_id, -1.0); - val = -1.0; - } - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } + MAST::ConstantFieldFunction + *E_f = new MAST::ConstantFieldFunction( "E", *E), + *E_v_f = new MAST::ConstantFieldFunction( "E", *E_v), + *rho_f = new MAST::ConstantFieldFunction( "rho", *rho), + *nu_f = new MAST::ConstantFieldFunction( "nu", *nu), + *kappa_f = new MAST::ConstantFieldFunction("kappa", *kappa), + *k_f = new MAST::ConstantFieldFunction( "k_th", *k), + *cp_f = new MAST::ConstantFieldFunction( "cp", *cp); - _level_set_sys->solution->close(); + _parameters[ E->name()] = E; + _parameters[ E_v->name()] = E_v; + _parameters[ rho->name()] = rho; + _parameters[ nu->name()] = nu; + _parameters[kappa->name()] = kappa; + _parameters[ k->name()] = k; + _parameters[ cp->name()] = cp; + _field_functions.insert(E_f); + _field_functions.insert(E_v_f); + _field_functions.insert(rho_f); + _field_functions.insert(nu_f); + _field_functions.insert(kappa_f); + _field_functions.insert(k_f); + _field_functions.insert(cp_f); + + _m_card1 = new MAST::IsotropicMaterialPropertyCard; + _m_card2 = new MAST::IsotropicMaterialPropertyCard; + _m_card1->add(*E_f); + _m_card1->add(*rho_f); + _m_card1->add(*nu_f); + _m_card1->add(*kappa_f); + _m_card1->add(*k_f); + _m_card1->add(*cp_f); + + // material for void + _m_card2->add(*E_v_f); + _m_card2->add(*rho_f); + _m_card2->add(*nu_f); + _m_card2->add(*kappa_f); + _m_card2->add(*k_f); + _m_card2->add(*cp_f); } - + // - // \subsection ex_5_eyebar_initial_level_set Eyebar + // \subsection ex_5_section_properties Section Properties // - void _init_phi_dvs_eye_bar() { + + void _init_section_property(){ + - libmesh_assert(_initialized); - // - // this assumes that level set is defined using lagrange shape functions - // - libmesh_assert_equal_to(_level_set_fetype.family, libMesh::LAGRANGE); Real - tol = 1.e-6, - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); + th_v = _input("th", "thickness of 2D element", 0.001); - unsigned int - dof_id = 0; + MAST::Parameter + *th = new MAST::Parameter("th", th_v), + *zero = new MAST::Parameter("zero", 0.); - Real - val = 0.; + MAST::ConstantFieldFunction + *th_f = new MAST::ConstantFieldFunction("h", *th), + *hoff_f = new MAST::ConstantFieldFunction("off", *zero); - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_level_set_mesh->is_replicated()); - std::vector local_phi(_level_set_sys->solution->size()); - _level_set_sys->solution->localize(local_phi); + _parameters[th->name()] = th; + _parameters[zero->name()] = zero; + _field_functions.insert(th_f); + _field_functions.insert(hoff_f); - // - // iterate over all the node values - // - libMesh::MeshBase::const_node_iterator - it = _level_set_mesh->nodes_begin(), - end = _level_set_mesh->nodes_end(); + MAST::Solid2DSectionElementPropertyCard + *p_card1 = new MAST::Solid2DSectionElementPropertyCard, + *p_card2 = new MAST::Solid2DSectionElementPropertyCard; - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_level_set_mesh->n_nodes()); - _n_vars = 0; + _p_card1 = p_card1; + _p_card2 = p_card2; + + // set nonlinear strain if requested + bool + nonlinear = _input("if_nonlinear", "flag to turn on/off nonlinear strain", false); + if (nonlinear) _p_card1->set_strain(MAST::NONLINEAR_STRAIN); + _p_card2->set_strain(MAST::LINEAR_STRAIN); + + p_card1->add(*th_f); + p_card1->add(*hoff_f); + p_card1->set_material(*_m_card1); + + // property card for void + p_card2->add(*th_f); + p_card2->add(*hoff_f); + p_card2->set_material(*_m_card2); - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(0, 0, 0); - - if (((n.norm() <= 1.5+filter_radius) && n(0) <= 0.) || // circle - (std::fabs(n(0)-_height*1.5) < filter_radius && // right edge - std::fabs(n(1)) <= 1.+filter_radius)) { // dirichlet constraint - - // - // set value at the constrained points to a small positive number - // material here - // - if (dof_id >= _level_set_sys->solution->first_local_index() && - dof_id < _level_set_sys->solution->last_local_index()) - _level_set_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - // - // on the boundary, set everything to be zero, so that there - // is always a boundary there that the optimizer can move - // - if (std::fabs(n(0)+_height*0.5) < tol || // left boundary - std::fabs(n(1)-_height*0.5) < tol || // top boundary - std::fabs(n(1)+_height*0.5) < tol || // bottom boundary - std::fabs(n(0)-_height*1.5) < tol) { // right boundary - - if (dof_id >= _level_set_sys->solution->first_local_index() && - dof_id < _level_set_sys->solution->last_local_index()) - _level_set_sys->solution->set(dof_id, -1.); - val = -1.; - } - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } + _discipline->set_property_for_subdomain(0, *p_card1); + _discipline->set_property_for_subdomain(1, *p_card1); - _level_set_sys->solution->close(); - } + // inactive + _discipline->set_property_for_subdomain(3, *p_card2); - + // negative level set + _discipline->set_property_for_subdomain(6, *p_card2); + _discipline->set_property_for_subdomain(7, *p_card2); + + _indicator_discipline->set_property_for_subdomain(0, *p_card1); + _indicator_discipline->set_property_for_subdomain(1, *p_card1); + _indicator_discipline->set_property_for_subdomain(3, *p_card2); + _indicator_discipline->set_property_for_subdomain(4, *p_card2); + } + // // \subsection ex_5_design_variable_init Design Variables // @@ -1604,6 +511,8 @@ public MAST::FunctionEvaluation { } } + + class ElemFlag: public libMesh::MeshRefinement::ElementFlagging { public: ElemFlag(libMesh::MeshBase& mesh, MAST::FieldFunction& phi, unsigned int max_h): @@ -1642,7 +551,87 @@ public MAST::FunctionEvaluation { MAST::FieldFunction& _phi; unsigned int _max_h; }; + + + void mark_shifted_boundary(unsigned int b_id) { + + // remove the previous information for boundary id + _mesh->boundary_info->remove_id(b_id); + + MAST::LevelSetIntersection intersection; + + libMesh::MeshBase::element_iterator + it = _mesh->active_local_elements_begin(), + end = _mesh->active_local_elements_end(); + + std::set sides; + std::vector bids; + + std::map neighbor_side_pairs; + neighbor_side_pairs[0] = 2; + neighbor_side_pairs[1] = 3; + neighbor_side_pairs[2] = 0; + neighbor_side_pairs[3] = 1; + + for ( ; it != end; it++) { + + libMesh::Elem* elem = *it; + + // begin by setting all subdomain id to 0. + elem->subdomain_id() = 0; + + intersection.init(*_level_set_function, *elem, 0., + _mesh->max_elem_id(), + _mesh->max_node_id()); + if (intersection.if_intersection_through_elem()) { + // set the shifted boundary to one which is + // completely inside the material without any intersection + // on the edge + + sides.clear(); + intersection.get_material_sides_without_intersection(sides); + + // add this side in the boundary info object + std::set::const_iterator + it = sides.begin(), + end = sides.end(); + for (; it != end; it++) { + + bids.clear(); + _mesh->boundary_info->boundary_ids(elem, *it, bids); + + // if this side has been identied as a dirichlet condition + // then we do not include it in the set + bool set_id = true; + //for (unsigned int i=0; ineighbor_ptr(*it); + // the side may be on the boundary, in which case + // no boundary should be set. + if (e) + _mesh->boundary_info->add_side(e, neighbor_side_pairs[*it], b_id); + } + } + + // any element with an intersection will be included in the void + // set since its interaction is included using the sifted boundary + // method. + elem->subdomain_id() = 3; + } + else if (intersection.if_elem_on_negative_phi()) + elem->subdomain_id() = 3; + intersection.clear(); + } + } // // \subsection ex_5_function_evaluation Function Evaluation @@ -1669,6 +658,25 @@ public MAST::FunctionEvaluation { _filter->compute_filtered_values(base_phi, *_level_set_sys->solution); _level_set_function->init(*_level_set_sys_init, *_level_set_sys->solution); _sys->solution->zero(); + + _level_set_vel->init(*_level_set_sys_init, + *_level_set_function, + *_level_set_sys->solution, nullptr); + + /*if (_mesh_refinement->initialized()) { + + _mesh_refinement->clear_mesh(); + _eq_sys->reinit(); + } + + if (_mesh_refinement->process_mesh(*_level_set_function, + true, // strong discontinuity + 0., + 6, // negative_level_set_subdomain_offset + 3, // inactive_subdomain_offset + 8)) // level_set_boundary_id + _eq_sys->reinit();*/ + //********************************************************************* // DO NOT zero out the gradient vector, since GCMMA needs it for the * @@ -1751,7 +759,6 @@ public MAST::FunctionEvaluation { level_set_assembly.set_level_set_velocity_function(*_level_set_vel); nonlinear_elem_ops.set_discipline_and_system(*_discipline, *_sys_init); modal_elem_ops.set_discipline_and_system(*_discipline, *_sys_init); - //nonlinear_assembly.plot_sub_elems(true, false, true); libMesh::MeshRefinement refine(*_mesh); @@ -1762,7 +769,7 @@ public MAST::FunctionEvaluation { bool continue_refining = true; Real - threshold = 0.05; + threshold = _input("refinement_threshold","threshold for element to be refined", 0.1); unsigned int n_refinements = 0, max_refinements = _input("max_refinements","maximum refinements", 3); @@ -1795,10 +802,17 @@ public MAST::FunctionEvaluation { else continue_refining = false; } + /*if (_mesh_refinement->process_mesh(*_level_set_function, + true, // strong discontinuity + 0., + 6, // negative_level_set_subdomain_offset + 3, // inactive_subdomain_offset + 8)) // level_set_boundary_id + _eq_sys->reinit();*/ - MAST::LevelSetVolume volume(level_set_assembly.get_intersection()); - MAST::LevelSetPerimeter perimeter(level_set_assembly.get_intersection()); + MAST::LevelSetVolume volume; + MAST::LevelSetPerimeter perimeter; MAST::StressStrainOutputBase stress; MAST::ComplianceOutput compliance; volume.set_discipline_and_system(*_level_set_discipline, *_level_set_sys_init); @@ -1833,12 +847,11 @@ public MAST::FunctionEvaluation { return; } - nonlinear_assembly.calculate_output(*_sys->solution, stress); - //nonlinear_assembly.calculate_output(*_sys->solution, compliance); - ////////////////////////////////////////////////////////////////////// - // evaluate the objective + // evaluate the functions ////////////////////////////////////////////////////////////////////// + + level_set_assembly.set_evaluate_output_on_negative_phi(false); level_set_assembly.calculate_output(*_level_set_sys->solution, volume); level_set_assembly.set_evaluate_output_on_negative_phi(true); @@ -1846,26 +859,43 @@ public MAST::FunctionEvaluation { level_set_assembly.set_evaluate_output_on_negative_phi(false); Real - max_vm = stress.get_maximum_von_mises_stress(), - vm_agg = stress.output_total(), - vf = _input("volume_fraction", "volume fraction", 0.3), + max_vm = 0., + vm_agg = 0., vol = volume.output_total(), per = perimeter.output_total(), - comp = compliance.output_total(); - - obj = _obj_scaling * (vol + _perimeter_penalty * per); - //_obj_scaling * (vol+ _perimeter_penalty * per) + - //_stress_penalty * (vm_agg);///_stress_lim - 1.); - - fvals[0] = stress.output_total()/_stress_lim - 1.; // g = sigma/sigma0-1 <= 0 - //fvals[0] = stress.output_total()/_length/_height; // g <= 0 for the smooth ramp function - //fvals[0] = vol/_length/_height - vf; // vol/vol0 - a <= - libMesh::out << "volume: " << vol << " perim: " << per << std::endl; - libMesh::out << "max: " << max_vm << " constr: " << vm_agg/_stress_lim - 1. - << std::endl; - libMesh::out << "compliance: " << comp << std::endl; + comp = 0.; - if (_n_eig_vals) { + libMesh::out << "volume: " << vol << " perim: " << per << std::endl; + + // evaluate the output based on specified problem type + if (_problem == "compliance_volume") { + + Real + vf = _input("volume_fraction", "volume fraction", 0.3); + + // if the shifted boundary is implementing a traction-free condition + // compliance does not need contribution from shifted boundary load + nonlinear_assembly.calculate_output(*_sys->solution, compliance); + comp = compliance.output_total(); + obj = _obj_scaling * (comp + _perimeter_penalty * per); + fvals[0] = vol/_volume - vf; // vol/vol0 - a <= + libMesh::out << "compliance: " << comp << std::endl; + } + else if (_problem == "volume_stress") { + + // set the elasticity penalty for stress evaluation + nonlinear_assembly.calculate_output(*_sys->solution, stress); + max_vm = stress.get_maximum_von_mises_stress(); + vm_agg = stress.output_total(); + obj = _obj_scaling * (vol + _perimeter_penalty * per); + fvals[0] = stress.output_total()/_stress_lim - 1.; // g = sigma/sigma0-1 <= 0 + //fvals[0] = stress.output_total()/_length/_height; // g <= 0 for the smooth ramp function + libMesh::out + << " max: " << max_vm + << " constr: " << vm_agg + << std::endl; + } + else if (_problem == "volume_eigenvalue" && _n_eig_vals) { ////////////////////////////////////////////////////////////////////// // evaluate the eigenvalue constraint @@ -1891,24 +921,39 @@ public MAST::FunctionEvaluation { for (unsigned int i=0; i<_n_eig_vals; i++) fvals[i+1] = -eig[i]/_ref_eig_val + 1.; } - + else + libmesh_error(); + ////////////////////////////////////////////////////////////////////// // evaluate the objective sensitivities, if requested ////////////////////////////////////////////////////////////////////// if (eval_obj_grad) { - std::vector - grad1(obj_grad.size(), 0.); - - _evaluate_volume_sensitivity(&volume, &perimeter, level_set_assembly, obj_grad); - - /*_evaluate_compliance_sensitivity(compliance, - nonlinear_elem_ops, - nonlinear_assembly, - grad1); - - for (unsigned int i=0; i + grad1(obj_grad.size(), 0.); + + _evaluate_volume_sensitivity(nullptr, &perimeter, level_set_assembly, obj_grad); + + _evaluate_compliance_sensitivity(compliance, + nonlinear_elem_ops, + nonlinear_assembly, + grad1); + + for (unsigned int i=0; iclose(); _filter->compute_filtered_values(*dphi_base, *dphi_filtered); - _level_set_vel->init(*_level_set_sys_init, *_level_set_sys->solution, *dphi_filtered); + _level_set_vel->init(*_level_set_sys_init, + *_level_set_function, + *_level_set_sys->solution, + dphi_filtered.get()); // if the volume output was specified then compute the sensitivity // and add to the grad vector @@ -1984,8 +1042,7 @@ public MAST::FunctionEvaluation { *_dv_params[i].second, *volume); - grad[i] = _obj_scaling * volume->output_sensitivity_total(*_dv_params[i].second); - //grad[i] = volume->output_sensitivity_total(*_dv_params[i].second)/_length/_height; + grad[i] = volume->output_sensitivity_total(*_dv_params[i].second); } // if the perimeter output was specified then compute the sensitivity @@ -1998,7 +1055,7 @@ public MAST::FunctionEvaluation { *perimeter); assembly.set_evaluate_output_on_negative_phi(false); - grad[i] += _obj_scaling * _perimeter_penalty * + grad[i] += _perimeter_penalty * perimeter->output_sensitivity_total(*_dv_params[i].second); } } @@ -2015,7 +1072,7 @@ public MAST::FunctionEvaluation { _evaluate_stress_sensitivity (MAST::StressStrainOutputBase& stress, MAST::AssemblyElemOperations& nonlinear_elem_ops, - MAST::LevelSetNonlinearImplicitAssembly& nonlinear_assembly, + MAST::NonlinearImplicitAssembly& nonlinear_assembly, MAST::StructuralModalEigenproblemAssemblyElemOperations& eigen_elem_ops, MAST::LevelSetEigenproblemAssembly& eigen_assembly, std::vector& grads) { @@ -2051,7 +1108,10 @@ public MAST::FunctionEvaluation { // // initialize the level set perturbation function to create a velocity // field - _level_set_vel->init(*_level_set_sys_init, *_level_set_sys->solution, *dphi_filtered); + _level_set_vel->init(*_level_set_sys_init, + *_level_set_function, + *_level_set_sys->solution, + dphi_filtered.get()); ////////////////////////////////////////////////////////////////////// // stress sensitivity @@ -2087,11 +1147,14 @@ public MAST::FunctionEvaluation { _evaluate_compliance_sensitivity (MAST::ComplianceOutput& compliance, MAST::AssemblyElemOperations& nonlinear_elem_ops, - MAST::LevelSetNonlinearImplicitAssembly& nonlinear_assembly, + MAST::NonlinearImplicitAssembly& nonlinear_assembly, std::vector& grads) { // Adjoint solution for compliance = - X - + // if the shifted boundary is implementing a traction-free condition + // compliance does not need contribution from shifted boundary load + _sys->adjoint_solve(nonlinear_elem_ops, compliance, nonlinear_assembly, false); + std::unique_ptr> dphi_base(_level_set_sys->solution->zero_clone().release()), dphi_filtered(_level_set_sys->solution->zero_clone().release()); @@ -2119,14 +1182,17 @@ public MAST::FunctionEvaluation { // // initialize the level set perturbation function to create a velocity // field - _level_set_vel->init(*_level_set_sys_init, *_level_set_sys->solution, *dphi_filtered); + _level_set_vel->init(*_level_set_sys_init, + *_level_set_function, + *_level_set_sys->solution, + dphi_filtered.get()); ////////////////////////////////////////////////////////////////////// // compliance sensitivity ////////////////////////////////////////////////////////////////////// - grads[i] = -1. * + grads[i] = 1. * nonlinear_assembly.calculate_output_adjoint_sensitivity(*_sys->solution, - *_sys->solution, + _sys->get_adjoint_solution(), *_dv_params[i].second, nonlinear_elem_ops, compliance); @@ -2134,6 +1200,9 @@ public MAST::FunctionEvaluation { nonlinear_assembly.clear_elem_parameter_dependence_object(); } + + + void set_n_vars(const unsigned int n_vars) {_n_vars = n_vars;} // // \subsection ex_5_design_output Output of Design Iterate @@ -2253,13 +1322,13 @@ public MAST::FunctionEvaluation { // \subsection ex_5_constructor Constructor // - TopologyOptimizationLevelSet2D(const libMesh::Parallel::Communicator& comm_in, + TopologyOptimizationLevelSet(const libMesh::Parallel::Communicator& comm_in, MAST::Examples::GetPotWrapper& input): MAST::FunctionEvaluation (comm_in), _initialized (false), _input (input), - _length (0.), - _height (0.), + _problem (), + _volume (0.), _obj_scaling (0.), _stress_penalty (0.), _perimeter_penalty (0.), @@ -2270,6 +1339,7 @@ public MAST::FunctionEvaluation { _n_eig_vals (0), _mesh (nullptr), _level_set_mesh (nullptr), + _mesh_refinement (nullptr), _eq_sys (nullptr), _level_set_eq_sys (nullptr), _sys (nullptr), @@ -2280,36 +1350,56 @@ public MAST::FunctionEvaluation { _level_set_sys_init_on_str_mesh (nullptr), _level_set_sys_init (nullptr), _indicator_sys_init (nullptr), + _nsp (nullptr), _discipline (nullptr), _indicator_discipline (nullptr), _level_set_discipline (nullptr), _filter (nullptr), - _m_card (nullptr), - _p_card (nullptr), + _m_card1 (nullptr), + _m_card2 (nullptr), + _p_card1 (nullptr), + _p_card2 (nullptr), _level_set_function (nullptr), _level_set_vel (nullptr), - _output (nullptr) { + _output (nullptr), + _shifted_boundary_load (nullptr) { libmesh_assert(!_initialized); // // call the initialization routines for each component // + + std::string + s = _input("mesh", "type of mesh to be analyzed {inplane, bracket, truss, eyebar}", + "inplane"); + + _mesh = new libMesh::SerialMesh(this->comm()); + _level_set_mesh = new libMesh::SerialMesh(this->comm()); + + _init_fetype(); - _init_mesh(); + T::init_analysis_mesh(*this, *_mesh); + T::init_level_set_mesh(*this, *_level_set_mesh); _init_system_and_discipline(); - _init_dirichlet_conditions(); + T::init_analysis_dirichlet_conditions(*this); + T::init_indicator_dirichlet_conditions(*this); _init_eq_sys(); _init_material(); - _init_loads(); + T::init_structural_loads(*this); + T::init_indicator_loads(*this); _init_section_property(); _initialized = true; + _nsp = new MAST::StructuralNearNullVectorSpace; + _sys->nonlinear_solver->nearnullspace_object = _nsp; + // // ask structure to use Mindlin bending operator // - dynamic_cast(*_p_card).set_bending_model(MAST::MINDLIN); - + dynamic_cast(*_p_card1).set_bending_model(MAST::MINDLIN); + dynamic_cast(*_p_card2).set_bending_model(MAST::MINDLIN); + ///////////////////////////////////////////////// // now initialize the design data. ///////////////////////////////////////////////// @@ -2317,15 +1407,25 @@ public MAST::FunctionEvaluation { // // first, initialize the level set functions over the domain // - this->initialize_solution(); + T::initialize_level_set_solution(*this); // // next, define a new parameter to define design variable for nodal level-set // function value // - this->_init_phi_dvs(); + T::init_level_set_dvs(*this); + + Real + filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); + _filter = new MAST::FilterBase(*_level_set_sys, filter_radius, _dv_dof_ids); + libMesh::NumericVector& vec = _level_set_sys->add_vector("base_values"); + vec = *_level_set_sys->solution; + vec.close(); + - _obj_scaling = 1./_length/_height; + _problem = _input("problem_type", "{compliance_volume, volume_stress}", "compliance_volume"); + _volume = T::reference_volume(*this); + _obj_scaling = 1./_volume; _stress_penalty = _input("stress_penalty", "penalty value for stress_constraint", 0.); _perimeter_penalty = _input("perimeter_penalty", "penalty value for perimeter in the objective function", 0.); _stress_lim = _input("vm_stress_limit", "limit von-mises stress value", 2.e8); @@ -2335,6 +1435,12 @@ public MAST::FunctionEvaluation { _level_set_function = new PhiMeshFunction; _output = new libMesh::ExodusII_IO(*_mesh); + MAST::BoundaryConditionBase + *bc = new MAST::BoundaryConditionBase(MAST::BOUNDARY_VELOCITY); + bc->add(*_level_set_vel); + _discipline->add_side_load(8, *bc); + _boundary_conditions.insert(bc); + _n_eig_vals = _input("n_eig", "number of eigenvalues to constrain", 0); if (_n_eig_vals) { // @@ -2359,7 +1465,7 @@ public MAST::FunctionEvaluation { // // \subsection ex_5_destructor Destructor // - ~TopologyOptimizationLevelSet2D() { + ~TopologyOptimizationLevelSet() { { std::set::iterator @@ -2388,10 +1494,15 @@ public MAST::FunctionEvaluation { if (!_initialized) return; - delete _m_card; - delete _p_card; - + delete _nsp; + + delete _m_card1; + delete _m_card2; + delete _p_card1; + delete _p_card2; + delete _eq_sys; + delete _mesh_refinement; delete _mesh; delete _discipline; @@ -2421,7 +1532,7 @@ public MAST::FunctionEvaluation { // \subsection ex_5_wrappers_snopt Wrappers for SNOPT // -TopologyOptimizationLevelSet2D* _my_func_eval = nullptr; +MAST::FunctionEvaluation* _my_func_eval = nullptr; #if MAST_ENABLE_SNOPT == 1 @@ -2585,12 +1696,40 @@ int main(int argc, char* argv[]) { MAST::Examples::GetPotWrapper input(argc, argv, "input"); - TopologyOptimizationLevelSet2D top_opt(init.comm(), input); - _my_func_eval = &top_opt; + + std::unique_ptr + top_opt; + + std::string + mesh = input("mesh", "inplane2d, bracket2d, truss2d, eyebar2d", "inplane2d"); + + if (mesh == "inplane2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else if (mesh == "bracket2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else if (mesh == "eyebar2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else if (mesh == "truss2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else + libmesh_error(); + + _my_func_eval = top_opt.get(); + - //MAST::NLOptOptimizationInterface optimizer(NLOPT_LD_SLSQP); - std::unique_ptr - optimizer; + std::unique_ptr optimizer; std::string s = input("optimizer", "optimizer to use in the example", "gcmma"); @@ -2627,14 +1766,18 @@ int main(int argc, char* argv[]) { if (optimizer.get()) { - optimizer->attach_function_evaluation_object(top_opt); + optimizer->attach_function_evaluation_object(*top_opt); - //std::vector xx1(top_opt.n_vars()), xx2(top_opt.n_vars()); - //top_opt.init_dvar(xx1, xx2, xx2); - //top_opt.initialize_dv_from_output_file("output1.txt", 24, xx1); - //top_opt.verify_gradients(xx1); - optimizer->optimize(); - //top_opt.parametric_line_study("output1.txt", 0, 450, 500); + bool + verify_grads = input("verify_gradients", "If true, the gradients of objective and constraints will be verified without optimization", false); + if (verify_grads) { + + std::vector xx1(top_opt->n_vars()), xx2(top_opt->n_vars()); + top_opt->init_dvar(xx1, xx2, xx2); + top_opt->verify_gradients(xx1); + } + else + optimizer->optimize(); } // END_TRANSLATE diff --git a/examples/structural/example_6/example_6.cpp b/examples/structural/example_6/example_6.cpp index 656f9f38..1cee8092 100644 --- a/examples/structural/example_6/example_6.cpp +++ b/examples/structural/example_6/example_6.cpp @@ -32,6 +32,7 @@ #include "elasticity/level_set_stress_assembly.h" #include "elasticity/compliance_output.h" #include "elasticity/structural_system_initialization.h" +#include "elasticity/structural_near_null_vector_space.h" #include "base/parameter.h" #include "base/constant_field_function.h" #include "base/mesh_field_function.h" @@ -46,10 +47,13 @@ #include "optimization/gcmma_optimization_interface.h" #include "optimization/npsol_optimization_interface.h" #include "optimization/function_evaluation.h" +#include "examples/structural/base/bracket_2d_model.h" +#include "examples/structural/base/inplane_2d_model.h" +#include "examples/structural/base/truss_2d_model.h" +#include "examples/structural/base/eyebar_2d_model.h" // libMesh includes -#include "libmesh/parallel.h" #include "libmesh/fe_type.h" #include "libmesh/serial_mesh.h" #include "libmesh/equation_systems.h" @@ -60,6 +64,7 @@ #include "libmesh/petsc_nonlinear_solver.h" #include "libmesh/mesh_refinement.h" #include "libmesh/error_vector.h" +#include "libmesh/parallel.h" void @@ -81,7 +86,7 @@ _optim_con(int* mode, int* nstate); // -// BEGIN_TRANSLATE 2D SIMP topology optimization +// BEGIN_TRANSLATE SIMP topology optimization // // \tableofcontents // @@ -153,17 +158,17 @@ public MAST::AssemblyBase::ElemParameterDependence { }; - -class TopologyOptimizationSIMP2D: +template +class TopologyOptimizationSIMP: public MAST::FunctionEvaluation { -protected: +public: bool _initialized; MAST::Examples::GetPotWrapper& _input; - Real _length; - Real _height; + std::string _problem; + Real _volume; Real _obj_scaling; Real _stress_penalty; Real _perimeter_penalty; @@ -183,7 +188,9 @@ public MAST::FunctionEvaluation { MAST::StructuralSystemInitialization* _sys_init; MAST::PhysicsDisciplineBase* _discipline; - + + MAST::StructuralNearNullVectorSpace* _nsp; + MAST::FilterBase* _filter; MAST::MaterialPropertyCardBase* _m_card; @@ -201,266 +208,13 @@ public MAST::FunctionEvaluation { std::set _field_functions; std::set _boundary_conditions; std::set _dv_dof_ids; - - std::vector> _dv_params; + std::set _dirichlet_bc_ids; -public: - - // \section ex_6_init_mesh Mesh Generation - // This creates the mesh for the specified problem type. - // - void _init_mesh() { - - // The mesh is created using classes written in MAST. The particular - // mesh to be used can be selected using the input parameter - // ` mesh=val `, where `val` can be one of the following: - // - `inplane` inplane structure with load on top and left and right boundaries constrained - // - `bracket` L-bracket - // - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, bracket}", "inplane"); - - if (s == "inplane" || s == "truss") - _init_mesh_inplane(); - else if (s == "bracket") - _init_mesh_bracket(); - else if (s == "eye_bar") - _init_mesh_eye_bar(); - else - libmesh_error(); - } - - // - // \subsection ex_6_inplane_mesh Inplane problem - // - void _init_mesh_inplane() { - - _mesh = new libMesh::SerialMesh(this->comm()); - - // - // identify the element type from the input file or from the order - // of the element - // - unsigned int - nx_divs = _input("nx_divs", "number of elements along x-axis", 20), - ny_divs = _input("ny_divs", "number of elements along y-axis", 20); - - _length = _input("length", "length of domain along x-axis", 0.3), - _height = _input("height", "length of domain along y-axis", 0.3); - - std::string - t = _input("elem_type", "type of geometric element in the mesh", "quad4"); - - libMesh::ElemType - e_type = libMesh::Utility::string_to_enum(t); - - // - // if high order FE is used, libMesh requires atleast a second order - // geometric element. - // - if (_fetype.order > 1 && e_type == libMesh::QUAD4) - e_type = libMesh::QUAD9; - else if (_fetype.order > 1 && e_type == libMesh::TRI3) - e_type = libMesh::TRI6; - - // - // initialize the mesh with one element - // - libMesh::MeshTools::Generation::build_square(*_mesh, - nx_divs, ny_divs, - 0, _length, - 0, _height, - e_type); - } - - // - // \subsection ex_6_bracket_mesh Bracket - // - void _init_mesh_bracket() { - - { - unsigned int - nx_divs = _input("nx_divs", "number of elements along x-axis", 20), - ny_divs = _input("ny_divs", "number of elements along y-axis", 20); - - if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error(); - } - - _init_mesh_inplane(); - _delete_elems_from_bracket_mesh(*_mesh); - } - - - void _delete_elems_from_bracket_mesh(libMesh::MeshBase &mesh) { - - Real - tol = 1.e-12, - x = -1., - y = -1., - length = _input("length", "length of domain along x-axis", 0.3), - width = _input( "height", "length of domain along y-axis", 0.3), - l_frac = 0.4, - w_frac = 0.4, - x_lim = length * l_frac, - y_lim = width * (1.-w_frac); - - // - // now, remove elements that are outside of the L-bracket domain - // - libMesh::MeshBase::element_iterator - e_it = mesh.elements_begin(), - e_end = mesh.elements_end(); - - for ( ; e_it!=e_end; e_it++) { - - libMesh::Elem* elem = *e_it; - x = length; - y = 0.; - for (unsigned int i=0; in_nodes(); i++) { - const libMesh::Node& n = elem->node_ref(i); - if (x > n(0)) x = n(0); - if (y < n(1)) y = n(1); - } - - // - // delete element if the lowest x,y locations are outside of the bracket - // domain - // - if (x >= x_lim && y<= y_lim) - mesh.delete_elem(elem); - } - - mesh.prepare_for_use(); - - // - // add the two additional boundaries to the boundary info so that - // we can apply loads on them - // - bool - facing_right = false, - facing_down = false; - - e_it = mesh.elements_begin(); - e_end = mesh.elements_end(); - - for ( ; e_it != e_end; e_it++) { - - libMesh::Elem* elem = *e_it; - - if (!elem->on_boundary()) continue; - - for (unsigned int i=0; in_sides(); i++) { - - if (elem->neighbor_ptr(i)) continue; - - std::unique_ptr s(elem->side_ptr(i).release()); - - const libMesh::Point p = s->centroid(); - - facing_right = true; - facing_down = true; - for (unsigned int j=0; jn_nodes(); j++) { - const libMesh::Node& n = s->node_ref(j); - - if (n(0) < x_lim || n(1) > y_lim) { - facing_right = false; - facing_down = false; - } - else if (std::fabs(n(0) - p(0)) > tol) - facing_right = false; - else if (std::fabs(n(1) - p(1)) > tol) - facing_down = false; - } - - if (facing_right) mesh.boundary_info->add_side(elem, i, 4); - if (facing_down) mesh.boundary_info->add_side(elem, i, 5); - } - } - - mesh.boundary_info->sideset_name(4) = "facing_right"; - mesh.boundary_info->sideset_name(5) = "facing_down"; - } + std::vector> _dv_params; // - // \subsection ex_6_eyebar_mesh Eyebar - // - void _init_mesh_eye_bar() { - - _mesh = new libMesh::SerialMesh(this->comm()); - - // - // identify the element type from the input file or from the order - // of the element - // - unsigned int - n_radial_divs = _input("n_radial_divs", "number of elements along radial direction", 20), - n_quarter_divs = _input("n_quarter_divs", "number of elements along height", 20); - - Real - radius = 1.5, - h_ratio = _input("h_ratio", "ratio of radial element size at cylinder and at edge", 2); - _height = 8.; - _length = _height*2; - - std::string - t = _input("elem_type", "type of geometric element in the mesh", "quad4"); - - libMesh::ElemType - e_type = libMesh::Utility::string_to_enum(t); - - // - // if high order FE is used, libMesh requires atleast a second order - // geometric element. - // - if (_fetype.order > 1 && e_type == libMesh::QUAD4) - e_type = libMesh::QUAD9; - else if (_fetype.order > 1 && e_type == libMesh::TRI3) - e_type = libMesh::TRI6; - - MAST::Examples::CylinderMesh2D cylinder; - cylinder.mesh(radius, _height/2., - n_radial_divs, n_quarter_divs, h_ratio, - *_mesh, e_type, - true, _height, n_quarter_divs*2); - - // - // add the boundary ids for Dirichlet conditions - // - libMesh::MeshBase::const_element_iterator - e_it = _mesh->elements_begin(), - e_end = _mesh->elements_end(); - - Real - tol = radius * 1.e-8; - - for (; e_it != e_end; e_it++) { - - libMesh::Elem* elem = *e_it; - - std::unique_ptr edge(elem->side_ptr(1)); - libMesh::Point p = edge->centroid(); - - if (std::fabs(p(0)-_height*1.5) < tol && - std::fabs(p(1)) <= 1.) // on the right edge - _mesh->boundary_info->add_side(elem, 1, 0); - - // check for the circumference of the circle where load will be - // applied - edge.reset(elem->side_ptr(3).release()); - p = edge->centroid(); - - if ((std::fabs(p.norm()-radius) < 1.e-2) && - p(0) < 0.) // left semi-circle - _mesh->boundary_info->add_side(elem, 3, 5); - } - - _mesh->boundary_info->sideset_name(0) = "dirichlet"; - _mesh->boundary_info->sideset_name(5) = "load"; - } - - // - // \section ex_6_system_discipline System and Discipline + // \section ex_6_system_discipline System and Discipline // void _init_system_and_discipline() { @@ -525,286 +279,14 @@ public MAST::FunctionEvaluation { fe = libMesh::Utility::string_to_enum(family_str); _fetype = libMesh::FEType(o, fe); } - - - - // - // \section ex_6_dirichlet Dirichlet Constraints - // - void _init_dirichlet_conditions() { - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, truss, bracket, eye_bar}", "inplane"); - - if (s == "inplane") - _init_dirichlet_conditions_inplane(); - else if (s == "truss") - _init_dirichlet_conditions_truss(); - else if (s == "bracket") - _init_dirichlet_conditions_bracket(); - else if (s == "eye_bar") - _init_dirichlet_conditions_eye_bar(); - else - libmesh_error(); - } // - // \subsection ex_6_inplane_dirichlet Inplane + // \section ex_6_properties Properties // - void _init_dirichlet_conditions_inplane() { - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(1, _sys_init->vars()); - _discipline->add_dirichlet_bc(1, *dirichlet); - - dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(3, _sys_init->vars()); - _discipline->add_dirichlet_bc(3, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - } - // - // \subsection ex_6_truss_dirichlet Truss // - void _init_dirichlet_conditions_truss() { - - Real - dirichlet_length_fraction = _input("truss_dirichlet_length_fraction", "length fraction of the truss boundary where dirichlet condition is applied", 0.05); - - // identify the boundaries for dirichlet condition - libMesh::MeshBase::const_element_iterator - e_it = _mesh->elements_begin(), - e_end = _mesh->elements_end(); - - for ( ; e_it != e_end; e_it++) { - - const libMesh::Elem* e = *e_it; - - if ((*e->node_ptr(0))(1) < 1.e-8 && - e->centroid()(0) <= _length*dirichlet_length_fraction) - _mesh->boundary_info->add_side(e, 0, 6); - else if ((*e->node_ptr(1))(1) < 1.e-8 && - e->centroid()(0) >= _length*(1.-dirichlet_length_fraction)) - _mesh->boundary_info->add_side(e, 0, 7); - - if ((*e->node_ptr(0))(0) < 1.e-8 && - (*e->node_ptr(0))(1) < 1.e-8 && - e->centroid()(0) <= _length*dirichlet_length_fraction) - _mesh->boundary_info->add_side(e, 0, 8); - } - - _mesh->boundary_info->sideset_name(6) = "left_dirichlet"; - _mesh->boundary_info->sideset_name(7) = "right_dirichlet"; - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - std::vector vars = {1, 2, 3, 4, 5}; - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // left support - dirichlet->init(6, vars); - _discipline->add_dirichlet_bc(6, *dirichlet); - - dirichlet = new MAST::DirichletBoundaryCondition; // right support - dirichlet->init(7, vars); - _discipline->add_dirichlet_bc(7, *dirichlet); - - vars = {0}; - dirichlet = new MAST::DirichletBoundaryCondition; // left support - dirichlet->init(8, vars); - _discipline->add_dirichlet_bc(8, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - } - - - // - // \subsection ex_6_bracket_dirichlet Bracket - // - void _init_dirichlet_conditions_bracket() { - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // bottom boundary - dirichlet->init(0, _sys_init->vars()); - _discipline->add_dirichlet_bc(0, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - } - - - // - // \subsection ex_6_eyebar_dirichlet Eyebar - // - void _init_dirichlet_conditions_eye_bar() { - - /////////////////////////////////////////////////////////////////////// - // initialize Dirichlet conditions for structural system - /////////////////////////////////////////////////////////////////////// - MAST::DirichletBoundaryCondition - *dirichlet = new MAST::DirichletBoundaryCondition; // right boundary - dirichlet->init(0, _sys_init->vars()); - _discipline->add_dirichlet_bc(0, *dirichlet); - - _discipline->init_system_dirichlet_bc(*_sys); - } - - - - // - // \section ex_6_loading Loading - // - // - void _init_loads() { - - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, truss, bracket, eye_bar}", "inplane"); - - if (s == "inplane" || s == "truss") - _init_loads_inplane(); - else if (s == "bracket") - _init_loads_bracket(); - else if (s == "eye_bar") - _init_loads_eye_bar(); - else - libmesh_error(); - } - - - // \subsection ex_6_inplane_loading Inplane - // - class FluxLoad: - public MAST::FieldFunction { - public: - FluxLoad(const std::string& nm, Real p, Real l1, Real fraction): - MAST::FieldFunction(nm), _p(p), _l1(l1), _frac(fraction) { } - virtual ~FluxLoad() {} - virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { - if (fabs(p(0)-_l1*0.5) <= 0.5*_frac*_l1) v = _p; - else v = 0.; - } - virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { - v = 0.; - } - protected: - Real _p, _l1, _frac; - }; - - - void _init_loads_inplane() { - - Real - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.2), - p_val = _input("pressure", "pressure on side of domain", 2.e4); - - FluxLoad - *press_f = new FluxLoad( "pressure", p_val, _length, frac); - - // initialize the load - MAST::BoundaryConditionBase - *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); - - p_load->add(*press_f); - _discipline->add_side_load(2, *p_load); - - _field_functions.insert(press_f); - } - - - // - // \subsection ex_6_bracket_loading Bracket - // - class BracketLoad: - public MAST::FieldFunction { - public: - BracketLoad(const std::string& nm, Real p, Real l1, Real fraction): - MAST::FieldFunction(nm), _p(p), _l1(l1), _frac(fraction) { } - virtual ~BracketLoad() {} - virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { - if (fabs(p(0) >= _l1*(1.-_frac))) v = _p; - else v = 0.; - } - virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { - v = 0.; - } - protected: - Real _p, _l1, _frac; - }; - - - - void _init_loads_bracket() { - - Real - length = _input("length", "length of domain along x-axis", 0.3), - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.125), - p_val = _input("pressure", "pressure on side of domain", 5.e7); - - BracketLoad - *press_f = new BracketLoad( "pressure", p_val, length, frac); - - // - // initialize the load - // - MAST::BoundaryConditionBase - *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); - - p_load->add(*press_f); - _discipline->add_side_load(5, *p_load); - - _field_functions.insert(press_f); - } - - - // - // \subsection ex_6_eyebar_loading Eyebar - // - class EyebarLoad: - public MAST::FieldFunction { - public: - EyebarLoad(): - MAST::FieldFunction("pressure") { } - virtual ~EyebarLoad() {} - virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { - if (p(0) <= 0.) v = (-std::pow(p(1), 2) + std::pow(1.5, 2))*1.e6; - else v = 0.; - } - virtual void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const Real t, Real& v) const { - v = 0.; - } - }; - - - - void _init_loads_eye_bar() { - - EyebarLoad - *press_f = new EyebarLoad(); - - // initialize the load - MAST::BoundaryConditionBase - *p_load = new MAST::BoundaryConditionBase(MAST::SURFACE_PRESSURE); - - p_load->add(*press_f); - _discipline->add_side_load(5, *p_load); - - _field_functions.insert(press_f); - } - - - // - // \section ex_6_properties Properties - // - // - // - // \subsection ex_6_material_properties Material Properties + // \subsection ex_6_material_properties Material Properties // void _init_material() { @@ -922,384 +404,7 @@ public MAST::FunctionEvaluation { _density_sys->solution->close(); } - - void _init_phi_dvs() { - std::string - s = _input("mesh", "type of mesh to be analyzed {inplane, truss, bracket, eye_bar}", "inplane"); - - if (s == "inplane" || s == "truss") - _init_phi_dvs_inplane(); - else if (s == "bracket") - _init_phi_dvs_bracket(); - else if (s == "eye_bar") - _init_phi_dvs_eye_bar(); - else - libmesh_error(); - - Real - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - _filter = new MAST::FilterBase(*_density_sys, filter_radius, _dv_dof_ids); - libMesh::NumericVector& vec = _density_sys->add_vector("base_values"); - vec = *_density_sys->solution; - vec.close(); - } - - // - // \subsection ex_6_inplane_initial_level_set Inplane - // - void _init_phi_dvs_inplane() { - - // - // this assumes that density variable has a constant value per element - // - libmesh_assert_equal_to(_density_fetype.family, libMesh::LAGRANGE); - - Real - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.2), - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - - unsigned int - sys_num = _density_sys->number(), - dof_id = 0; - - Real - val = 0.; - - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_mesh->is_replicated()); - - std::vector local_phi(_density_sys->solution->size()); - _density_sys->solution->localize(local_phi); - - // iterate over all the element values - libMesh::MeshBase::const_node_iterator - it = _mesh->nodes_begin(), - end = _mesh->nodes_end(); - - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_mesh->n_elem()); - _n_vars = 0; - - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(sys_num, 0, 0); - - // only if node is not on the upper edge - if ((n(1)+filter_radius >= _height) && - (n(0)-filter_radius <= _length*.5*(1.+frac)) && - (n(0)+filter_radius >= _length*.5*(1.-frac))) { - - // set value at the material points to a small positive number - if (dof_id >= _density_sys->solution->first_local_index() && - dof_id < _density_sys->solution->last_local_index()) - _density_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } - - _density_sys->solution->close(); - } - - // - // \subsection ex_6_truss_initial_level_set Truss - // - void _init_phi_dvs_truss() { - - // - // this assumes that density variable has a constant value per element - // - libmesh_assert_equal_to(_density_fetype.family, libMesh::LAGRANGE); - - Real - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.2), - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - - unsigned int - sys_num = _density_sys->number(), - dof_id = 0; - - Real - val = 0.; - - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_mesh->is_replicated()); - - std::vector local_phi(_density_sys->solution->size()); - _density_sys->solution->localize(local_phi); - - // iterate over all the element values - libMesh::MeshBase::const_node_iterator - it = _mesh->nodes_begin(), - end = _mesh->nodes_end(); - - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_mesh->n_elem()); - _n_vars = 0; - - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(sys_num, 0, 0); - - // only if node is not on the upper edge - if ((n(1)-filter_radius <= 0.) && - (n(0)-filter_radius <= _length*.5*(1.+frac)) && - (n(0)+filter_radius >= _length*.5*(1.-frac))) { - - // set value at the material points to a small positive number - if (dof_id >= _density_sys->solution->first_local_index() && - dof_id < _density_sys->solution->last_local_index()) - _density_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } - - _density_sys->solution->close(); - } - - // - // \subsection ex_6_bracket_initial_level_set Bracket - // - void _init_phi_dvs_bracket() { - - libmesh_assert(_initialized); - - // - // this assumes that density variable has a constant value per element - // - libmesh_assert_equal_to(_density_fetype.family, libMesh::LAGRANGE); - - Real - tol = 1.e-12, - length = _input("length", "length of domain along x-axis", 0.3), - height = _input("height", "length of domain along y-axis", 0.3), - l_frac = 0.4,//_input("length_fraction", "fraction of length along x-axis that is in the bracket", 0.4), - h_frac = 0.4,//_input( "height_fraction", "fraction of length along y-axis that is in the bracket", 0.4), - x_lim = length * l_frac, - y_lim = height * (1.-h_frac), - frac = _input("load_length_fraction", "fraction of boundary length on which pressure will act", 0.125), - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - - unsigned int - sys_num = _density_sys->number(), - dof_id = 0; - - Real - val = 0.; - - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_mesh->is_replicated()); - - std::vector local_phi(_density_sys->solution->size()); - _density_sys->solution->localize(local_phi); - - // iterate over all the element values - libMesh::MeshBase::const_node_iterator - it = _mesh->nodes_begin(), - end = _mesh->nodes_end(); - - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_mesh->n_elem()); - _n_vars = 0; - - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(sys_num, 0, 0); - - if ((n(1)-filter_radius) <= y_lim && (n(0)+filter_radius) >= length*(1.-frac)) { - - // - // set value at the constrained points to a small positive number - // material here - // - if (dof_id >= _density_sys->solution->first_local_index() && - dof_id < _density_sys->solution->last_local_index()) - _density_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - // - // on the boundary, set everything to be zero, so that there - // is always a boundary there that the optimizer can move - // - if (n(0) < tol || // left boundary - std::fabs(n(0) - length) < tol || // right boundary - std::fabs(n(1) - height) < tol || // top boundary - (n(0) >= x_lim && n(1) <= y_lim)) { - - if (dof_id >= _density_sys->solution->first_local_index() && - dof_id < _density_sys->solution->last_local_index()) - _density_sys->solution->set(dof_id, _rho_min); - val = _rho_min; - } - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } - - _density_sys->solution->close(); - } - - - // - // \subsection ex_6_eyebar_initial_level_set Eyebar - // - void _init_phi_dvs_eye_bar() { - - libmesh_assert(_initialized); - - // - // this assumes that density variable has a constant value per element - // - libmesh_assert_equal_to(_density_fetype.family, libMesh::LAGRANGE); - - Real - tol = 1.e-6, - filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); - - unsigned int - sys_num = _density_sys->number(), - dof_id = 0; - - Real - val = 0.; - - // - // all ranks will have DVs defined for all variables. So, we should be - // operating on a replicated mesh - // - libmesh_assert(_mesh->is_replicated()); - - std::vector local_phi(_density_sys->solution->size()); - _density_sys->solution->localize(local_phi); - - // iterate over all the element values - // iterate over all the element values - libMesh::MeshBase::const_node_iterator - it = _mesh->nodes_begin(), - end = _mesh->nodes_end(); - - // - // maximum number of dvs is the number of nodes on the level set function - // mesh. We will evaluate the actual number of dvs - // - _dv_params.reserve(_mesh->n_elem()); - _n_vars = 0; - - for ( ; it!=end; it++) { - - const libMesh::Node& n = **it; - - dof_id = n.dof_number(sys_num, 0, 0); - - - - if (((n.norm() <= 1.5+filter_radius) && n(0) <= 0.) || // circle - (std::fabs(n(0)-_height*1.5) < filter_radius && // right edge - std::fabs(n(1)) <= 1.+filter_radius)) { // dirichlet constraint - - // - // set value at the constrained points to material - // - if (dof_id >= _density_sys->solution->first_local_index() && - dof_id < _density_sys->solution->last_local_index()) - _density_sys->solution->set(dof_id, 1.e0); - } - else { - - std::ostringstream oss; - oss << "dv_" << _n_vars; - val = local_phi[dof_id]; - - // - // on the boundary, set everything to be zero, so that there - // is always a boundary there that the optimizer can move - // - if (std::fabs(n(0)+_height*0.5) < tol || // left boundary - std::fabs(n(1)-_height*0.5) < tol || // top boundary - std::fabs(n(1)+_height*0.5) < tol || // bottom boundary - std::fabs(n(0)-_height*1.5) < tol) { // right boundary - - if (dof_id >= _density_sys->solution->first_local_index() && - dof_id < _density_sys->solution->last_local_index()) - _density_sys->solution->set(dof_id, _rho_min); - val = _rho_min; - } - - _dv_params.push_back(std::pair()); - _dv_params[_n_vars].first = dof_id; - _dv_params[_n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n); - _dv_params[_n_vars].second->set_as_topology_parameter(true); - _dv_dof_ids.insert(dof_id); - - _n_vars++; - } - } - - _density_sys->solution->close(); - } - - // // \subsection ex_6_design_variable_init Design Variables // @@ -1416,54 +521,66 @@ public MAST::FunctionEvaluation { return; } - // evaluate compliance - //nonlinear_assembly.calculate_output(*_sys->solution, compliance); - - // set the elasticity penalty for stress evaluation - _Ef->set_penalty_val(stress_penalty); - nonlinear_assembly.calculate_output(*_sys->solution, stress); - - ////////////////////////////////////////////////////////////////////// - // evaluate the objective - ////////////////////////////////////////////////////////////////////// Real - max_vm = stress.get_maximum_von_mises_stress(), - vm_agg = stress.output_total(), + max_vm = 0., + vm_agg = 0., vol = 0., - comp = compliance.output_total(); + comp = 0.; + // evaluate the volume for used in the problem setup _evaluate_volume(&vol, nullptr); - - //obj = _obj_scaling * comp; - obj = _obj_scaling * vol; - //_obj_scaling * (vol+ _perimeter_penalty * per) + - //_stress_penalty * (vm_agg);///_stress_lim - 1.); - - fvals[0] = stress.output_total()/_stress_lim - 1.; // g = sigma/sigma0-1 <= 0 - //fvals[0] = stress.output_total(); // g = sigma/sigma0-1 <= 0 - //fvals[0] = vol/_length/_height - _vf; // vol/vol0 - a <= libMesh::out << "volume: " << vol << std::endl; - libMesh::out << "max: " << max_vm << " constr: " << vm_agg///_stress_lim - 1. - << std::endl; - libMesh::out << "compliance: " << comp << std::endl; + + // evaluate the output based on specified problem type + if (_problem == "compliance_volume") { + + nonlinear_assembly.calculate_output(*_sys->solution, compliance); + comp = compliance.output_total(); + obj = _obj_scaling * comp; + fvals[0] = vol/_volume - _vf; // vol/vol0 - a <= + libMesh::out << "compliance: " << comp << std::endl; + } + else if (_problem == "volume_stress") { + + // set the elasticity penalty for stress evaluation + _Ef->set_penalty_val(stress_penalty); + nonlinear_assembly.calculate_output(*_sys->solution, stress); + max_vm = stress.get_maximum_von_mises_stress(); + vm_agg = stress.output_total(); + obj = _obj_scaling * vol; + fvals[0] = stress.output_total()/_stress_lim - 1.; // g = sigma/sigma0-1 <= 0 + libMesh::out + << " max: " << max_vm + << " constr: " << vm_agg + << std::endl; + } + else + libmesh_error(); + ////////////////////////////////////////////////////////////////////// // evaluate the objective sensitivities, if requested ////////////////////////////////////////////////////////////////////// if (eval_obj_grad) { - _evaluate_volume(nullptr, &obj_grad); - for (unsigned int i=0; i -// grad1(obj_grad.size(), 0.); -// -// _evaluate_compliance_sensitivity(compliance, -// nonlinear_elem_ops, -// nonlinear_assembly, -// grad1); -// -// for (unsigned int i=0; iset_penalty_val(stress_penalty); stress_assembly.update_stress_strain_data(stress, *_sys->solution); _density_function->clear(); @@ -1750,6 +876,8 @@ public MAST::FunctionEvaluation { nonlinear_assembly.clear_elem_parameter_dependence_object(); } + void set_n_vars(const unsigned int n_vars) {_n_vars = n_vars;} + // // \subsection ex_6_design_output Output of Design Iterate // @@ -1831,7 +959,7 @@ public MAST::FunctionEvaluation { _optimization_interface->set_real_parameter ("initial_rel_step", initial_step); } - MAST::FunctionEvaluation::output(iter, x, obj/_obj_scaling, fval, if_write_to_optim_file); + MAST::FunctionEvaluation::output(iter, x, obj/_obj_scaling, f, if_write_to_optim_file); } #if MAST_ENABLE_SNOPT == 1 @@ -1855,13 +983,13 @@ public MAST::FunctionEvaluation { // \subsection ex_6_constructor Constructor // - TopologyOptimizationSIMP2D(const libMesh::Parallel::Communicator& comm_in, + TopologyOptimizationSIMP(const libMesh::Parallel::Communicator& comm_in, MAST::Examples::GetPotWrapper& input): MAST::FunctionEvaluation (comm_in), _initialized (false), _input (input), - _length (0.), - _height (0.), + _problem (""), + _volume (0.), _obj_scaling (0.), _stress_penalty (0.), _perimeter_penalty (0.), @@ -1875,6 +1003,7 @@ public MAST::FunctionEvaluation { _sys (nullptr), _sys_init (nullptr), _discipline (nullptr), + _nsp (nullptr), _filter (nullptr), _m_card (nullptr), _p_card (nullptr), @@ -1885,19 +1014,24 @@ public MAST::FunctionEvaluation { // // call the initialization routines for each component // + _mesh = new libMesh::SerialMesh(this->comm()); + _init_fetype(); - _init_mesh(); + T::init_analysis_mesh(*this, *_mesh); _init_system_and_discipline(); - _init_dirichlet_conditions(); + T::init_analysis_dirichlet_conditions(*this); _init_eq_sys(); - + + _nsp = new MAST::StructuralNearNullVectorSpace; + _sys->nonlinear_solver->nearnullspace_object = _nsp; + // density function is used by elasticity modulus function. So, we // initialize this here _density_function = new MAST::MeshFieldFunction(*_density_sys, "rho"); _density_sens_function = new MAST::MeshFieldFunction(*_density_sys, "rho"); _init_material(); - _init_loads(); + T::init_structural_loads(*this); _init_section_property(); _initialized = true; @@ -1911,17 +1045,32 @@ public MAST::FunctionEvaluation { ///////////////////////////////////////////////// // - // first, initialize the level set functions over the domain + // initialize density field to a constant value of the specified + // volume fraction // - this->initialize_solution(); - + _vf = _input("volume_fraction", "upper limit for the voluem fraction", 0.5); + + _density_sys->solution->zero(); + _density_sys->solution->add(_vf); + _density_sys->solution->close(); + // // next, define a new parameter to define design variable for nodal level-set // function value // - this->_init_phi_dvs(); + T::init_simp_dvs(*this); - _obj_scaling = 1./_length/_height; + Real + filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); + _filter = new MAST::FilterBase(*_density_sys, filter_radius, _dv_dof_ids); + libMesh::NumericVector& vec = _density_sys->add_vector("base_values"); + vec = *_density_sys->solution; + vec.close(); + + + _problem = _input("problem_type", "{compliance_volume, volume_stress}", "compliance_volume"); + _volume = T::reference_volume(*this); + _obj_scaling = 1./_volume; _stress_penalty = _input("stress_penalty", "penalty value for stress_constraint", 0.); _perimeter_penalty = _input("perimeter_penalty", "penalty value for perimeter in the objective function", 0.); _stress_lim = _input("vm_stress_limit", "limit von-mises stress value", 2.e8); @@ -1944,7 +1093,7 @@ public MAST::FunctionEvaluation { // // \subsection ex_6_destructor Destructor // - ~TopologyOptimizationSIMP2D() { + ~TopologyOptimizationSIMP() { { std::set::iterator @@ -1972,6 +1121,8 @@ public MAST::FunctionEvaluation { if (!_initialized) return; + + delete _nsp; delete _m_card; delete _p_card; @@ -1999,7 +1150,7 @@ public MAST::FunctionEvaluation { // \subsection ex_6_wrappers_snopt Wrappers for SNOPT // -TopologyOptimizationSIMP2D* _my_func_eval = nullptr; +MAST::FunctionEvaluation* _my_func_eval = nullptr; #if MAST_ENABLE_SNOPT == 1 @@ -2163,8 +1314,36 @@ int main(int argc, char* argv[]) { MAST::Examples::GetPotWrapper input(argc, argv, "input"); - TopologyOptimizationSIMP2D top_opt(init.comm(), input); - _my_func_eval = &top_opt; + std::unique_ptr + top_opt; + + std::string + mesh = input("mesh", "inplane2d, bracket2d, truss2d, eyebar2d", "inplane2d"); + + if (mesh == "inplane2d") { + top_opt.reset + (new TopologyOptimizationSIMP + (init.comm(), input)); + } + else if (mesh == "bracket2d") { + top_opt.reset + (new TopologyOptimizationSIMP + (init.comm(), input)); + } + else if (mesh == "eyebar2d") { + top_opt.reset + (new TopologyOptimizationSIMP + (init.comm(), input)); + } + else if (mesh == "truss2d") { + top_opt.reset + (new TopologyOptimizationSIMP + (init.comm(), input)); + } + else + libmesh_error(); + + _my_func_eval = top_opt.get(); //MAST::NLOptOptimizationInterface optimizer(NLOPT_LD_SLSQP); std::unique_ptr @@ -2205,12 +1384,18 @@ int main(int argc, char* argv[]) { if (optimizer.get()) { - optimizer->attach_function_evaluation_object(top_opt); + optimizer->attach_function_evaluation_object(*top_opt); - //std::vector xx1(top_opt.n_vars()), xx2(top_opt.n_vars()); - //top_opt.init_dvar(xx1, xx2, xx2); - //top_opt.verify_gradients(xx1); - optimizer->optimize(); + bool + verify_grads = input("verify_gradients", "If true, the gradients of objective and constraints will be verified without optimization", false); + if (verify_grads) { + + std::vector xx1(top_opt->n_vars()), xx2(top_opt->n_vars()); + top_opt->init_dvar(xx1, xx2, xx2); + top_opt->verify_gradients(xx1); + } + else + optimizer->optimize(); } // END_TRANSLATE diff --git a/examples/structural/example_7/CMakeLists.txt b/examples/structural/example_7/CMakeLists.txt index 1f761cd5..a3fc24fc 100644 --- a/examples/structural/example_7/CMakeLists.txt +++ b/examples/structural/example_7/CMakeLists.txt @@ -1,12 +1,16 @@ -add_executable(structural_example_7 - example_7.cpp) +if(ENABLE_NASTRANIO) -target_link_libraries(structural_example_7 mast) + add_executable(structural_example_7 + example_7.cpp) -install(TARGETS structural_example_7 - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/examples) + target_link_libraries(structural_example_7 mast) -configure_file(${CMAKE_CURRENT_LIST_DIR}/example_7_acoss_mesh.bdf ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) + install(TARGETS structural_example_7 + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/examples) -add_test(NAME structural_example_7_mpi - COMMAND ${MPIEXEC_EXECUTABLE} -np 2 $ -ksp_type "preonly" -pc_type "lu") \ No newline at end of file + configure_file(${CMAKE_CURRENT_LIST_DIR}/example_7_acoss_mesh.bdf ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) + + add_test(NAME structural_example_7_mpi + COMMAND ${MPIEXEC_EXECUTABLE} -np 2 $ -ksp_type "preonly" -pc_type "lu") + +endif() diff --git a/examples/structural/example_8/CMakeLists.txt b/examples/structural/example_8/CMakeLists.txt new file mode 100644 index 00000000..06f86fec --- /dev/null +++ b/examples/structural/example_8/CMakeLists.txt @@ -0,0 +1,16 @@ +# since an optimizer is required for optimization we turn this on only if +# NLOPT was enabled for MAST +if(ENABLE_NLOPT OR ENABLE_SNOPT OR ENABLE_GCMMA) + add_executable(structural_example_8 + ${MAST_ROOT_DIR}/examples/fluid/meshing/cylinder.cpp + example_8.cpp) + + target_include_directories(structural_example_8 PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/src) + target_link_libraries(structural_example_8 mast) + + install(TARGETS structural_example_8 + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/examples) +endif() diff --git a/examples/structural/example_8/example_8.cpp b/examples/structural/example_8/example_8.cpp new file mode 100644 index 00000000..86d0a0c4 --- /dev/null +++ b/examples/structural/example_8/example_8.cpp @@ -0,0 +1,1651 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// C++ includes +#include + +// MAST includes +#include "examples/base/input_wrapper.h" +#include "level_set/level_set_discipline.h" +#include "level_set/level_set_system_initialization.h" +#include "level_set/level_set_nonlinear_implicit_assembly.h" +#include "level_set/level_set_volume_output.h" +#include "level_set/level_set_perimeter_output.h" +#include "level_set/level_set_constrain_dofs.h" +#include "level_set/heaviside_elem_homogenization_function.h" +#include "level_set/filter_base.h" +#include "level_set/level_set_parameter.h" +#include "level_set/sub_elem_mesh_refinement.h" +#include "elasticity/structural_nonlinear_assembly.h" +#include "elasticity/structural_modal_eigenproblem_assembly.h" +#include "elasticity/ks_stress_output.h" +#include "elasticity/smooth_ramp_stress_output.h" +#include "elasticity/level_set_stress_assembly.h" +#include "elasticity/compliance_output.h" +#include "elasticity/structural_system_initialization.h" +#include "elasticity/structural_near_null_vector_space.h" +#include "base/constant_field_function.h" +#include "base/nonlinear_system.h" +#include "base/transient_assembly.h" +#include "base/boundary_condition_base.h" +#include "base/mesh_field_function.h" +#include "boundary_condition/dirichlet_boundary_condition.h" +#include "solver/slepc_eigen_solver.h" +#include "property_cards/isotropic_material_property_card.h" +#include "property_cards/solid_2d_section_element_property_card.h" +#include "optimization/gcmma_optimization_interface.h" +#include "optimization/npsol_optimization_interface.h" +#include "optimization/function_evaluation.h" +#include "examples/structural/base/bracket_2d_model.h" +#include "examples/structural/base/inplane_2d_model.h" +#include "examples/structural/base/truss_2d_model.h" +#include "examples/structural/base/eyebar_2d_model.h" + + +// libMesh includes +#include "libmesh/fe_type.h" +#include "libmesh/serial_mesh.h" +#include "libmesh/equation_systems.h" +#include "libmesh/string_to_enum.h" +#include "libmesh/dof_map.h" +#include "libmesh/exodusII_io.h" +#include "libmesh/petsc_nonlinear_solver.h" +#include "libmesh/mesh_refinement.h" +#include "libmesh/error_vector.h" +#include "libmesh/parallel.h" + + +void +_optim_obj(int* mode, + int* n, + double* x, + double* f, + double* g, + int* nstate); +void +_optim_con(int* mode, + int* ncnln, + int* n, + int* ldJ, + int* needc, + double* x, + double* c, + double* cJac, + int* nstate); + +// +// BEGIN_TRANSLATE Topology optimization using homogenized level-set approach +// +// \tableofcontents +// +// This example computes the optimal topology of a structure subject to +// specified boundary conditions (Dirichlet and Neumann). A level-set function +// is used to implicitly define the geometry inside a mesh using the +// immersed boundary approach. +// +// Level Set Mesh Function +class PhiMeshFunction: +public MAST::FieldFunction { +public: + PhiMeshFunction(): + MAST::FieldFunction("phi"), _phi(nullptr), _dphi(nullptr) { } + virtual ~PhiMeshFunction(){ + if ( _phi) delete _phi; + if (_dphi) delete _dphi; } + + void init(MAST::SystemInitialization& sys, + const libMesh::NumericVector* sol, + const libMesh::NumericVector* dsol = nullptr) { + + if ( sol) { + if (! _phi) _phi = new MAST::MeshFieldFunction(sys, "phi"); + else _phi->clear(); + _phi->init(*sol); + } + + if (dsol) { + if (!_dphi) _dphi = new MAST::MeshFieldFunction(sys, "dphi"); + else _dphi->clear(); + _dphi->init(*dsol); + } + } + + MAST::MeshFieldFunction& get_mesh_function() {return *_phi;} + + virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { + libmesh_assert(_phi); + RealVectorX v1; + (*_phi)(p, t, v1); + v = v1(0); + } + + virtual void derivative(const MAST::FunctionBase& f, + const libMesh::Point& p, const Real t, Real& v) const { + libmesh_assert(_dphi); + RealVectorX v1; + (*_dphi)(p, t, v1); + v = v1(0); + } + +protected: + MAST::MeshFieldFunction *_phi, *_dphi; +}; + + +// Elasticity function with the penalty term +class ElasticityFunction: +public MAST::FieldFunction { +public: + ElasticityFunction(Real E0, Real rho_min, Real penalty, + MAST::HomogenizedDensityFunctionBase& vf): + MAST::FieldFunction("E"), + _E0(E0), + _rho_min(rho_min), + _penalty(penalty), + _vf(vf) { } + virtual ~ElasticityFunction(){} + void set_penalty_val(Real penalty) {_penalty = penalty;} + + virtual bool depends_on(const MAST::FunctionBase& f) const { return true;} + virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const { + + Real vf; + _vf(p, t, vf); + + v = _E0 * (_rho_min + (1.-_rho_min) * pow(vf, _penalty)); + } + + virtual void derivative(const MAST::FunctionBase& f, + const libMesh::Point& p, const Real t, Real& v) const { + + Real vf, dvf; + _vf(p, t, vf); + _vf.derivative(f, p, t, dvf); + + v = _E0 * (1.-_rho_min) * _penalty * pow(vf, _penalty-1.) * dvf; + } + +protected: + Real _E0; // value of the material Young's modulus + Real _rho_min; // lower limit on density + Real _penalty; // penalty for density penalization + MAST::HomogenizedDensityFunctionBase & _vf; +}; + + + +class ElementParameterDependence: +public MAST::AssemblyBase::ElemParameterDependence { +public: + ElementParameterDependence(const MAST::FilterBase& filter): + MAST::AssemblyBase::ElemParameterDependence(true), _filter(filter) {} + virtual ~ElementParameterDependence() {} + virtual bool if_elem_depends_on_parameter(const libMesh::Elem& e, + const MAST::FunctionBase& p) const { + const MAST::LevelSetParameter + &p_ls = dynamic_cast(p); + + return _filter.if_elem_in_domain_of_influence(e, *p_ls.level_set_node()); + } + +private: + const MAST::FilterBase& _filter; +}; + + +template +class TopologyOptimizationLevelSet: +public MAST::FunctionEvaluation { + +public: + + bool _initialized; + MAST::Examples::GetPotWrapper& _input; + + std::string _problem; + Real _volume; + Real _obj_scaling; + Real _stress_penalty; + Real _perimeter_penalty; + Real _stress_lim; + Real _p_val, _vm_rho; + + MAST::HeavisideElemHomogenizedDensityFunction* _vf; + ElasticityFunction* _Ef; + libMesh::UnstructuredMesh* _mesh; + libMesh::UnstructuredMesh* _level_set_mesh; + + MAST::SubElemMeshRefinement* _mesh_refinement; + + libMesh::EquationSystems* _eq_sys; + libMesh::EquationSystems* _level_set_eq_sys; + + MAST::NonlinearSystem* _sys; + MAST::NonlinearSystem* _level_set_sys; + MAST::NonlinearSystem* _level_set_sys_on_str_mesh; + + MAST::StructuralSystemInitialization* _sys_init; + MAST::LevelSetSystemInitialization* _level_set_sys_init_on_str_mesh; + MAST::LevelSetSystemInitialization* _level_set_sys_init; + + MAST::StructuralNearNullVectorSpace* _nsp; + + MAST::PhysicsDisciplineBase* _discipline; + MAST::LevelSetDiscipline* _level_set_discipline; + + MAST::FilterBase* _filter; + + MAST::MaterialPropertyCardBase *_m_card1, *_m_card2; + MAST::ElementPropertyCardBase *_p_card1, *_p_card2; + + PhiMeshFunction* _level_set_function; + libMesh::ExodusII_IO* _output; + + libMesh::FEType _fetype; + libMesh::FEType _level_set_fetype; + + std::vector _params_for_sensitivity; + std::map _parameters; + std::set _field_functions; + std::set _boundary_conditions; + std::set _dv_dof_ids; + std::set _dirichlet_bc_ids; + + std::vector> _dv_params; + + + // + // \section ex_8_system_discipline System and Discipline + // + void _init_system_and_discipline() { + + // + // make sure that the mesh has been initialized + // + libmesh_assert(_mesh); + + // + // create the equation system + // + _eq_sys = new libMesh::EquationSystems(*_mesh); + + // + // create the libmesh system and set the preferences for structural + // eigenvalue problems + // + _sys = &(_eq_sys->add_system("structural")); + _sys->set_eigenproblem_type(libMesh::GHEP); + + _mesh_refinement = new MAST::SubElemMeshRefinement(*_mesh, *_sys); + _sys->attach_constraint_object(*_mesh_refinement); + + // + // initialize the system to the right set of variables + // + _sys_init = new MAST::StructuralSystemInitialization(*_sys, + _sys->name(), + _fetype); + _discipline = new MAST::PhysicsDisciplineBase(*_eq_sys); + + // + // Initialize the system for level set function. + // A level set function is defined on a coarser mesh than the structural + // mesh. + // A level set function is assumed to be a first-order Lagrange finite element + // + _level_set_fetype = libMesh::FEType(libMesh::FIRST, libMesh::LAGRANGE); + _level_set_eq_sys = new libMesh::EquationSystems(*_level_set_mesh); + _level_set_sys = &(_level_set_eq_sys->add_system("level_set")); + _level_set_sys->extra_quadrature_order = 4; + _level_set_sys_init = new MAST::LevelSetSystemInitialization(*_level_set_sys, + _level_set_sys->name(), + _level_set_fetype); + _level_set_discipline = new MAST::LevelSetDiscipline(*_eq_sys); + + // + // A system with level set function is defined on the strucutral mesh + // for the purpose of plotting. + // + _level_set_sys_on_str_mesh = &(_eq_sys->add_system("level_set")); + _level_set_sys_init_on_str_mesh = new MAST::LevelSetSystemInitialization(*_level_set_sys_on_str_mesh, + _level_set_sys->name(), + _level_set_fetype); + } + + + void _init_eq_sys() { + + _eq_sys->init(); + _sys->eigen_solver->set_position_of_spectrum(libMesh::LARGEST_MAGNITUDE); + _sys->set_exchange_A_and_B(true); + + _level_set_eq_sys->init(); + } + + + // + // variables added to the mesh + // + void _init_fetype() { + + // FEType to initialize the system. Get the order and type of element. + std::string + order_str = _input("fe_order", "order of finite element shape basis functions", "first"), + family_str = _input("fe_family", "family of finite element shape functions", "lagrange"); + + libMesh::Order + o = libMesh::Utility::string_to_enum(order_str); + libMesh::FEFamily + fe = libMesh::Utility::string_to_enum(family_str); + _fetype = libMesh::FEType(o, fe); + } + + + // + // \section ex_8_properties Properties + // + // + // + // \subsection ex_8_material_properties Material Properties + // + + void _init_material() { + + Real + Eval = _input("E", "modulus of elasticity", 72.e9), + rho_min = _input("rho_min", "minimum value of volume fraction", 1.e-8), + penalty = _input("rho_penalty", "penalty parameter of volume fraction", 4.), + rhoval = _input("rho", "material density", 2700.), + nu_val = _input("nu", "Poisson's ratio", 0.33), + kappa_val = _input("kappa", "shear correction factor", 5./6.), + kval = _input("k", "thermal conductivity", 1.e-2), + cpval = _input("cp", "thermal capacitance", 864.); + + + MAST::Parameter + *rho = new MAST::Parameter("rho", rhoval), + *nu = new MAST::Parameter("nu", nu_val), + *kappa = new MAST::Parameter("kappa", kappa_val), + *k = new MAST::Parameter("k", kval), + *cp = new MAST::Parameter("cp", cpval); + + MAST::ConstantFieldFunction + *rho_f = new MAST::ConstantFieldFunction( "rho", *rho), + *nu_f = new MAST::ConstantFieldFunction( "nu", *nu), + *kappa_f = new MAST::ConstantFieldFunction("kappa", *kappa), + *k_f = new MAST::ConstantFieldFunction( "k_th", *k), + *cp_f = new MAST::ConstantFieldFunction( "cp", *cp); + + _Ef = new ElasticityFunction(Eval, rho_min, penalty, *_vf); + + _parameters[ rho->name()] = rho; + _parameters[ nu->name()] = nu; + _parameters[kappa->name()] = kappa; + _parameters[ k->name()] = k; + _parameters[ cp->name()] = cp; + _field_functions.insert(_Ef); + _field_functions.insert(rho_f); + _field_functions.insert(nu_f); + _field_functions.insert(kappa_f); + _field_functions.insert(k_f); + _field_functions.insert(cp_f); + + _m_card1 = new MAST::IsotropicMaterialPropertyCard; + _m_card2 = new MAST::IsotropicMaterialPropertyCard; + _m_card1->add(*_Ef); + _m_card1->add(*rho_f); + _m_card1->add(*nu_f); + _m_card1->add(*kappa_f); + _m_card1->add(*k_f); + _m_card1->add(*cp_f); + + // material for void + _m_card2->add(*_Ef); + _m_card2->add(*rho_f); + _m_card2->add(*nu_f); + _m_card2->add(*kappa_f); + _m_card2->add(*k_f); + _m_card2->add(*cp_f); + } + + + // + // \subsection ex_8_section_properties Section Properties + // + + void _init_section_property(){ + + + + Real + th_v = _input("th", "thickness of 2D element", 0.001); + + MAST::Parameter + *th = new MAST::Parameter("th", th_v), + *zero = new MAST::Parameter("zero", 0.); + + MAST::ConstantFieldFunction + *th_f = new MAST::ConstantFieldFunction("h", *th), + *hoff_f = new MAST::ConstantFieldFunction("off", *zero); + + + _parameters[th->name()] = th; + _parameters[zero->name()] = zero; + _field_functions.insert(th_f); + _field_functions.insert(hoff_f); + + MAST::Solid2DSectionElementPropertyCard + *p_card1 = new MAST::Solid2DSectionElementPropertyCard, + *p_card2 = new MAST::Solid2DSectionElementPropertyCard; + + _p_card1 = p_card1; + _p_card2 = p_card2; + + // set nonlinear strain if requested + bool + nonlinear = _input("if_nonlinear", "flag to turn on/off nonlinear strain", false); + if (nonlinear) _p_card1->set_strain(MAST::NONLINEAR_STRAIN); + _p_card2->set_strain(MAST::LINEAR_STRAIN); + + p_card1->add(*th_f); + p_card1->add(*hoff_f); + p_card1->set_material(*_m_card1); + + // property card for void + p_card2->add(*th_f); + p_card2->add(*hoff_f); + p_card2->set_material(*_m_card2); + + _discipline->set_property_for_subdomain(0, *p_card1); + _discipline->set_property_for_subdomain(1, *p_card1); + + // inactive + _discipline->set_property_for_subdomain(3, *p_card2); + + // negative level set + _discipline->set_property_for_subdomain(6, *p_card2); + _discipline->set_property_for_subdomain(7, *p_card2); + } + + // + // \subsection ex_8_design_variable_init Design Variables + // + // initializes the design variable vector, called by the + // optimization interface. + // + void init_dvar(std::vector& x, + std::vector& xmin, + std::vector& xmax) { + + x.resize(_n_vars); + xmin.resize(_n_vars); + xmax.resize(_n_vars); + + std::fill(xmin.begin(), xmin.end(), -1.e1); + std::fill(xmax.begin(), xmax.end(), 1.e1); + + // + // now, check if the user asked to initialize dvs from a previous file + // + std::string + nm = _input("restart_optimization_file", "filename with optimization history for restart", ""); + + if (nm.length()) { + + unsigned int + iter = _input("restart_optimization_iter", "restart iteration number from file", 0); + this->initialize_dv_from_output_file(nm, iter, x); + } + else { + + for (unsigned int i=0; i<_n_vars; i++) + x[i] = (*_dv_params[i].second)(); + } + } + + // + // \section ex_8_analysis Function Evaluation and Sensitivity + // + // + // \subsection ex_8_element_error_metric Element Error Metric + // + void + _compute_element_errors(libMesh::ErrorVector& error) { + + Real + vf = 0.; + + libMesh::MeshBase::const_element_iterator + it = _mesh->active_elements_begin(), + end = _mesh->active_elements_end(); + + for ( ; it != end; it++) { + + const libMesh::Elem* elem = *it; + vf = _vf->get_elem_volume_fraction(*elem); + error[elem->id()] = vf; + } + } + + + + class ElemFlag: public libMesh::MeshRefinement::ElementFlagging { + public: + ElemFlag(libMesh::MeshBase& mesh, + MAST::HomogenizedDensityFunctionBase& vf, + unsigned int max_h): + _mesh(mesh), _vf(vf), _max_h(max_h) {} + virtual ~ElemFlag() {} + virtual void flag_elements () { + + Real + vf = 0.; + + libMesh::MeshBase::element_iterator + it = _mesh.active_elements_begin(), + end = _mesh.active_elements_end(); + + for ( ; it != end; it++) { + + libMesh::Elem* elem = *it; + vf = _vf.get_elem_volume_fraction(*elem); + + if (vf < 0.5 && elem->level() < _max_h) + elem->set_refinement_flag(libMesh::Elem::REFINE); + else if (vf > 0.90) + elem->set_refinement_flag(libMesh::Elem::COARSEN); + } + } + + protected: + libMesh::MeshBase& _mesh; + MAST::HomogenizedDensityFunctionBase& _vf; + unsigned int _max_h; + }; + + + + // + // \subsection ex_8_function_evaluation Function Evaluation + // + void evaluate(const std::vector& dvars, + Real& obj, + bool eval_obj_grad, + std::vector& obj_grad, + std::vector& fvals, + std::vector& eval_grads, + std::vector& grads) { + + libMesh::out << "New Evaluation" << std::endl; + + // copy DVs to level set function + libMesh::NumericVector + &base_phi = _level_set_sys->get_vector("base_values"); + + for (unsigned int i=0; i<_n_vars; i++) + if (_dv_params[i].first >= base_phi.first_local_index() && + _dv_params[i].first < base_phi.last_local_index()) + base_phi.set(_dv_params[i].first, dvars[i]); + base_phi.close(); + _filter->compute_filtered_values(base_phi, *_level_set_sys->solution); + _level_set_function->init(*_level_set_sys_init, _level_set_sys->solution.get()); + _vf->initialize_element_volume_fractions(); + _sys->solution->zero(); + + //********************************************************************* + // DO NOT zero out the gradient vector, since GCMMA needs it for the * + // subproblem solution * + //********************************************************************* + MAST::NonlinearImplicitAssembly nonlinear_assembly; + MAST::NonlinearImplicitAssembly perimeter_assembly; + MAST::StressAssembly stress_assembly; + MAST::StructuralNonlinearAssemblyElemOperations nonlinear_elem_ops; + + // + // reinitialize the dof constraints before solution of the linear system + // FIXME: we should be able to clear the constraint object from the + // system before it goes out of scope, but libMesh::System does not + // have a clear method. So, we are going to leave it as is, hoping + // that libMesh::System will not attempt to use it (most likely, we + // shoudl be ok). + // + + ///////////////////////////////////////////////////////////////////// + // solve + ///////////////////////////////////////////////////////////////////// + nonlinear_assembly.set_discipline_and_system(*_discipline, *_sys_init); + perimeter_assembly.set_discipline_and_system(*_level_set_discipline, *_level_set_sys_init); + stress_assembly.set_discipline_and_system(*_discipline, *_sys_init); + nonlinear_elem_ops.set_discipline_and_system(*_discipline, *_sys_init); + + + libMesh::MeshRefinement refine(*_mesh); + + libMesh::out << "before refinement" << std::endl; + _mesh->print_info(); + + bool + continue_refining = true; + Real + threshold = _input("refinement_threshold","threshold for element to be refined", 0.1); + unsigned int + n_refinements = 0, + max_refinements = _input("max_refinements","maximum refinements", 3); + + while (n_refinements < max_refinements && continue_refining) { + + // The ErrorVector is a particular StatisticsVector + // for computing error information on a finite element mesh. + libMesh::ErrorVector error(_mesh->max_elem_id(), _mesh); + _compute_element_errors(error); + libMesh::out + << "After refinement: " << n_refinements << std::endl + << "max error: " << error.maximum() + << ", mean error: " << error.mean() << std::endl; + + if (error.maximum() > threshold) { + + ElemFlag flag(*_mesh, *_vf, max_refinements); + refine.max_h_level() = max_refinements; + refine.refine_fraction() = 1.; + refine.coarsen_fraction() = 0.5; + refine.flag_elements_by (flag); + if (refine.refine_and_coarsen_elements()) + _eq_sys->reinit (); + + _mesh->print_info(); + + n_refinements++; + } + else + continue_refining = false; + } + + MAST::LevelSetPerimeter perimeter; + MAST::StressStrainOutputBase stress; + MAST::ComplianceOutput compliance; + perimeter.set_discipline_and_system(*_level_set_discipline, *_level_set_sys_init); + stress.set_discipline_and_system(*_discipline, *_sys_init); + perimeter.set_participating_elements_to_all(); + stress.set_participating_elements_to_all(); + stress.set_aggregation_coefficients(_p_val, 1., _vm_rho, _stress_lim) ; + compliance.set_participating_elements_to_all(); + compliance.set_discipline_and_system(*_discipline, *_sys_init); + + ////////////////////////////////////////////////////////////////////// + // evaluate the stress constraint + ////////////////////////////////////////////////////////////////////// + // tell the thermal jacobian scaling object about the assembly object + + SNESConvergedReason r; + + libMesh::out << "Static Solve" << std::endl; + + Real + penalty = _input("rho_penalty", "SIMP modulus of elasticity penalty", 4.), + stress_penalty = _input("stress_rho_penalty", "SIMP modulus of elasticity penalty for stress evaluation", 0.5); + perimeter.set_heaviside_smoothing_delta(_input("heaviside_smoothing_width", + "width over which approximate Heaviside function is smoothed", + 0.1)); + + // set the elasticity penalty for solution + _Ef->set_penalty_val(penalty); + + _sys->solve(nonlinear_elem_ops, nonlinear_assembly); + r = dynamic_cast&> + (*_sys->nonlinear_solver).get_converged_reason(); + + // if the solver diverged due to linear solve, then there is a problem with + // this geometry and we need to return with a high value set for the + // constraints + if (r == SNES_DIVERGED_LINEAR_SOLVE || + _sys->final_nonlinear_residual() > 1.e-1) { + + obj = 1.e10; + for (unsigned int i=0; i<_n_ineq; i++) + fvals[i] = 1.e10; + return; + } + + ////////////////////////////////////////////////////////////////////// + // evaluate the functions + ////////////////////////////////////////////////////////////////////// + + perimeter_assembly.calculate_output(*_level_set_sys->solution, perimeter); + + Real + max_vm = 0., + vm_agg = 0., + vol = 0., + per = perimeter.output_total(), + comp = 0.; + + _evaluate_volume(&vol, nullptr); + + libMesh::out << "volume: " << vol << " perim: " << per << std::endl; + + // evaluate the output based on specified problem type + if (_problem == "compliance_volume") { + + Real + vf = _input("volume_fraction", "volume fraction", 0.3); + + // if the shifted boundary is implementing a traction-free condition + // compliance does not need contribution from shifted boundary load + nonlinear_assembly.calculate_output(*_sys->solution, compliance); + comp = compliance.output_total(); + obj = _obj_scaling * (comp + _perimeter_penalty * per); + fvals[0] = vol/_volume - vf; // vol/vol0 - a <= + libMesh::out << "compliance: " << comp << std::endl; + } + else if (_problem == "volume_stress") { + + // set the elasticity penalty for stress evaluation + _Ef->set_penalty_val(stress_penalty); + nonlinear_assembly.calculate_output(*_sys->solution, stress); + max_vm = stress.get_maximum_von_mises_stress(); + vm_agg = stress.output_total(); + obj = _obj_scaling * (vol + _perimeter_penalty * per); + fvals[0] = stress.output_total()/_stress_lim - 1.; // g = sigma/sigma0-1 <= 0 + //fvals[0] = stress.output_total()/_length/_height; // g <= 0 for the smooth ramp function + libMesh::out + << " max: " << max_vm + << " constr: " << vm_agg + << std::endl; + } + else + libmesh_error(); + + ////////////////////////////////////////////////////////////////////// + // evaluate the objective sensitivities, if requested + ////////////////////////////////////////////////////////////////////// + if (eval_obj_grad) { + + std::vector + grad1(obj_grad.size(), 0.); + + if (_problem == "compliance_volume") { + + + _evaluate_compliance_sensitivity(compliance, + nonlinear_elem_ops, + nonlinear_assembly, + obj_grad); + + _evaluate_perimeter_sensitivity(perimeter, perimeter_assembly, grad1); + + for (unsigned int i=0; iset_penalty_val(stress_penalty); + stress_assembly.update_stress_strain_data(stress, *_sys->solution); + _vf->clear_element_volume_fractions(); + } + + // + // \subsection ex_8_volume_sensitivity Sensitivity of Material Volume + // + void _evaluate_volume(Real* volume, + std::vector* grad) { + + if (volume) { + + // iterate over all elements, and use the volume fraction to + // compute the total volume + libMesh::MeshBase::const_element_iterator + e_it = _mesh->active_local_elements_begin(), + e_end = _mesh->active_local_elements_end(); + + *volume = 0.; + + for ( ; e_it != e_end; e_it++) { + + const libMesh::Elem* e = *e_it; + + *volume += _vf->get_elem_volume_fraction(*e) * e->volume(); + } + + this->comm().sum(*volume); + } + + if (grad) { + + std::fill(grad->begin(), grad->end(), 0.); + + // + // iterate over each DV, create a sensitivity vector and calculate the + // volume sensitivity explicitly + // + std::unique_ptr> + dphi_base(_level_set_sys->solution->zero_clone().release()), + dphi_filtered(_level_set_sys->solution->zero_clone().release()); + + ElementParameterDependence dep(*_filter); + + for (unsigned int i=0; i<_n_vars; i++) { + + dphi_base->zero(); + dphi_filtered->zero(); + + // + // set the value only if the dof corresponds to a local node + // + if (_dv_params[i].first >= dphi_base->first_local_index() && + _dv_params[i].first < dphi_base->last_local_index()) + dphi_base->set(_dv_params[i].first, 1.); + + dphi_base->close(); + _filter->compute_filtered_values(*dphi_base, *dphi_filtered); + _level_set_function->init(*_level_set_sys_init, nullptr, dphi_filtered.get()); + _vf->clear_element_volume_fraction_sensitivity(); + _vf->initialize_element_volume_fraction_sensitivity(*_dv_params[i].second); + + libMesh::MeshBase::element_iterator + e_it = _mesh->active_local_elements_begin(), + e_end = _mesh->active_local_elements_end(); + + for ( ; e_it != e_end; e_it++) { + + const libMesh::Elem* e = *e_it; + + // do not compute if the element is not in the domain + // of influence of the parameter + if (!dep.if_elem_depends_on_parameter(*e, *_dv_params[i].second)) + continue; + + (*grad)[i] += e->volume() * + _vf->get_elem_volume_fraction_sensitivity(*_dv_params[i].second, *e); + } + } + + this->comm().sum(*grad); + _vf->clear_element_volume_fraction_sensitivity(); + } + } + + + // + // \subsection ex_8_perimeter_sensitivity Sensitivity of approximate + // Perimeter + // + void _evaluate_perimeter_sensitivity(MAST::LevelSetPerimeter& perimeter, + MAST::NonlinearImplicitAssembly& assembly, + std::vector& grad) { + + std::fill(grad.begin(), grad.end(), 0.); + + // + // iterate over each DV, create a sensitivity vector and calculate the + // volume sensitivity explicitly + // + std::unique_ptr> + dphi_base(_level_set_sys->solution->zero_clone().release()), + dphi_filtered(_level_set_sys->solution->zero_clone().release()); + + ElementParameterDependence dep(*_filter); + assembly.attach_elem_parameter_dependence_object(dep); + + for (unsigned int i=0; i<_n_vars; i++) { + + dphi_base->zero(); + dphi_filtered->zero(); + // + // set the value only if the dof corresponds to a local node + // + if (_dv_params[i].first >= dphi_base->first_local_index() && + _dv_params[i].first < dphi_base->last_local_index()) + dphi_base->set(_dv_params[i].first, 1.); + dphi_base->close(); + _filter->compute_filtered_values(*dphi_base, *dphi_filtered); + + // if the perimeter output was specified then compute the sensitivity + // and add to the grad vector + assembly.calculate_output_direct_sensitivity(*_level_set_sys->solution, + dphi_filtered.get(), + *_dv_params[i].second, + perimeter); + + grad[i] += perimeter.output_sensitivity_total(*_dv_params[i].second); + } + + assembly.clear_elem_parameter_dependence_object(); + } + + + // + // \subsection ex_8_stress_sensitivity Sensitivity of Stress and Eigenvalues + // + void + _evaluate_stress_sensitivity + (const Real penalty, + const Real stress_penalty, + MAST::StressStrainOutputBase& stress, + MAST::AssemblyElemOperations& nonlinear_elem_ops, + MAST::NonlinearImplicitAssembly& nonlinear_assembly, + std::vector& grads) { + + _sys->adjoint_solve(nonlinear_elem_ops, stress, nonlinear_assembly, false); + + std::unique_ptr> + dphi_base(_level_set_sys->solution->zero_clone().release()), + dphi_filtered(_level_set_sys->solution->zero_clone().release()); + + ElementParameterDependence dep(*_filter); + nonlinear_assembly.attach_elem_parameter_dependence_object(dep); + + ////////////////////////////////////////////////////////////////// + // indices used by GCMMA follow this rule: + // grad_k = dfi/dxj , where k = j*NFunc + i + ////////////////////////////////////////////////////////////////// + for (unsigned int i=0; i<_n_vars; i++) { + + dphi_base->zero(); + dphi_filtered->zero(); + // + // set the value only if the dof corresponds to a local node + // + if (_dv_params[i].first >= dphi_base->first_local_index() && + _dv_params[i].first < dphi_base->last_local_index()) + dphi_base->set(_dv_params[i].first, 1.); + dphi_base->close(); + _filter->compute_filtered_values(*dphi_base, *dphi_filtered); + _level_set_function->init(*_level_set_sys_init, nullptr, dphi_filtered.get()); + _vf->clear_element_volume_fraction_sensitivity(); + _vf->initialize_element_volume_fraction_sensitivity(*_dv_params[i].second); + + ////////////////////////////////////////////////////////////////////// + // stress sensitivity + ////////////////////////////////////////////////////////////////////// + // set the elasticity penalty for solution, which is needed for + // computation of the residual sensitivity + _Ef->set_penalty_val(penalty); + + grads[1*i+0] = 1./_stress_lim* + nonlinear_assembly.calculate_output_adjoint_sensitivity(*_sys->solution, + _sys->get_adjoint_solution(), + *_dv_params[i].second, + nonlinear_elem_ops, + stress, + false); + + _Ef->set_penalty_val(stress_penalty); + nonlinear_assembly.calculate_output_direct_sensitivity(*_sys->solution, + nullptr, + *_dv_params[i].second, + stress); + grads[1*i+0] += 1./_stress_lim* stress.output_sensitivity_total(*_dv_params[i].second); + + + stress.clear_sensitivity_data(); + _vf->clear_element_volume_fraction_sensitivity(); + } + + nonlinear_assembly.clear_elem_parameter_dependence_object(); + } + + + void + _evaluate_compliance_sensitivity + (MAST::ComplianceOutput& compliance, + MAST::AssemblyElemOperations& nonlinear_elem_ops, + MAST::NonlinearImplicitAssembly& nonlinear_assembly, + std::vector& grads) { + + // Adjoint solution for compliance = - X + // if the shifted boundary is implementing a traction-free condition + // compliance does not need contribution from shifted boundary load + _sys->adjoint_solve(nonlinear_elem_ops, compliance, nonlinear_assembly, false); + + std::unique_ptr> + dphi_base(_level_set_sys->solution->zero_clone().release()), + dphi_filtered(_level_set_sys->solution->zero_clone().release()); + + ElementParameterDependence dep(*_filter); + nonlinear_assembly.attach_elem_parameter_dependence_object(dep); + + ////////////////////////////////////////////////////////////////// + // indices used by GCMMA follow this rule: + // grad_k = dfi/dxj , where k = j*NFunc + i + ////////////////////////////////////////////////////////////////// + for (unsigned int i=0; i<_n_vars; i++) { + + dphi_base->zero(); + dphi_filtered->zero(); + // + // set the value only if the dof corresponds to a local node + // + if (_dv_params[i].first >= dphi_base->first_local_index() && + _dv_params[i].first < dphi_base->last_local_index()) + dphi_base->set(_dv_params[i].first, 1.); + dphi_base->close(); + _filter->compute_filtered_values(*dphi_base, *dphi_filtered); + _level_set_function->init(*_level_set_sys_init, nullptr, dphi_filtered.get()); + _vf->clear_element_volume_fraction_sensitivity(); + _vf->initialize_element_volume_fraction_sensitivity(*_dv_params[i].second); + + ////////////////////////////////////////////////////////////////////// + // compliance sensitivity + ////////////////////////////////////////////////////////////////////// + grads[i] = 1. * + nonlinear_assembly.calculate_output_adjoint_sensitivity(*_sys->solution, + _sys->get_adjoint_solution(), + *_dv_params[i].second, + nonlinear_elem_ops, + compliance); + _vf->clear_element_volume_fraction_sensitivity(); + } + + nonlinear_assembly.clear_elem_parameter_dependence_object(); + } + + + void set_n_vars(const unsigned int n_vars) {_n_vars = n_vars;} + + // + // \subsection ex_8_design_output Output of Design Iterate + // + void output(unsigned int iter, + const std::vector& x, + Real obj, + const std::vector& fval, + bool if_write_to_optim_file) { + + libmesh_assert_equal_to(x.size(), _n_vars); + + Real + sys_time = _sys->time; + + std::string + output_name = _input("output_file_root", "prefix of output file names", "output"), + modes_name = output_name + "modes.exo"; + + std::ostringstream oss; + oss << "output_optim.e-s." << std::setfill('0') << std::setw(5) << iter ; + + // + // copy DVs to level set function + // + libMesh::NumericVector + &base_phi = _level_set_sys->get_vector("base_values"); + + for (unsigned int i=0; i<_n_vars; i++) + if (_dv_params[i].first >= base_phi.first_local_index() && + _dv_params[i].first < base_phi.last_local_index()) + base_phi.set(_dv_params[i].first, x[i]); + base_phi.close(); + _filter->compute_filtered_values(base_phi, *_level_set_sys->solution); + _level_set_function->init(*_level_set_sys_init, _level_set_sys->solution.get()); + _level_set_sys_init_on_str_mesh->initialize_solution(_level_set_function->get_mesh_function()); + + std::vector eval_grads(this->n_ineq(), false); + std::vector f(this->n_ineq(), 0.), grads; + this->evaluate(x, obj, false, grads, f, eval_grads, grads); + + _sys->time = iter; + _sys_init->get_stress_sys().time = iter; + // "1" is the number of time-steps in the file, as opposed to the time-step number. + libMesh::ExodusII_IO(*_mesh).write_timestep(oss.str(), *_eq_sys, 1, (1.*iter)); + + // + // set the value of time back to its original value + // + _sys->time = sys_time; + + // + // increment the parameter values + // + unsigned int + update_freq = _input("update_freq_optim_params", "number of iterations after which the optimization parameters are updated", 50), + factor = iter/update_freq ; + if (factor > 0 && iter%update_freq == 0) { + + Real + p_val = _input("constraint_aggregation_p_val", "value of p in p-norm stress aggregation", 2.0), + vm_rho = _input("constraint_aggregation_rho_val", "value of rho in p-norm stress aggregation", 2.0), + constr_penalty = _input("constraint_penalty", "constraint penalty in GCMMA", 50.), + max_penalty = _input("max_constraint_penalty", "maximum constraint penalty in GCMMA", 1.e7), + initial_step = _input("initial_rel_step", "initial relative step length in GCMMA", 0.5), + min_step = _input("minimum_rel_step", "minimum relative step length in GCMMA", 0.001); + + constr_penalty = std::min(constr_penalty*pow(10, factor), max_penalty); + initial_step = std::max(initial_step-0.01*factor, min_step); + _p_val = std::min(p_val+2*factor, 10.); + _vm_rho = std::min(vm_rho+factor*0.5, 2.); + libMesh::out + << "Updated values: c = " << constr_penalty + << " step = " << initial_step + << " p = " << _p_val + << " rho = " << _vm_rho << std::endl; + + _optimization_interface->set_real_parameter ( "constraint_penalty", constr_penalty); + _optimization_interface->set_real_parameter ("initial_rel_step", initial_step); + } + + MAST::FunctionEvaluation::output(iter, x, obj/_obj_scaling, f, if_write_to_optim_file); + } + +#if MAST_ENABLE_SNOPT == 1 + MAST::FunctionEvaluation::funobj + get_objective_evaluation_function() { + + return _optim_obj; + } + + MAST::FunctionEvaluation::funcon + get_constraint_evaluation_function() { + + return _optim_con; + } +#endif + + + // + // \section ex_8_initialization Initialization + // + // \subsection ex_8_constructor Constructor + // + + TopologyOptimizationLevelSet(const libMesh::Parallel::Communicator& comm_in, + MAST::Examples::GetPotWrapper& input): + MAST::FunctionEvaluation (comm_in), + _initialized (false), + _input (input), + _problem (), + _volume (0.), + _obj_scaling (0.), + _stress_penalty (0.), + _perimeter_penalty (0.), + _stress_lim (0.), + _p_val (0.), + _vm_rho (0.), + _vf (nullptr), + _Ef (nullptr), + _mesh (nullptr), + _level_set_mesh (nullptr), + _mesh_refinement (nullptr), + _eq_sys (nullptr), + _level_set_eq_sys (nullptr), + _sys (nullptr), + _level_set_sys (nullptr), + _level_set_sys_on_str_mesh (nullptr), + _sys_init (nullptr), + _level_set_sys_init_on_str_mesh (nullptr), + _level_set_sys_init (nullptr), + _nsp (nullptr), + _discipline (nullptr), + _level_set_discipline (nullptr), + _filter (nullptr), + _m_card1 (nullptr), + _m_card2 (nullptr), + _p_card1 (nullptr), + _p_card2 (nullptr), + _level_set_function (nullptr), + _output (nullptr) { + + libmesh_assert(!_initialized); + + // + // call the initialization routines for each component + // + + std::string + s = _input("mesh", "type of mesh to be analyzed {inplane, bracket, truss, eyebar}", + "inplane"); + + _mesh = new libMesh::SerialMesh(this->comm()); + _level_set_mesh = new libMesh::SerialMesh(this->comm()); + _level_set_function = new PhiMeshFunction; + _vf = new MAST::HeavisideElemHomogenizedDensityFunction("volume_fraction"); + _vf->set_smoothing_width(_input("heaviside_smoothing_width", "width over which approximate Heaviside function is smoothed", 0.1)); + + _init_fetype(); + T::init_analysis_mesh(*this, *_mesh); + T::init_level_set_mesh(*this, *_level_set_mesh); + _init_system_and_discipline(); + T::init_analysis_dirichlet_conditions(*this); + _init_eq_sys(); + _init_material(); + T::init_structural_loads(*this); + _init_section_property(); + _initialized = true; + + _nsp = new MAST::StructuralNearNullVectorSpace; + _sys->nonlinear_solver->nearnullspace_object = _nsp; + + // + // ask structure to use Mindlin bending operator + // + dynamic_cast(*_p_card1).set_bending_model(MAST::MINDLIN); + dynamic_cast(*_p_card2).set_bending_model(MAST::MINDLIN); + + ///////////////////////////////////////////////// + // now initialize the design data. + ///////////////////////////////////////////////// + + // + // first, initialize the level set functions over the domain + // + T::initialize_level_set_solution(*this); + + // + // next, define a new parameter to define design variable for nodal level-set + // function value + // + T::init_level_set_dvs(*this); + + Real + filter_radius = _input("filter_radius", "radius of geometric filter for level set field", 0.015); + _filter = new MAST::FilterBase(*_level_set_sys, filter_radius, _dv_dof_ids); + libMesh::NumericVector& vec = _level_set_sys->add_vector("base_values"); + vec = *_level_set_sys->solution; + vec.close(); + _vf->init(*_level_set_sys_init, *_mesh, *_level_set_function, *_filter); + + + _problem = _input("problem_type", "{compliance_volume, volume_stress}", "compliance_volume"); + _volume = T::reference_volume(*this); + _obj_scaling = 1./_volume; + _stress_penalty = _input("stress_penalty", "penalty value for stress_constraint", 0.); + _perimeter_penalty = _input("perimeter_penalty", "penalty value for perimeter in the objective function", 0.); + _stress_lim = _input("vm_stress_limit", "limit von-mises stress value", 2.e8); + _p_val = _input("constraint_aggregation_p_val", "value of p in p-norm stress aggregation", 2.0); + _vm_rho = _input("constraint_aggregation_rho_val", "value of rho in p-norm stress aggregation", 2.0); + _output = new libMesh::ExodusII_IO(*_mesh); + + // + // two inequality constraints: stress and eigenvalue. + // + _n_ineq = 1; + + std::string + output_name = _input("output_file_root", "prefix of output file names", "output"); + output_name += "_optim_history.txt"; + this->set_output_file(output_name); + + } + + // + // \subsection ex_8_destructor Destructor + // + ~TopologyOptimizationLevelSet() { + + { + std::set::iterator + it = _boundary_conditions.begin(), + end = _boundary_conditions.end(); + for ( ; it!=end; it++) + delete *it; + } + + { + std::set::iterator + it = _field_functions.begin(), + end = _field_functions.end(); + for ( ; it!=end; it++) + delete *it; + } + + { + std::map::iterator + it = _parameters.begin(), + end = _parameters.end(); + for ( ; it!=end; it++) + delete it->second; + } + + if (!_initialized) + return; + + delete _nsp; + + delete _m_card1; + delete _m_card2; + delete _p_card1; + delete _p_card2; + + delete _eq_sys; + delete _mesh_refinement; + delete _mesh; + delete _vf; + + delete _discipline; + delete _sys_init; + + delete _level_set_function; + delete _level_set_sys_init; + delete _level_set_discipline; + delete _filter; + delete _level_set_eq_sys; + delete _level_set_mesh; + delete _output; + delete _level_set_sys_init_on_str_mesh; + + for (unsigned int i=0; i<_dv_params.size(); i++) + delete _dv_params[i].second; + } + + +}; + + +// +// \subsection ex_8_wrappers_snopt Wrappers for SNOPT +// + +MAST::FunctionEvaluation* _my_func_eval = nullptr; + +#if MAST_ENABLE_SNOPT == 1 + +unsigned int +it_num = 0; + +void +_optim_obj(int* mode, + int* n, + double* x, + double* f, + double* g, + int* nstate) { + + // + // make sure that the global variable has been setup + // + libmesh_assert(_my_func_eval); + + // + // initialize the local variables + // + Real + obj = 0.; + + unsigned int + n_vars = _my_func_eval->n_vars(), + n_con = _my_func_eval->n_eq()+_my_func_eval->n_ineq(); + + libmesh_assert_equal_to(*n, n_vars); + + std::vector + dvars (*n, 0.), + obj_grad(*n, 0.), + fvals (n_con, 0.), + grads (0); + + std::vector + eval_grads(n_con); + std::fill(eval_grads.begin(), eval_grads.end(), false); + + // + // copy the dvars + // + for (unsigned int i=0; i_evaluate_wrapper(dvars, + obj, + *mode>0, // request the derivatives of obj + obj_grad, + fvals, + eval_grads, + grads); + + // + // now copy them back as necessary + // + *f = obj; + if (*mode > 0) { + + // output data to the file + _my_func_eval->_output_wrapper(it_num, dvars, obj, fvals, true); + it_num++; + + for (unsigned int i=0; i 1.e5) *mode = -1; +} + + + + + + +void +_optim_con(int* mode, + int* ncnln, + int* n, + int* ldJ, + int* needc, + double* x, + double* c, + double* cJac, + int* nstate) { + + // + // make sure that the global variable has been setup + // + libmesh_assert(_my_func_eval); + + // + // initialize the local variables + // + Real + obj = 0.; + + unsigned int + n_vars = _my_func_eval->n_vars(), + n_con = _my_func_eval->n_eq()+_my_func_eval->n_ineq(); + + libmesh_assert_equal_to( *n, n_vars); + libmesh_assert_equal_to(*ncnln, n_con); + + std::vector + dvars (*n, 0.), + obj_grad(*n, 0.), + fvals (n_con, 0.), + grads (n_vars*n_con, 0.); + + std::vector + eval_grads(n_con); + std::fill(eval_grads.begin(), eval_grads.end(), *mode>0); + + // + // copy the dvars + // + for (unsigned int i=0; i_evaluate_wrapper(dvars, + obj, + false, // request the derivatives of obj + obj_grad, + fvals, + eval_grads, + grads); + + // + // now copy them back as necessary + // + // first the constraint functions + // + for (unsigned int i=0; i 0) { + // + // next, the constraint gradients + // + for (unsigned int i=0; i 1.e5) *mode = -1; +} +#endif + +// +// \subsection ex_8_main Main function +// + +int main(int argc, char* argv[]) { + + libMesh::LibMeshInit init(argc, argv); + + MAST::Examples::GetPotWrapper + input(argc, argv, "input"); + + + std::unique_ptr + top_opt; + + std::string + mesh = input("mesh", "inplane2d, bracket2d, truss2d, eyebar2d", "inplane2d"); + + if (mesh == "inplane2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else if (mesh == "bracket2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else if (mesh == "eyebar2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else if (mesh == "truss2d") { + top_opt.reset + (new TopologyOptimizationLevelSet + (init.comm(), input)); + } + else + libmesh_error(); + + _my_func_eval = top_opt.get(); + + + std::unique_ptr optimizer; + + std::string + s = input("optimizer", "optimizer to use in the example", "gcmma"); + + if (s == "gcmma") { + + optimizer.reset(new MAST::GCMMAOptimizationInterface); + + unsigned int + max_inner_iters = input("max_inner_iters", "maximum inner iterations in GCMMA", 15); + + Real + constr_penalty = input("constraint_penalty", "constraint penalty in GCMMA", 50.), + initial_rel_step = input("initial_rel_step", "initial step size in GCMMA", 1.e-2), + asymptote_reduction = input("asymptote_reduction", "reduction of aymptote in GCMMA", 0.7), + asymptote_expansion = input("asymptote_expansion", "expansion of asymptote in GCMMA", 1.2); + + optimizer->set_real_parameter ("constraint_penalty", constr_penalty); + optimizer->set_real_parameter ("initial_rel_step", initial_rel_step); + optimizer->set_real_parameter ("asymptote_reduction", asymptote_reduction); + optimizer->set_real_parameter ("asymptote_expansion", asymptote_expansion); + optimizer->set_integer_parameter( "max_inner_iters", max_inner_iters); + } + else if (s == "snopt") { + + optimizer.reset(new MAST::NPSOLOptimizationInterface); + } + else { + + libMesh::out + << "Unrecognized optimizer specified: " << s << std::endl; + libmesh_error(); + } + + if (optimizer.get()) { + + optimizer->attach_function_evaluation_object(*top_opt); + + bool + verify_grads = input("verify_gradients", "If true, the gradients of objective and constraints will be verified without optimization", false); + if (verify_grads) { + + std::vector xx1(top_opt->n_vars()), xx2(top_opt->n_vars()); + top_opt->init_dvar(xx1, xx2, xx2); + top_opt->verify_gradients(xx1); + } + else + optimizer->optimize(); + } + + // END_TRANSLATE + return 0; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3833d1d9..0346ddba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,7 +37,9 @@ target_link_libraries(mast ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} debug ${libMesh_dbg_LIBRARIES} + debug ${timpi_dbg_LIBRARIES} optimized ${libMesh_opt_LIBRARIES} + optimized ${timpi_opt_LIBRARIES} ${LIBGFORTRAN_LIBRARIES}) # NOTE: Use of PUBLIC keyword above means other CMake target (like an example) diff --git a/src/base/assembly_base.cpp b/src/base/assembly_base.cpp index dea08ed5..b0dbd802 100644 --- a/src/base/assembly_base.cpp +++ b/src/base/assembly_base.cpp @@ -288,6 +288,9 @@ MAST::AssemblyBase::calculate_output(const libMesh::NumericVector& X, const libMesh::Elem* elem = *el; + if (diagonal_elem_subdomain_id.count(elem->subdomain_id())) + continue; + dof_map.dof_indices (elem, dof_indices); // get the solution @@ -366,7 +369,10 @@ calculate_output_derivative(const libMesh::NumericVector& X, for ( ; el != end_el; ++el) { const libMesh::Elem* elem = *el; - + + if (diagonal_elem_subdomain_id.count(elem->subdomain_id())) + continue; + dof_map.dof_indices (elem, dof_indices); // get the solution @@ -457,6 +463,9 @@ calculate_output_direct_sensitivity(const libMesh::NumericVector& X, const libMesh::Elem* elem = *el; + if (diagonal_elem_subdomain_id.count(elem->subdomain_id())) + continue; + // no sensitivity computation assembly is neeed in these cases if (_param_dependence && // if object is specified and elem does not depend on it diff --git a/src/base/assembly_base.h b/src/base/assembly_base.h index 8ebb042f..565cdcec 100644 --- a/src/base/assembly_base.h +++ b/src/base/assembly_base.h @@ -63,7 +63,14 @@ namespace MAST { */ virtual ~AssemblyBase(); + /*! + * subdomain ids for which residuakl and Jacobian contributions will not be computed. Instead, + * a small diagonal value will be added to the Jacobian for the dofs corresponding to this + * element. + */ + std::set diagonal_elem_subdomain_id; + class SolverMonitor { public: SolverMonitor(){} diff --git a/src/base/assembly_elem_operation.cpp b/src/base/assembly_elem_operation.cpp index 4986bd8a..a465de1c 100644 --- a/src/base/assembly_elem_operation.cpp +++ b/src/base/assembly_elem_operation.cpp @@ -21,6 +21,10 @@ #include "base/assembly_elem_operation.h" #include "base/elem_base.h" #include "base/assembly_base.h" +#include "base/physics_discipline_base.h" +#include "base/boundary_condition_base.h" +#include "base/field_function_base.h" +#include "mesh/geom_elem.h" #include "mesh/fe_base.h" @@ -195,3 +199,51 @@ MAST::AssemblyElemOperations::clear_elem() { _physics_elem = nullptr; } + + +std::pair*, unsigned int> +MAST::AssemblyElemOperations:: +get_elem_boundary_velocity_data() { + + libmesh_assert(_physics_elem); + + std::pair*, unsigned int> + val = std::make_pair(nullptr, 0); + + std::map> loads; + _physics_elem->elem().external_side_loads_for_quadrature_elem(_discipline->side_loads(), + loads); + + std::map>::const_iterator + it = loads.begin(), + end = loads.end(); + + for ( ; it != end; it++) { + + std::vector::const_iterator + bc_it = it->second.begin(), + bc_end = it->second.end(); + + for ( ; bc_it != bc_end; bc_it++) { + + // apply all the types of loading + switch ((*bc_it)->type()) { + + case MAST::BOUNDARY_VELOCITY: { + + val.first = &((*bc_it)->get>("phi_vel")); + val.second = it->first; + } + break; + + + default: + // nothing to be done here + break; + } + } + } + + return val; +} + diff --git a/src/base/assembly_elem_operation.h b/src/base/assembly_elem_operation.h index 5e11ca5e..2c24d99e 100644 --- a/src/base/assembly_elem_operation.h +++ b/src/base/assembly_elem_operation.h @@ -37,6 +37,7 @@ namespace MAST { class SystemInitialization; class PhysicsDisciplineBase; class GeomElem; + template class FieldFunction; class AssemblyElemOperations { @@ -167,6 +168,16 @@ namespace MAST { virtual void set_elem_perturbed_acceleration(const RealVectorX& accel); + /*! + * searches through the side load data and populates the data + * with the boundary id and velocity function on the + * boundary. Returns a null pointer for velocity if no boundary is + * specified for this elem. + */ + virtual std::pair*, unsigned int> + get_elem_boundary_velocity_data(); + + protected: MAST::SystemInitialization *_system; diff --git a/src/base/boundary_condition_base.h b/src/base/boundary_condition_base.h index 540423c1..eb4cdb31 100644 --- a/src/base/boundary_condition_base.h +++ b/src/base/boundary_condition_base.h @@ -29,6 +29,8 @@ namespace MAST { enum BoundaryConditionType { SURFACE_PRESSURE, + SURFACE_TRACTION, + SURFACE_TRACTION_SHIFTED_BOUNDARY, POINT_LOAD, POINT_MOMENT, PISTON_THEORY, @@ -44,7 +46,8 @@ namespace MAST { FAR_FIELD, EXHAUST, ISOTHERMAL, - ADIABATIC + ADIABATIC, + BOUNDARY_VELOCITY }; diff --git a/src/base/eigenproblem_assembly_elem_operations.h b/src/base/eigenproblem_assembly_elem_operations.h index 138d50f7..77b5b8f5 100644 --- a/src/base/eigenproblem_assembly_elem_operations.h +++ b/src/base/eigenproblem_assembly_elem_operations.h @@ -59,6 +59,15 @@ namespace MAST { RealMatrixX& mat_A, RealMatrixX& mat_B) = 0; + /*! + * performs the element topology sensitivity calculations over \p elem. + */ + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + bool base_sol, + RealMatrixX& mat_A, + RealMatrixX& mat_B) = 0; + /*! * performs the element topology sensitivity calculations over \p elem. */ diff --git a/src/base/elem_base.cpp b/src/base/elem_base.cpp index cec649f6..3eea64ad 100644 --- a/src/base/elem_base.cpp +++ b/src/base/elem_base.cpp @@ -25,10 +25,8 @@ MAST::ElementBase::ElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem): _system (sys), -_assembly (assembly), _elem (elem), _active_sol_function (nullptr), _time (_system.system().time) { diff --git a/src/base/elem_base.h b/src/base/elem_base.h index 8021915a..9a858db5 100644 --- a/src/base/elem_base.h +++ b/src/base/elem_base.h @@ -80,7 +80,6 @@ namespace MAST { * performed. */ ElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem); @@ -97,13 +96,6 @@ namespace MAST { return _system; } - /*! - * @returns a reference to the libMesh::System object - */ - MAST::AssemblyBase& assembly() { - return _assembly; - } - /*! * @returns a reference to the libMesh::System object @@ -207,11 +199,6 @@ namespace MAST { */ MAST::SystemInitialization& _system; - /*! - * Assembly object - */ - MAST::AssemblyBase& _assembly; - /*! * geometric element for which the computations are performed */ diff --git a/src/base/mast_config.h.in b/src/base/mast_config.h.in index a65a9ab7..3882b118 100644 --- a/src/base/mast_config.h.in +++ b/src/base/mast_config.h.in @@ -26,6 +26,8 @@ #define MAST_ENABLE_SNOPT @MAST_ENABLE_SNOPT@ +#define MAST_ENABLE_NASTRANIO @MAST_ENABLE_NASTRANIO@ + #define MAST_ENABLE_CYTHON @MAST_ENABLE_CYTHON@ #define MAST_ENABLE_MATPLOTLIB @MAST_ENABLE_MATPLOTLIB@ diff --git a/src/base/mesh_field_function.cpp b/src/base/mesh_field_function.cpp index 2e33d0e7..48ae8d19 100644 --- a/src/base/mesh_field_function.cpp +++ b/src/base/mesh_field_function.cpp @@ -167,6 +167,38 @@ MAST::MeshFieldFunction::perturbation(const libMesh::Point& p, } +void +MAST::MeshFieldFunction::perturbation_gradient (const libMesh::Point& p, + const Real t, + RealMatrixX& v) const { + + // if the element has provided a quadrature point solution, + // then use it + if (_use_qp_sol) { + v = _qp_sol; + return; + } + + // make sure that the object was initialized + libmesh_assert(_function); + + unsigned int + n_vars = _sys->n_vars(); + + std::vector v1; + _perturbed_function->gradient(p, t, v1); + + // make sure that the mesh function was able to find the element + // and a solution + libmesh_assert_equal_to(v1.size(), n_vars); + + // now copy this to the output vector + v = RealMatrixX::Zero(n_vars, 3); // assume 3-dimensional by default + for (unsigned int i=0; i& X, dof_map.dof_indices (elem, dof_indices); - MAST::GeomElem geom_elem; - ops.set_elem_data(elem->dim(), *elem, geom_elem); - geom_elem.init(*elem, *_system); - - ops.init(geom_elem); + if (diagonal_elem_subdomain_id.count(elem->subdomain_id())) { - // get the solution - unsigned int ndofs = (unsigned int)dof_indices.size(); - sol.setZero(ndofs); - vec.setZero(ndofs); - mat.setZero(ndofs, ndofs); - - for (unsigned int i=0; iattach_active_solution_function(*_sol_function); - - //_check_element_numerical_jacobian(*physics_elem, sol); - - // perform the element level calculations - ops.elem_calculations(J!=nullptr?true:false, - vec, mat); - -// physics_elem->detach_active_solution_function(); - - ops.clear_elem(); - - // copy to the libMesh matrix for further processing - DenseRealVector v; - DenseRealMatrix m; - if (R) - MAST::copy(v, vec); - if (J) - MAST::copy(m, mat); - - // constrain the quantities to account for hanging dofs, - // Dirichlet constraints, etc. - if (R && J) - dof_map.constrain_element_matrix_and_vector(m, v, dof_indices); - else if (R) - dof_map.constrain_element_vector(v, dof_indices); - else - dof_map.constrain_element_matrix(m, dof_indices); - - // add to the global matrices - if (R) R->add_vector(v, dof_indices); - if (J) J->add_matrix(m, dof_indices); - dof_indices.clear(); + if (J) { + + unsigned int ndofs = (unsigned int)dof_indices.size(); + mat.setIdentity(ndofs, ndofs); + mat *= 1.e-24; + DenseRealMatrix m; + MAST::copy(m, mat); + dof_map.constrain_element_matrix(m, dof_indices); + J->add_matrix(m, dof_indices); + dof_indices.clear(); + } + } + else { + + MAST::GeomElem geom_elem; + ops.set_elem_data(elem->dim(), *elem, geom_elem); + geom_elem.init(*elem, *_system); + + ops.init(geom_elem); + + // get the solution + unsigned int ndofs = (unsigned int)dof_indices.size(); + sol.setZero(ndofs); + vec.setZero(ndofs); + mat.setZero(ndofs, ndofs); + + for (unsigned int i=0; iattach_active_solution_function(*_sol_function); + + //_check_element_numerical_jacobian(*physics_elem, sol); + + // perform the element level calculations + ops.elem_calculations(J!=nullptr?true:false, + vec, mat); + + // physics_elem->detach_active_solution_function(); + + ops.clear_elem(); + + // copy to the libMesh matrix for further processing + DenseRealVector v; + DenseRealMatrix m; + if (R) + MAST::copy(v, vec); + if (J) + MAST::copy(m, mat); + + // constrain the quantities to account for hanging dofs, + // Dirichlet constraints, etc. + if (R && J) + dof_map.constrain_element_matrix_and_vector(m, v, dof_indices); + else if (R) + dof_map.constrain_element_vector(v, dof_indices); + else + dof_map.constrain_element_matrix(m, dof_indices); + + // add to the global matrices + if (R) R->add_vector(v, dof_indices); + if (J) J->add_matrix(m, dof_indices); + dof_indices.clear(); + } } @@ -307,6 +324,9 @@ linearized_jacobian_solution_product (const libMesh::NumericVector& X, const libMesh::Elem* elem = *el; + if (diagonal_elem_subdomain_id.count(elem->subdomain_id())) + continue; + dof_map.dof_indices (elem, dof_indices); MAST::GeomElem geom_elem; @@ -420,6 +440,9 @@ second_derivative_dot_solution_assembly (const libMesh::NumericVector& X, const libMesh::Elem* elem = *el; + if (diagonal_elem_subdomain_id.count(elem->subdomain_id())) + continue; + dof_map.dof_indices (elem, dof_indices); MAST::GeomElem geom_elem; @@ -490,7 +513,7 @@ sensitivity_assemble (const MAST::FunctionBase& f, // iterate over each element, initialize it and get the relevant // analysis quantities - RealVectorX vec, sol; + RealVectorX vec, vec1, sol; std::vector dof_indices; const libMesh::DofMap& dof_map = nonlin_sys.get_dof_map(); @@ -515,7 +538,10 @@ sensitivity_assemble (const MAST::FunctionBase& f, for ( ; el != end_el; ++el) { const libMesh::Elem* elem = *el; - + + if (diagonal_elem_subdomain_id.count(elem->subdomain_id())) + continue; + // no sensitivity computation assembly is neeed in these cases if (_param_dependence && // if object is specified and elem does not depend on it @@ -534,7 +560,8 @@ sensitivity_assemble (const MAST::FunctionBase& f, unsigned int ndofs = (unsigned int)dof_indices.size(); sol.setZero(ndofs); vec.setZero(ndofs); - + vec1.setZero(ndofs); + for (unsigned int i=0; iattach_active_solution_function(*_sol_function); ops.elem_sensitivity_calculations(f, vec); + if (f.is_topology_parameter()) { + ops.elem_topology_sensitivity_calculations(f, vec1); + vec += vec1; + } + // physics_elem->detach_active_solution_function(); ops.clear_elem(); diff --git a/src/base/nonlinear_implicit_assembly_elem_operations.h b/src/base/nonlinear_implicit_assembly_elem_operations.h index 664df7d9..ee8df6d5 100644 --- a/src/base/nonlinear_implicit_assembly_elem_operations.h +++ b/src/base/nonlinear_implicit_assembly_elem_operations.h @@ -79,6 +79,15 @@ namespace MAST { elem_shape_sensitivity_calculations(const MAST::FunctionBase& f, RealVectorX& vec) = 0; + /*! + * performs the element topology sensitivity calculations over \p elem, + * and returns the element residual sensitivity in \p vec . + */ + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec) = 0; + + /*! * performs the element topology sensitivity calculations over \p elem, * and returns the element residual sensitivity in \p vec . diff --git a/src/base/output_assembly_elem_operations.h b/src/base/output_assembly_elem_operations.h index 86073e89..f6e04a70 100644 --- a/src/base/output_assembly_elem_operations.h +++ b/src/base/output_assembly_elem_operations.h @@ -154,6 +154,15 @@ namespace MAST { */ virtual void evaluate_shape_sensitivity(const MAST::FunctionBase& f) = 0; + /*! + * this evaluates all relevant topological sensitivity components on + * the element. + * This is only done on the current element for which this + * object has been initialized. + */ + virtual void + evaluate_topology_sensitivity(const MAST::FunctionBase& f) = 0; + /*! * this evaluates all relevant topological sensitivity components on * the element. diff --git a/src/base/physics_discipline_base.cpp b/src/base/physics_discipline_base.cpp index 0b4a5cfb..b2e20de9 100644 --- a/src/base/physics_discipline_base.cpp +++ b/src/base/physics_discipline_base.cpp @@ -57,6 +57,24 @@ MAST::PhysicsDisciplineBase::add_side_load(libMesh::boundary_id_type bid, +void +MAST::PhysicsDisciplineBase::remove_side_load(libMesh::boundary_id_type bid, + MAST::BoundaryConditionBase& load) { + // make sure that this boundary and load have been applied + std::pair it = + _side_bc_map.equal_range(bid); + + for ( ; it.first != it.second; it.first++) + if (it.first->second == &load) { + + _side_bc_map.erase(it.first); + break; + } +} + + + + void MAST::PhysicsDisciplineBase::add_dirichlet_bc(libMesh::boundary_id_type bid, @@ -102,6 +120,22 @@ MAST::PhysicsDisciplineBase::add_volume_load(libMesh::subdomain_id_type sid, } +void +MAST::PhysicsDisciplineBase::remove_volume_load(libMesh::subdomain_id_type sid, + MAST::BoundaryConditionBase& load) { + std::pair it = + _vol_bc_map.equal_range(sid); + + for ( ; it.first != it.second; it.first++) { + if (it.first->second == &load) { + + _vol_bc_map.erase(it.first); + break; + } + } +} + + void diff --git a/src/base/physics_discipline_base.h b/src/base/physics_discipline_base.h index f799f87b..ce708ba1 100644 --- a/src/base/physics_discipline_base.h +++ b/src/base/physics_discipline_base.h @@ -93,7 +93,13 @@ namespace MAST { */ void add_side_load(libMesh::boundary_id_type bid, MAST::BoundaryConditionBase& load); - + + /*! + * remove the specified side loads for the boudnary with tag \p b_id + */ + void remove_side_load(libMesh::boundary_id_type bid, + MAST::BoundaryConditionBase& load); + /*! * adds the specified Dirichlet boundary condition for the boundary * with tag \p b_id @@ -123,6 +129,13 @@ namespace MAST { void add_volume_load(libMesh::subdomain_id_type bid, MAST::BoundaryConditionBase& load); + /*! + * remove the specified volume loads for the elements with + * subdomain tag \p s_id + */ + void remove_volume_load(libMesh::subdomain_id_type bid, + MAST::BoundaryConditionBase& load); + /*! * adds the specified point load diff --git a/src/elasticity/bending_structural_element.cpp b/src/elasticity/bending_structural_element.cpp index 69ef11d0..4f410c14 100644 --- a/src/elasticity/bending_structural_element.cpp +++ b/src/elasticity/bending_structural_element.cpp @@ -25,10 +25,9 @@ MAST::BendingStructuralElem:: BendingStructuralElem(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p): -MAST::StructuralElementBase(sys, assembly, elem, p) { +MAST::StructuralElementBase(sys, elem, p) { } diff --git a/src/elasticity/bending_structural_element.h b/src/elasticity/bending_structural_element.h index aab961a0..12d02035 100644 --- a/src/elasticity/bending_structural_element.h +++ b/src/elasticity/bending_structural_element.h @@ -38,7 +38,6 @@ namespace MAST { public: BendingStructuralElem(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p); diff --git a/src/elasticity/compliance_output.cpp b/src/elasticity/compliance_output.cpp index 46b9698a..169819ef 100644 --- a/src/elasticity/compliance_output.cpp +++ b/src/elasticity/compliance_output.cpp @@ -153,6 +153,46 @@ MAST::ComplianceOutput::evaluate_sensitivity(const MAST::FunctionBase &f) { +void +MAST::ComplianceOutput:: +evaluate_topology_sensitivity(const MAST::FunctionBase &f) { + + // the primal data should have been calculated + libmesh_assert(_physics_elem); + libmesh_assert(f.is_topology_parameter()); + + if (this->if_evaluate_for_element(_physics_elem->elem())) { + + std::pair*, unsigned int> + val = this->get_elem_boundary_velocity_data(); + + if (val.first) { + + MAST::StructuralElementBase& e = + dynamic_cast(*_physics_elem); + + RealVectorX + vec = RealVectorX::Zero(e.sol().size()); + + RealMatrixX + dummy = RealMatrixX::Zero(vec.size(), vec.size()); + + e.volume_external_residual_boundary_velocity(f, + val.second, + *val.first, + _discipline->volume_loads(), + false, + vec, + dummy); + + // compute the contribution of this element to compliance + _dcompliance_dp -= vec.dot(e.sol()); + } + } +} + + + void MAST::ComplianceOutput:: evaluate_topology_sensitivity(const MAST::FunctionBase &f, @@ -280,7 +320,7 @@ MAST::ComplianceOutput::init(const MAST::GeomElem& elem) { dynamic_cast(_discipline->get_property_card(elem)); _physics_elem = - MAST::build_structural_element(*_system, *_assembly, elem, p).release(); + MAST::build_structural_element(*_system, elem, p).release(); } diff --git a/src/elasticity/compliance_output.h b/src/elasticity/compliance_output.h index c5b3a0de..543fa534 100644 --- a/src/elasticity/compliance_output.h +++ b/src/elasticity/compliance_output.h @@ -114,6 +114,15 @@ namespace MAST { libmesh_assert(false); // to be implemented } + /*! + * this evaluates all relevant topological sensitivity components on + * the element. + * This is only done on the current element for which this + * object has been initialized. + */ + virtual void + evaluate_topology_sensitivity(const MAST::FunctionBase& f); + /*! * This evaluates the contribution to the topology sensitivity on the * boundary. Given that the integral is nonlinear due to the \f$p-\f$norm, diff --git a/src/elasticity/fluid_structure_assembly_elem_operations.cpp b/src/elasticity/fluid_structure_assembly_elem_operations.cpp index 941df910..c2670e9d 100644 --- a/src/elasticity/fluid_structure_assembly_elem_operations.cpp +++ b/src/elasticity/fluid_structure_assembly_elem_operations.cpp @@ -69,7 +69,7 @@ MAST::FluidStructureAssemblyElemOperations::init(const MAST::GeomElem& elem) { dynamic_cast(_discipline->get_property_card(elem)); _physics_elem = - MAST::build_structural_element(*_system, *_assembly, elem, p).release(); + MAST::build_structural_element(*_system, elem, p).release(); } diff --git a/src/elasticity/smooth_ramp_stress_output.cpp b/src/elasticity/smooth_ramp_stress_output.cpp index 92577f85..12dd5137 100644 --- a/src/elasticity/smooth_ramp_stress_output.cpp +++ b/src/elasticity/smooth_ramp_stress_output.cpp @@ -35,6 +35,7 @@ #include "libmesh/parallel.h" + MAST::SmoothRampStressStrainOutput::SmoothRampStressStrainOutput(): MAST::StressStrainOutputBase() { diff --git a/src/elasticity/solid_element_3d.cpp b/src/elasticity/solid_element_3d.cpp index eccca5a1..e2d955e7 100644 --- a/src/elasticity/solid_element_3d.cpp +++ b/src/elasticity/solid_element_3d.cpp @@ -33,10 +33,9 @@ MAST::StructuralElement3D:: StructuralElement3D(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p): -MAST::StructuralElementBase(sys, assembly, elem, p) { +MAST::StructuralElementBase(sys, elem, p) { } @@ -610,7 +609,7 @@ surface_pressure_residual(bool request_jacobian, // prepare the side finite element std::unique_ptr - fe(_elem.init_side_fe(side, false)); + fe(_elem.init_side_fe(side, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& qpoint = fe->get_xyz(); @@ -678,7 +677,7 @@ surface_pressure_residual_sensitivity(const MAST::FunctionBase& p, // prepare the side finite element std::unique_ptr - fe(_elem.init_side_fe(side, false)); + fe(_elem.init_side_fe(side, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& qpoint = fe->get_xyz(); diff --git a/src/elasticity/solid_element_3d.h b/src/elasticity/solid_element_3d.h index 864b5faa..c8a4bea6 100644 --- a/src/elasticity/solid_element_3d.h +++ b/src/elasticity/solid_element_3d.h @@ -37,7 +37,6 @@ namespace MAST { public: StructuralElement3D(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p); @@ -149,6 +148,55 @@ namespace MAST { protected: + /*! + * Calculates the force vector and Jacobian due to surface traction. + */ + virtual bool + surface_traction_residual(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) {} + + + /*! + * Calculates the sensitivity of element vector and matrix quantities for surface traction + * boundary condition. + */ + virtual bool + surface_traction_residual_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) {} + + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) {} + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) {} + + + /*! * Calculates the force vector and Jacobian due to surface pressure. */ diff --git a/src/elasticity/stress_assembly.cpp b/src/elasticity/stress_assembly.cpp index 1eb7a779..12854411 100644 --- a/src/elasticity/stress_assembly.cpp +++ b/src/elasticity/stress_assembly.cpp @@ -165,6 +165,8 @@ update_stress_strain_data(MAST::StressStrainOutputBase& ops, ops.set_elem_data(elem->dim(), *elem, geom_elem); geom_elem.init(*elem, *_system); + if (!ops.if_evaluate_for_element(geom_elem)) continue; + ops.init(geom_elem); ops.set_elem_solution(sol); ops.evaluate(); @@ -301,6 +303,8 @@ update_stress_strain_sensitivity_data(MAST::StressStrainOutputBase& ops, ops.set_elem_data(elem->dim(), *elem, geom_elem); geom_elem.init(*elem, *_system); + if (!ops.if_evaluate_for_element(geom_elem)) continue; + ops.init(geom_elem); ops.set_stress_plot_mode(true); ops.set_elem_solution(sol); diff --git a/src/elasticity/stress_output_base.cpp b/src/elasticity/stress_output_base.cpp index 16af69ce..ad987e03 100644 --- a/src/elasticity/stress_output_base.cpp +++ b/src/elasticity/stress_output_base.cpp @@ -352,6 +352,34 @@ MAST::StressStrainOutputBase::evaluate_sensitivity(const MAST::FunctionBase &f) +void +MAST::StressStrainOutputBase:: +evaluate_topology_sensitivity(const MAST::FunctionBase &f) { + + // the primal data should have been calculated + libmesh_assert(_physics_elem); + libmesh_assert(f.is_topology_parameter()); + if (!_if_stress_plot_mode) + libmesh_assert(_primal_data_initialized); + + // sensitivity only exists at the boundary. So, we proceed with calculation + // only if this element has an intersection in the interior, or with a side. + + if (this->if_evaluate_for_element(_physics_elem->elem())) { + + std::pair*, unsigned int> + val = this->get_elem_boundary_velocity_data(); + + if (val.first) + dynamic_cast + (_physics_elem)->calculate_stress_boundary_velocity(f, *this, + val.second, + *val.first); + } +} + + + void MAST::StressStrainOutputBase:: evaluate_topology_sensitivity(const MAST::FunctionBase &f, @@ -482,7 +510,7 @@ MAST::StressStrainOutputBase::init(const MAST::GeomElem& elem) { dynamic_cast(_discipline->get_property_card(elem)); _physics_elem = - MAST::build_structural_element(*_system, *_assembly, elem, p).release(); + MAST::build_structural_element(*_system, elem, p).release(); } diff --git a/src/elasticity/stress_output_base.h b/src/elasticity/stress_output_base.h index f7267c91..9cb91739 100644 --- a/src/elasticity/stress_output_base.h +++ b/src/elasticity/stress_output_base.h @@ -328,6 +328,16 @@ namespace MAST { libmesh_assert(false); // to be implemented } + /*! + * this evaluates all relevant topological sensitivity components on + * the element. + * This is only done on the current element for which this + * object has been initialized. + */ + virtual void + evaluate_topology_sensitivity(const MAST::FunctionBase& f); + + /*! * This evaluates the contribution to the topology sensitivity on the * boundary. Given that the integral is nonlinear due to the \f$p-\f$norm, diff --git a/src/elasticity/structural_buckling_eigenproblem_elem_operations.cpp b/src/elasticity/structural_buckling_eigenproblem_elem_operations.cpp index 449dcb4f..4c98b3e4 100644 --- a/src/elasticity/structural_buckling_eigenproblem_elem_operations.cpp +++ b/src/elasticity/structural_buckling_eigenproblem_elem_operations.cpp @@ -71,7 +71,7 @@ MAST::StructuralBucklingEigenproblemElemOperations::init(const MAST::GeomElem& e (_discipline->get_property_card(elem)); _physics_elem = - MAST::build_structural_element(*_system, *_assembly, elem, p).release(); + MAST::build_structural_element(*_system, elem, p).release(); } diff --git a/src/elasticity/structural_element_1d.cpp b/src/elasticity/structural_element_1d.cpp index a790b537..745b1170 100644 --- a/src/elasticity/structural_element_1d.cpp +++ b/src/elasticity/structural_element_1d.cpp @@ -35,10 +35,9 @@ MAST::StructuralElement1D::StructuralElement1D(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p): -MAST::BendingStructuralElem(sys, assembly, elem, p) { +MAST::BendingStructuralElem(sys, elem, p) { } @@ -1709,7 +1708,7 @@ surface_pressure_residual(bool request_jacobian, libmesh_assert(!follower_forces); // not implemented yet for follower forces // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(side, false)); + std::unique_ptr fe(_elem.init_side_fe(side, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& qpoint = fe->get_xyz(); @@ -1788,7 +1787,7 @@ surface_pressure_residual_sensitivity(const MAST::FunctionBase& p, libmesh_assert(!follower_forces); // not implemented yet for follower forces // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(side, false)); + std::unique_ptr fe(_elem.init_side_fe(side, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& qpoint = fe->get_xyz(); diff --git a/src/elasticity/structural_element_1d.h b/src/elasticity/structural_element_1d.h index 0bd890ef..497c9255 100644 --- a/src/elasticity/structural_element_1d.h +++ b/src/elasticity/structural_element_1d.h @@ -39,7 +39,6 @@ namespace MAST { public: StructuralElement1D(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p); @@ -160,7 +159,55 @@ namespace MAST { protected: - + /*! + * Calculates the force vector and Jacobian due to surface traction. + */ + virtual bool + surface_traction_residual(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc){ return false;} + + + /*! + * Calculates the sensitivity of element vector and matrix quantities for surface traction + * boundary condition. + */ + virtual bool + surface_traction_residual_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) {} + + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) {} + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) {} + + + /*! * Calculates the force vector and Jacobian due to surface pressure. */ diff --git a/src/elasticity/structural_element_2d.cpp b/src/elasticity/structural_element_2d.cpp index 6b45c81b..33e1b981 100644 --- a/src/elasticity/structural_element_2d.cpp +++ b/src/elasticity/structural_element_2d.cpp @@ -32,14 +32,14 @@ #include "base/parameter.h" #include "base/constant_field_function.h" #include "base/assembly_base.h" +#include "level_set/level_set_boundary_velocity.h" MAST::StructuralElement2D:: StructuralElement2D(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p): -MAST::BendingStructuralElem(sys, assembly, elem, p) { +MAST::BendingStructuralElem(sys, elem, p) { } @@ -249,6 +249,60 @@ initialize_green_lagrange_strain_operator(const unsigned int qp, } + +void +MAST::StructuralElement2D:: +initialize_strain_operator_gradient(const unsigned int qp, + const MAST::FEBase& fe, + const RealVectorX& local_disp, + RealMatrixX& epsilon_grad, + std::vector& dBmat_lin) { + + epsilon_grad.setZero(); + + const std::vector >& + d2phi = fe.get_d2phi(); + + unsigned int n_phi = (unsigned int)d2phi.size(); + RealVectorX + phi = RealVectorX::Zero(n_phi), + depsilon = RealVectorX::Zero(3); + + // make sure all matrices are the right size + libmesh_assert_equal_to(epsilon_grad.rows(), 3); + libmesh_assert_equal_to(epsilon_grad.cols(), 2); + libmesh_assert_equal_to(dBmat_lin.size(), 2); + + + for (unsigned int i=0; i<2; i++) { + + libmesh_assert_equal_to(dBmat_lin[i].m(), 3); + libmesh_assert_equal_to(dBmat_lin[i].n(), 6*n_phi); + + // now set the shape function values + // d2N/dxdxi + for ( unsigned int i_nd=0; i_nd& vel_f) { - std::unique_ptr fe(_elem.init_side_fe(s, true)); + std::unique_ptr fe(_elem.init_side_fe(s, true, false)); const unsigned int qp_loc_fe_size = (unsigned int)fe->get_qpoints().size(), @@ -2301,7 +2355,7 @@ MAST::StructuralElement2D::prestress_residual_sensitivity (const MAST::FunctionB bool MAST::StructuralElement2D:: -surface_pressure_residual(bool request_jacobian, +surface_traction_residual(bool request_jacobian, RealVectorX &f, RealMatrixX &jac, const unsigned int side, @@ -2310,12 +2364,11 @@ surface_pressure_residual(bool request_jacobian, libmesh_assert(!follower_forces); // not implemented yet for follower forces // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(side, false)); + std::unique_ptr fe(_elem.init_side_fe(side, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& qpoint = fe->get_xyz(); const std::vector >& phi = fe->get_phi(); - const std::vector& face_normals = fe->get_normals_for_local_coordinate(); const unsigned int n_phi = (unsigned int)phi.size(), n1 = 3, @@ -2323,16 +2376,17 @@ surface_pressure_residual(bool request_jacobian, // get the function from this boundary condition - const MAST::FieldFunction& p_func = - bc.get >("pressure"); + const MAST::FieldFunction& trac_func = + bc.get >("traction"); // get the thickness function to calculate the force const MAST::FieldFunction& t_func = _property.get >("h"); FEMOperatorMatrix Bmat; + RealVectorX + trac = RealVectorX::Zero(3); Real - press = 0., t_val = 0.; RealVectorX @@ -2350,12 +2404,12 @@ surface_pressure_residual(bool request_jacobian, Bmat.reinit(2*n1, phi_vec); // get pressure and thickness values - p_func(qpoint[qp], _time, press); - t_func(qpoint[qp], _time, t_val); + trac_func(qpoint[qp], _time, trac); + t_func (qpoint[qp], _time, t_val); // calculate force for (unsigned int i_dim=0; i_dim fe(_elem.init_side_fe(side, false)); + std::unique_ptr fe(_elem.init_side_fe(side, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& qpoint = fe->get_xyz(); const std::vector >& phi = fe->get_phi(); - const std::vector& face_normals = fe->get_normals_for_local_coordinate(); const unsigned int n_phi = (unsigned int)phi.size(), n1 = 3, @@ -2399,20 +2450,20 @@ surface_pressure_residual_sensitivity(const MAST::FunctionBase& p, // get the function from this boundary condition - const MAST::FieldFunction& p_func = - bc.get >("pressure"); - + const MAST::FieldFunction& trac_func = + bc.get >("traction"); + // get the thickness function to calculate the force const MAST::FieldFunction& t_func = _property.get >("h"); - FEMOperatorMatrix Bmat; + RealVectorX + trac = RealVectorX::Zero(3), + dtrac = RealVectorX::Zero(3); Real - press = 0., - dpress = 0., - t_val = 0., - dt_val = 0.; + t_val = 0., + dt_val = 0.; RealVectorX phi_vec = RealVectorX::Zero(n_phi), @@ -2428,15 +2479,15 @@ surface_pressure_residual_sensitivity(const MAST::FunctionBase& p, Bmat.reinit(2*n1, phi_vec); - // get pressure and thickness values and their sensitivities - p_func(qpoint[qp], _time, press); - p_func.derivative(p, qpoint[qp], _time, dpress); - t_func(qpoint[qp], _time, t_val); + // get pressure and thickness values + trac_func(qpoint[qp], _time, trac); + trac_func.derivative(p, qpoint[qp], _time, dtrac); + t_func (qpoint[qp], _time, t_val); t_func.derivative(p, qpoint[qp], _time, dt_val); - + // calculate force for (unsigned int i_dim=0; i_dim fe(_elem.init_fe(true, - false, - _property.extra_quadrature_order(_elem))); - - const std::vector& JxW = fe->get_JxW(); - const std::vector& xyz = fe->get_xyz(); + libmesh_assert(!follower_forces); // not implemented yet for follower forces + // prepare the side finite element + std::unique_ptr fe(_elem.init_side_fe(side, true, true)); + + const std::vector &JxW = fe->get_JxW(); + const std::vector& qpoint = fe->get_xyz(); + const std::vector >& phi = fe->get_phi(); + const std::vector& face_normals = fe->get_normals_for_local_coordinate(); const unsigned int - n_phi = (unsigned int)fe->get_phi().size(), - n1 = this->n_direct_strain_components(), - n2 = 6*n_phi, - n3 = this->n_von_karman_strain_components(); + n_phi = (unsigned int)phi.size(), + n1 = 3, + n2 = 6*n_phi; + + + // get the function from this boundary condition at the projected point + const MAST::FieldFunction& trac_func = + bc.get >("traction"); + const MAST::LevelSetBoundaryVelocity& interface = + bc.get("phi_vel"); - RealMatrixX - material_exp_A_mat, - material_exp_B_mat, - mat1_n1n2 = RealMatrixX::Zero(n1,n2), - mat2_n2n2 = RealMatrixX::Zero(n2,n2), - mat3 = RealMatrixX::Zero(2, n2), - mat4_n3n2 = RealMatrixX::Zero(n3,n2), - vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), - stress = RealMatrixX::Zero(2,2), - mat_x = RealMatrixX::Zero(3,2), - mat_y = RealMatrixX::Zero(3,2), - local_jac = RealMatrixX::Zero(n2,n2); + // get the thickness function to calculate the force + const MAST::FieldFunction& t_func = + _property.get >("h"); + + FEMOperatorMatrix Bmat; RealVectorX - vec1_n1 = RealVectorX::Zero(n1), - vec2_n1 = RealVectorX::Zero(n1), - vec3_n2 = RealVectorX::Zero(n2), - vec4_2 = RealVectorX::Zero(2), - vec5_n3 = RealVectorX::Zero(n3), + trac = RealVectorX::Zero(3), + stress = RealVectorX::Zero(3), + dnormal = RealVectorX::Zero(3), + normal = RealVectorX::Zero(3), + bnd_pt = RealVectorX::Zero(3), + phi_vec = RealVectorX::Zero(n_phi), + force = RealVectorX::Zero(2*n1), local_f = RealVectorX::Zero(n2), - strain = RealVectorX::Zero(3), - delta_t = RealVectorX::Zero(1); - - FEMOperatorMatrix - Bmat_lin, - Bmat_nl_x, - Bmat_nl_y, - Bmat_nl_u, - Bmat_nl_v, - Bmat_bend, - Bmat_vk; - - Bmat_lin.reinit(n1, _system.n_vars(), n_phi); // three stress-strain components - Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); - Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); - Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); - Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); - Bmat_bend.reinit(n1, _system.n_vars(), n_phi); - Bmat_vk.reinit(n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + vec_n2 = RealVectorX::Zero(n2); - bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN); + RealMatrixX + normal_mat = RealMatrixX::Zero(2,n1), + stress_grad = RealMatrixX::Zero(n1,2), + mat1_n1n2 = RealMatrixX::Zero(2*n1, n2), + mat2_n2n2 = RealMatrixX::Zero(n2, n2), + local_jac = RealMatrixX::Zero(n2, n2), + dstress_dX = RealMatrixX::Zero(n1,n2), + *dstress_mat= request_jacobian?&dstress_dX:nullptr; - MAST::BendingOperatorType - bending_model = _property.bending_model(_elem); + Real + t_val = 0., + elem_h = _elem.get_reference_elem().hmax(); - std::unique_ptr bend; + libMesh::Point + pt; - if (bending_model != MAST::NO_BENDING) - bend.reset(MAST::build_bending_operator_2D(bending_model, - *this, - fe->get_qpoints()).release()); - - std::unique_ptr > - expansion_A = _property.thermal_expansion_A_matrix(*this), - expansion_B = _property.thermal_expansion_B_matrix(*this); + std::vector + stress_grad_dX(2, RealMatrixX::Zero(n1,n2)), + *stress_grad_mat = request_jacobian?&stress_grad_dX:nullptr; - const MAST::FieldFunction - &temp_func = bc.get >("temperature"), - &ref_temp_func = bc.get >("ref_temperature"); - Real - t = 0., - t0 = 0., - scaling = 1.; - - if (bc.contains("thermal_jacobian_scaling")) - bc.get>("thermal_jacobian_scaling")(scaling); - - for (unsigned int qp=0; qpinitialize_green_lagrange_strain_operator(qp, - *fe, - _local_sol, - strain, - mat_x, - mat_y, - Bmat_lin, - Bmat_nl_x, - Bmat_nl_y, - Bmat_nl_u, - Bmat_nl_v); + // compute difference in normal vector + for (unsigned int i_dim=0; i_diminitialize_bending_strain_operator(*fe, qp, Bmat_bend); - Bmat_bend.vector_mult_transpose(vec3_n2, vec2_n1); - local_f += JxW[qp] * vec3_n2; - - // von Karman strain - if (if_vk) { - // get the vonKarman strain operator if needed - this->initialize_von_karman_strain_operator(qp, - *fe, - vec2_n1, // epsilon_vk - vk_dwdxi_mat, - Bmat_vk); - // von Karman strain - vec4_2 = vk_dwdxi_mat.transpose() * vec1_n1; - Bmat_vk.vector_mult_transpose(vec3_n2, vec4_2); - local_f += JxW[qp] * vec3_n2; - } - } + // contribution from gradient of stress, which uses the + // surface normal at the mapped point + _surface_normal_voigt_notation(normal, normal_mat); + _compute_stress_gradient(*fe, qp, stress_grad, stress_grad_mat); - if (request_jacobian && - _property.strain_type() == MAST::NONLINEAR_STRAIN) { - - // u-disp - Bmat_nl_u.left_multiply(mat3, stress); - Bmat_nl_u.right_multiply_transpose(mat2_n2n2, mat3); - local_jac.topLeftCorner(n2, n2) += JxW[qp] * mat2_n2n2; + for (unsigned int i_dim=0; i_dim<2; i_dim++) + force.topRows(2) -= normal_mat * stress_grad.col(i_dim) * (bnd_pt(i_dim) - qpoint[qp](i_dim)); + + Bmat.vector_mult_transpose(vec_n2, force); + + /*libMesh::out + << "id: " << _elem.get_reference_elem().id() << std::endl + << "qp: " << qpoint[qp](0) << " " << qpoint[qp](1) << std::endl + << "x-mapped: " << bnd_pt(0) << " " << bnd_pt(1) << std::endl + << "trac " << trac(0) << " " << trac(1) << std::endl + << "normal: " << normal(0) << " " << normal(1) << std::endl + << "force: " << force.transpose() << std::endl << std::endl;*/ + + local_f += t_val * JxW[qp] * vec_n2; + + if (request_jacobian) { - // v-disp - Bmat_nl_v.left_multiply(mat3, stress); - Bmat_nl_v.right_multiply_transpose(mat2_n2n2, mat3); - local_jac.topLeftCorner(n2, n2) += JxW[qp] * mat2_n2n2; - } + // contribution from the stress + _surface_normal_voigt_notation(dnormal, normal_mat); + mat1_n1n2.topLeftCorner(2, n2) = normal_mat * dstress_dX; - if (request_jacobian && if_vk) { - // vk - vk - mat3 = RealMatrixX::Zero(2, n2); - Bmat_vk.left_multiply(mat3, stress); - Bmat_vk.right_multiply_transpose(mat2_n2n2, mat3); - local_jac += JxW[qp] * mat2_n2n2; + // contribution from the stress gradient + _surface_normal_voigt_notation(normal, normal_mat); + for (unsigned int i_dim=0; i_dim<2; i_dim++) { + + mat1_n1n2.topLeftCorner(2, n2) -= + (bnd_pt(i_dim) - qpoint[qp](i_dim)) * normal_mat * stress_grad_dX[i_dim]; + } + + Bmat.right_multiply_transpose(mat2_n2n2, mat1_n1n2); + local_jac += t_val * JxW[qp] * mat2_n2n2; } } - // now transform to the global coorodinate system - transform_vector_to_global_system(local_f, vec3_n2); - f -= vec3_n2; - if (request_jacobian && if_vk) { - transform_matrix_to_global_system(local_jac, mat2_n2n2); - jac -= scaling * mat2_n2n2; - } - - // Jacobian contribution from von Karman strain + // now transform to the global system and add + transform_vector_to_global_system(local_f, vec_n2); + f -= vec_n2; + + if (request_jacobian) { + transform_matrix_to_global_system(local_jac, mat2_n2n2); + jac -= mat2_n2n2; + } + + return request_jacobian; } - bool MAST::StructuralElement2D:: -thermal_residual_sensitivity (const MAST::FunctionBase& p, - bool request_jacobian, - RealVectorX& f, - RealMatrixX& jac, - MAST::BoundaryConditionBase& bc) -{ - std::unique_ptr fe(_elem.init_fe(true, - false, - _property.extra_quadrature_order(_elem))); - - const std::vector& JxW = fe->get_JxW(); - const std::vector& xyz = fe->get_xyz(); +surface_traction_residual_shifted_boundary_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX &f, + RealMatrixX &jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) { + + libmesh_assert(!follower_forces); // not implemented yet for follower forces + + // prepare the side finite element + std::unique_ptr fe(_elem.init_side_fe(side, true, true)); + const std::vector &JxW = fe->get_JxW(); + const std::vector& qpoint = fe->get_xyz(); + const std::vector >& phi = fe->get_phi(); + const std::vector& face_normals = fe->get_normals_for_local_coordinate(); const unsigned int - n_phi = (unsigned int)fe->get_phi().size(), - n1 = this->n_direct_strain_components(), - n2 = 6*n_phi, - n3 = this->n_von_karman_strain_components(); + n_phi = (unsigned int)phi.size(), + n1 = 3, + n2 = 6*n_phi; - RealMatrixX - material_exp_A_mat, - material_exp_B_mat, - material_exp_A_mat_sens, - material_exp_B_mat_sens, - mat1_n1n2 = RealMatrixX::Zero(n1,n2), - mat2_n2n2 = RealMatrixX::Zero(n2,n2), - mat3 = RealMatrixX::Zero(2, n2), - mat4_n3n2 = RealMatrixX::Zero(n3,n2), - vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), - stress = RealMatrixX::Zero(2,2), - mat_x = RealMatrixX::Zero(3,2), - mat_y = RealMatrixX::Zero(3,2), - local_jac = RealMatrixX::Zero(n2,n2); - RealVectorX - vec1_n1 = RealVectorX::Zero(n1), - vec2_n1 = RealVectorX::Zero(n1), - vec3_n2 = RealVectorX::Zero(n2), - vec4_2 = RealVectorX::Zero(2), - vec5_n1 = RealVectorX::Zero(n1), - local_f = RealVectorX::Zero(n2), - strain = RealVectorX::Zero(3), - delta_t = RealVectorX::Zero(1), - delta_t_sens = RealVectorX::Zero(1); + // get the function from this boundary condition at the projected point + const MAST::FieldFunction& trac_func = + bc.get >("traction"); + const MAST::LevelSetBoundaryVelocity& interface = + bc.get("phi_vel"); + + // get the thickness function to calculate the force + const MAST::FieldFunction& t_func = + _property.get >("h"); - FEMOperatorMatrix - Bmat_lin, - Bmat_nl_x, - Bmat_nl_y, - Bmat_nl_u, - Bmat_nl_v, - Bmat_bend, - Bmat_vk; + FEMOperatorMatrix Bmat; - Bmat_lin.reinit(n1, _system.n_vars(), n_phi); // three stress-strain components - Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); - Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); - Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); - Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); - Bmat_bend.reinit(n1, _system.n_vars(), n_phi); - Bmat_vk.reinit(n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + RealVectorX + trac = RealVectorX::Zero(3), + dtrac = RealVectorX::Zero(3), + stress = RealVectorX::Zero(3), + dnormal = RealVectorX::Zero(3), + normal = RealVectorX::Zero(3), + normal_sens = RealVectorX::Zero(3), + bnd_pt = RealVectorX::Zero(3), + bnd_pt_sens = RealVectorX::Zero(3), + phi_vec = RealVectorX::Zero(n_phi), + force = RealVectorX::Zero(2*n1), + local_f = RealVectorX::Zero(n2), + vec_n2 = RealVectorX::Zero(n2); - bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN); - - MAST::BendingOperatorType - bending_model = _property.bending_model(_elem); + RealMatrixX + normal_mat = RealMatrixX::Zero(2,n1), + stress_grad = RealMatrixX::Zero(n1,2), + mat1_n1n2 = RealMatrixX::Zero(2*n1, n2), + mat2_n2n2 = RealMatrixX::Zero(n2, n2), + local_jac = RealMatrixX::Zero(n2, n2), + dstress_dX = RealMatrixX::Zero(n1,n2), + *dstress_mat= request_jacobian?&dstress_dX:nullptr; - std::unique_ptr bend; + Real + t_val = 0., + dt_val = 0., + elem_h = _elem.get_reference_elem().hmax(); - if (bending_model != MAST::NO_BENDING) - bend.reset(MAST::build_bending_operator_2D(bending_model, - *this, - fe->get_qpoints()).release()); - - std::unique_ptr > - expansion_A = _property.thermal_expansion_A_matrix(*this), - expansion_B = _property.thermal_expansion_B_matrix(*this); + libMesh::Point + pt; - // temperature function - const MAST::FieldFunction - &temp_func = bc.get >("temperature"), - &ref_temp_func = bc.get >("ref_temperature"); + std::vector + stress_grad_dX(2, RealMatrixX::Zero(n1,n2)), + *stress_grad_mat = request_jacobian?&stress_grad_dX:nullptr; - Real t, t0, t_sens, t0_sens; - for (unsigned int qp=0; qpderivative(p, xyz[qp], _time, material_exp_A_mat_sens); - expansion_B->derivative(p, xyz[qp], _time, material_exp_B_mat_sens); + // now set the shape function values + for ( unsigned int i_nd=0; i_ndinitialize_green_lagrange_strain_operator(qp, - *fe, - _local_sol, - strain, - mat_x, - mat_y, - Bmat_lin, - Bmat_nl_x, - Bmat_nl_y, - Bmat_nl_u, - Bmat_nl_v); + local_f += t_val * JxW[qp] * vec_n2; - // membrane strain - Bmat_lin.vector_mult_transpose(vec3_n2, vec1_n1); - local_f += JxW[qp] * vec3_n2; - if (_property.strain_type() == MAST::NONLINEAR_STRAIN) { - - // nonlinear strain operotor - // x - vec4_2 = mat_x.transpose() * vec1_n1; - Bmat_nl_x.vector_mult_transpose(vec3_n2, vec4_2); - local_f.topRows(n2) += JxW[qp] * vec3_n2; - - // y - vec4_2 = mat_y.transpose() * vec1_n1; - Bmat_nl_y.vector_mult_transpose(vec3_n2, vec4_2); - local_f.topRows(n2) += JxW[qp] * vec3_n2; - } + /*libMesh::out + << "id: " << _elem.get_reference_elem().id() << std::endl + << "qp: " << qpoint[qp](0) << " " << qpoint[qp](1) << std::endl + << "x-mapped: " << bnd_pt(0) << " " << bnd_pt(1) << std::endl + << "x-mapped_sens: " << bnd_pt_sens(0) << " " << bnd_pt_sens(1) << std::endl + << "trac " << trac(0) << " " << trac(1) << std::endl + << "trac_sens: " << dtrac(0) << " " << dtrac(1) << std::endl + << "normal: " << normal(0) << " " << normal(1) << std::endl + << "normal_sens: " << normal_sens(0) << " " << normal_sens(1) << std::endl + << "force: " << force.transpose() << std::endl << std::endl;*/ + + + // also include contribution from sensitivity of thickness + for (unsigned int i_dim=0; i_diminitialize_bending_strain_operator(*fe, qp, Bmat_bend); - Bmat_bend.vector_mult_transpose(vec3_n2, vec2_n1); - local_f += JxW[qp] * vec3_n2; - - // von Karman strain - if (if_vk) { - // get the vonKarman strain operator if needed - this->initialize_von_karman_strain_operator(qp, - *fe, - vec2_n1, // epsilon_vk - vk_dwdxi_mat, - Bmat_vk); - // von Karman strain - vec4_2 = vk_dwdxi_mat.transpose() * vec1_n1; - Bmat_vk.vector_mult_transpose(vec3_n2, vec4_2); - local_f += JxW[qp] * vec3_n2; - } - } + // compute stress + _compute_stress(*fe, qp, stress, dstress_mat); + + // contribution from dnormal + force.topRows(2) = trac.topRows(2) + normal_mat * stress; + + // contribution from gradient of stress, which uses the + // surface normal at the mapped point + _surface_normal_voigt_notation(normal, normal_mat); + _compute_stress_gradient(*fe, qp, stress_grad, nullptr); + + for (unsigned int i_dim=0; i_dim<2; i_dim++) + force.topRows(2) -= normal_mat * stress_grad.col(i_dim) * (bnd_pt(i_dim) - qpoint[qp](i_dim)); + + Bmat.vector_mult_transpose(vec_n2, force); + + local_f += dt_val * JxW[qp] * vec_n2; - if (request_jacobian && - _property.strain_type() == MAST::NONLINEAR_STRAIN) { - - // u-disp - Bmat_nl_u.left_multiply(mat3, stress); - Bmat_nl_u.right_multiply_transpose(mat2_n2n2, mat3); - local_jac.topLeftCorner(n2, n2) += JxW[qp] * mat2_n2n2; - - // v-disp - Bmat_nl_v.left_multiply(mat3, stress); - Bmat_nl_v.right_multiply_transpose(mat2_n2n2, mat3); - local_jac.topLeftCorner(n2, n2) += JxW[qp] * mat2_n2n2; - } - if (request_jacobian && if_vk) { // Jacobian only for vk strain + if (request_jacobian) { - // vk - vk - mat3 = RealMatrixX::Zero(2, n2); - Bmat_vk.left_multiply(mat3, stress); - Bmat_vk.right_multiply_transpose(mat2_n2n2, mat3); - local_jac += JxW[qp] * mat2_n2n2; + libmesh_assert(false); // to be implemented } } - - // now transform to the global coorodinate system - transform_vector_to_global_system(local_f, vec3_n2); - f -= vec3_n2; - if (request_jacobian && if_vk) { + // now transform to the global system and add + transform_vector_to_global_system(local_f, vec_n2); + f -= vec_n2; + + if (request_jacobian) { transform_matrix_to_global_system(local_jac, mat2_n2n2); jac -= mat2_n2n2; } + - // Jacobian contribution from von Karman strain return request_jacobian; } - -void +bool MAST::StructuralElement2D:: -thermal_residual_boundary_velocity(const MAST::FunctionBase& p, - const unsigned int s, - const MAST::FieldFunction& vel_f, - MAST::BoundaryConditionBase& bc, - bool request_jacobian, - RealVectorX& f, - RealMatrixX& jac) { +surface_pressure_residual(bool request_jacobian, + RealVectorX &f, + RealMatrixX &jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) { + + libmesh_assert(!follower_forces); // not implemented yet for follower forces // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, - true, - _property.extra_quadrature_order(_elem))); + std::unique_ptr fe(_elem.init_side_fe(side, false, false)); - std::vector JxW_Vn = fe->get_JxW(); - const std::vector& xyz = fe->get_xyz(); + const std::vector &JxW = fe->get_JxW(); + const std::vector& qpoint = fe->get_xyz(); + const std::vector >& phi = fe->get_phi(); const std::vector& face_normals = fe->get_normals_for_local_coordinate(); + const unsigned int + n_phi = (unsigned int)phi.size(), + n1 = 3, + n2 = 6*n_phi; + + + // get the function from this boundary condition + const MAST::FieldFunction& p_func = + bc.get >("pressure"); + + // get the thickness function to calculate the force + const MAST::FieldFunction& t_func = + _property.get >("h"); + + FEMOperatorMatrix Bmat; + Real + press = 0., + t_val = 0.; + + RealVectorX + phi_vec = RealVectorX::Zero(n_phi), + force = RealVectorX::Zero(2*n1), + local_f = RealVectorX::Zero(n2), + vec_n2 = RealVectorX::Zero(n2); + + for (unsigned int qp=0; qp fe(_elem.init_side_fe(side, false, false)); + const std::vector &JxW = fe->get_JxW(); + const std::vector& qpoint = fe->get_xyz(); + const std::vector >& phi = fe->get_phi(); + const std::vector& face_normals = fe->get_normals_for_local_coordinate(); const unsigned int - n_phi = (unsigned int)fe->get_phi().size(), - n1 = this->n_direct_strain_components(), n2=6*n_phi, - n3 = this->n_von_karman_strain_components(), - dim = 2; + n_phi = (unsigned int)phi.size(), + n1 = 3, + n2 = 6*n_phi; + + + // get the function from this boundary condition + const MAST::FieldFunction& p_func = + bc.get >("pressure"); + + // get the thickness function to calculate the force + const MAST::FieldFunction& t_func = + _property.get >("h"); + + + FEMOperatorMatrix Bmat; + Real + press = 0., + dpress = 0., + t_val = 0., + dt_val = 0.; + + RealVectorX + phi_vec = RealVectorX::Zero(n_phi), + force = RealVectorX::Zero(2*n1), + local_f = RealVectorX::Zero(n2), + vec_n2 = RealVectorX::Zero(n2); + + for (unsigned int qp=0; qp fe(_elem.init_fe(true, + false, + _property.extra_quadrature_order(_elem))); + + const std::vector& JxW = fe->get_JxW(); + const std::vector& xyz = fe->get_xyz(); + + const unsigned int + n_phi = (unsigned int)fe->get_phi().size(), + n1 = this->n_direct_strain_components(), + n2 = 6*n_phi, + n3 = this->n_von_karman_strain_components(); RealMatrixX material_exp_A_mat, @@ -2908,9 +3048,8 @@ thermal_residual_boundary_velocity(const MAST::FunctionBase& p, vec4_2 = RealVectorX::Zero(2), vec5_n3 = RealVectorX::Zero(n3), local_f = RealVectorX::Zero(n2), - delta_t = RealVectorX::Zero(1), strain = RealVectorX::Zero(3), - vel = RealVectorX::Zero(dim); + delta_t = RealVectorX::Zero(1); FEMOperatorMatrix Bmat_lin, @@ -2921,13 +3060,13 @@ thermal_residual_boundary_velocity(const MAST::FunctionBase& p, Bmat_bend, Bmat_vk; - Bmat_lin.reinit (n1, _system.n_vars(), n_phi); // three stress-strain components + Bmat_lin.reinit(n1, _system.n_vars(), n_phi); // three stress-strain components Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); Bmat_bend.reinit(n1, _system.n_vars(), n_phi); - Bmat_vk.reinit (n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + Bmat_vk.reinit(n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN); @@ -2950,21 +3089,14 @@ thermal_residual_boundary_velocity(const MAST::FunctionBase& p, &ref_temp_func = bc.get >("ref_temperature"); Real - vn = 0., - t = 0., - t0 = 0.; - - // modify the JxW_Vn by multiplying the normal velocity to it - for (unsigned int qp=0; qp>("thermal_jacobian_scaling")(scaling); + + for (unsigned int qp=0; qpinitialize_green_lagrange_strain_operator(qp, *fe, _local_sol, @@ -2996,27 +3128,27 @@ thermal_residual_boundary_velocity(const MAST::FunctionBase& p, // membrane strain Bmat_lin.vector_mult_transpose(vec3_n2, vec1_n1); - local_f += JxW_Vn[qp] * vec3_n2; + local_f += JxW[qp] * vec3_n2; if (_property.strain_type() == MAST::NONLINEAR_STRAIN) { - + // nonlinear strain operotor // x vec4_2 = mat_x.transpose() * vec1_n1; Bmat_nl_x.vector_mult_transpose(vec3_n2, vec4_2); - local_f.topRows(n2) += JxW_Vn[qp] * vec3_n2; + local_f.topRows(n2) += JxW[qp] * vec3_n2; // y vec4_2 = mat_y.transpose() * vec1_n1; Bmat_nl_y.vector_mult_transpose(vec3_n2, vec4_2); - local_f.topRows(n2) += JxW_Vn[qp] * vec3_n2; + local_f.topRows(n2) += JxW[qp] * vec3_n2; } - + if (bend.get()) { // bending strain bend->initialize_bending_strain_operator(*fe, qp, Bmat_bend); Bmat_bend.vector_mult_transpose(vec3_n2, vec2_n1); - local_f += JxW_Vn[qp] * vec3_n2; + local_f += JxW[qp] * vec3_n2; // von Karman strain if (if_vk) { @@ -3029,7 +3161,7 @@ thermal_residual_boundary_velocity(const MAST::FunctionBase& p, // von Karman strain vec4_2 = vk_dwdxi_mat.transpose() * vec1_n1; Bmat_vk.vector_mult_transpose(vec3_n2, vec4_2); - local_f += JxW_Vn[qp] * vec3_n2; + local_f += JxW[qp] * vec3_n2; } } @@ -3039,20 +3171,20 @@ thermal_residual_boundary_velocity(const MAST::FunctionBase& p, // u-disp Bmat_nl_u.left_multiply(mat3, stress); Bmat_nl_u.right_multiply_transpose(mat2_n2n2, mat3); - local_jac.topLeftCorner(n2, n2) += JxW_Vn[qp] * mat2_n2n2; + local_jac.topLeftCorner(n2, n2) += JxW[qp] * mat2_n2n2; // v-disp Bmat_nl_v.left_multiply(mat3, stress); Bmat_nl_v.right_multiply_transpose(mat2_n2n2, mat3); - local_jac.topLeftCorner(n2, n2) += JxW_Vn[qp] * mat2_n2n2; + local_jac.topLeftCorner(n2, n2) += JxW[qp] * mat2_n2n2; } - - if (request_jacobian && if_vk) { // Jacobian only for vk strain - // vk - vk - mat3 = RealMatrixX::Zero(2, n2); - Bmat_vk.left_multiply(mat3, stress); - Bmat_vk.right_multiply_transpose(mat2_n2n2, mat3); - local_jac += JxW_Vn[qp] * mat2_n2n2; + + if (request_jacobian && if_vk) { + // vk - vk + mat3 = RealMatrixX::Zero(2, n2); + Bmat_vk.left_multiply(mat3, stress); + Bmat_vk.right_multiply_transpose(mat2_n2n2, mat3); + local_jac += JxW[qp] * mat2_n2n2; } } @@ -3061,57 +3193,62 @@ thermal_residual_boundary_velocity(const MAST::FunctionBase& p, f -= vec3_n2; if (request_jacobian && if_vk) { transform_matrix_to_global_system(local_jac, mat2_n2n2); - jac -= mat2_n2n2; + jac -= scaling * mat2_n2n2; } + + // Jacobian contribution from von Karman strain + return request_jacobian; } -void + +bool MAST::StructuralElement2D:: -thermal_residual_temperature_derivative(const MAST::FEBase& fe_thermal, - RealMatrixX& m) { - - - const std::vector>& phi_temp = fe_thermal.get_phi(); - const std::vector& JxW = fe_thermal.get_JxW(); - const std::vector& xyz = fe_thermal.get_xyz(); - - std::unique_ptr fe_str(_elem.init_fe(true, false, - _property.extra_quadrature_order(_elem))); - libmesh_assert(fe_str->get_fe_type() == fe_thermal.get_fe_type()); - // this is a weak assertion. We really want that the same qpoints be used, - // but we are assuming that if the elem type is the same, and if the - // number fo qpoints is the same, then the location of qpoints will also - // be the same. - libmesh_assert_equal_to(fe_str->get_qpoints().size(), fe_thermal.get_qpoints().size()); - - - const unsigned int - n_phi_str = (unsigned int)fe_str->get_phi().size(), - nt = (unsigned int)fe_thermal.get_phi().size(), - n1 = this->n_direct_strain_components(), - n2 = 6*n_phi_str, - n3 = this->n_von_karman_strain_components(); +thermal_residual_sensitivity (const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + MAST::BoundaryConditionBase& bc) +{ + std::unique_ptr fe(_elem.init_fe(true, + false, + _property.extra_quadrature_order(_elem))); + + const std::vector& JxW = fe->get_JxW(); + const std::vector& xyz = fe->get_xyz(); + + const unsigned int + n_phi = (unsigned int)fe->get_phi().size(), + n1 = this->n_direct_strain_components(), + n2 = 6*n_phi, + n3 = this->n_von_karman_strain_components(); RealMatrixX material_exp_A_mat, material_exp_B_mat, - mat1_n1nt = RealMatrixX::Zero(n1,nt), - mat2_n1nt = RealMatrixX::Zero(n1,nt), - mat3_n2nt = RealMatrixX::Zero(n2,nt), - mat5, - vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), - stress = RealMatrixX::Zero(2,2), - mat_x = RealMatrixX::Zero(3,2), - mat_y = RealMatrixX::Zero(3,2), - Bmat_temp = RealMatrixX::Zero(1,nt), - local_m = RealMatrixX::Zero(n2,nt); + material_exp_A_mat_sens, + material_exp_B_mat_sens, + mat1_n1n2 = RealMatrixX::Zero(n1,n2), + mat2_n2n2 = RealMatrixX::Zero(n2,n2), + mat3 = RealMatrixX::Zero(2, n2), + mat4_n3n2 = RealMatrixX::Zero(n3,n2), + vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), + stress = RealMatrixX::Zero(2,2), + mat_x = RealMatrixX::Zero(3,2), + mat_y = RealMatrixX::Zero(3,2), + local_jac = RealMatrixX::Zero(n2,n2); RealVectorX - phi = RealVectorX::Zero(nt), - vec_n1 = RealVectorX::Zero(n1), - strain = RealVectorX::Zero(3); + vec1_n1 = RealVectorX::Zero(n1), + vec2_n1 = RealVectorX::Zero(n1), + vec3_n2 = RealVectorX::Zero(n2), + vec4_2 = RealVectorX::Zero(2), + vec5_n1 = RealVectorX::Zero(n1), + local_f = RealVectorX::Zero(n2), + strain = RealVectorX::Zero(3), + delta_t = RealVectorX::Zero(1), + delta_t_sens = RealVectorX::Zero(1); FEMOperatorMatrix Bmat_lin, @@ -3122,14 +3259,14 @@ thermal_residual_temperature_derivative(const MAST::FEBase& fe_thermal, Bmat_bend, Bmat_vk; - Bmat_lin.reinit(n1, _system.n_vars(), n_phi_str); // three stress-strain components - Bmat_nl_x.reinit(2, _system.n_vars(), n_phi_str); - Bmat_nl_y.reinit(2, _system.n_vars(), n_phi_str); - Bmat_nl_u.reinit(2, _system.n_vars(), n_phi_str); - Bmat_nl_v.reinit(2, _system.n_vars(), n_phi_str); - Bmat_bend.reinit(n1, _system.n_vars(), n_phi_str); - Bmat_vk.reinit(n3, _system.n_vars(), n_phi_str); // only dw/dx and dw/dy - + Bmat_lin.reinit(n1, _system.n_vars(), n_phi); // three stress-strain components + Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); + Bmat_bend.reinit(n1, _system.n_vars(), n_phi); + Bmat_vk.reinit(n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN); MAST::BendingOperatorType @@ -3140,24 +3277,56 @@ thermal_residual_temperature_derivative(const MAST::FEBase& fe_thermal, if (bending_model != MAST::NO_BENDING) bend.reset(MAST::build_bending_operator_2D(bending_model, *this, - fe_str->get_qpoints()).release()); + fe->get_qpoints()).release()); std::unique_ptr > expansion_A = _property.thermal_expansion_A_matrix(*this), expansion_B = _property.thermal_expansion_B_matrix(*this); + // temperature function + const MAST::FieldFunction + &temp_func = bc.get >("temperature"), + &ref_temp_func = bc.get >("ref_temperature"); + + Real t, t0, t_sens, t0_sens; + for (unsigned int qp=0; qpderivative(p, xyz[qp], _time, material_exp_A_mat_sens); + expansion_B->derivative(p, xyz[qp], _time, material_exp_B_mat_sens); + + // get the temperature function + temp_func(xyz[qp], _time, t); + ref_temp_func(xyz[qp], _time, t0); + delta_t(0) = t-t0; + + // get the temperature function + temp_func(xyz[qp], _time, t); + temp_func.derivative(p, xyz[qp], _time, t_sens); + ref_temp_func(xyz[qp], _time, t0); + ref_temp_func.derivative(p, xyz[qp], _time, t0_sens); + delta_t(0) = t-t0; + delta_t_sens(0) = t_sens-t0_sens; + + // now prepare the membrane force sensitivity + vec1_n1 = material_exp_A_mat * delta_t_sens; // [C]{alpha dT/dp} (with membrane strain) + vec2_n1 = material_exp_A_mat_sens * delta_t; // d([C]alpha)/dp (T - T0)} (with membrane strain) + vec1_n1 += vec2_n1; + stress(0,0) = vec1_n1(0); // sigma_xx + stress(0,1) = vec1_n1(2); // sigma_xy + stress(1,0) = vec1_n1(2); // sigma_yx + stress(1,1) = vec1_n1(1); // sigma_yy + + vec2_n1 = material_exp_B_mat * delta_t_sens; // [C]{alpha dT/dp} (with bending strain) + vec5_n1 = material_exp_B_mat_sens * delta_t; // d([C] alpha)/dp (T - T0) (with bending strain) + vec2_n1 += vec5_n1; + this->initialize_green_lagrange_strain_operator(qp, - *fe_str, + *fe, _local_sol, strain, mat_x, @@ -3167,451 +3336,1747 @@ thermal_residual_temperature_derivative(const MAST::FEBase& fe_thermal, Bmat_nl_y, Bmat_nl_u, Bmat_nl_v); - - mat1_n1nt = material_exp_A_mat * Bmat_temp; - mat2_n1nt = material_exp_B_mat * Bmat_temp; - Bmat_lin.right_multiply_transpose(mat3_n2nt, mat1_n1nt); - local_m += JxW[qp] * mat3_n2nt; - + // membrane strain + Bmat_lin.vector_mult_transpose(vec3_n2, vec1_n1); + local_f += JxW[qp] * vec3_n2; + if (_property.strain_type() == MAST::NONLINEAR_STRAIN) { // nonlinear strain operotor // x - mat5 = mat_x.transpose() * mat1_n1nt; - Bmat_nl_x.right_multiply_transpose(mat3_n2nt, mat5); - local_m.topRows(n2) += JxW[qp] * mat3_n2nt; + vec4_2 = mat_x.transpose() * vec1_n1; + Bmat_nl_x.vector_mult_transpose(vec3_n2, vec4_2); + local_f.topRows(n2) += JxW[qp] * vec3_n2; // y - mat5 = mat_y.transpose() * mat1_n1nt; - Bmat_nl_y.right_multiply_transpose(mat3_n2nt, mat5); - local_m.topRows(n2) += JxW[qp] * mat3_n2nt; + vec4_2 = mat_y.transpose() * vec1_n1; + Bmat_nl_y.vector_mult_transpose(vec3_n2, vec4_2); + local_f.topRows(n2) += JxW[qp] * vec3_n2; } - + if (bend.get()) { // bending strain - bend->initialize_bending_strain_operator(*fe_str, qp, Bmat_bend); - Bmat_bend.right_multiply_transpose(mat3_n2nt, mat2_n1nt); - local_m += JxW[qp] * mat3_n2nt; + bend->initialize_bending_strain_operator(*fe, qp, Bmat_bend); + Bmat_bend.vector_mult_transpose(vec3_n2, vec2_n1); + local_f += JxW[qp] * vec3_n2; // von Karman strain if (if_vk) { // get the vonKarman strain operator if needed this->initialize_von_karman_strain_operator(qp, - *fe_str, - vec_n1, // epsilon_vk + *fe, + vec2_n1, // epsilon_vk vk_dwdxi_mat, Bmat_vk); // von Karman strain - mat5 = vk_dwdxi_mat.transpose() * mat1_n1nt; - Bmat_vk.right_multiply_transpose(mat3_n2nt, mat5); - local_m += JxW[qp] * mat3_n2nt; + vec4_2 = vk_dwdxi_mat.transpose() * vec1_n1; + Bmat_vk.vector_mult_transpose(vec3_n2, vec4_2); + local_f += JxW[qp] * vec3_n2; } } - } - mat3_n2nt.setZero(); - phi = RealVectorX::Zero(n2); - vec_n1 = RealVectorX::Zero(n2); - for (unsigned int i=0; i& vel_f, + MAST::BoundaryConditionBase& bc, bool request_jacobian, - RealVectorX &f, - RealMatrixX& jac_xdot, - RealMatrixX& jac, - const unsigned int side, - MAST::BoundaryConditionBase& bc) { - - libmesh_assert(false); // to be implemented + RealVectorX& f, + RealMatrixX& jac) { - return (request_jacobian); -} - - - - + // prepare the side finite element + std::unique_ptr fe(_elem.init_side_fe(s, + true, + _property.extra_quadrature_order(_elem))); -bool -MAST::StructuralElement2D:: -piston_theory_residual(bool request_jacobian, - RealVectorX &f, - RealMatrixX& jac_xdot, - RealMatrixX& jac, - MAST::BoundaryConditionBase& bc) { - - libmesh_assert(_elem.dim() < 3); // only applicable for lower dimensional elements - libmesh_assert(!follower_forces); // not implemented yet for follower forces - - std::unique_ptr fe(_elem.init_fe(true, false)); + std::vector JxW_Vn = fe->get_JxW(); + const std::vector& xyz = fe->get_xyz(); + const std::vector& face_normals = fe->get_normals_for_local_coordinate(); - const std::vector &JxW = fe->get_JxW(); - const std::vector& qpoint = fe->get_xyz(); - const std::vector >& phi = fe->get_phi(); const unsigned int - n_phi = (unsigned int)phi.size(), - n1 = 2, - n2 = _system.n_vars()*n_phi; - - - // normal for face integration - libMesh::Point normal; - // direction of pressure assumed to be normal (along local z-axis) - // to the element face for 2D and along local y-axis for 1D element. - normal(_elem.dim()) = -1.; - - - // convert to piston theory boundary condition so that the necessary - // flow properties can be obtained - const MAST::PistonTheoryBoundaryCondition& piston_bc = - dynamic_cast(bc); + n_phi = (unsigned int)fe->get_phi().size(), + n1 = this->n_direct_strain_components(), n2=6*n_phi, + n3 = this->n_von_karman_strain_components(), + dim = 2; + RealMatrixX + material_exp_A_mat, + material_exp_B_mat, + mat1_n1n2 = RealMatrixX::Zero(n1,n2), + mat2_n2n2 = RealMatrixX::Zero(n2,n2), + mat3 = RealMatrixX::Zero(2, n2), + mat4_n3n2 = RealMatrixX::Zero(n3,n2), + vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), + stress = RealMatrixX::Zero(2,2), + mat_x = RealMatrixX::Zero(3,2), + mat_y = RealMatrixX::Zero(3,2), + local_jac = RealMatrixX::Zero(n2,n2); - // create the constant field functions to pass the dwdx and dwdt values - // to the piston theory pressure functions - MAST::Parameter - dwdx_p ("dwdx", 0.), - dwdt_p ("dwdt", 0.); - - MAST::ConstantFieldFunction - dwdx_f ("dwdx", dwdx_p), - dwdt_f ("dwdx", dwdt_p); + RealVectorX + vec1_n1 = RealVectorX::Zero(n1), + vec2_n1 = RealVectorX::Zero(n1), + vec3_n2 = RealVectorX::Zero(n2), + vec4_2 = RealVectorX::Zero(2), + vec5_n3 = RealVectorX::Zero(n3), + local_f = RealVectorX::Zero(n2), + delta_t = RealVectorX::Zero(1), + strain = RealVectorX::Zero(3), + vel = RealVectorX::Zero(dim); + FEMOperatorMatrix + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v, + Bmat_bend, + Bmat_vk; - std::unique_ptr > - pressure (piston_bc.get_pressure_function(dwdx_f, dwdt_f).release()), - dpressure_dx (piston_bc.get_dpdx_function (dwdx_f, dwdt_f).release()), - dpressure_dxdot (piston_bc.get_dpdxdot_function (dwdx_f, dwdt_f).release()); + Bmat_lin.reinit (n1, _system.n_vars(), n_phi); // three stress-strain components + Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); + Bmat_bend.reinit(n1, _system.n_vars(), n_phi); + Bmat_vk.reinit (n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + + bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN); - FEMOperatorMatrix - Bmat_w, // operator matrix for the w-displacement - dBmat; // operator matrix to calculate the derivativ of w wrt x and y + MAST::BendingOperatorType + bending_model = _property.bending_model(_elem); - dBmat.reinit(n1, _system.n_vars(), n_phi); + std::unique_ptr bend; - RealVectorX - phi_vec = RealVectorX::Zero(n_phi), - force = RealVectorX::Zero(n1), - local_f = RealVectorX::Zero(n2), - vec_n1 = RealVectorX::Zero(n1), - vec_n2 = RealVectorX::Zero(n2), - vel_vec = RealVectorX::Zero(3), - dummy = RealVectorX::Zero(3); + if (bending_model != MAST::NO_BENDING) + bend.reset(MAST::build_bending_operator_2D(bending_model, + *this, + fe->get_qpoints()).release()); - RealMatrixX - dwdx = RealMatrixX::Zero(3,2), - local_jac_xdot = RealMatrixX::Zero(n2,n2), - local_jac = RealMatrixX::Zero(n2,n2), - mat_n2n2 = RealMatrixX::Zero(n2,n2), - mat_n1n2 = RealMatrixX::Zero(n1,n2), - mat_22 = RealMatrixX::Zero(2,2); + std::unique_ptr > + expansion_A = _property.thermal_expansion_A_matrix(*this), + expansion_B = _property.thermal_expansion_B_matrix(*this); - // we need the velocity vector in the local coordinate system so that - // the appropriate component of the w-derivative can be used - vel_vec = _elem.T_matrix().transpose() * piston_bc.vel_vec(); + const MAST::FieldFunction + &temp_func = bc.get >("temperature"), + &ref_temp_func = bc.get >("ref_temperature"); Real - dwdt_val = 0., - dwdx_val = 0., - p_val = 0.; + vn = 0., + t = 0., + t0 = 0.; + // modify the JxW_Vn by multiplying the normal velocity to it + for (unsigned int qp=0; qpinitialize_von_karman_strain_operator(qp, - *fe, - dummy, - dwdx, - dBmat); + vec1_n1 = material_exp_A_mat * delta_t; // [C]{alpha (T - T0)} (with membrane strain) + vec2_n1 = material_exp_B_mat * delta_t; // [C]{alpha (T - T0)} (with bending strain) + stress(0,0) = vec1_n1(0); // sigma_xx + stress(0,1) = vec1_n1(2); // sigma_xy + stress(1,0) = vec1_n1(2); // sigma_yx + stress(1,1) = vec1_n1(1); // sigma_yy - // the diagonal of dwdx matrix stores the - dwdx_val = 0.; - for (unsigned int i=0; i<2; i++) - dwdx_val += dwdx(i,i) * vel_vec(i); // (dw/dx_i)*U_inf . n_i - - // calculate the pressure value - dwdx_p = dwdx_val; - dwdt_p = dwdt_val; - (*pressure)(qpoint[qp], _time, p_val); - - // calculate force - force(0) = p_val * normal(2); - - - Bmat_w.vector_mult_transpose(vec_n2, force); - local_f += JxW[qp] * vec_n2; - + this->initialize_green_lagrange_strain_operator(qp, + *fe, + _local_sol, + strain, + mat_x, + mat_y, + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v); - // calculate the Jacobian if requested - if (request_jacobian) { + // membrane strain + Bmat_lin.vector_mult_transpose(vec3_n2, vec1_n1); + local_f += JxW_Vn[qp] * vec3_n2; + + if (_property.strain_type() == MAST::NONLINEAR_STRAIN) { - // we need the derivative of cp wrt normal velocity - (*dpressure_dxdot)(qpoint[qp], _time, p_val); + // nonlinear strain operotor + // x + vec4_2 = mat_x.transpose() * vec1_n1; + Bmat_nl_x.vector_mult_transpose(vec3_n2, vec4_2); + local_f.topRows(n2) += JxW_Vn[qp] * vec3_n2; - // calculate the component of Jacobian due to w-velocity - Bmat_w.right_multiply_transpose(mat_n2n2, Bmat_w); - local_jac_xdot += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + // y + vec4_2 = mat_y.transpose() * vec1_n1; + Bmat_nl_y.vector_mult_transpose(vec3_n2, vec4_2); + local_f.topRows(n2) += JxW_Vn[qp] * vec3_n2; + } - // now calculate the component of Jacobian - (*dpressure_dx)(qpoint[qp], _time, p_val); + if (bend.get()) { + // bending strain + bend->initialize_bending_strain_operator(*fe, qp, Bmat_bend); + Bmat_bend.vector_mult_transpose(vec3_n2, vec2_n1); + local_f += JxW_Vn[qp] * vec3_n2; - // derivative wrt x - mat_22.setZero(2,2); - mat_22(0,0) = vel_vec(0); - dBmat.left_multiply(mat_n1n2, mat_22); - Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dx - local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + // von Karman strain + if (if_vk) { + // get the vonKarman strain operator if needed + this->initialize_von_karman_strain_operator(qp, + *fe, + vec2_n1, // epsilon_vk + vk_dwdxi_mat, + Bmat_vk); + // von Karman strain + vec4_2 = vk_dwdxi_mat.transpose() * vec1_n1; + Bmat_vk.vector_mult_transpose(vec3_n2, vec4_2); + local_f += JxW_Vn[qp] * vec3_n2; + } + } + + if (request_jacobian && + _property.strain_type() == MAST::NONLINEAR_STRAIN) { - // derivative wrt y - mat_22.setZero(2,2); - mat_22(1,1) = vel_vec(1); - dBmat.left_multiply(mat_n1n2, mat_22); - Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dy - local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + // u-disp + Bmat_nl_u.left_multiply(mat3, stress); + Bmat_nl_u.right_multiply_transpose(mat2_n2n2, mat3); + local_jac.topLeftCorner(n2, n2) += JxW_Vn[qp] * mat2_n2n2; + + // v-disp + Bmat_nl_v.left_multiply(mat3, stress); + Bmat_nl_v.right_multiply_transpose(mat2_n2n2, mat3); + local_jac.topLeftCorner(n2, n2) += JxW_Vn[qp] * mat2_n2n2; } - } - - - // now transform to the global system and add - transform_vector_to_global_system(local_f, vec_n2); - f -= vec_n2; + + if (request_jacobian && if_vk) { // Jacobian only for vk strain + // vk - vk + mat3 = RealMatrixX::Zero(2, n2); + Bmat_vk.left_multiply(mat3, stress); + Bmat_vk.right_multiply_transpose(mat2_n2n2, mat3); + local_jac += JxW_Vn[qp] * mat2_n2n2; + } + } + + // now transform to the global coorodinate system + transform_vector_to_global_system(local_f, vec3_n2); + f -= vec3_n2; + if (request_jacobian && if_vk) { + transform_matrix_to_global_system(local_jac, mat2_n2n2); + jac -= mat2_n2n2; + } +} + + + +void +MAST::StructuralElement2D:: +thermal_residual_temperature_derivative(const MAST::FEBase& fe_thermal, + RealMatrixX& m) { + + + const std::vector>& phi_temp = fe_thermal.get_phi(); + const std::vector& JxW = fe_thermal.get_JxW(); + const std::vector& xyz = fe_thermal.get_xyz(); + + std::unique_ptr fe_str(_elem.init_fe(true, false, + _property.extra_quadrature_order(_elem))); + libmesh_assert(fe_str->get_fe_type() == fe_thermal.get_fe_type()); + // this is a weak assertion. We really want that the same qpoints be used, + // but we are assuming that if the elem type is the same, and if the + // number fo qpoints is the same, then the location of qpoints will also + // be the same. + libmesh_assert_equal_to(fe_str->get_qpoints().size(), fe_thermal.get_qpoints().size()); + + + const unsigned int + n_phi_str = (unsigned int)fe_str->get_phi().size(), + nt = (unsigned int)fe_thermal.get_phi().size(), + n1 = this->n_direct_strain_components(), + n2 = 6*n_phi_str, + n3 = this->n_von_karman_strain_components(); + + RealMatrixX + material_exp_A_mat, + material_exp_B_mat, + mat1_n1nt = RealMatrixX::Zero(n1,nt), + mat2_n1nt = RealMatrixX::Zero(n1,nt), + mat3_n2nt = RealMatrixX::Zero(n2,nt), + mat5, + vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), + stress = RealMatrixX::Zero(2,2), + mat_x = RealMatrixX::Zero(3,2), + mat_y = RealMatrixX::Zero(3,2), + Bmat_temp = RealMatrixX::Zero(1,nt), + local_m = RealMatrixX::Zero(n2,nt); + + RealVectorX + phi = RealVectorX::Zero(nt), + vec_n1 = RealVectorX::Zero(n1), + strain = RealVectorX::Zero(3); + + FEMOperatorMatrix + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v, + Bmat_bend, + Bmat_vk; + + Bmat_lin.reinit(n1, _system.n_vars(), n_phi_str); // three stress-strain components + Bmat_nl_x.reinit(2, _system.n_vars(), n_phi_str); + Bmat_nl_y.reinit(2, _system.n_vars(), n_phi_str); + Bmat_nl_u.reinit(2, _system.n_vars(), n_phi_str); + Bmat_nl_v.reinit(2, _system.n_vars(), n_phi_str); + Bmat_bend.reinit(n1, _system.n_vars(), n_phi_str); + Bmat_vk.reinit(n3, _system.n_vars(), n_phi_str); // only dw/dx and dw/dy + + bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN); + + MAST::BendingOperatorType + bending_model = _property.bending_model(_elem); + + std::unique_ptr bend; + + if (bending_model != MAST::NO_BENDING) + bend.reset(MAST::build_bending_operator_2D(bending_model, + *this, + fe_str->get_qpoints()).release()); + + std::unique_ptr > + expansion_A = _property.thermal_expansion_A_matrix(*this), + expansion_B = _property.thermal_expansion_B_matrix(*this); + + for (unsigned int qp=0; qpinitialize_green_lagrange_strain_operator(qp, + *fe_str, + _local_sol, + strain, + mat_x, + mat_y, + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v); + + mat1_n1nt = material_exp_A_mat * Bmat_temp; + mat2_n1nt = material_exp_B_mat * Bmat_temp; + Bmat_lin.right_multiply_transpose(mat3_n2nt, mat1_n1nt); + local_m += JxW[qp] * mat3_n2nt; + + + if (_property.strain_type() == MAST::NONLINEAR_STRAIN) { + + // nonlinear strain operotor + // x + mat5 = mat_x.transpose() * mat1_n1nt; + Bmat_nl_x.right_multiply_transpose(mat3_n2nt, mat5); + local_m.topRows(n2) += JxW[qp] * mat3_n2nt; + + // y + mat5 = mat_y.transpose() * mat1_n1nt; + Bmat_nl_y.right_multiply_transpose(mat3_n2nt, mat5); + local_m.topRows(n2) += JxW[qp] * mat3_n2nt; + } + + if (bend.get()) { + // bending strain + bend->initialize_bending_strain_operator(*fe_str, qp, Bmat_bend); + Bmat_bend.right_multiply_transpose(mat3_n2nt, mat2_n1nt); + local_m += JxW[qp] * mat3_n2nt; + + // von Karman strain + if (if_vk) { + // get the vonKarman strain operator if needed + this->initialize_von_karman_strain_operator(qp, + *fe_str, + vec_n1, // epsilon_vk + vk_dwdxi_mat, + Bmat_vk); + // von Karman strain + mat5 = vk_dwdxi_mat.transpose() * mat1_n1nt; + Bmat_vk.right_multiply_transpose(mat3_n2nt, mat5); + local_m += JxW[qp] * mat3_n2nt; + } + } + } + + mat3_n2nt.setZero(); + phi = RealVectorX::Zero(n2); + vec_n1 = RealVectorX::Zero(n2); + for (unsigned int i=0; i fe(_elem.init_fe(true, false)); + + const std::vector &JxW = fe->get_JxW(); + const std::vector& qpoint = fe->get_xyz(); + const std::vector >& phi = fe->get_phi(); + const unsigned int + n_phi = (unsigned int)phi.size(), + n1 = 2, + n2 = _system.n_vars()*n_phi; + + + // normal for face integration + libMesh::Point normal; + // direction of pressure assumed to be normal (along local z-axis) + // to the element face for 2D and along local y-axis for 1D element. + normal(_elem.dim()) = -1.; + + + // convert to piston theory boundary condition so that the necessary + // flow properties can be obtained + const MAST::PistonTheoryBoundaryCondition& piston_bc = + dynamic_cast(bc); + + + // create the constant field functions to pass the dwdx and dwdt values + // to the piston theory pressure functions + MAST::Parameter + dwdx_p ("dwdx", 0.), + dwdt_p ("dwdt", 0.); + + MAST::ConstantFieldFunction + dwdx_f ("dwdx", dwdx_p), + dwdt_f ("dwdx", dwdt_p); + + + std::unique_ptr > + pressure (piston_bc.get_pressure_function(dwdx_f, dwdt_f).release()), + dpressure_dx (piston_bc.get_dpdx_function (dwdx_f, dwdt_f).release()), + dpressure_dxdot (piston_bc.get_dpdxdot_function (dwdx_f, dwdt_f).release()); + + FEMOperatorMatrix + Bmat_w, // operator matrix for the w-displacement + dBmat; // operator matrix to calculate the derivativ of w wrt x and y + + dBmat.reinit(n1, _system.n_vars(), n_phi); + + RealVectorX + phi_vec = RealVectorX::Zero(n_phi), + force = RealVectorX::Zero(n1), + local_f = RealVectorX::Zero(n2), + vec_n1 = RealVectorX::Zero(n1), + vec_n2 = RealVectorX::Zero(n2), + vel_vec = RealVectorX::Zero(3), + dummy = RealVectorX::Zero(3); + + RealMatrixX + dwdx = RealMatrixX::Zero(3,2), + local_jac_xdot = RealMatrixX::Zero(n2,n2), + local_jac = RealMatrixX::Zero(n2,n2), + mat_n2n2 = RealMatrixX::Zero(n2,n2), + mat_n1n2 = RealMatrixX::Zero(n1,n2), + mat_22 = RealMatrixX::Zero(2,2); + + // we need the velocity vector in the local coordinate system so that + // the appropriate component of the w-derivative can be used + vel_vec = _elem.T_matrix().transpose() * piston_bc.vel_vec(); + + Real + dwdt_val = 0., + dwdx_val = 0., + p_val = 0.; + + + for (unsigned int qp=0; qpinitialize_von_karman_strain_operator(qp, + *fe, + dummy, + dwdx, + dBmat); + + // the diagonal of dwdx matrix stores the + dwdx_val = 0.; + for (unsigned int i=0; i<2; i++) + dwdx_val += dwdx(i,i) * vel_vec(i); // (dw/dx_i)*U_inf . n_i + + // calculate the pressure value + dwdx_p = dwdx_val; + dwdt_p = dwdt_val; + (*pressure)(qpoint[qp], _time, p_val); + + // calculate force + force(0) = p_val * normal(2); + + + Bmat_w.vector_mult_transpose(vec_n2, force); + local_f += JxW[qp] * vec_n2; + + + // calculate the Jacobian if requested + if (request_jacobian) { + + // we need the derivative of cp wrt normal velocity + (*dpressure_dxdot)(qpoint[qp], _time, p_val); + + // calculate the component of Jacobian due to w-velocity + Bmat_w.right_multiply_transpose(mat_n2n2, Bmat_w); + local_jac_xdot += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + + // now calculate the component of Jacobian + (*dpressure_dx)(qpoint[qp], _time, p_val); + + // derivative wrt x + mat_22.setZero(2,2); + mat_22(0,0) = vel_vec(0); + dBmat.left_multiply(mat_n1n2, mat_22); + Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dx + local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + + // derivative wrt y + mat_22.setZero(2,2); + mat_22(1,1) = vel_vec(1); + dBmat.left_multiply(mat_n1n2, mat_22); + Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dy + local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + } + } + + + // now transform to the global system and add + transform_vector_to_global_system(local_f, vec_n2); + f -= vec_n2; + + // if the Jacobian was requested, then transform it and add to the + // global Jacobian + if (request_jacobian) { + transform_matrix_to_global_system(local_jac_xdot, mat_n2n2); + jac_xdot -= mat_n2n2; + + transform_matrix_to_global_system(local_jac, mat_n2n2); + jac -= mat_n2n2; + } + + return request_jacobian; +} + + + + +bool +MAST::StructuralElement2D:: +piston_theory_residual_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX &f, + RealMatrixX& jac_xdot, + RealMatrixX& jac, + MAST::BoundaryConditionBase& bc) { + + + libmesh_assert(_elem.dim() < 3); // only applicable for lower dimensional elements + libmesh_assert(!follower_forces); // not implemented yet for follower forces + + std::unique_ptr fe(_elem.init_fe(true, false)); + + const std::vector &JxW = fe->get_JxW(); + const std::vector& qpoint = fe->get_xyz(); + const std::vector >& phi = fe->get_phi(); + const unsigned int + n_phi = (unsigned int)phi.size(), + n1 = 2, + n2 = _system.n_vars()*n_phi; + + + // normal for face integration + libMesh::Point normal; + // direction of pressure assumed to be normal (along local z-axis) + // to the element face for 2D and along local y-axis for 1D element. + normal(_elem.dim()) = -1.; + + + // convert to piston theory boundary condition so that the necessary + // flow properties can be obtained + const MAST::PistonTheoryBoundaryCondition& piston_bc = + dynamic_cast(bc); + + + // create the constant field functions to pass the dwdx and dwdt values + // to the piston theory pressure functions + MAST::Parameter + dwdx_p ("dwdx", 0.), + dwdt_p ("dwdt", 0.); + + MAST::ConstantFieldFunction + dwdx_f ("dwdx", dwdx_p), + dwdt_f ("dwdx", dwdt_p); + + + std::unique_ptr > + pressure (piston_bc.get_pressure_function(dwdx_f, dwdt_f).release()), + dpressure_dx (piston_bc.get_dpdx_function (dwdx_f, dwdt_f).release()), + dpressure_dxdot (piston_bc.get_dpdxdot_function (dwdx_f, dwdt_f).release()); + + FEMOperatorMatrix + Bmat_w, // operator matrix for the w-displacement + dBmat; // operator matrix to calculate the derivativ of w wrt x and y + + dBmat.reinit(n1, _system.n_vars(), n_phi); + + RealVectorX + phi_vec = RealVectorX::Zero(n_phi), + force = RealVectorX::Zero(n1), + local_f = RealVectorX::Zero(n2), + vec_n1 = RealVectorX::Zero(n1), + vec_n2 = RealVectorX::Zero(n2), + vel_vec = RealVectorX::Zero(3), + dummy = RealVectorX::Zero(3); + + RealMatrixX + dwdx = RealMatrixX::Zero(3,2), + local_jac_xdot = RealMatrixX::Zero(n2,n2), + local_jac = RealMatrixX::Zero(n2,n2), + mat_n2n2 = RealMatrixX::Zero(n2,n2), + mat_n1n2 = RealMatrixX::Zero(n1,n2), + mat_22 = RealMatrixX::Zero(2,2); + + // we need the velocity vector in the local coordinate system so that + // the appropriate component of the w-derivative can be used + vel_vec = _elem.T_matrix().transpose() * piston_bc.vel_vec(); + + Real + dwdt_val = 0., + dwdx_val = 0., + p_val = 0.; + + + for (unsigned int qp=0; qpinitialize_von_karman_strain_operator(qp, + *fe, + dummy, + dwdx, + dBmat); + + // the diagonal of dwdx matrix stores the + dwdx_val = 0.; + for (unsigned int i=0; i<2; i++) + dwdx_val += dwdx(i,i) * vel_vec(i); // (dw/dx_i)*U_inf . n_i + + // calculate the pressure value + dwdx_p = dwdx_val; + dwdt_p = dwdt_val; + pressure->derivative(p, qpoint[qp], _time, p_val); + + // calculate force + force(0) = p_val * normal(2); + + + Bmat_w.vector_mult_transpose(vec_n2, force); + local_f += JxW[qp] * vec_n2; + + + // calculate the Jacobian if requested + if (request_jacobian) { + + // we need the derivative of cp wrt normal velocity + dpressure_dxdot->derivative(p, qpoint[qp], _time, p_val); + + // calculate the component of Jacobian due to w-velocity + Bmat_w.right_multiply_transpose(mat_n2n2, Bmat_w); + local_jac_xdot += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + + // now calculate the component of Jacobian + dpressure_dx->derivative(p, qpoint[qp], _time, p_val); + + // derivative wrt x + mat_22.setZero(2,2); + mat_22(0,0) = vel_vec(0); + dBmat.left_multiply(mat_n1n2, mat_22); + Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dx + local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + + // derivative wrt y + mat_22.setZero(2,2); + mat_22(1,1) = vel_vec(1); + dBmat.left_multiply(mat_n1n2, mat_22); + Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dy + local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + } + } + + + // now transform to the global system and add + transform_vector_to_global_system(local_f, vec_n2); + f -= vec_n2; + + // if the Jacobian was requested, then transform it and add to the + // global Jacobian + if (request_jacobian) { + transform_matrix_to_global_system(local_jac_xdot, mat_n2n2); + jac_xdot -= mat_n2n2; + + transform_matrix_to_global_system(local_jac, mat_n2n2); + jac -= mat_n2n2; + } + + + return request_jacobian; +} + + + + + + +void +MAST::StructuralElement2D::_compute_stress(MAST::FEBase& fe, + unsigned int qp, + RealVectorX& stress, + RealMatrixX* dstress_dX) { + + /* + MAST::BendingOperatorType bending_model = + _property.bending_model(_elem); + + std::unique_ptr + bend(MAST::build_bending_operator_2D(bending_model, + *this, + qp_loc_fe).release()); + */ + + std::vector JxW = fe.get_JxW(); + const std::vector& xyz = fe.get_xyz(); + + const unsigned int + n_phi = (unsigned int)fe.n_shape_functions(), + n1 = this->n_direct_strain_components(), + n2 = 6*n_phi, + n3 = this->n_von_karman_strain_components(); + + /* + Real + z = 0., + z_off = 0., + temp = 0., + ref_t = 0., + alpha = 0., + dtemp = 0., + dref_t= 0., + dalpha= 0.; + */ + + RealMatrixX + material_mat, + vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), + dstrain_dX = RealMatrixX::Zero(n1,n2), + mat_n1n2 = RealMatrixX::Zero(n1,n2), + eye = RealMatrixX::Identity(n1, n1), + mat_x = RealMatrixX::Zero(3,2), + mat_y = RealMatrixX::Zero(3,2), + dstrain_dX_3D= RealMatrixX::Zero(6,n2), + dstress_dX_3D= RealMatrixX::Zero(6,n2); + + RealVectorX + strain = RealVectorX::Zero(n1), + strain_vk = RealVectorX::Zero(n1), + strain_bend = RealVectorX::Zero(n1), + strain_3D = RealVectorX::Zero(6), + stress_3D = RealVectorX::Zero(6), + dstrain_dp = RealVectorX::Zero(n1), + dstress_dp = RealVectorX::Zero(n1), + vec1 = RealVectorX::Zero(n2), + vec2 = RealVectorX::Zero(n2); + + + FEMOperatorMatrix + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v, + Bmat_bend, + Bmat_vk; + + Bmat_lin.reinit (n1, _system.n_vars(), n_phi); // three stress-strain components + Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); + Bmat_bend.reinit(n1, _system.n_vars(), n_phi); + Bmat_vk.reinit (n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + + // TODO: remove this const-cast, which may need change in API of + // material card + const MAST::FieldFunction& + mat_stiff = + const_cast(_property.get_material()). + stiffness_matrix(2); + + /* + // get the thickness values for the bending strain calculation + const MAST::FieldFunction + &h = _property.get >("h"), + &h_off = _property.get >("off"); + + + bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN), + if_bending = (_property.bending_model(_elem) != MAST::NO_BENDING); + + + // check to see if the element has any thermal loads specified + // The object returns null + MAST::BoundaryConditionBase *thermal_load = + stress_output.get_thermal_load_for_elem(_elem); + + const MAST::FieldFunction + *temp_func = nullptr, + *ref_temp_func = nullptr, + *alpha_func = nullptr; + + // get pointers to the temperature, if thermal load is specified + if (thermal_load) { + temp_func = + &(thermal_load->get >("temperature")); + ref_temp_func = + &(thermal_load->get >("ref_temperature")); + alpha_func = + &(_property.get_material().get >("alpha_expansion")); + } + */ + + /////////////////////////////////////////////////////////////////////// + // get the material matrix + mat_stiff(xyz[qp], _time, material_mat); + + this->initialize_green_lagrange_strain_operator(qp, + fe, + _local_sol, + strain, + mat_x, + mat_y, + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v); + + /* + // if thermal load was specified, then set the thermal strain + // component of the total strain + if (thermal_load) { + (*temp_func) (xyz[qp_loc_index], _time, temp); + (*ref_temp_func)(xyz[qp_loc_index], _time, ref_t); + (*alpha_func) (xyz[qp_loc_index], _time, alpha); + strain(0) -= alpha*(temp-ref_t); // epsilon-xx + strain(1) -= alpha*(temp-ref_t); // epsilon-yy + } + + if (if_bending) { + + // von Karman strain + if (if_vk) { // get the vonKarman strain operator if needed + + this->initialize_von_karman_strain_operator(qp, + fe, + strain_vk, + vk_dwdxi_mat, + Bmat_vk); + strain += strain_vk; + } + + // add to this the bending strain + // TODO: add coupling due to h_offset + h (xyz[qp], _time, z); + h_off(xyz[qp], _time, z_off); + // TODO: this assumes isotropic section. Multilayered sections need + // special considerations + bend->initialize_bending_strain_operator_for_z(fe, + qp, + qp_loc[qp](2) * z/2.+z_off, + Bmat_bend); + Bmat_bend.vector_mult(strain_bend, _local_sol); + + + // add stress due to bending. + strain += strain_bend; + }*/ + + // note that this assumes linear material laws + stress = material_mat * strain; + + // calculate the derivative if requested + if (dstress_dX) { + + Bmat_lin.left_multiply(dstrain_dX, eye); + + /*if (_property.strain_type() == MAST::NONLINEAR_STRAIN) { + + Bmat_nl_x.left_multiply(mat_n1n2, mat_x); + dstrain_dX += mat_n1n2; + Bmat_nl_y.left_multiply(mat_n1n2, mat_y); + dstrain_dX += mat_n1n2; + } + + if (if_bending) { + + // von Karman strain + if (if_vk) { + + Bmat_vk.left_multiply(mat_n1n2, vk_dwdxi_mat); + dstrain_dX += mat_n1n2; + } + + // bending strain + Bmat_bend.left_multiply(mat_n1n2, eye); + dstrain_dX += mat_n1n2; + } + */ + + // note: this assumes linear material laws + *dstress_dX = material_mat * dstrain_dX; + + /* + if (p) { + // sensitivity of the response, s, is + // ds/dp = partial s/partial p + + // partial s/partial X dX/dp + // the first part of the sensitivity is obtained from + // + // the first term includes direct sensitivity of the stress + // with respect to the parameter, while holding the solution + // constant. This should include influence of shape changes, + // if the parameter is shape-dependent. + // TODO: include shape sensitivity. + // presently, only material parameter is included + + + dstrain_dp = RealVectorX::Zero(n1); + + // if thermal load was specified, then set the thermal strain + // component of the total strain + if (thermal_load) { + temp_func->derivative(*p, xyz[qp_loc_index], _time, dtemp); + ref_temp_func->derivative(*p, xyz[qp_loc_index], _time, dref_t); + alpha_func->derivative(*p, xyz[qp_loc_index], _time, dalpha); + dstrain_dp(0) -= alpha*(dtemp-dref_t) + dalpha*(temp-ref_t); // epsilon-xx + dstrain_dp(1) -= alpha*(dtemp-dref_t) + dalpha*(temp-ref_t); // epsilon-yy + } + + + + if (if_bending) { + + // add to this the bending strain + h.derivative (*p, + xyz[qp_loc_index], _time, z); + h_off.derivative(*p, + xyz[qp_loc_index], _time, z_off); + // TODO: this assumes isotropic section. Multilayered sections need + // special considerations + bend->initialize_bending_strain_operator_for_z(*fe, + qp_loc_index, + qp_loc[qp](2) * z/2.+z_off, + Bmat_bend); + Bmat_bend.vector_mult(strain_bend, _local_sol); + + + // add stress due to bending. + dstrain_dp += strain_bend; + } + + + // now use this to calculate the stress sensitivity. + dstress_dp = material_mat * dstrain_dp; + + // get the material matrix sensitivity + mat_stiff.derivative(*p, + xyz[qp_loc_index], + _time, + material_mat); + + // partial sensitivity of strain is zero unless it is a + // shape parameter. + // TODO: shape sensitivity of strain operator + + // now use this to calculate the stress sensitivity. + dstress_dp += material_mat * strain; + + // + // use the derivative data to evaluate the second term in the + // sensitivity + // + dstress_dp += dstress_dX * _local_sol_sens; + dstrain_dp += dstrain_dX * _local_sol_sens; + + // copy the 3D object + stress_3D(0) = dstress_dp(0); // sigma-xx + stress_3D(1) = dstress_dp(1); // sigma-yy + stress_3D(3) = dstress_dp(2); // tau-xy + strain_3D(0) = dstrain_dp(0); // epsilon-xx + strain_3D(1) = dstrain_dp(1); // epsilon-yy + strain_3D(3) = dstrain_dp(2); // gamma-xy + + // tell the data object about the sensitivity values + data->set_sensitivity(*p, + stress_3D, + strain_3D); + } + */ + } +} + + + +void +MAST::StructuralElement2D::_compute_stress_sensitivity(const MAST::FunctionBase& p, + MAST::FEBase& fe, + unsigned int qp, + RealVectorX& stress) { - // if the Jacobian was requested, then transform it and add to the - // global Jacobian - if (request_jacobian) { - transform_matrix_to_global_system(local_jac_xdot, mat_n2n2); - jac_xdot -= mat_n2n2; + /* + MAST::BendingOperatorType bending_model = + _property.bending_model(_elem); + + std::unique_ptr + bend(MAST::build_bending_operator_2D(bending_model, + *this, + qp_loc_fe).release()); + */ + + std::vector JxW = fe.get_JxW(); + const std::vector& xyz = fe.get_xyz(); + + const unsigned int + n_phi = (unsigned int)fe.n_shape_functions(), + n1 = this->n_direct_strain_components(), + n2 = 6*n_phi, + n3 = this->n_von_karman_strain_components(); + + /* + Real + z = 0., + z_off = 0., + temp = 0., + ref_t = 0., + alpha = 0., + dtemp = 0., + dref_t= 0., + dalpha= 0.; + */ + + RealMatrixX + material_mat, + vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), + dstrain_dX = RealMatrixX::Zero(n1,n2), + mat_n1n2 = RealMatrixX::Zero(n1,n2), + eye = RealMatrixX::Identity(n1, n1), + mat_x = RealMatrixX::Zero(3,2), + mat_y = RealMatrixX::Zero(3,2), + dstrain_dX_3D= RealMatrixX::Zero(6,n2), + dstress_dX_3D= RealMatrixX::Zero(6,n2); + + RealVectorX + strain = RealVectorX::Zero(n1), + strain_vk = RealVectorX::Zero(n1), + strain_bend = RealVectorX::Zero(n1), + strain_3D = RealVectorX::Zero(6), + stress_3D = RealVectorX::Zero(6), + dstrain_dp = RealVectorX::Zero(n1), + dstress_dp = RealVectorX::Zero(n1), + vec1 = RealVectorX::Zero(n2), + vec2 = RealVectorX::Zero(n2); + + + FEMOperatorMatrix + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v, + Bmat_bend, + Bmat_vk; + + Bmat_lin.reinit (n1, _system.n_vars(), n_phi); // three stress-strain components + Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); + Bmat_bend.reinit(n1, _system.n_vars(), n_phi); + Bmat_vk.reinit (n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + + // TODO: remove this const-cast, which may need change in API of + // material card + const MAST::FieldFunction& + mat_stiff = + const_cast(_property.get_material()). + stiffness_matrix(2); + + /* + // get the thickness values for the bending strain calculation + const MAST::FieldFunction + &h = _property.get >("h"), + &h_off = _property.get >("off"); + + + bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN), + if_bending = (_property.bending_model(_elem) != MAST::NO_BENDING); + + + // check to see if the element has any thermal loads specified + // The object returns null + MAST::BoundaryConditionBase *thermal_load = + stress_output.get_thermal_load_for_elem(_elem); + + const MAST::FieldFunction + *temp_func = nullptr, + *ref_temp_func = nullptr, + *alpha_func = nullptr; + + // get pointers to the temperature, if thermal load is specified + if (thermal_load) { + temp_func = + &(thermal_load->get >("temperature")); + ref_temp_func = + &(thermal_load->get >("ref_temperature")); + alpha_func = + &(_property.get_material().get >("alpha_expansion")); + } + */ + + /////////////////////////////////////////////////////////////////////// + // get the material matrix + mat_stiff.derivative(p, xyz[qp], _time, material_mat); + + this->initialize_green_lagrange_strain_operator(qp, + fe, + _local_sol, + strain, + mat_x, + mat_y, + Bmat_lin, + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v); + + /* + // if thermal load was specified, then set the thermal strain + // component of the total strain + if (thermal_load) { + (*temp_func) (xyz[qp_loc_index], _time, temp); + (*ref_temp_func)(xyz[qp_loc_index], _time, ref_t); + (*alpha_func) (xyz[qp_loc_index], _time, alpha); + strain(0) -= alpha*(temp-ref_t); // epsilon-xx + strain(1) -= alpha*(temp-ref_t); // epsilon-yy + } + + if (if_bending) { + + // von Karman strain + if (if_vk) { // get the vonKarman strain operator if needed + + this->initialize_von_karman_strain_operator(qp, + fe, + strain_vk, + vk_dwdxi_mat, + Bmat_vk); + strain += strain_vk; + } + + // add to this the bending strain + // TODO: add coupling due to h_offset + h (xyz[qp], _time, z); + h_off(xyz[qp], _time, z_off); + // TODO: this assumes isotropic section. Multilayered sections need + // special considerations + bend->initialize_bending_strain_operator_for_z(fe, + qp, + qp_loc[qp](2) * z/2.+z_off, + Bmat_bend); + Bmat_bend.vector_mult(strain_bend, _local_sol); + + + // add stress due to bending. + strain += strain_bend; + }*/ + + // note that this assumes linear material laws + stress = material_mat * strain; +} + + + +void +MAST::StructuralElement2D:: +_compute_stress_gradient(MAST::FEBase& fe, + unsigned int qp, + RealMatrixX& stress_grad, + std::vector* dstress_grad_dX) { + + /* + MAST::BendingOperatorType bending_model = + _property.bending_model(_elem); + + std::unique_ptr + bend(MAST::build_bending_operator_2D(bending_model, + *this, + qp_loc_fe).release()); + */ + + std::vector JxW = fe.get_JxW(); + const std::vector& xyz = fe.get_xyz(); + + const unsigned int + n_phi = (unsigned int)fe.n_shape_functions(), + n1 = this->n_direct_strain_components(), + n2 = 6*n_phi, + n3 = this->n_von_karman_strain_components(); + + /* + Real + z = 0., + z_off = 0., + temp = 0., + ref_t = 0., + alpha = 0., + dtemp = 0., + dref_t= 0., + dalpha= 0.; + */ + + RealMatrixX + material_mat, + vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), + dstrain = RealMatrixX::Zero(n1,2), + dstrain_dX = RealMatrixX::Zero(n1,n2), + mat_n1n2 = RealMatrixX::Zero(n1,n2), + eye = RealMatrixX::Identity(n1, n1), + mat_x = RealMatrixX::Zero(3,2), + mat_y = RealMatrixX::Zero(3,2), + dstrain_dX_3D= RealMatrixX::Zero(6,n2), + dstress_dX_3D= RealMatrixX::Zero(6,n2); + + RealVectorX + strain_vk = RealVectorX::Zero(n1), + strain_bend = RealVectorX::Zero(n1), + strain_3D = RealVectorX::Zero(6), + stress_3D = RealVectorX::Zero(6), + dstrain_dp = RealVectorX::Zero(n1), + dstress_dp = RealVectorX::Zero(n1), + vec1 = RealVectorX::Zero(n2), + vec2 = RealVectorX::Zero(n2); + + + FEMOperatorMatrix + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v, + Bmat_bend, + Bmat_vk; + + std::vector + dBmat_lin(2); + + for (unsigned int i=0; i<2; i++) + dBmat_lin[i].reinit (n1, _system.n_vars(), n_phi); // three stress-strain components + + /* + Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); + Bmat_bend.reinit(n1, _system.n_vars(), n_phi); + Bmat_vk.reinit (n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + */ + + // TODO: remove this const-cast, which may need change in API of + // material card + const MAST::FieldFunction& + mat_stiff = + const_cast(_property.get_material()). + stiffness_matrix(2); + + /* + // get the thickness values for the bending strain calculation + const MAST::FieldFunction + &h = _property.get >("h"), + &h_off = _property.get >("off"); + + + bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN), + if_bending = (_property.bending_model(_elem) != MAST::NO_BENDING); + + + // check to see if the element has any thermal loads specified + // The object returns null + MAST::BoundaryConditionBase *thermal_load = + stress_output.get_thermal_load_for_elem(_elem); + + const MAST::FieldFunction + *temp_func = nullptr, + *ref_temp_func = nullptr, + *alpha_func = nullptr; + + // get pointers to the temperature, if thermal load is specified + if (thermal_load) { + temp_func = + &(thermal_load->get >("temperature")); + ref_temp_func = + &(thermal_load->get >("ref_temperature")); + alpha_func = + &(_property.get_material().get >("alpha_expansion")); + } + */ + + /////////////////////////////////////////////////////////////////////// + // get the material matrix + mat_stiff(xyz[qp], _time, material_mat); + + this->initialize_strain_operator_gradient(qp, + fe, + _local_sol, + dstrain, + dBmat_lin); + + /* + // if thermal load was specified, then set the thermal strain + // component of the total strain + if (thermal_load) { + (*temp_func) (xyz[qp_loc_index], _time, temp); + (*ref_temp_func)(xyz[qp_loc_index], _time, ref_t); + (*alpha_func) (xyz[qp_loc_index], _time, alpha); + strain(0) -= alpha*(temp-ref_t); // epsilon-xx + strain(1) -= alpha*(temp-ref_t); // epsilon-yy + } + + if (if_bending) { + + // von Karman strain + if (if_vk) { // get the vonKarman strain operator if needed + + this->initialize_von_karman_strain_operator(qp, + fe, + strain_vk, + vk_dwdxi_mat, + Bmat_vk); + strain += strain_vk; + } + + // add to this the bending strain + // TODO: add coupling due to h_offset + h (xyz[qp], _time, z); + h_off(xyz[qp], _time, z_off); + // TODO: this assumes isotropic section. Multilayered sections need + // special considerations + bend->initialize_bending_strain_operator_for_z(fe, + qp, + qp_loc[qp](2) * z/2.+z_off, + Bmat_bend); + Bmat_bend.vector_mult(strain_bend, _local_sol); + + + // add stress due to bending. + strain += strain_bend; + }*/ + + // note that this assumes linear material laws + stress_grad = material_mat * dstrain; + + // calculate the derivative if requested + if (dstress_grad_dX) { - transform_matrix_to_global_system(local_jac, mat_n2n2); - jac -= mat_n2n2; + for (unsigned int i=0; i<2; i++) { + + dBmat_lin[i].left_multiply(dstrain_dX, eye); + + /*if (_property.strain_type() == MAST::NONLINEAR_STRAIN) { + + Bmat_nl_x.left_multiply(mat_n1n2, mat_x); + dstrain_dX += mat_n1n2; + Bmat_nl_y.left_multiply(mat_n1n2, mat_y); + dstrain_dX += mat_n1n2; + } + + if (if_bending) { + + // von Karman strain + if (if_vk) { + + Bmat_vk.left_multiply(mat_n1n2, vk_dwdxi_mat); + dstrain_dX += mat_n1n2; + } + + // bending strain + Bmat_bend.left_multiply(mat_n1n2, eye); + dstrain_dX += mat_n1n2; + } + */ + + // note: this assumes linear material laws + (*dstress_grad_dX)[i] = material_mat * dstrain_dX; + } } - - return request_jacobian; } -bool +void MAST::StructuralElement2D:: -piston_theory_residual_sensitivity(const MAST::FunctionBase& p, - bool request_jacobian, - RealVectorX &f, - RealMatrixX& jac_xdot, - RealMatrixX& jac, - MAST::BoundaryConditionBase& bc) { +_compute_stress_gradient_sensitivity(const MAST::FunctionBase& p, + MAST::FEBase& fe, + unsigned int qp, + RealMatrixX& stress_grad) { - - libmesh_assert(_elem.dim() < 3); // only applicable for lower dimensional elements - libmesh_assert(!follower_forces); // not implemented yet for follower forces + /* + MAST::BendingOperatorType bending_model = + _property.bending_model(_elem); - std::unique_ptr fe(_elem.init_fe(true, false)); + std::unique_ptr + bend(MAST::build_bending_operator_2D(bending_model, + *this, + qp_loc_fe).release()); + */ + + stress_grad.setZero(); + + std::vector JxW = fe.get_JxW(); + const std::vector& xyz = fe.get_xyz(); - const std::vector &JxW = fe->get_JxW(); - const std::vector& qpoint = fe->get_xyz(); - const std::vector >& phi = fe->get_phi(); const unsigned int - n_phi = (unsigned int)phi.size(), - n1 = 2, - n2 = _system.n_vars()*n_phi; - - - // normal for face integration - libMesh::Point normal; - // direction of pressure assumed to be normal (along local z-axis) - // to the element face for 2D and along local y-axis for 1D element. - normal(_elem.dim()) = -1.; - + n_phi = (unsigned int)fe.n_shape_functions(), + n1 = this->n_direct_strain_components(), + n2 = 6*n_phi, + n3 = this->n_von_karman_strain_components(); - // convert to piston theory boundary condition so that the necessary - // flow properties can be obtained - const MAST::PistonTheoryBoundaryCondition& piston_bc = - dynamic_cast(bc); + /* + Real + z = 0., + z_off = 0., + temp = 0., + ref_t = 0., + alpha = 0., + dtemp = 0., + dref_t= 0., + dalpha= 0.; + */ + RealMatrixX + material_mat, + vk_dwdxi_mat = RealMatrixX::Zero(n1,n3), + dstrain = RealMatrixX::Zero(n1,2), + dstrain_dX = RealMatrixX::Zero(n1,n2), + mat_n1n2 = RealMatrixX::Zero(n1,n2), + eye = RealMatrixX::Identity(n1, n1), + mat_x = RealMatrixX::Zero(3,2), + mat_y = RealMatrixX::Zero(3,2), + dstrain_dX_3D= RealMatrixX::Zero(6,n2), + dstress_dX_3D= RealMatrixX::Zero(6,n2); - // create the constant field functions to pass the dwdx and dwdt values - // to the piston theory pressure functions - MAST::Parameter - dwdx_p ("dwdx", 0.), - dwdt_p ("dwdt", 0.); + RealVectorX + strain_vk = RealVectorX::Zero(n1), + strain_bend = RealVectorX::Zero(n1), + strain_3D = RealVectorX::Zero(6), + stress_3D = RealVectorX::Zero(6), + dstrain_dp = RealVectorX::Zero(n1), + dstress_dp = RealVectorX::Zero(n1), + vec1 = RealVectorX::Zero(n2), + vec2 = RealVectorX::Zero(n2); - MAST::ConstantFieldFunction - dwdx_f ("dwdx", dwdx_p), - dwdt_f ("dwdx", dwdt_p); + FEMOperatorMatrix + Bmat_nl_x, + Bmat_nl_y, + Bmat_nl_u, + Bmat_nl_v, + Bmat_bend, + Bmat_vk; + + std::vector + dBmat_lin(2); + + for (unsigned int i=0; i<2; i++) + dBmat_lin[i].reinit (n1, _system.n_vars(), n_phi); // three stress-strain components + + /* + Bmat_nl_x.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_y.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_u.reinit(2, _system.n_vars(), n_phi); + Bmat_nl_v.reinit(2, _system.n_vars(), n_phi); + Bmat_bend.reinit(n1, _system.n_vars(), n_phi); + Bmat_vk.reinit (n3, _system.n_vars(), n_phi); // only dw/dx and dw/dy + */ - std::unique_ptr > - pressure (piston_bc.get_pressure_function(dwdx_f, dwdt_f).release()), - dpressure_dx (piston_bc.get_dpdx_function (dwdx_f, dwdt_f).release()), - dpressure_dxdot (piston_bc.get_dpdxdot_function (dwdx_f, dwdt_f).release()); + // TODO: remove this const-cast, which may need change in API of + // material card + const MAST::FieldFunction& + mat_stiff = + const_cast(_property.get_material()). + stiffness_matrix(2); - FEMOperatorMatrix - Bmat_w, // operator matrix for the w-displacement - dBmat; // operator matrix to calculate the derivativ of w wrt x and y + /* + // get the thickness values for the bending strain calculation + const MAST::FieldFunction + &h = _property.get >("h"), + &h_off = _property.get >("off"); - dBmat.reinit(n1, _system.n_vars(), n_phi); - RealVectorX - phi_vec = RealVectorX::Zero(n_phi), - force = RealVectorX::Zero(n1), - local_f = RealVectorX::Zero(n2), - vec_n1 = RealVectorX::Zero(n1), - vec_n2 = RealVectorX::Zero(n2), - vel_vec = RealVectorX::Zero(3), - dummy = RealVectorX::Zero(3); + bool if_vk = (_property.strain_type() == MAST::NONLINEAR_STRAIN), + if_bending = (_property.bending_model(_elem) != MAST::NO_BENDING); - RealMatrixX - dwdx = RealMatrixX::Zero(3,2), - local_jac_xdot = RealMatrixX::Zero(n2,n2), - local_jac = RealMatrixX::Zero(n2,n2), - mat_n2n2 = RealMatrixX::Zero(n2,n2), - mat_n1n2 = RealMatrixX::Zero(n1,n2), - mat_22 = RealMatrixX::Zero(2,2); - // we need the velocity vector in the local coordinate system so that - // the appropriate component of the w-derivative can be used - vel_vec = _elem.T_matrix().transpose() * piston_bc.vel_vec(); + // check to see if the element has any thermal loads specified + // The object returns null + MAST::BoundaryConditionBase *thermal_load = + stress_output.get_thermal_load_for_elem(_elem); - Real - dwdt_val = 0., - dwdx_val = 0., - p_val = 0.; + const MAST::FieldFunction + *temp_func = nullptr, + *ref_temp_func = nullptr, + *alpha_func = nullptr; + // get pointers to the temperature, if thermal load is specified + if (thermal_load) { + temp_func = + &(thermal_load->get >("temperature")); + ref_temp_func = + &(thermal_load->get >("ref_temperature")); + alpha_func = + &(_property.get_material().get >("alpha_expansion")); + } + */ - for (unsigned int qp=0; qpinitialize_von_karman_strain_operator(qp, - *fe, - dummy, - dwdx, - dBmat); - - // the diagonal of dwdx matrix stores the - dwdx_val = 0.; - for (unsigned int i=0; i<2; i++) - dwdx_val += dwdx(i,i) * vel_vec(i); // (dw/dx_i)*U_inf . n_i - - // calculate the pressure value - dwdx_p = dwdx_val; - dwdt_p = dwdt_val; - pressure->derivative(p, qpoint[qp], _time, p_val); - - // calculate force - force(0) = p_val * normal(2); - - - Bmat_w.vector_mult_transpose(vec_n2, force); - local_f += JxW[qp] * vec_n2; - + /////////////////////////////////////////////////////////////////////// + // get the material matrix + mat_stiff.derivative(p, xyz[qp], _time, material_mat); + + this->initialize_strain_operator_gradient(qp, + fe, + _local_sol, + dstrain, + dBmat_lin); + + /* + // if thermal load was specified, then set the thermal strain + // component of the total strain + if (thermal_load) { + (*temp_func) (xyz[qp_loc_index], _time, temp); + (*ref_temp_func)(xyz[qp_loc_index], _time, ref_t); + (*alpha_func) (xyz[qp_loc_index], _time, alpha); + strain(0) -= alpha*(temp-ref_t); // epsilon-xx + strain(1) -= alpha*(temp-ref_t); // epsilon-yy + } + + if (if_bending) { - // calculate the Jacobian if requested - if (request_jacobian) { - - // we need the derivative of cp wrt normal velocity - dpressure_dxdot->derivative(p, qpoint[qp], _time, p_val); - - // calculate the component of Jacobian due to w-velocity - Bmat_w.right_multiply_transpose(mat_n2n2, Bmat_w); - local_jac_xdot += (JxW[qp] * p_val * normal(2)) * mat_n2n2; - - // now calculate the component of Jacobian - dpressure_dx->derivative(p, qpoint[qp], _time, p_val); - - // derivative wrt x - mat_22.setZero(2,2); - mat_22(0,0) = vel_vec(0); - dBmat.left_multiply(mat_n1n2, mat_22); - Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dx - local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + // von Karman strain + if (if_vk) { // get the vonKarman strain operator if needed - // derivative wrt y - mat_22.setZero(2,2); - mat_22(1,1) = vel_vec(1); - dBmat.left_multiply(mat_n1n2, mat_22); - Bmat_w.right_multiply_transpose(mat_n2n2, mat_n1n2); // v: B^T dB/dy - local_jac += (JxW[qp] * p_val * normal(2)) * mat_n2n2; + this->initialize_von_karman_strain_operator(qp, + fe, + strain_vk, + vk_dwdxi_mat, + Bmat_vk); + strain += strain_vk; } - } - - - // now transform to the global system and add - transform_vector_to_global_system(local_f, vec_n2); - f -= vec_n2; - - // if the Jacobian was requested, then transform it and add to the - // global Jacobian - if (request_jacobian) { - transform_matrix_to_global_system(local_jac_xdot, mat_n2n2); - jac_xdot -= mat_n2n2; - transform_matrix_to_global_system(local_jac, mat_n2n2); - jac -= mat_n2n2; - } - + // add to this the bending strain + // TODO: add coupling due to h_offset + h (xyz[qp], _time, z); + h_off(xyz[qp], _time, z_off); + // TODO: this assumes isotropic section. Multilayered sections need + // special considerations + bend->initialize_bending_strain_operator_for_z(fe, + qp, + qp_loc[qp](2) * z/2.+z_off, + Bmat_bend); + Bmat_bend.vector_mult(strain_bend, _local_sol); + + + // add stress due to bending. + strain += strain_bend; + }*/ - return request_jacobian; + // note that this assumes linear material laws + stress_grad = material_mat * dstrain; +} + + +void +MAST::StructuralElement2D::_surface_normal_voigt_notation(const RealVectorX& normal, + RealMatrixX& normal_mat) { + + libmesh_assert_equal_to(normal.size(), 3); + libmesh_assert_equal_to(normal_mat.rows(), 2); + libmesh_assert_equal_to(normal_mat.cols(), 3); + normal_mat.setZero(); + + /* + // for a 3D stress-tensor the relation will be + normal_mat = RealMatrixX::Zero(3, 6); + // nx + normal_mat(0,0) = normal_mat(1,3) = normal_mat(2,5) = normal(0); + // ny + normal_mat(0,3) = normal_mat(1,1) = normal_mat(2,4) = normal(1); + // nz + normal_mat(0,5) = normal_mat(1,4) = normal_mat(2,2) = normal(2); + */ + + // for a 2D stress tensor the relation will be + normal_mat = RealMatrixX::Zero(2, 3); + + // nx + normal_mat(0,0) = normal_mat(1,2) = normal(0); + + // ny + normal_mat(0,2) = normal_mat(1,1) = normal(1); } + + diff --git a/src/elasticity/structural_element_2d.h b/src/elasticity/structural_element_2d.h index 7cbeaf3b..038a80b9 100644 --- a/src/elasticity/structural_element_2d.h +++ b/src/elasticity/structural_element_2d.h @@ -44,7 +44,6 @@ namespace MAST { public: StructuralElement2D(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p); @@ -153,7 +152,55 @@ namespace MAST { protected: - + + /*! + * Calculates the force vector and Jacobian due to surface traction. + */ + virtual bool + surface_traction_residual(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc); + + + /*! + * Calculates the sensitivity of element vector and matrix quantities for surface traction + * boundary condition. + */ + virtual bool + surface_traction_residual_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc); + + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc); + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc); + + /*! * Calculates the force vector and Jacobian due to surface pressure. */ @@ -336,6 +383,52 @@ namespace MAST { MAST::FEMOperatorMatrix& Bmat_nl_u, MAST::FEMOperatorMatrix& Bmat_nl_v); + + virtual void + initialize_strain_operator_gradient(const unsigned int qp, + const MAST::FEBase& fe, + const RealVectorX& local_disp, + RealMatrixX& epsilon_grad, + std::vector& dBmat_lin); + + /*! + * Computes the stress at the specified quadrature point of the FE data structure. The + * FE object must already be initialized. The derivative of the stress with respect + * to the state variables is computed if a pointer to the matrix is provided. + */ + void _compute_stress(MAST::FEBase& fe, unsigned int qp, + RealVectorX& stress, + RealMatrixX* dstress_dX); + + + void _compute_stress_sensitivity(const MAST::FunctionBase& f, + MAST::FEBase& fe, unsigned int qp, + RealVectorX& stress); + + /*! + * computes the gradient of stress in Voigt notation in \p stress_grad where the three + * columns are the derivative of stress in x, y, z, respectively. If a pointer is provided in + * \p dstress_grad_dX then each element of this vector contains the derive of the gradient + * matrix column wrt the state variables. + */ + void _compute_stress_gradient(MAST::FEBase& fe, + unsigned int qp, + RealMatrixX& stress_grad, + std::vector* dstress_grad_dX); + + void _compute_stress_gradient_sensitivity(const MAST::FunctionBase& f, + MAST::FEBase& fe, + unsigned int qp, + RealMatrixX& stress_grad); + + /*! + * creates a matrix that can be multiplied with the Voigt notation of stress to compute the + * surface normal traction + */ + void _surface_normal_voigt_notation(const RealVectorX& normal, + RealMatrixX& normal_mat); + + /*! * performs integration at the quadrature point for the provided * matrices. The temperature vector and matrix entities are provided for diff --git a/src/elasticity/structural_element_base.cpp b/src/elasticity/structural_element_base.cpp index 07ee820e..411976c4 100644 --- a/src/elasticity/structural_element_base.cpp +++ b/src/elasticity/structural_element_base.cpp @@ -36,10 +36,9 @@ MAST::StructuralElementBase::StructuralElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p): -MAST::ElementBase(sys, assembly, elem), +MAST::ElementBase(sys, elem), follower_forces (false), _property (p), _incompatible_sol (nullptr) { @@ -516,7 +515,7 @@ inertial_residual_boundary_velocity (const MAST::FunctionBase& p, RealMatrixX& jac) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); @@ -658,6 +657,22 @@ side_external_residual(bool request_jacobian, it->first, **bc_it); break; + + + case MAST::SURFACE_TRACTION: + surface_traction_residual(request_jacobian, + f, jac, + it->first, + **bc_it); + break; + + + case MAST::SURFACE_TRACTION_SHIFTED_BOUNDARY: + surface_traction_residual_shifted_boundary(request_jacobian, + f, jac, + it->first, + **bc_it); + break; case MAST::PISTON_THEORY: @@ -670,6 +685,7 @@ side_external_residual(bool request_jacobian, break; + case MAST::BOUNDARY_VELOCITY: case MAST::DIRICHLET: // nothing to be done here break; @@ -711,6 +727,7 @@ linearized_side_external_residual // apply all the types of loading switch ((*bc_it)->type()) { + case MAST::BOUNDARY_VELOCITY: case MAST::DIRICHLET: // nothing to be done here break; @@ -765,6 +782,7 @@ linearized_frequency_domain_side_external_residual break; + case MAST::BOUNDARY_VELOCITY: case MAST::DIRICHLET: // nothing to be done here break; @@ -985,6 +1003,24 @@ side_external_residual_sensitivity(const MAST::FunctionBase& p, break; + case MAST::SURFACE_TRACTION: + surface_traction_residual_sensitivity(p, + request_jacobian, + f, jac, + it->first, + **bc_it); + break; + + + case MAST::SURFACE_TRACTION_SHIFTED_BOUNDARY: + surface_traction_residual_shifted_boundary_sensitivity(p, + request_jacobian, + f, jac, + it->first, + **bc_it); + break; + + case MAST::PISTON_THEORY: piston_theory_residual_sensitivity(p, request_jacobian, @@ -996,6 +1032,7 @@ side_external_residual_sensitivity(const MAST::FunctionBase& p, break; + case MAST::BOUNDARY_VELOCITY: case MAST::DIRICHLET: // nothing to be done here break; @@ -1290,7 +1327,7 @@ surface_pressure_boundary_velocity(const MAST::FunctionBase& p, libmesh_assert(!follower_forces); // not implemented yet for follower forces // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); @@ -1642,7 +1679,6 @@ transform_vector_to_global_system(const ValType& local_vec, std::unique_ptr MAST::build_structural_element(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p) { @@ -1650,15 +1686,15 @@ MAST::build_structural_element(MAST::SystemInitialization& sys, switch (elem.dim()) { case 1: - e.reset(new MAST::StructuralElement1D(sys, assembly, elem, p)); + e.reset(new MAST::StructuralElement1D(sys, elem, p)); break; case 2: - e.reset(new MAST::StructuralElement2D(sys, assembly, elem, p)); + e.reset(new MAST::StructuralElement2D(sys, elem, p)); break; case 3: - e.reset(new MAST::StructuralElement3D(sys, assembly, elem, p)); + e.reset(new MAST::StructuralElement3D(sys, elem, p)); break; default: diff --git a/src/elasticity/structural_element_base.h b/src/elasticity/structural_element_base.h index a45d2ab7..57897db3 100644 --- a/src/elasticity/structural_element_base.h +++ b/src/elasticity/structural_element_base.h @@ -47,7 +47,6 @@ namespace MAST { * Constructor. */ StructuralElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p); @@ -454,6 +453,52 @@ namespace MAST { protected: + /*! + * Calculates the force vector and Jacobian due to surface traction. + */ + virtual bool + surface_traction_residual(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) = 0; + + /*! + * Calculates the sensitivity of element vector and matrix quantities for surface traction + * boundary condition. + */ + virtual bool + surface_traction_residual_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) = 0; + + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary(bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) = 0; + + /*! + * Calculates the sensitivity of force vector and Jacobian due to surface traction and + * sensitiity due to boundary movement. + */ + virtual bool + surface_traction_residual_shifted_boundary_sensitivity(const MAST::FunctionBase& p, + bool request_jacobian, + RealVectorX& f, + RealMatrixX& jac, + const unsigned int side, + MAST::BoundaryConditionBase& bc) = 0; + /*! * Calculates the force vector and Jacobian due to surface pressure. */ @@ -786,7 +831,6 @@ namespace MAST { */ std::unique_ptr build_structural_element(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p); diff --git a/src/elasticity/structural_modal_eigenproblem_assembly.cpp b/src/elasticity/structural_modal_eigenproblem_assembly.cpp index 426f1f10..088a8dc7 100644 --- a/src/elasticity/structural_modal_eigenproblem_assembly.cpp +++ b/src/elasticity/structural_modal_eigenproblem_assembly.cpp @@ -32,7 +32,8 @@ MAST::StructuralModalEigenproblemAssemblyElemOperations:: StructuralModalEigenproblemAssemblyElemOperations(): -MAST::EigenproblemAssemblyElemOperations() { } +MAST::EigenproblemAssemblyElemOperations(), +_incompatible_sol_assembly(nullptr) { } @@ -151,6 +152,62 @@ elem_sensitivity_calculations(const MAST::FunctionBase& f, +void +MAST::StructuralModalEigenproblemAssemblyElemOperations:: +elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + bool base_sol, + RealMatrixX& mat_A, + RealMatrixX& mat_B) { + + libmesh_assert(_physics_elem); + libmesh_assert(f.is_topology_parameter()); + + RealVectorX vec = RealVectorX::Zero(mat_A.rows()); // dummy vector + RealMatrixX mat = RealMatrixX::Zero(mat_A.rows(), mat_A.cols()); // dummy matrix + mat_A.setZero(); + mat_B.setZero(); + + std::pair*, unsigned int> + val = this->get_elem_boundary_velocity_data(); + + if (val.first) { + + MAST::StructuralElementBase& e = + dynamic_cast(*_physics_elem); + + e.internal_residual_boundary_velocity(f, + val.second, + *val.first, + true, + vec, + mat_A); + e.volume_external_residual_boundary_velocity(f, + val.second, + *val.first, + _discipline->volume_loads(), + true, + vec, + mat_A); + + e.inertial_residual_boundary_velocity(f, + val.second, + *val.first, + true, + vec, + mat_B, + mat, + mat_A); + + // if the linearization is about a base state, then the sensitivity of + // the base state will influence the sensitivity of the Jacobian + if (base_sol) + libmesh_assert(false); // to be implemented + //e.internal_residual_jac_dot_state_sensitivity(mat_A); + } +} + + + void MAST::StructuralModalEigenproblemAssemblyElemOperations:: elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, @@ -242,6 +299,6 @@ init(const MAST::GeomElem& elem) { (_discipline->get_property_card(elem)); _physics_elem = - MAST::build_structural_element(*_system, *_assembly, elem, p).release(); + MAST::build_structural_element(*_system, elem, p).release(); } diff --git a/src/elasticity/structural_modal_eigenproblem_assembly.h b/src/elasticity/structural_modal_eigenproblem_assembly.h index 503fada4..5dd5286e 100644 --- a/src/elasticity/structural_modal_eigenproblem_assembly.h +++ b/src/elasticity/structural_modal_eigenproblem_assembly.h @@ -88,6 +88,16 @@ namespace MAST { RealMatrixX& mat_A, RealMatrixX& mat_B); + /*! + * performs the element topology sensitivity calculations over \p elem. + */ + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + bool base_sol, + RealMatrixX& mat_A, + RealMatrixX& mat_B); + + /*! * performs the element topology sensitivity calculations over \p elem. */ diff --git a/src/elasticity/structural_nonlinear_assembly.cpp b/src/elasticity/structural_nonlinear_assembly.cpp index 71482a53..1366f446 100644 --- a/src/elasticity/structural_nonlinear_assembly.cpp +++ b/src/elasticity/structural_nonlinear_assembly.cpp @@ -103,7 +103,7 @@ init(const MAST::GeomElem& elem) { (_discipline->get_property_card(elem)); _physics_elem = - MAST::build_structural_element(*_system, *_assembly, elem, p).release(); + MAST::build_structural_element(*_system, elem, p).release(); } @@ -198,6 +198,49 @@ elem_sensitivity_calculations(const MAST::FunctionBase& f, +void +MAST::StructuralNonlinearAssemblyElemOperations:: +elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec) { + + libmesh_assert(_physics_elem); + libmesh_assert(f.is_topology_parameter()); + + std::pair*, unsigned int> + val = this->get_elem_boundary_velocity_data(); + + if (val.first) { + + MAST::StructuralElementBase& e = + dynamic_cast(*_physics_elem); + + vec.setZero(); + RealMatrixX + dummy = RealMatrixX::Zero(vec.size(), vec.size()); + + e.internal_residual_boundary_velocity(f, + val.second, + *val.first, + false, + vec, + dummy); + e.volume_external_residual_boundary_velocity(f, + val.second, + *val.first, + _discipline->volume_loads(), + false, + vec, + dummy); + /*e.side_external_residual_sensitivity(f, false, + vec, + dummy, + dummy, + _discipline->side_loads());*/ + } +} + + + void MAST::StructuralNonlinearAssemblyElemOperations:: elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, diff --git a/src/elasticity/structural_nonlinear_assembly.h b/src/elasticity/structural_nonlinear_assembly.h index 3c9d37b8..cb83b79f 100644 --- a/src/elasticity/structural_nonlinear_assembly.h +++ b/src/elasticity/structural_nonlinear_assembly.h @@ -104,6 +104,15 @@ namespace MAST { libmesh_assert(false); // to be implemented } + /*! + * performs the element topology sensitivity calculations over \p elem, + * and returns the element residual sensitivity in \p vec . + */ + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec); + + /*! * performs the element topology sensitivity calculations over \p elem, * and returns the element residual sensitivity in \p vec . diff --git a/src/elasticity/structural_transient_assembly.cpp b/src/elasticity/structural_transient_assembly.cpp index 91e90352..0e1f4728 100644 --- a/src/elasticity/structural_transient_assembly.cpp +++ b/src/elasticity/structural_transient_assembly.cpp @@ -242,6 +242,6 @@ MAST::StructuralTransientAssemblyElemOperations::init(const MAST::GeomElem& elem dynamic_cast(_discipline->get_property_card(elem)); _physics_elem = - MAST::build_structural_element(*_system, *_assembly, elem, p).release(); + MAST::build_structural_element(*_system, elem, p).release(); } diff --git a/src/fluid/conservative_fluid_element_base.cpp b/src/fluid/conservative_fluid_element_base.cpp index a9dd16a6..97b23573 100644 --- a/src/fluid/conservative_fluid_element_base.cpp +++ b/src/fluid/conservative_fluid_element_base.cpp @@ -35,11 +35,10 @@ MAST::ConservativeFluidElementBase:: ConservativeFluidElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::FlightCondition& f): MAST::FluidElemBase(elem.dim(), f), -MAST::ElementBase(sys, assembly, elem) { +MAST::ElementBase(sys, elem) { } @@ -775,7 +774,7 @@ MAST::ConservativeFluidElementBase::side_integrated_force(const unsigned int s, RealMatrixX* dfdX) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, if_viscous())); + std::unique_ptr fe(_elem.init_side_fe(s, if_viscous(), false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -884,7 +883,7 @@ side_integrated_force_sensitivity(const MAST::FunctionBase& p, RealVectorX& f) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, if_viscous())); + std::unique_ptr fe(_elem.init_side_fe(s, if_viscous(), false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -980,7 +979,7 @@ symmetry_surface_residual(bool request_jacobian, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -1087,7 +1086,7 @@ slip_wall_surface_residual(bool request_jacobian, // qi ni = 0 (since heat flux occurs only on no-slip wall and far-field bc) // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -1231,7 +1230,7 @@ linearized_slip_wall_surface_residual(bool request_jacobian, // qi ni = 0 (since heat flux occurs only on no-slip wall and far-field bc) // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -1434,7 +1433,7 @@ noslip_wall_surface_residual(bool request_jacobian, // qi ni = 0 (since heat flux occurs only on no-slip wall and far-field bc) // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, true)); + std::unique_ptr fe(_elem.init_side_fe(s, true, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -1637,7 +1636,7 @@ far_field_surface_residual(bool request_jacobian, // -- f_diff_i ni = f_diff (evaluation of diffusion flux based on domain solution) // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -1772,7 +1771,7 @@ far_field_surface_residual_sensitivity(const MAST::FunctionBase& p, // -- f_diff_i ni = f_diff (evaluation of diffusion flux based on domain solution) // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -1870,7 +1869,7 @@ _calculate_surface_integrated_load(bool request_derivative, MAST::OutputAssemblyElemOperations& output) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); diff --git a/src/fluid/conservative_fluid_element_base.h b/src/fluid/conservative_fluid_element_base.h index d11d163f..c568553f 100644 --- a/src/fluid/conservative_fluid_element_base.h +++ b/src/fluid/conservative_fluid_element_base.h @@ -46,7 +46,6 @@ namespace MAST { public: ConservativeFluidElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::FlightCondition& f); diff --git a/src/fluid/conservative_fluid_transient_assembly.cpp b/src/fluid/conservative_fluid_transient_assembly.cpp index f20831ef..22a070c6 100644 --- a/src/fluid/conservative_fluid_transient_assembly.cpp +++ b/src/fluid/conservative_fluid_transient_assembly.cpp @@ -152,6 +152,6 @@ init(const MAST::GeomElem& elem) { (_assembly->discipline()).flight_condition(); _physics_elem = - new MAST::ConservativeFluidElementBase(*_system, *_assembly, elem, p); + new MAST::ConservativeFluidElementBase(*_system, elem, p); } diff --git a/src/fluid/frequency_domain_linearized_complex_assembly.cpp b/src/fluid/frequency_domain_linearized_complex_assembly.cpp index 2df79b30..f15f8457 100644 --- a/src/fluid/frequency_domain_linearized_complex_assembly.cpp +++ b/src/fluid/frequency_domain_linearized_complex_assembly.cpp @@ -122,7 +122,7 @@ init(const MAST::GeomElem& elem) { FrequencyDomainLinearizedConservativeFluidElem *freq_elem = - new MAST::FrequencyDomainLinearizedConservativeFluidElem(*_system, *_assembly, elem, p); + new MAST::FrequencyDomainLinearizedConservativeFluidElem(*_system, elem, p); freq_elem->freq = _frequency; _physics_elem = freq_elem; diff --git a/src/fluid/frequency_domain_linearized_conservative_fluid_elem.cpp b/src/fluid/frequency_domain_linearized_conservative_fluid_elem.cpp index 44082a8a..f18f7556 100644 --- a/src/fluid/frequency_domain_linearized_conservative_fluid_elem.cpp +++ b/src/fluid/frequency_domain_linearized_conservative_fluid_elem.cpp @@ -36,10 +36,9 @@ MAST::FrequencyDomainLinearizedConservativeFluidElem:: FrequencyDomainLinearizedConservativeFluidElem(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::FlightCondition& f): -MAST::ConservativeFluidElementBase(sys, assembly, elem, f), +MAST::ConservativeFluidElementBase(sys, elem, f), freq(nullptr) { @@ -487,7 +486,7 @@ slip_wall_surface_residual(bool request_jacobian, // qi ni = 0 (since heat flux occurs only on no-slip wall and far-field bc) // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); @@ -717,7 +716,7 @@ slip_wall_surface_residual_sensitivity(const MAST::FunctionBase& p, // qi ni = 0 (since heat flux occurs only on no-slip wall and far-field bc) // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); const std::vector &JxW = fe->get_JxW(); const std::vector& normals = fe->get_normals_for_reference_coordinate(); diff --git a/src/fluid/frequency_domain_linearized_conservative_fluid_elem.h b/src/fluid/frequency_domain_linearized_conservative_fluid_elem.h index c59bd49e..90cb2bb4 100644 --- a/src/fluid/frequency_domain_linearized_conservative_fluid_elem.h +++ b/src/fluid/frequency_domain_linearized_conservative_fluid_elem.h @@ -38,7 +38,6 @@ namespace MAST { FrequencyDomainLinearizedConservativeFluidElem (MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::FlightCondition& f); diff --git a/src/fluid/integrated_force_output.cpp b/src/fluid/integrated_force_output.cpp index 82f5830d..e531d0e3 100644 --- a/src/fluid/integrated_force_output.cpp +++ b/src/fluid/integrated_force_output.cpp @@ -62,7 +62,7 @@ MAST::IntegratedForceOutput::init(const MAST::GeomElem& elem) { (_assembly->discipline()).flight_condition(); _physics_elem = - new MAST::ConservativeFluidElementBase(*_system, *_assembly, elem, p); + new MAST::ConservativeFluidElementBase(*_system, elem, p); } diff --git a/src/fluid/integrated_force_output.h b/src/fluid/integrated_force_output.h index 3c67d88e..9e5e8969 100644 --- a/src/fluid/integrated_force_output.h +++ b/src/fluid/integrated_force_output.h @@ -144,6 +144,17 @@ namespace MAST { libmesh_error(); // not yet implemented } + /*! + * this evaluates all relevant topological sensitivity components on + * the element. + * This is only done on the current element for which this + * object has been initialized. + */ + virtual void + evaluate_topology_sensitivity(const MAST::FunctionBase& f) { + libmesh_error(); // not yet implemented + } + /*! * this evaluates all relevant topological sensitivity components on * the element. diff --git a/src/heat_conduction/heat_conduction_elem_base.cpp b/src/heat_conduction/heat_conduction_elem_base.cpp index 474182da..3ee0a2fa 100644 --- a/src/heat_conduction/heat_conduction_elem_base.cpp +++ b/src/heat_conduction/heat_conduction_elem_base.cpp @@ -34,10 +34,9 @@ MAST::HeatConductionElementBase:: HeatConductionElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p): -MAST::ElementBase (sys, assembly, elem), +MAST::ElementBase (sys, elem), _property (p) { } @@ -605,7 +604,7 @@ internal_residual_boundary_velocity (const MAST::FunctionBase& p, const MAST::FieldFunction& vel_f) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, true)); + std::unique_ptr fe(_elem.init_side_fe(s, true, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); @@ -744,7 +743,7 @@ velocity_residual_boundary_velocity (const MAST::FunctionBase& p, const MAST::FieldFunction& vel_f) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); @@ -816,7 +815,7 @@ surface_flux_residual(bool request_jacobian, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); // get the function from this boundary condition @@ -895,7 +894,7 @@ surface_flux_residual_sensitivity(const MAST::FunctionBase& p, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); // get the function from this boundary condition @@ -976,7 +975,7 @@ surface_flux_boundary_velocity(const MAST::FunctionBase& p, // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); @@ -1032,7 +1031,7 @@ surface_convection_residual(bool request_jacobian, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); // get the function from this boundary condition const MAST::FieldFunction @@ -1147,7 +1146,7 @@ surface_convection_residual_sensitivity(const MAST::FunctionBase& p, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); // get the function from this boundary condition const MAST::FieldFunction @@ -1256,7 +1255,7 @@ surface_convection_boundary_velocity(const MAST::FunctionBase& p, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); @@ -1327,7 +1326,7 @@ surface_radiation_residual(bool request_jacobian, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); // get the function from this boundary condition const MAST::FieldFunction @@ -1447,7 +1446,7 @@ surface_radiation_residual_sensitivity(const MAST::FunctionBase& p, // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); // get the function from this boundary condition const MAST::FieldFunction @@ -1551,7 +1550,7 @@ surface_radiation_boundary_velocity(const MAST::FunctionBase& p, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); @@ -1703,7 +1702,7 @@ volume_heat_source_boundary_velocity(const MAST::FunctionBase& p, MAST::BoundaryConditionBase& bc) { // prepare the side finite element - std::unique_ptr fe(_elem.init_side_fe(s, false)); + std::unique_ptr fe(_elem.init_side_fe(s, false, false)); std::vector JxW_Vn = fe->get_JxW(); const std::vector& xyz = fe->get_xyz(); diff --git a/src/heat_conduction/heat_conduction_elem_base.h b/src/heat_conduction/heat_conduction_elem_base.h index 1bafc055..dd16f1f3 100644 --- a/src/heat_conduction/heat_conduction_elem_base.h +++ b/src/heat_conduction/heat_conduction_elem_base.h @@ -58,7 +58,6 @@ namespace MAST { * Constructor */ HeatConductionElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem, const MAST::ElementPropertyCardBase& p); diff --git a/src/heat_conduction/heat_conduction_nonlinear_assembly.cpp b/src/heat_conduction/heat_conduction_nonlinear_assembly.cpp index 8f7cc60a..7fbcd6cd 100644 --- a/src/heat_conduction/heat_conduction_nonlinear_assembly.cpp +++ b/src/heat_conduction/heat_conduction_nonlinear_assembly.cpp @@ -72,7 +72,7 @@ init(const MAST::GeomElem& elem) { (_discipline->get_property_card(elem)); _physics_elem = - new MAST::HeatConductionElementBase(*_system, *_assembly, elem, p); + new MAST::HeatConductionElementBase(*_system, elem, p); } @@ -118,6 +118,42 @@ elem_sensitivity_calculations(const MAST::FunctionBase& f, } +void +MAST::HeatConductionNonlinearAssemblyElemOperations:: +elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec) { + + libmesh_assert(_physics_elem); + libmesh_assert(f.is_topology_parameter()); + + std::pair*, unsigned int> + val = this->get_elem_boundary_velocity_data(); + + if (val.first) { + + MAST::HeatConductionElementBase& e = + dynamic_cast(*_physics_elem); + + vec.setZero(); + RealMatrixX + dummy = RealMatrixX::Zero(vec.size(), vec.size()); + + e.internal_residual_boundary_velocity(f, vec, + val.second, + *val.first); + e.volume_external_residual_boundary_velocity(f, vec, + val.second, + *val.first, + _discipline->volume_loads()); + /*e.side_external_residual_sensitivity(f, false, + vec, + dummy, + dummy, + _discipline->side_loads());*/ + } +} + + void MAST::HeatConductionNonlinearAssemblyElemOperations:: elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, diff --git a/src/heat_conduction/heat_conduction_nonlinear_assembly.h b/src/heat_conduction/heat_conduction_nonlinear_assembly.h index 18cd5fd4..0696e145 100644 --- a/src/heat_conduction/heat_conduction_nonlinear_assembly.h +++ b/src/heat_conduction/heat_conduction_nonlinear_assembly.h @@ -74,6 +74,15 @@ namespace MAST { libmesh_assert(false); // to be implemented } + /*! + * performs the element topology sensitivity calculations over \p elem, + * and returns the element residual sensitivity in \p vec . + */ + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec); + + /*! * performs the element topology sensitivity calculations over \p elem, * and returns the element residual sensitivity in \p vec . diff --git a/src/heat_conduction/heat_conduction_transient_assembly.cpp b/src/heat_conduction/heat_conduction_transient_assembly.cpp index 463285e9..0705fdfb 100644 --- a/src/heat_conduction/heat_conduction_transient_assembly.cpp +++ b/src/heat_conduction/heat_conduction_transient_assembly.cpp @@ -132,6 +132,6 @@ init(const MAST::GeomElem& elem) { dynamic_cast(_discipline->get_property_card(elem)); _physics_elem = - new MAST::HeatConductionElementBase(*_system, *_assembly, elem, p); + new MAST::HeatConductionElementBase(*_system, elem, p); } diff --git a/src/level_set/CMakeLists.txt b/src/level_set/CMakeLists.txt index ae643d71..69ab7b76 100644 --- a/src/level_set/CMakeLists.txt +++ b/src/level_set/CMakeLists.txt @@ -2,10 +2,16 @@ target_sources(mast PRIVATE ${CMAKE_CURRENT_LIST_DIR}/filter_base.cpp ${CMAKE_CURRENT_LIST_DIR}/filter_base.h + ${CMAKE_CURRENT_LIST_DIR}/heaviside_elem_homogenization_function.cpp + ${CMAKE_CURRENT_LIST_DIR}/heaviside_elem_homogenization_function.h + ${CMAKE_CURRENT_LIST_DIR}/homogenized_density_function_base.cpp + ${CMAKE_CURRENT_LIST_DIR}/homogenized_density_function_base.h ${CMAKE_CURRENT_LIST_DIR}/indicator_function_constrain_dofs.cpp ${CMAKE_CURRENT_LIST_DIR}/indicator_function_constrain_dofs.h ${CMAKE_CURRENT_LIST_DIR}/interface_dof_handler.cpp ${CMAKE_CURRENT_LIST_DIR}/interface_dof_handler.h + ${CMAKE_CURRENT_LIST_DIR}/intersected_elem_homogenization_function.cpp + ${CMAKE_CURRENT_LIST_DIR}/intersected_elem_homogenization_function.h ${CMAKE_CURRENT_LIST_DIR}/level_set_boundary_velocity.cpp ${CMAKE_CURRENT_LIST_DIR}/level_set_boundary_velocity.h ${CMAKE_CURRENT_LIST_DIR}/level_set_constrain_dofs.cpp @@ -37,7 +43,11 @@ target_sources(mast ${CMAKE_CURRENT_LIST_DIR}/material_patch.cpp ${CMAKE_CURRENT_LIST_DIR}/material_patch.h ${CMAKE_CURRENT_LIST_DIR}/sub_cell_fe.cpp - ${CMAKE_CURRENT_LIST_DIR}/sub_cell_fe.h) + ${CMAKE_CURRENT_LIST_DIR}/sub_cell_fe.h + ${CMAKE_CURRENT_LIST_DIR}/sub_elem_mesh_refinement.cpp + ${CMAKE_CURRENT_LIST_DIR}/sub_elem_mesh_refinement.h + ${CMAKE_CURRENT_LIST_DIR}/sub_elem_node_map.h + ${CMAKE_CURRENT_LIST_DIR}/sub_elem_node_map.cpp) # Install MAST headers for this directory. install(DIRECTORY ./ DESTINATION include/level_set diff --git a/src/level_set/filter_base.cpp b/src/level_set/filter_base.cpp index ff122359..678b8272 100644 --- a/src/level_set/filter_base.cpp +++ b/src/level_set/filter_base.cpp @@ -1,21 +1,21 @@ /* -* MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit -* Copyright (C) 2013-2019 Manav Bhatia -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ // C++ includes #include @@ -27,7 +27,9 @@ #include "libmesh/mesh_base.h" #include "libmesh/node.h" #include "libmesh/numeric_vector.h" - +#ifdef LIBMESH_HAVE_NANOFLANN +#include "libmesh/nanoflann.hpp" +#endif MAST::FilterBase::FilterBase(libMesh::System& sys, @@ -40,7 +42,11 @@ _dv_dof_ids (dv_dof_ids) { libmesh_assert_greater(radius, 0.); - _init(); +#ifdef LIBMESH_HAVE_NANOFLANN + _init2(); // KD-tree search using NanoFlann +#else + _init(); // linear filter search +#endif } @@ -95,7 +101,7 @@ MAST::FilterBase::compute_filtered_values(const std::vector& input, libmesh_assert_equal_to(input.size(), _filter_map.size()); libmesh_assert_equal_to(output.size(), _filter_map.size()); - + std::fill(output.begin(), output.end(), 0.); std::map>>::const_iterator @@ -147,6 +153,183 @@ if_elem_in_domain_of_influence(const libMesh::Elem& elem, } +#ifdef LIBMESH_HAVE_NANOFLANN +// Nanoflann uses "duck typing" to allow users to define their own adaptors... +template +class NanoflannMeshAdaptor +{ +private: + // Constant reference to the Mesh we are adapting for use in Nanoflann + const libMesh::MeshBase & _mesh; + +public: + NanoflannMeshAdaptor (const libMesh::MeshBase & mesh) : + _mesh(mesh) + {} + + /** + * libMesh \p Point coordinate type + */ + typedef Real coord_t; + + /** + * Must return the number of data points + */ + inline size_t + kdtree_get_point_count() const { return _mesh.n_nodes(); } + + /** + * Returns the distance between the vector "p1[0:size-1]" + * and the data point with index "idx_p2" stored in _mesh + */ + inline coord_t + kdtree_distance(const coord_t * p1, + const size_t idx_p2, + size_t size) const { + + libmesh_assert_equal_to (size, Dim); + + // Construct a libmesh Point object from the input coord_t. This + // assumes LIBMESH_DIM==3. + libMesh::Point point1(p1[0], + size > 1 ? p1[1] : 0., + size > 2 ? p1[2] : 0.); + + // Get the referred-to point from the Mesh + const libMesh::Point & point2 = _mesh.point(idx_p2); + + // Compute Euclidean distance + return (point1 - point2).norm_sq(); + } + + /** + * Returns the dim'th component of the idx'th point in the class: + * Since this is inlined and the "dim" argument is typically an immediate value, the + * "if's" are actually solved at compile time. + */ + inline coord_t + kdtree_get_pt(const size_t idx, int dim) const + { + libmesh_assert_less (dim, (int) Dim); + libmesh_assert_less (idx, _mesh.n_nodes()); + libmesh_assert_less (dim, 3); + + return _mesh.point(idx)(dim); + } + + /** + * Optional bounding-box computation: return false to default to a standard bbox computation loop. + * Return true if the BBOX was already computed by the class and returned in "bb" so it can be + * avoided to redo it again. Look at bb.size() to find out the expected dimensionality + * (e.g. 2 or 3 for point clouds) + */ + template + bool kdtree_get_bbox(BBOX & /* bb */) const { return false; } +}; + + +void +MAST::FilterBase::_init2() { + + //libmesh_assert(!_filter_map.size()); + + libMesh::MeshBase& mesh = _level_set_system.get_mesh(); + + // currently implemented for replicated mesh + libmesh_assert(mesh.is_replicated()); + + // Loop over nodes to try and detect duplicates. We use nanoflann + // for this, inspired by + // https://gist.github.com/jwpeterson/7a36f9f794df67d51126#file-detect_slit-cc-L65 + // which was inspired by nanoflann example in libMesh source: + // contrib/nanoflann/examples/pointcloud_adaptor_example.cpp + + // Declare a type templated on NanoflannMeshAdaptor + typedef nanoflann::L2_Simple_Adaptor > adatper_t; + + // Declare a KDTree type based on NanoflannMeshAdaptor + typedef nanoflann::KDTreeSingleIndexAdaptor, 3> kd_tree_t; + + // Build adaptor and tree objects + NanoflannMeshAdaptor<3> mesh_adaptor(mesh); + kd_tree_t kd_tree(3, mesh_adaptor, nanoflann::KDTreeSingleIndexAdaptorParams(/*max leaf=*/10)); + + // Construct the tree + kd_tree.buildIndex(); + + Real + d_12 = 0., + sum = 0.; + + unsigned int + dof_1, + dof_2; + + libMesh::MeshBase::const_node_iterator + node_it = mesh.nodes_begin(), + node_end = mesh.nodes_end(); + + // For every node in the mesh, search the KDtree and find any + // nodes at _radius distance from the current + // node being searched... this will be added to the . + for (; node_it != node_end; node_it++) { + + const libMesh::Node* node = *node_it; + + dof_1 = node->dof_number(_level_set_system.number(), 0, 0); + + Real query_pt[3] = {(*node)(0), (*node)(1), (*node)(2)}; + + std::vector> + indices_dists; + nanoflann::RadiusResultSet + resultSet(_radius*_radius, indices_dists); + + kd_tree.findNeighbors(resultSet, query_pt, nanoflann::SearchParams()); + + sum = 0.; + + for (unsigned r=0; rdof_number(_level_set_system.number(), 0, 0); + + _filter_map[dof_1].push_back(std::pair(dof_2, _radius - d_12)); + } + + libmesh_assert_greater(sum, 0.); + + // with the coefficients computed for dof_1, divide each coefficient + // with the sum + std::vector>& vec = _filter_map[dof_1]; + for (unsigned int i=0; ihmax(); + + if (_level_set_fe_size < d_12) + _level_set_fe_size = d_12; + } +} +#endif + void MAST::FilterBase::_init() { @@ -193,7 +376,7 @@ MAST::FilterBase::_init() { sum += _radius - d_12; dof_2 = (*node_it_2)->dof_number(_level_set_system.number(), 0, 0); - + _filter_map[dof_1].push_back(std::pair(dof_2, _radius - d_12)); } } @@ -229,7 +412,7 @@ void MAST::FilterBase::print(std::ostream& o) const { o << "Filter radius: " << _radius << std::endl; - + o << std::setw(20) << "Filtered ID" << std::setw(20) << "Dependent Vars" << std::endl; diff --git a/src/level_set/filter_base.h b/src/level_set/filter_base.h index 01c0dccc..f762dd7d 100644 --- a/src/level_set/filter_base.h +++ b/src/level_set/filter_base.h @@ -81,6 +81,7 @@ namespace MAST { * initializes the algebraic data structures */ void _init(); + void _init2(); /*! * system on which the level set discrete function is defined diff --git a/src/level_set/heaviside_elem_homogenization_function.cpp b/src/level_set/heaviside_elem_homogenization_function.cpp new file mode 100644 index 00000000..a1d5d53f --- /dev/null +++ b/src/level_set/heaviside_elem_homogenization_function.cpp @@ -0,0 +1,175 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// MAST includes +#include "level_set/heaviside_elem_homogenization_function.h" +#include "level_set/level_set_elem_base.h" +#include "level_set/filter_base.h" +#include "level_set/level_set_parameter.h" +#include "base/system_initialization.h" +#include "base/nonlinear_system.h" +#include "mesh/geom_elem.h" + +// libMesh includes +#include "libmesh/elem.h" + + +MAST::HeavisideElemHomogenizedDensityFunction:: +HeavisideElemHomogenizedDensityFunction(const std::string& nm): +MAST::HomogenizedDensityFunctionBase(nm), +_width (0.1) { + +} + + + +MAST::HeavisideElemHomogenizedDensityFunction:: +~HeavisideElemHomogenizedDensityFunction() { + +} + + +void +MAST::HeavisideElemHomogenizedDensityFunction:: +initialize_element_volume_fractions() { + + libmesh_assert(_analysis_mesh); + libmesh_assert(_elem_volume_fraction.empty()); + + // make sure that the system uses Lagrange shape functions for + // the level set. + libmesh_assert(_level_set_sys->system().variable_type(0).family == libMesh::LAGRANGE); + + // Next, compute the value of level-set at the nodes of the analysis mesh + // and obtain the value of level-set from the mesh function. + // These values will be provided to the elmeent for computation of + // the homogenized volume fraction. + + RealVectorX + phi; + + unsigned int + n_nodes = 0; + + Real + v = 0.; + + libMesh::MeshBase::const_element_iterator + e_it = _analysis_mesh->active_local_elements_begin(), + e_end = _analysis_mesh->active_local_elements_end(); + + for ( ; e_it != e_end; e_it++) { + + const libMesh::Elem* e = *e_it; + + n_nodes = e->n_nodes(); + phi.setZero(n_nodes); + + // get the nodal values for this element + for (unsigned int i=0; inode_ptr(i), 0., v); + phi(i) = v; + } + + // compute the integral of Heaviside function to approximate the + // volume fraction + MAST::GeomElem geom_elem; + geom_elem.init(*e, *_level_set_sys); + MAST::LevelSetElementBase level_set_elem(*_level_set_sys, geom_elem); + level_set_elem.set_solution(phi); + v = level_set_elem.homogenized_volume_fraction(_width); + + // store the value for this elmeent + _elem_volume_fraction[e] = v; + } +} + + + +void +MAST::HeavisideElemHomogenizedDensityFunction:: +initialize_element_volume_fraction_sensitivity(const MAST::FunctionBase& f) { + + libmesh_assert(_analysis_mesh); + libmesh_assert(_elem_volume_fraction_sensitivity.empty()); + libmesh_assert(f.is_topology_parameter()); + + // make sure that the system uses Lagrange shape functions for + // the level set. + libmesh_assert(_level_set_sys->system().variable_type(0).family == libMesh::LAGRANGE); + + // Next, compute the value of level-set at the nodes of the analysis mesh + // and obtain the value of level-set from the mesh function. + // These values will be provided to the elmeent for computation of + // the homogenized volume fraction. + + RealVectorX + phi, + phi_sens; + + unsigned int + n_nodes = 0; + + Real + v = 0.; + + const MAST::LevelSetParameter + &p_ls = dynamic_cast(f); + + libMesh::MeshBase::const_element_iterator + e_it = _analysis_mesh->active_local_elements_begin(), + e_end = _analysis_mesh->active_local_elements_end(); + + for ( ; e_it != e_end; e_it++) { + + const libMesh::Elem* e = *e_it; + + if (_filter->if_elem_in_domain_of_influence(*e, *p_ls.level_set_node())) { + + n_nodes = e->n_nodes(); + phi.setZero(n_nodes); + phi_sens.setZero(n_nodes); + + // get the nodal values for this element + for (unsigned int i=0; inode_ptr(i), 0., v); + phi(i) = v; + _level_set->derivative(f, *e->node_ptr(i), 0., v); + phi_sens(i) = v; + } + + // compute the integral of Heaviside function to approximate the + // volume fraction + MAST::GeomElem geom_elem; + geom_elem.init(*e, *_level_set_sys); + MAST::LevelSetElementBase level_set_elem(*_level_set_sys, geom_elem); + level_set_elem.set_solution(phi); + level_set_elem.set_solution(phi_sens, true); + v = level_set_elem.homogenized_volume_fraction_sensitivity(_width); + + // store the value for this elmeent + _elem_volume_fraction_sensitivity[e] = v; + } + else + _elem_volume_fraction_sensitivity[e] = 0.; + } +} + + diff --git a/src/level_set/heaviside_elem_homogenization_function.h b/src/level_set/heaviside_elem_homogenization_function.h new file mode 100644 index 00000000..f1147cb4 --- /dev/null +++ b/src/level_set/heaviside_elem_homogenization_function.h @@ -0,0 +1,58 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast__heaviside_elem_homogenized_density_function_h__ +#define __mast__heaviside_elem_homogenized_density_function_h__ + +// MAST includes +#include "level_set/homogenized_density_function_base.h" + + +namespace MAST { + + class HeavisideElemHomogenizedDensityFunction: + public MAST::HomogenizedDensityFunctionBase { + + public: + + HeavisideElemHomogenizedDensityFunction(const std::string& nm); + + virtual ~HeavisideElemHomogenizedDensityFunction(); + + /*! + * width over which the approximate Heaviside function is smoothed. Note that this is the + * width fo the level-set function value, and not necessarily the width of the geometric element. + * For level-set functions that are initialized to have a unit gradient norm, these two would + * coincide. The default value is 0.1. + */ + void set_smoothing_width(Real w) { _width = w;} + + virtual void initialize_element_volume_fractions(); + + virtual void initialize_element_volume_fraction_sensitivity(const MAST::FunctionBase& f); + + protected: + + + Real _width; + }; +} + +#endif // __mast__heaviside_elem_homogenized_density_function_h__ + diff --git a/src/level_set/homogenized_density_function_base.cpp b/src/level_set/homogenized_density_function_base.cpp new file mode 100644 index 00000000..903ceeb1 --- /dev/null +++ b/src/level_set/homogenized_density_function_base.cpp @@ -0,0 +1,131 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// MAST includes +#include "level_set/homogenized_density_function_base.h" +#include "level_set/level_set_parameter.h" +#include "level_set/filter_base.h" + + +MAST::HomogenizedDensityFunctionBase:: +HomogenizedDensityFunctionBase(const std::string& nm): +MAST::FieldFunction (nm), +_level_set_sys (nullptr), +_analysis_mesh (nullptr), +_level_set (nullptr), +_filter (nullptr) { + +} + + +MAST::HomogenizedDensityFunctionBase:: +~HomogenizedDensityFunctionBase() { + +} + + +void +MAST::HomogenizedDensityFunctionBase::init(MAST::SystemInitialization& level_set_sys, + libMesh::MeshBase& analysis_mesh, + MAST::FieldFunction& level_set, + MAST::FilterBase& filter) { + + libmesh_assert(!_level_set_sys); + + _level_set_sys = &level_set_sys; + _analysis_mesh = &analysis_mesh; + _level_set = &level_set; + _filter = &filter; + _sub_point_locator.reset(_analysis_mesh->sub_point_locator().release()); +} + + +void +MAST::HomogenizedDensityFunctionBase:: +operator() (const libMesh::Point& p, const Real t, Real& v) const { + + libmesh_assert(_analysis_mesh); + libmesh_assert(!_elem_volume_fraction.empty()); + + // identify the element in the analysis mesh that this point belongs to + // and return the value of the homogenized function + const libMesh::Elem* e = (*_sub_point_locator)(p); + + libmesh_assert(e); + + std::map::const_iterator + it = _elem_volume_fraction.find(e); + + libmesh_assert(it != _elem_volume_fraction.end()); + + v = it->second; +} + + + +void +MAST::HomogenizedDensityFunctionBase:: +derivative(const MAST::FunctionBase& f, + const libMesh::Point& p, const Real t, Real& v) const { + + libmesh_assert(_analysis_mesh); + libmesh_assert(!_elem_volume_fraction_sensitivity.empty()); + + // identify the element in the analysis mesh that this point belongs to + // and return the value of the homogenized function + const libMesh::Elem* e = (*_sub_point_locator)(p); + + libmesh_assert(e); + + std::map::const_iterator + it = _elem_volume_fraction_sensitivity.find(e); + + libmesh_assert(it != _elem_volume_fraction.end()); + + v = it->second; +} + + +Real +MAST::HomogenizedDensityFunctionBase:: +get_elem_volume_fraction(const libMesh::Elem& e) const { + + std::map::const_iterator + it = _elem_volume_fraction.find(&e); + + libmesh_assert(it != _elem_volume_fraction.end()); + + return it->second; +} + + + +Real +MAST::HomogenizedDensityFunctionBase:: +get_elem_volume_fraction_sensitivity(const MAST::FunctionBase& f, + const libMesh::Elem& e) const { + + std::map::const_iterator + it = _elem_volume_fraction_sensitivity.find(&e); + + libmesh_assert (it != _elem_volume_fraction_sensitivity.end()); + + return it->second; +} + diff --git a/src/level_set/homogenized_density_function_base.h b/src/level_set/homogenized_density_function_base.h new file mode 100644 index 00000000..dd7bf9e1 --- /dev/null +++ b/src/level_set/homogenized_density_function_base.h @@ -0,0 +1,102 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast__homogenized_density_function_base_h__ +#define __mast__homogenized_density_function_base_h__ + +// MAST includes +#include "base/field_function_base.h" + +// libMesh includes +#include "libmesh/mesh_base.h" + + +namespace MAST { + + // Forward declerations + class FilterBase; + class SystemInitialization; + + + class HomogenizedDensityFunctionBase: + public MAST::FieldFunction { + + public: + + HomogenizedDensityFunctionBase(const std::string& nm); + + virtual ~HomogenizedDensityFunctionBase(); + + virtual void init(MAST::SystemInitialization& level_set_sys, + libMesh::MeshBase& analysis_mesh, + MAST::FieldFunction& level_set, + MAST::FilterBase& filter); + + virtual bool depends_on(const MAST::FunctionBase& f) const { return true;} + + virtual void operator() (const libMesh::Point& p, const Real t, Real& v) const; + + virtual void derivative(const MAST::FunctionBase& f, + const libMesh::Point& p, const Real t, Real& v) const; + + const std::map& + get_elem_volume_fraction_map() const { return _elem_volume_fraction;} + + const std::map& + get_elem_volume_fraction_sensitivity_map() const + { return _elem_volume_fraction_sensitivity;} + + Real + get_elem_volume_fraction(const libMesh::Elem& e) const; + + Real + get_elem_volume_fraction_sensitivity(const MAST::FunctionBase& f, + const libMesh::Elem& e) const; + + virtual void initialize_element_volume_fractions() = 0; + + virtual void + initialize_element_volume_fraction_sensitivity(const MAST::FunctionBase& f) = 0; + + virtual void + clear_element_volume_fractions() { _elem_volume_fraction.clear(); } + + virtual void + clear_element_volume_fraction_sensitivity() { _elem_volume_fraction_sensitivity.clear();} + + protected: + + MAST::SystemInitialization *_level_set_sys; + + libMesh::MeshBase *_analysis_mesh; + + MAST::FieldFunction *_level_set; + + MAST::FilterBase *_filter; + + std::map _elem_volume_fraction; + + std::map _elem_volume_fraction_sensitivity; + + std::unique_ptr _sub_point_locator; + }; +} + +#endif // __mast__homogenized_density_function_base_h__ + diff --git a/src/level_set/intersected_elem_homogenization_function.cpp b/src/level_set/intersected_elem_homogenization_function.cpp new file mode 100644 index 00000000..60be3e31 --- /dev/null +++ b/src/level_set/intersected_elem_homogenization_function.cpp @@ -0,0 +1,118 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// MAST includes +#include "level_set/intersected_elem_homogenization_function.h" +#include "level_set/level_set_intersection.h" +#include "level_set/level_set_intersected_elem.h" + + +// libMesh includes +#include "libmesh/elem.h" + +MAST::IntersectedElemHomogenizedDensityFunction:: +IntersectedElemHomogenizedDensityFunction(const std::string& nm): +MAST::HomogenizedDensityFunctionBase(nm), +_intersection (nullptr) { + +} + + + +MAST::IntersectedElemHomogenizedDensityFunction:: +~IntersectedElemHomogenizedDensityFunction() { + +} + + +void +MAST::IntersectedElemHomogenizedDensityFunction:: +initialize_element_volume_fractions() { + + libmesh_assert(_analysis_mesh); + libmesh_assert(_intersection); + libmesh_assert(_elem_volume_fraction.empty()); + + libMesh::MeshBase::const_element_iterator + e_it = _analysis_mesh->local_elements_begin(), + e_end = _analysis_mesh->local_elements_begin(); + + for ( ; e_it != e_end; e_it++) { + + _intersection->init(*_level_set, **e_it, 0., + _analysis_mesh->max_elem_id(), + _analysis_mesh->max_node_id()); + + _elem_volume_fraction[*e_it] = _intersection->get_positive_phi_volume_fraction(); + _intersection->clear(); + } +} + + +void +MAST::IntersectedElemHomogenizedDensityFunction:: +initialize_element_volume_fraction_sensitivity(const MAST::FunctionBase& f) { + + libmesh_assert(_analysis_mesh); + libmesh_assert(_intersection); + libmesh_assert(_elem_volume_fraction_sensitivity.empty()); + + libMesh::MeshBase::const_element_iterator + e_it = _analysis_mesh->local_elements_begin(), + e_end = _analysis_mesh->local_elements_begin(); + + for ( ; e_it != e_end; e_it++) { + + const libMesh::Elem* elem = *e_it; + + _intersection->init(*_level_set, *elem, 0., + _analysis_mesh->max_elem_id(), + _analysis_mesh->max_node_id()); + + // iterate over all elements on the positive side and + // add compute the sensitivity of the volume + const std::vector & + elems_hi = _intersection->get_sub_elems_positive_phi(); + + std::vector::const_iterator + hi_sub_elem_it = elems_hi.begin(), + hi_sub_elem_end = elems_hi.end(); + +// for (; hi_sub_elem_it != hi_sub_elem_end; hi_sub_elem_it++ ) { +// +// const libMesh::Elem* sub_elem = *hi_sub_elem_it; +// +// MAST::LevelSetIntersectedElem geom_elem; +// ops.set_elem_data(elem->dim(), *elem, geom_elem); +// geom_elem.init(*sub_elem, *_system, *_intersection); +// +// ops.init(geom_elem); +// +// ops.elem_calculations(J!=nullptr?true:false, sub_elem_vec, sub_elem_mat); +// +// ops.clear_elem(); +// } + + _elem_volume_fraction_sensitivity[elem] = + _intersection->get_positive_phi_volume_fraction(); + + _intersection->clear(); + } +} + diff --git a/src/level_set/intersected_elem_homogenization_function.h b/src/level_set/intersected_elem_homogenization_function.h new file mode 100644 index 00000000..d56e7191 --- /dev/null +++ b/src/level_set/intersected_elem_homogenization_function.h @@ -0,0 +1,58 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast__intersected_elem_homogenized_density_function_h__ +#define __mast__intersected_elem_homogenized_density_function_h__ + +// MAST includes +#include "level_set/homogenized_density_function_base.h" + + +namespace MAST { + + // Forward declerations + + class LevelSetIntersection; + + class IntersectedElemHomogenizedDensityFunction: + public MAST::HomogenizedDensityFunctionBase { + + public: + + IntersectedElemHomogenizedDensityFunction(const std::string& nm); + + virtual ~IntersectedElemHomogenizedDensityFunction(); + + /*! + * computes and stores the volume fraction of each local element + */ + virtual void initialize_element_volume_fractions(); + + virtual void + initialize_element_volume_fraction_sensitivity(const MAST::FunctionBase& f); + + protected: + + MAST::LevelSetIntersection *_intersection; + + }; +} + +#endif // __mast__intersected_elem_homogenized_density_function_h__ + diff --git a/src/level_set/level_set_boundary_velocity.cpp b/src/level_set/level_set_boundary_velocity.cpp index cc6a63d1..1fdf806a 100644 --- a/src/level_set/level_set_boundary_velocity.cpp +++ b/src/level_set/level_set_boundary_velocity.cpp @@ -19,12 +19,17 @@ // MAST includes #include "level_set/level_set_boundary_velocity.h" +#include "level_set/level_set_intersection.h" +#include "base/system_initialization.h" +#include "base/nonlinear_system.h" MAST::LevelSetBoundaryVelocity::LevelSetBoundaryVelocity(const unsigned int dim): -MAST::FieldFunction("phi"), -_dim(dim), -_phi(nullptr) { +MAST::FieldFunction("phi_vel"), +_dim (dim), +_phi (nullptr), +_mesh (nullptr), +_level_set_func (nullptr) { } @@ -34,16 +39,20 @@ MAST::LevelSetBoundaryVelocity::~LevelSetBoundaryVelocity() { delete _phi; } + void MAST::LevelSetBoundaryVelocity::init(MAST::SystemInitialization& sys, + const MAST::FieldFunction& phi_func, const libMesh::NumericVector& sol, - const libMesh::NumericVector& dsol) { + const libMesh::NumericVector* dsol) { if (!_phi) - _phi = new MAST::MeshFieldFunction(sys, "phi"); + _phi = new MAST::MeshFieldFunction(sys, "phi_vel"); else _phi->clear(); - _phi->init(sol, &dsol); + _phi->init(sol, dsol); + _mesh = &sys.system().get_mesh(); + _level_set_func = &phi_func; } @@ -51,12 +60,17 @@ void MAST::LevelSetBoundaryVelocity::operator() (const libMesh::Point& p, const Real t, RealVectorX& v) const { + this->velocity(p, t, v); +} + + +void +MAST::LevelSetBoundaryVelocity::velocity(const libMesh::Point& p, + const Real t, + RealVectorX& v) const { libmesh_assert(_phi); - Real - tol = 1.e-6; - RealVectorX val = RealVectorX::Zero(1), grad = RealVectorX::Zero(_dim), @@ -96,3 +110,326 @@ MAST::LevelSetBoundaryVelocity::operator() (const libMesh::Point& p, v = -dval(0)/grad.squaredNorm()*grad; } + + +#include +void +MAST::LevelSetBoundaryVelocity:: +search_nearest_interface_point(const libMesh::Elem& e, + const unsigned int side, + const libMesh::Point& p, + const Real t, + RealVectorX& pt) const { + + MAST::LevelSetIntersection intersection; + + const libMesh::Elem* + elem = e.neighbor_ptr(side); + + libmesh_assert(elem); + + intersection.init(*_level_set_func, + *elem, 0., + _mesh->max_elem_id(), + _mesh->max_node_id()); + + libMesh::Point p2; + intersection.get_nearest_intersection_point(p, p2); + pt(0) = p2(0); pt(1) = p2(1); pt(2) = p2(2); +// std::fstream o; +// o.open("pts.csv", std::fstream::app); +// o << p(0) << " , " << p(1) << " , 0 \n"; +// o << pt(0) << " , " << pt(1) << " , 0 \n"; +} + + +void +MAST::LevelSetBoundaryVelocity:: +search_nearest_interface_point_derivative(const MAST::FunctionBase& f, + const libMesh::Elem& e, + const unsigned int side, + const libMesh::Point& p, + const Real t, + RealVectorX& v) const { + + libmesh_assert(_phi); + + // velocity at this interface point is given by the normal velocity + // So, we first find the point and then compute its velocity + + this->search_nearest_interface_point(e, side, p, t, v); + + libMesh::Point + pt(v(0), v(1), v(2)); + + // now compute the velocity at this point. + v.setZero(); + this->velocity(pt, t, v); +} + + + + +void +MAST::LevelSetBoundaryVelocity:: +search_nearest_interface_point_old(const libMesh::Point& p, + const Real t, + const Real length, + RealVectorX& v, + bool allow_sub_search) const { + + libmesh_assert(_phi); + + RealVectorX + phi = RealVectorX::Zero(1), + grad_phi = RealVectorX::Zero(_dim), + grad0 = RealVectorX::Zero(3), + grad1 = RealVectorX::Zero(3), + z = RealVectorX::Zero(3), + dval = RealVectorX::Zero(1), + p_ref = RealVectorX::Zero(3), + dv0 = RealVectorX::Zero(4), + dv1 = RealVectorX::Zero(4), + dx = RealVectorX::Zero(4), + gradL = RealVectorX::Zero(4); + + RealMatrixX + hess = RealMatrixX::Zero(3, 3), + gradmat = RealMatrixX::Zero(1, _dim), + coeffs = RealMatrixX::Zero(3, 3); + + libMesh::Point + p_opt = p; + + p_ref(0) = p(0); + p_ref(1) = p(1); + p_ref(2) = p(2); + + // The point is identified from a constrained optimization problem. + // p* = argmin { .5 * || p* - p ||^2: phi(p*) = 0 } + // This is formulated as a constrained minimization problem: + // (p*, l) = argmin { .5 * || p* - p ||^2 + l * phi(p*) }, + // where the Lagrangian is defined as + // L (p*, l) = .5 * || p* - p ||^2 + l * phi(p*) + // = .5 * ((px* - px)^2 + (py* - py)^2 + (pz* - pz)^2) + l * phi(p*) + // The first order optimality for this provides + // grad ( L (p*, l)) = { (p*-p) + l * grad(phi(p*)); phi(p*) } = 0 + // We solve this using Newton-Raphson with an approximate Hessian + // + + bool + if_cont = true; + + unsigned int + n_iters = 0, + max_iters = 80; + + Real + tol = 1.e-8, + L0 = 1.e12, + damp = 0.5, + L1 = 0.; + + // initialize the design point + dv0.topRows(3) = p_ref; + dv0(3) = 1.; // arbitrary value of Lagrange multiplier + dv1 = dv0; + + while (if_cont) { + + // compute the gradient of Lagrangian + (*_phi) (p_opt, t, phi); + _phi->gradient (p_opt, t, gradmat); + + // this defines the gradient of Lagrangian + gradL.topRows(3) = (dv0.topRows(3)-p_ref) + dv0(3) * gradmat.row(0).transpose(); + gradL(3) = phi(0); + + // approximate hessian + coeffs.setZero(4,4); + for (unsigned int i=0; i<3; i++) { + coeffs(i,i) = 1.; + coeffs(3,i) = coeffs(i,3) = gradmat(0,i); + } + coeffs.topLeftCorner(3, 3) += hess; + + Eigen::FullPivLU solver(coeffs); + + bool + continue_search = true; + dx = - solver.solve(gradL); + Real + factor = 1.; + unsigned int + n_it = 0, + max_it = 10; + + + // some searches may end up outside the mesh, and libMesh will through + // an exception. So, we catch the exception and take a smaller step. + while (continue_search) { + try { + + // update the design points and check for convergence + dv1 = dv0 + damp*factor * dx; + L1 = _evaluate_point_search_obj(p, t, dv1); + continue_search = false; + } + catch (...) { + + factor *= 0.5; + n_it ++; + if (n_it == max_it) { + // could not find a point inside the mesh. returning + // the reference point. + v.topRows(_dim) = dv0.topRows(_dim); + return; + } + else + continue_search = true; + } + } + + p_opt(0) = dv1(0); p_opt(1) = dv1(1); p_opt(2) = dv1(2); + L1 = _evaluate_point_search_obj(p, t, dv1); + + // update Jacobian + grad1 = gradmat.row(0); + z = (dv1.topRows(3)-dv0.topRows(3)) - hess * (grad1-grad0); + //hess += (z*z.transpose())/z.dot(grad1-grad0); + grad0 = grad1; + dv0 = dv1; + // reduce the step if the gradient is upated + /*if (z.norm()) + damp = 0.3; + else + damp = 0.5; + */ + if (n_iters == max_iters) { + + // instead, find the point closest to the latest point returned + // by the failed search. Do not allow another sub-search here + if (allow_sub_search) { + this->search_nearest_interface_point_old(p_opt, t, length, v, false); + + p_opt(0) = v(0); p_opt(1) = v(1); p_opt(2) = v(2); + dv0.topRows(3) = v; + (*_phi) (p_opt, t, phi); + } + + if_cont = false; + libMesh::Point dp = p_opt - p; + libMesh::out + << "Warning: nearest interface point search did not converge. Point found from sub-search. " + << std::endl; + libMesh::out + << " given pt: (" + << p(0) << " , " << p(1) << " , " << p(2) + << ") -> mapped pt: (" + << p_opt(0) << " , " << p_opt(1) << " , " << p_opt(2) + << "). phi = " << phi(0) + << " d/h = " << dp.norm()/length << std::endl; + } + if (std::fabs(gradL.norm()) <= tol) if_cont = false; + if (std::fabs((L1-L0)/L0) <= tol) if_cont = false; + + L0 = L1; + n_iters++; + } + + v.setZero(); + v.topRows(_dim) = dv0.topRows(_dim); +} + + + +void +MAST::LevelSetBoundaryVelocity:: +search_nearest_interface_point_derivative_old(const MAST::FunctionBase& f, + const libMesh::Point& p, + const Real t, + const Real length, + RealVectorX& v) const { + + libmesh_assert(_phi); + + // velocity at this interface point is given by the normal velocity + // So, we first find the point and then compute its velocity + + this->search_nearest_interface_point_old(p, t, length, v); + + libMesh::Point + pt(v(0), v(1), v(2)); + + // now compute the velocity at this point. + v.setZero(); + this->velocity(pt, t, v); +} + + + + +void +MAST::LevelSetBoundaryVelocity::normal_at_point(const libMesh::Point& p, + const Real t, + RealVectorX& n) const { + + libmesh_assert(_phi); + + RealMatrixX + gradmat = RealMatrixX::Zero(1, _dim); + + n.setZero(); + + // the velocity is identified using the level set function gradient + // and its sensitivity + _phi->gradient(p, t, gradmat); + n.topRows(3) = -gradmat.row(0); + + n /= n.norm(); +} + + + +void +MAST::LevelSetBoundaryVelocity::normal_derivative_at_point(const MAST::FunctionBase& f, + const libMesh::Point& p, + const Real t, + RealVectorX& n) const { + + libmesh_assert(_phi); + + RealMatrixX + gradmat = RealMatrixX::Zero(1, _dim), + dgrad = RealMatrixX::Zero(1, _dim); + + n.setZero(); + + // the velocity is identified using the level set function gradient + // and its sensitivity + _phi->gradient(p, t, gradmat); + _phi->perturbation_gradient(p, t, dgrad); + + RealVectorX + v = gradmat.row(0), + dv = dgrad.row(0); + + n.topRows(3) = (-dv/v.norm() + v.dot(dv)/std::pow(v.dot(v),1.5) * v); +} + + + +Real +MAST::LevelSetBoundaryVelocity:: +_evaluate_point_search_obj(const libMesh::Point& p, + const Real t, + const RealVectorX& dv) const { + + libMesh::Point p1(dv(0), dv(1), dv(2)); + RealVectorX phi; + (*_phi)(p1, t, phi); + p1 -= p; + return .5 * p1.norm_sq() + dv(3)*phi(0); +} + + diff --git a/src/level_set/level_set_boundary_velocity.h b/src/level_set/level_set_boundary_velocity.h index d1ba8dd5..46fafd93 100644 --- a/src/level_set/level_set_boundary_velocity.h +++ b/src/level_set/level_set_boundary_velocity.h @@ -35,17 +35,77 @@ namespace MAST { virtual ~LevelSetBoundaryVelocity(); void init(MAST::SystemInitialization& sys, + const MAST::FieldFunction& phi, const libMesh::NumericVector& sol, - const libMesh::NumericVector& dsol); + const libMesh::NumericVector* dsol); virtual void operator() (const libMesh::Point& p, const Real t, RealVectorX& v) const; + + void velocity(const libMesh::Point& p, + const Real t, + RealVectorX& v) const; + + void search_nearest_interface_point(const libMesh::Elem& e, + const unsigned int side, + const libMesh::Point& p, + const Real t, + RealVectorX& pt) const; + + void search_nearest_interface_point_derivative(const MAST::FunctionBase& f, + const libMesh::Elem& e, + const unsigned int side, + const libMesh::Point& p, + const Real t, + RealVectorX& v) const; + + + /*! + * serches for a point \p pt in the vicinity of \p p on the level set interface, where + * level set function is zero. \p length is a reference length that is used to identify + * the step-size for the search. If the interface point is expected to be within a few elements, + * then this length coudl be the element edge length. + */ + void search_nearest_interface_point_old(const libMesh::Point& p, + const Real t, + const Real length, + RealVectorX& pt, + bool allow_sub_search = true) const; + + /*! + * serches for a point \p pt in the vicinity of \p p on the level set interface, where + * level set function is zero. \p length is a reference length that is used to identify + * the step-size for the search. If the interface point is expected to be within a few elements, + * then this length coudl be the element edge length. + */ + void search_nearest_interface_point_derivative_old(const MAST::FunctionBase& f, + const libMesh::Point& p, + const Real t, + const Real length, + RealVectorX& v) const; + + void normal_at_point(const libMesh::Point& p, + const Real t, + RealVectorX& n) const; + + + void normal_derivative_at_point(const MAST::FunctionBase& f, + const libMesh::Point& p, + const Real t, + RealVectorX& n) const; + protected: - unsigned int _dim; - MAST::MeshFieldFunction *_phi; + Real _evaluate_point_search_obj(const libMesh::Point& p, + const Real t, + const RealVectorX& dv) const; + + unsigned int _dim; + MAST::MeshFieldFunction* _phi; + libMesh::MeshBase* _mesh; + const MAST::FieldFunction* _level_set_func; }; } diff --git a/src/level_set/level_set_elem_base.cpp b/src/level_set/level_set_elem_base.cpp index b6bd6b7f..bab5b5be 100644 --- a/src/level_set/level_set_elem_base.cpp +++ b/src/level_set/level_set_elem_base.cpp @@ -35,9 +35,8 @@ MAST::LevelSetElementBase:: LevelSetElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem): -MAST::ElementBase(sys, assembly, elem), +MAST::ElementBase(sys, elem), _phi_vel (nullptr), _if_propagation (true) { @@ -342,7 +341,79 @@ MAST::LevelSetElementBase::volume() { Real -MAST::LevelSetElementBase::perimeter() { +MAST::LevelSetElementBase::homogenized_volume_fraction(Real delta) { + + std::unique_ptr fe(_elem.init_fe(true, false)); + + const std::vector& JxW = fe->get_JxW(); + const unsigned int + dim = _elem.dim(); + + RealVectorX + phi = RealVectorX::Zero(1); + + Real + pi = acos(-1.), + heav = 0., + vol = 0.; + + std::vector dBmat(dim); + MAST::FEMOperatorMatrix Bmat; + + + for (unsigned int qp=0; qp fe(_elem.init_fe(true, false)); + + const std::vector& JxW = fe->get_JxW(); + const unsigned int + dim = _elem.dim(); + + RealVectorX + phi = RealVectorX::Zero(1), + dphidp = RealVectorX::Zero(1); + + Real + pi = acos(-1.), + heav = 0., + vol = 0.; + + std::vector dBmat(dim); + MAST::FEMOperatorMatrix Bmat; + + + for (unsigned int qp=0; qp fe(_elem.init_fe(true, false)); @@ -354,7 +425,6 @@ MAST::LevelSetElementBase::perimeter() { phi = RealVectorX::Zero(1); Real - d = 1.e-1, pi = acos(-1.), per = 0.; @@ -375,7 +445,7 @@ MAST::LevelSetElementBase::perimeter() { Real -MAST::LevelSetElementBase::perimeter_sensitivity() { +MAST::LevelSetElementBase::perimeter_sensitivity(Real d) { std::unique_ptr fe(_elem.init_fe(true, false)); @@ -388,7 +458,6 @@ MAST::LevelSetElementBase::perimeter_sensitivity() { dphidp = RealVectorX::Zero(1); Real - d = 1.e-1, pi = acos(-1.), dper_dp = 0.; @@ -414,7 +483,7 @@ MAST::LevelSetElementBase::perimeter_sensitivity() { Real MAST::LevelSetElementBase::volume_boundary_velocity_on_side(unsigned int s) { - std::unique_ptr fe(_elem.init_side_fe(s, true)); + std::unique_ptr fe(_elem.init_side_fe(s, true, false)); const std::vector& JxW = fe->get_JxW(); const unsigned int diff --git a/src/level_set/level_set_elem_base.h b/src/level_set/level_set_elem_base.h index e890c1f7..a8fc1721 100644 --- a/src/level_set/level_set_elem_base.h +++ b/src/level_set/level_set_elem_base.h @@ -41,7 +41,6 @@ namespace MAST { * Constructor */ LevelSetElementBase(MAST::SystemInitialization& sys, - MAST::AssemblyBase& assembly, const MAST::GeomElem& elem); @@ -149,6 +148,24 @@ namespace MAST { Real volume(); + + /*! + * Approximates the volume fraction of the element based on integration of approximated + * Heaviside function over the element. \p delta is the width over which the function + * is smoothed. + */ + Real + homogenized_volume_fraction(Real delta=0.1); + + + /*! + * Sensitivity of the homogenized volume fraction for the specified level set value and + * its sensitivity. + */ + Real + homogenized_volume_fraction_sensitivity(Real delta=0.1); + + /*! * Approximates the integral of the Dirac delta function to approximate * the perimeter. The approximation of Dirac delta function is obtained @@ -163,7 +180,7 @@ namespace MAST { * @returns the computed integral of \f$ \delta_d(\phi) \f$ */ Real - perimeter(); + perimeter(Real delta=0.1); /*! * computes the partial derivative of the integral of the Dirac @@ -171,7 +188,7 @@ namespace MAST { * set for this element. */ Real - perimeter_sensitivity(); + perimeter_sensitivity(Real delta=0.1); /*! diff --git a/src/level_set/level_set_intersected_elem.cpp b/src/level_set/level_set_intersected_elem.cpp index 959df515..0509045c 100644 --- a/src/level_set/level_set_intersected_elem.cpp +++ b/src/level_set/level_set_intersected_elem.cpp @@ -128,12 +128,14 @@ MAST::LevelSetIntersectedElem::init_fe(bool init_grads, std::unique_ptr MAST::LevelSetIntersectedElem::init_side_fe(unsigned int s, bool init_grads, + bool init_second_order_derivative, int extra_quadrature_order) const { libmesh_assert(_intersection); std::unique_ptr fe(new MAST::SubCellFE(*_sys_init, *_intersection)); fe->set_extra_quadrature_order(extra_quadrature_order); - + fe->set_evaluate_second_order_derivatives(init_second_order_derivative); + fe->init_for_side(*this, s, init_grads); return fe; diff --git a/src/level_set/level_set_intersected_elem.h b/src/level_set/level_set_intersected_elem.h index c72e88b0..e0269475 100644 --- a/src/level_set/level_set_intersected_elem.h +++ b/src/level_set/level_set_intersected_elem.h @@ -110,6 +110,7 @@ namespace MAST { virtual std::unique_ptr init_side_fe(unsigned int s, bool init_grads, + bool init_second_order_derivative, int extra_quadrature_order = 0) const; diff --git a/src/level_set/level_set_intersection.cpp b/src/level_set/level_set_intersection.cpp index 68efa87b..7c1d2958 100644 --- a/src/level_set/level_set_intersection.cpp +++ b/src/level_set/level_set_intersection.cpp @@ -36,6 +36,7 @@ _max_mesh_node_id (0), _max_elem_divs (4), _elem (nullptr), _initialized (false), +_phi (nullptr), _if_elem_on_positive_phi (false), _if_elem_on_negative_phi (false), _mode (MAST::NO_INTERSECTION), @@ -112,12 +113,21 @@ MAST::LevelSetIntersection::if_elem_on_negative_phi() const { +bool +MAST::LevelSetIntersection::if_elem_has_negative_phi_region() const { + + libmesh_assert(_initialized); + + return !_if_elem_on_positive_phi; +} + + bool MAST::LevelSetIntersection::if_elem_has_positive_phi_region() const { libmesh_assert(_initialized); - return (_if_elem_on_positive_phi || !_if_elem_on_negative_phi); + return !_if_elem_on_negative_phi; } @@ -180,6 +190,13 @@ MAST::LevelSetIntersection::get_node_phi_value(const libMesh::Node* n) const { +bool +MAST::LevelSetIntersection::if_hanging_node(const libMesh::Node* n) const { + + return _hanging_node.count(n); +} + + void MAST::LevelSetIntersection::clear() { @@ -187,6 +204,7 @@ MAST::LevelSetIntersection::clear() { _max_iters = 10; _elem = nullptr; _initialized = false; + _phi = nullptr; _if_elem_on_positive_phi = false; _if_elem_on_negative_phi = false; _mode = MAST::NO_INTERSECTION; @@ -198,6 +216,7 @@ MAST::LevelSetIntersection::clear() { _negative_phi_elems.clear(); _elem_sides_on_interface.clear(); _node_local_coords.clear(); + _hanging_node.clear(); std::vector::iterator e_it = _new_elems.begin(), @@ -216,6 +235,9 @@ MAST::LevelSetIntersection::clear() { _new_nodes.clear(); _new_elems.clear(); + + _interior_nodes.clear(); + _bounding_nodes.clear(); } @@ -234,6 +256,7 @@ MAST::LevelSetIntersection::init(const MAST::FieldFunction& phi, _max_mesh_node_id = max_node_id; _elem = &e; + _phi = φ switch (e.type()) { case libMesh::QUAD4: @@ -344,8 +367,7 @@ _init_on_first_order_ref_elem(const MAST::FieldFunction& phi, // if the sign of function on all nodes is the same, then it is assumed // that the element is not intersected - if (min_val > _tol && - max_val > _tol) { + if (min_val > _tol) { // element is completely on the positive side with no intersection _mode = MAST::NO_INTERSECTION; @@ -355,8 +377,7 @@ _init_on_first_order_ref_elem(const MAST::FieldFunction& phi, _initialized = true; return; } - else if (min_val < _tol && - max_val < _tol) { + else if (max_val < -_tol) { // element is completely on the negative side, with no intersection @@ -367,12 +388,9 @@ _init_on_first_order_ref_elem(const MAST::FieldFunction& phi, _initialized = true; return; } - else if (min_val < _tol && - max_val > _tol) { + else { // if it did not get caught in the previous two cases, then there is // an intersection. - // If it got here, then there is an intersection in the domain and the - // element is not on the positive side completely. _if_elem_on_positive_phi = false; _if_elem_on_negative_phi = false; @@ -917,6 +935,8 @@ MAST::LevelSetIntersection::_find_quad4_intersections nd->set_id(_max_mesh_node_id + node_id_incr); node_id_incr++; _new_nodes.push_back(nd); + _bounding_nodes[nd] = std::make_pair(_elem->node_ptr(ref_side%n_nodes), + _elem->node_ptr((ref_side+1)%n_nodes)); side_p0 = side_nondim_points[ref_side%n_nodes].first; side_p1 = side_nondim_points[ref_side%n_nodes].second; _node_local_coords[nd] = side_p0 + xi_ref * (side_p1 - side_p0); @@ -958,6 +978,8 @@ MAST::LevelSetIntersection::_find_quad4_intersections nd->set_id(_max_mesh_node_id + node_id_incr); node_id_incr++; _new_nodes.push_back(nd); + _bounding_nodes[nd] = std::make_pair(_elem->node_ptr((ref_side+2)%n_nodes), + _elem->node_ptr((ref_side+3)%n_nodes)); side_p0 = side_nondim_points[(ref_side+2)%n_nodes].first; side_p1 = side_nondim_points[(ref_side+2)%n_nodes].second; _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0); @@ -1032,6 +1054,8 @@ MAST::LevelSetIntersection::_find_quad4_intersections nd->set_id(_max_mesh_node_id + node_id_incr); node_id_incr++; _new_nodes.push_back(nd); + _bounding_nodes[nd] = std::make_pair(_elem->node_ptr((ref_side+1)%n_nodes), + _elem->node_ptr((ref_side+2)%n_nodes)); side_p0 = side_nondim_points[(ref_side+1)%n_nodes].first; side_p1 = side_nondim_points[(ref_side+1)%n_nodes].second; _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0); @@ -1045,6 +1069,8 @@ MAST::LevelSetIntersection::_find_quad4_intersections nd->set_id(_max_mesh_node_id + node_id_incr); node_id_incr++; _new_nodes.push_back(nd); + _bounding_nodes[nd] = std::make_pair(_elem->node_ptr((ref_side+2)%n_nodes), + _elem->node_ptr((ref_side+3)%n_nodes)); side_p0 = side_nondim_points[(ref_side+2)%n_nodes].first; side_p1 = side_nondim_points[(ref_side+2)%n_nodes].second; _node_local_coords[nd] = side_p1 + xi_ref * (side_p0 - side_p1); @@ -1094,6 +1120,7 @@ MAST::LevelSetIntersection::_find_quad4_intersections e2->set_node(0) = _new_nodes[0]; e2->set_node(1) = _new_nodes[2]; + _hanging_node.insert(_new_nodes[2]); e2->set_node(2) = const_cast(e.node_ptr((ref_side+3)%n_nodes)); e2->set_node(3) = const_cast(e.node_ptr(ref_side)); @@ -1318,6 +1345,8 @@ MAST::LevelSetIntersection::_find_quad4_intersections nd->set_id(_max_mesh_node_id + node_id_incr); node_id_incr++; _new_nodes.push_back(nd); + _bounding_nodes[nd] = std::make_pair(_elem->node_ptr((ref_side+1)%n_nodes), + _elem->node_ptr((ref_side+2)%n_nodes)); side_p0 = side_nondim_points[(ref_side+1)%n_nodes].first; side_p1 = side_nondim_points[(ref_side+1)%n_nodes].second; _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0); @@ -1433,6 +1462,8 @@ MAST::LevelSetIntersection::_find_quad4_intersections nd->set_id(_max_mesh_node_id + node_id_incr); node_id_incr++; _new_nodes.push_back(nd); + _bounding_nodes[nd] = std::make_pair(_elem->node_ptr((ref_side+i)%n_nodes), + _elem->node_ptr((ref_side+i+1)%n_nodes)); side_p0 = side_nondim_points[(ref_side+i)%n_nodes].first; side_p1 = side_nondim_points[(ref_side+i)%n_nodes].second; _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0); @@ -1628,3 +1659,134 @@ MAST::LevelSetIntersection::get_nondimensional_coordinate_for_node return it->second; } + + +bool +MAST::LevelSetIntersection::if_node_is_new(const libMesh::Node& node) const { + + libmesh_assert(_initialized); + + for (unsigned int i=0; i<_new_nodes.size(); i++) + if (&node == _new_nodes[i]) + return true; + + // if it gets here then the node was not created by this intersection + // operation + return false; +} + + + +bool +MAST::LevelSetIntersection::if_interior_node(const libMesh::Node& node) const { + + libmesh_assert(_initialized); + + return _interior_nodes.count(&node); +} + + + +std::pair +MAST::LevelSetIntersection::get_bounding_nodes_for_node(const libMesh::Node& node) const { + + libmesh_assert(_initialized); + + libmesh_assert(this->if_node_is_new(node)); + + std::map>::const_iterator + it = _bounding_nodes.find(&node); + + libmesh_assert(it != _bounding_nodes.end()); + + return it->second; +} + + +void +MAST::LevelSetIntersection:: +get_material_sides_without_intersection(std::set& sides) const { + + libmesh_assert_equal_to(sides.size(), 0); + + std::map >::const_iterator + it, + end = _node_phi_vals.end(); + + for (unsigned int i=0; i<_elem->n_sides(); i++) { + + std::unique_ptr side(_elem->side_ptr(i).release()); + + // check to see if all nodes of this side are on the material side + bool + on_material = true; + for (unsigned int j=0; jn_nodes(); j++) { + + it = _node_phi_vals.find(side->node_ptr(j)); + // the node level set value should be in the code + libmesh_assert(it != end); + + // neither on level set nor on positive side + if (!(it->second.second || it->second.first > _tol)) { + + on_material = false; + break; + } + } + + // if the on_material flag is still true then the side is added to + // the set + if (on_material) + sides.insert(i); + } +} + + +void +MAST::LevelSetIntersection:: +get_nearest_intersection_point(const libMesh::Point &p, + libMesh::Point &pt) { + + libmesh_assert(_initialized); + + // check the element nodes for closest node + std::map >::const_iterator + it = _node_phi_vals.begin(), + end = _node_phi_vals.end(); + + libMesh::Point + dp; + + Real + v = 0., + dist = 1.e12; + + for ( ; it != end; it++) { + + if (it->second.second) { // on level set + + dp = *it->first - p; + + if (dp.norm() < dist) { + pt = *it->first; + dist = dp.norm(); + } + } + } + + // check the new nodes for possible candidates + for (unsigned int i=0; i<_new_nodes.size(); i++) { + + (*_phi)(*_new_nodes[i], 0., v); + + if (std::fabs(v) <= _tol) { + + dp = *_new_nodes[i] - p; + + if (dp.norm() < dist) { + pt = *_new_nodes[i]; + dist = dp.norm(); + } + } + } +} diff --git a/src/level_set/level_set_intersection.h b/src/level_set/level_set_intersection.h index 415c53fe..72f5a87d 100644 --- a/src/level_set/level_set_intersection.h +++ b/src/level_set/level_set_intersection.h @@ -107,6 +107,14 @@ namespace MAST { */ Real get_node_phi_value(const libMesh::Node* n) const; + + /*! + * The case of two adjacent edges results in a new node on an edge that is not coincident + * with the level set interface. This will end up as a hanging node if added to the mesh. + * @returns true if this node \p n is the hanging node. + */ + bool if_hanging_node(const libMesh::Node* n) const; + /*! * @returns true if the intersection is through the element, or * has colinear edge. @@ -138,10 +146,17 @@ namespace MAST { /*! * @returns \p true if there is any portion of the element (interior - * or edge) that is on the positive side of the level set function. + * edge, or node) that is on the positive side of the level set function. */ bool if_elem_has_positive_phi_region() const; + + /*! + * @returns \p true if there is any portion of the element (interior + * edge, or node) that is on the negative side of the level set function. + */ + bool if_elem_has_negative_phi_region() const; + const std::vector& get_sub_elems_positive_phi() const; @@ -153,8 +168,8 @@ namespace MAST { /*! * @returns the id of side that is on the interface. In case the - * element does not have a side on the interface, then a negative - * value is returned. + * element does not have a side on the interface, then an error is + * thrown. */ unsigned int get_side_on_interface(const libMesh::Elem& e) const; @@ -164,6 +179,42 @@ namespace MAST { const libMesh::Point& get_nondimensional_coordinate_for_node(const libMesh::Node& n) const; + + + /*! + * identifies if the node from the subelements is a new node or + * an existing node from the parent element. + */ + bool if_node_is_new(const libMesh::Node& node) const; + + + /*! + * identifies if the new node is on an edge along the level-set method + * in the interior of the element (as opposed to on the edges of the + * original element). + */ + bool if_interior_node(const libMesh::Node& node) const; + + + /*! + * for new nodes required to create the subelements this method + * returns the nodes on an edge that bound the given node. An + * error is thrown if the node is not a + */ + std::pair + get_bounding_nodes_for_node(const libMesh::Node& node) const; + + + /*! + * identifies the sides of the element that are completely on the material side without + * any intersection on them. + */ + void + get_material_sides_without_intersection(std::set& sides) const; + + + void get_nearest_intersection_point(const libMesh::Point& p, + libMesh::Point& pt); protected: @@ -217,6 +268,8 @@ namespace MAST { const libMesh::Elem* _elem; bool _initialized; + const MAST::FieldFunction* _phi; + /*! * \p true if element is completely on the positive side of level set * with no intersection @@ -245,6 +298,9 @@ namespace MAST { std::vector _new_elems; std::map _node_local_coords; std::map > _node_phi_vals; + std::set _interior_nodes; + std::map> _bounding_nodes; + std::set _hanging_node; }; } diff --git a/src/level_set/level_set_nonlinear_implicit_assembly.h b/src/level_set/level_set_nonlinear_implicit_assembly.h index 13ee9974..0e4fa6f5 100644 --- a/src/level_set/level_set_nonlinear_implicit_assembly.h +++ b/src/level_set/level_set_nonlinear_implicit_assembly.h @@ -165,28 +165,8 @@ namespace MAST { MAST::OutputAssemblyElemOperations& output); - /*! - * Evaluates the total sensitivity of \p output wrt \p p using - * the adjoint solution provided in \p dq_dX for a linearization - * about solution \p X. - */ - /*virtual Real - calculate_output_adjoint_sensitivity(const libMesh::NumericVector& X, - const libMesh::NumericVector& dq_dX, - const MAST::FunctionBase& p, - MAST::AssemblyElemOperations& elem_ops, - MAST::OutputAssemblyElemOperations& output, - const bool include_partial_sens = true); - */ - protected: - /*Real - _adjoint_sensitivity_dot_product (const MAST::FunctionBase& f, - const libMesh::NumericVector& X, - const libMesh::NumericVector& dq_dX); - */ - bool _enable_dof_handler; bool _evaluate_output_on_negative_phi; diff --git a/src/level_set/level_set_perimeter_output.cpp b/src/level_set/level_set_perimeter_output.cpp index 78aae29a..0f6184ad 100644 --- a/src/level_set/level_set_perimeter_output.cpp +++ b/src/level_set/level_set_perimeter_output.cpp @@ -29,11 +29,11 @@ #include "libmesh/parallel.h" -MAST::LevelSetPerimeter::LevelSetPerimeter(MAST::LevelSetIntersection& intersection): +MAST::LevelSetPerimeter::LevelSetPerimeter(): MAST::OutputAssemblyElemOperations(), -_intersection (intersection), -_per (0.), -_dper_dp (0.) { +_per (0.), +_dper_dp (0.), +_heaviside_smooth_delta (0.1) { } @@ -53,7 +53,7 @@ MAST::LevelSetPerimeter::init(const MAST::GeomElem& elem) { libmesh_assert(_system); libmesh_assert(_assembly); - _physics_elem = new MAST::LevelSetElementBase(*_system, *_assembly, elem); + _physics_elem = new MAST::LevelSetElementBase(*_system, elem); } @@ -89,7 +89,7 @@ MAST::LevelSetPerimeter::output_for_elem() { MAST::LevelSetElementBase& e = dynamic_cast(*_physics_elem); - return e.perimeter(); + return e.perimeter(_heaviside_smooth_delta); } else return 0.; @@ -125,7 +125,7 @@ MAST::LevelSetPerimeter::output_sensitivity_for_elem(const MAST::FunctionBase& p MAST::LevelSetElementBase& e = dynamic_cast(*_physics_elem); - return e.perimeter_sensitivity(); + return e.perimeter_sensitivity(_heaviside_smooth_delta); } else return 0.; @@ -157,7 +157,7 @@ MAST::LevelSetPerimeter::evaluate() { MAST::LevelSetElementBase& e = dynamic_cast(*_physics_elem); - _per += e.perimeter(); + _per += e.perimeter(_heaviside_smooth_delta); } } @@ -171,6 +171,13 @@ MAST::LevelSetPerimeter::evaluate_sensitivity(const MAST::FunctionBase& f) { +void +MAST::LevelSetPerimeter::evaluate_topology_sensitivity(const MAST::FunctionBase& f) { + + libmesh_assert(false); +} + + void MAST::LevelSetPerimeter::evaluate_topology_sensitivity(const MAST::FunctionBase& f, const MAST::FieldFunction& vel) { @@ -192,7 +199,7 @@ MAST::LevelSetPerimeter::evaluate_topology_sensitivity(const MAST::FunctionBase& MAST::LevelSetElementBase& e = dynamic_cast(*_physics_elem); - _dper_dp += e.perimeter_sensitivity(); + _dper_dp += e.perimeter_sensitivity(_heaviside_smooth_delta); } } diff --git a/src/level_set/level_set_perimeter_output.h b/src/level_set/level_set_perimeter_output.h index 6fbc4448..33c544b0 100644 --- a/src/level_set/level_set_perimeter_output.h +++ b/src/level_set/level_set_perimeter_output.h @@ -42,10 +42,16 @@ namespace MAST { public: - LevelSetPerimeter(MAST::LevelSetIntersection& intersection); + LevelSetPerimeter(); virtual ~LevelSetPerimeter(); + /*! + * Sets the value of the heaviside smooth delta, which is the width +/- d of the level set function + * about which the approximate Heaviside function is smoothed. The default value is 0.1 + */ + void set_heaviside_smoothing_delta(Real d=0.1) { _heaviside_smooth_delta = d;} + /*! * virtual function, nothing to be done for level set */ @@ -156,6 +162,15 @@ namespace MAST { libmesh_assert(false); // to be implemented } + /*! + * this evaluates all relevant topological sensitivity components on + * the element. + * This is only done on the current element for which this + * object has been initialized. + */ + virtual void + evaluate_topology_sensitivity(const MAST::FunctionBase& f); + /*! * This evaluates the contribution to the topology sensitivity on the * boundary \f$ \int_\Gamma V_n~d\Gamma \f$ @@ -165,9 +180,9 @@ namespace MAST { protected: - const MAST::LevelSetIntersection& _intersection; Real _per; Real _dper_dp; + Real _heaviside_smooth_delta; }; } diff --git a/src/level_set/level_set_transient_assembly.cpp b/src/level_set/level_set_transient_assembly.cpp index 3e16bc02..5847d2d4 100644 --- a/src/level_set/level_set_transient_assembly.cpp +++ b/src/level_set/level_set_transient_assembly.cpp @@ -131,7 +131,7 @@ init(const MAST::GeomElem& elem) { discipline = dynamic_cast(*_discipline); MAST::LevelSetElementBase - *e = new MAST::LevelSetElementBase(*_system, *_assembly, elem); + *e = new MAST::LevelSetElementBase(*_system, elem); if (discipline.has_velocity_function()) e->set_velocity_function(discipline.get_velocity_function()); diff --git a/src/level_set/level_set_volume_output.cpp b/src/level_set/level_set_volume_output.cpp index 549b0c3a..1ee16fca 100644 --- a/src/level_set/level_set_volume_output.cpp +++ b/src/level_set/level_set_volume_output.cpp @@ -20,7 +20,6 @@ // MAST includes #include "level_set/level_set_volume_output.h" #include "level_set/level_set_elem_base.h" -#include "level_set/level_set_intersection.h" #include "level_set/level_set_intersected_elem.h" #include "base/system_initialization.h" #include "base/nonlinear_system.h" @@ -29,9 +28,8 @@ #include "libmesh/parallel.h" -MAST::LevelSetVolume::LevelSetVolume(MAST::LevelSetIntersection& intersection): +MAST::LevelSetVolume::LevelSetVolume(): MAST::OutputAssemblyElemOperations(), -_intersection (intersection), _vol (0.), _dvol_dp (0.) { @@ -53,7 +51,7 @@ MAST::LevelSetVolume::init(const MAST::GeomElem& elem) { libmesh_assert(_system); libmesh_assert(_assembly); - _physics_elem = new MAST::LevelSetElementBase(*_system, *_assembly, elem); + _physics_elem = new MAST::LevelSetElementBase(*_system, elem); } @@ -171,6 +169,14 @@ MAST::LevelSetVolume::evaluate_sensitivity(const MAST::FunctionBase& f) { +void +MAST::LevelSetVolume::evaluate_topology_sensitivity(const MAST::FunctionBase& f) { + + libmesh_assert(false); +} + + + void MAST::LevelSetVolume::evaluate_topology_sensitivity(const MAST::FunctionBase& f, const MAST::FieldFunction& vel) { diff --git a/src/level_set/level_set_volume_output.h b/src/level_set/level_set_volume_output.h index 13ee9db7..f5d79e9a 100644 --- a/src/level_set/level_set_volume_output.h +++ b/src/level_set/level_set_volume_output.h @@ -27,15 +27,12 @@ namespace MAST { - // Forward declerations - class LevelSetIntersection; - class LevelSetVolume: public MAST::OutputAssemblyElemOperations { public: - LevelSetVolume(MAST::LevelSetIntersection& intersection); + LevelSetVolume(); virtual ~LevelSetVolume(); @@ -149,6 +146,15 @@ namespace MAST { libmesh_assert(false); // to be implemented } + /*! + * this evaluates all relevant topological sensitivity components on + * the element. + * This is only done on the current element for which this + * object has been initialized. + */ + virtual void + evaluate_topology_sensitivity(const MAST::FunctionBase& f); + /*! * This evaluates the contribution to the topology sensitivity on the * boundary \f$ \int_\Gamma V_n~d\Gamma \f$ @@ -158,7 +164,6 @@ namespace MAST { protected: - const MAST::LevelSetIntersection& _intersection; Real _vol; Real _dvol_dp; }; diff --git a/src/level_set/sub_cell_fe.cpp b/src/level_set/sub_cell_fe.cpp index c854ea81..6277a8cd 100644 --- a/src/level_set/sub_cell_fe.cpp +++ b/src/level_set/sub_cell_fe.cpp @@ -300,6 +300,7 @@ MAST::SubCellFE::init_for_side(const MAST::GeomElem& elem, _fe->get_JxW(); if (if_calculate_dphi) _fe->get_dphi(); + if (_init_second_order_derivatives) _fe->get_d2phi(); ////////////////////////////////////////////////////////////// diff --git a/src/level_set/sub_elem_mesh_refinement.cpp b/src/level_set/sub_elem_mesh_refinement.cpp new file mode 100644 index 00000000..e8b00340 --- /dev/null +++ b/src/level_set/sub_elem_mesh_refinement.cpp @@ -0,0 +1,727 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +// MAST includes +#include "level_set/sub_elem_mesh_refinement.h" +#include "level_set/level_set_intersection.h" +#include "level_set/sub_elem_node_map.h" + +// libMesh includes +#include "libmesh/mesh_communication.h" +#include "libmesh/partitioner.h" +#include "libmesh/dof_map.h" + + +MAST::SubElemMeshRefinement::SubElemMeshRefinement(libMesh::MeshBase& mesh, + libMesh::System& sys): +libMesh::System::Constraint (), +_initialized (false), +_strong_discontinuity (false), +_negative_level_set_subdomain_offset (0), +_inactive_subdomain_offset (0), +_level_set_boundary_id (0), +_mesh (mesh), +_system (sys), +_node_map (new MAST::SubElemNodeMap) { + +} + + + +MAST::SubElemMeshRefinement::~SubElemMeshRefinement() { + + this->clear_mesh(); + delete _node_map; +} + + + +bool +MAST::SubElemMeshRefinement::process_mesh(const MAST::FieldFunction& phi, + bool strong_discontinuity, + Real time, + unsigned int negative_level_set_subdomain_offset, + unsigned int inactive_subdomain_offset, + unsigned int level_set_boundary_id) { + + libmesh_assert(!_initialized); + + // currently only implemented for replicated mesh + libmesh_assert(_mesh.is_replicated()); + + // if strong discontinuity is required then coincident nodes are + // created which will need unique_id support in libMesh + if (strong_discontinuity) { +#ifndef LIBMESH_ENABLE_UNIQUE_ID + libmesh_assert(false); +#endif + } + + MAST::LevelSetIntersection intersect; + + libMesh::MeshBase::element_iterator + e_it = _mesh.active_local_elements_begin(), + e_end = _mesh.active_local_elements_end(); + + // for a replicated mesh all processors have to do the exct same operations + // to the mesh in the same order. Hence, we modify the iterators. + if (_mesh.is_replicated()) { + + e_it = _mesh.active_elements_begin(), + e_end = _mesh.active_elements_end(); + } + + // first we need to identify all the elements that will be refined. + // Then we will iterate over all of them. Otherwise, the addition of + // new elemnts can invalidate the element iterators. + std::vector + elems_to_partition; + + for ( ; e_it != e_end; e_it++) { + + libMesh::Elem* elem = *e_it; + + intersect.init(phi, *elem, time, _mesh.max_elem_id(), _mesh.max_node_id()); + + if (intersect.if_intersection_through_elem() || + ((intersect.get_intersection_mode() == MAST::COLINEAR_EDGE || + intersect.get_intersection_mode() == MAST::THROUGH_NODE) && + intersect.get_sub_elems_negative_phi().size() == 1) || + intersect.if_elem_on_negative_phi()) + elems_to_partition.push_back(elem); + + intersect.clear(); + } + + + // now we process only the selected elements + bool + mesh_changed = false; + + for (unsigned int i=0; isubdomain_id())); + elem->subdomain_id() += inactive_subdomain_offset; + mesh_changed = true; + } + else if ((intersect.get_intersection_mode() == MAST::COLINEAR_EDGE || + intersect.get_intersection_mode() == MAST::THROUGH_NODE) && + intersect.get_sub_elems_negative_phi().size() == 1) { + + if (strong_discontinuity) { + _process_negative_element(negative_level_set_subdomain_offset, + level_set_boundary_id, + *elem, + intersect); + + _old_elems.push_back(std::make_pair(elem, elem->subdomain_id())); + elem->subdomain_id() += inactive_subdomain_offset; + } + else { + + _old_elems.push_back(std::make_pair(elem, elem->subdomain_id())); + elem->subdomain_id() += negative_level_set_subdomain_offset; + + } + } + else if (intersect.if_elem_on_negative_phi()) { + // if the element has no positive region, then we set its + // subdomain id to that of negative level set offset + + _old_elems.push_back(std::make_pair(elem, elem->subdomain_id())); + elem->subdomain_id() += negative_level_set_subdomain_offset; + } + + intersect.clear(); + } + + // If the mesh changed on any processor, it changed globally + _mesh.comm().max(mesh_changed); + + // And we may need to update DistributedMesh values reflecting the changes + if (mesh_changed) + _mesh.update_parallel_id_counts(); + + if (mesh_changed && !_mesh.is_replicated()) + { + libMesh::MeshCommunication().make_elems_parallel_consistent (_mesh); + libMesh::MeshCommunication().make_new_nodes_parallel_consistent (_mesh); +#ifdef DEBUG + _mesh.libmesh_assert_valid_parallel_ids(); +#endif + } + + // If we're refining a Replicated_mesh, then we haven't yet assigned + // node processor ids. But if we're refining a partitioned + // Replicated_mesh, then we *need* to assign node processor ids. + if (mesh_changed && _mesh.is_replicated() && + (_mesh.unpartitioned_elements_begin() == + _mesh.unpartitioned_elements_end())) + libMesh::Partitioner::set_node_processor_ids(_mesh); + + if (mesh_changed) + _mesh.prepare_for_use(/*skip_renumber =*/ false); + + _strong_discontinuity = strong_discontinuity; + _negative_level_set_subdomain_offset = negative_level_set_subdomain_offset; + _inactive_subdomain_offset = inactive_subdomain_offset; + _level_set_boundary_id = level_set_boundary_id; + _initialized = true; + + return mesh_changed; +} + + +bool +MAST::SubElemMeshRefinement::clear_mesh() { + + // clear the data structure + _hanging_node.clear(); + + // modify the original element subdomain + for (unsigned int i=0; i<_old_elems.size(); i++) + _old_elems[i].first->subdomain_id() = _old_elems[i].second; + _old_elems.clear(); + + // remove all the new nodes and elements from the mesh + for (unsigned int i=0; i<_new_elems.size(); i++) { + + // Remove this element from any neighbor + // lists that point to it. + _new_elems[i]->nullify_neighbors(); + + // Remove any boundary information associated + // with this element + _mesh.get_boundary_info().remove(_new_elems[i]); + + _mesh.delete_elem(_new_elems[i]); + } + + for (unsigned int i=0; i<_new_nodes.size(); i++) + _mesh.delete_node(_new_nodes[i]); + + + bool + mesh_changed = false; + + if (_new_elems.size() || _new_nodes.size()) mesh_changed = true; + + _mesh.comm().max(mesh_changed); + + if (mesh_changed) { + + _mesh.update_parallel_id_counts(); + _new_elems.clear(); + _new_nodes.clear(); + _node_map->clear(); + _mesh.prepare_for_use(); + } + + if (mesh_changed && !_mesh.is_serial()) { + + libMesh::MeshCommunication().make_nodes_parallel_consistent (_mesh); + +#ifdef DEBUG + MeshTools::libmesh_assert_valid_procids(_mesh); +#endif + } + + _negative_level_set_ids.clear(); + _strong_discontinuity = false; + _negative_level_set_subdomain_offset = 0; + _inactive_subdomain_offset = 0; + _level_set_boundary_id = 0; + _initialized = false; + + return mesh_changed; +} + + + +void +MAST::SubElemMeshRefinement::constrain() { + + // we will constrain only if the mesh has been processed for the level set + // no constraint is to be added for weak discontinuity. + if (!_initialized || !_strong_discontinuity) return; + + // For strong discontinuity, iterate over all elements and constrain all + // dofs on them + libMesh::DofMap& dof_map = _system.get_dof_map(); + + libMesh::MeshBase::const_element_iterator el = + _system.get_mesh().active_local_elements_begin(); + const libMesh::MeshBase::const_element_iterator end_el = + _system.get_mesh().active_local_elements_end(); + + const libMesh::dof_id_type + first_dof = dof_map.first_dof(_mesh.comm().rank()), + last_dof = dof_map.end_dof(_mesh.comm().rank()); + + std::vector + dof_indices; + + for ( ; el != end_el; ++el) { + + const libMesh::Elem* elem = *el; + + if (_negative_level_set_ids.count(elem->subdomain_id())) { + + dof_indices.clear(); + dof_map.dof_indices(elem, dof_indices); + + // constrain all dofs if they have not already been constrained. + for (unsigned int i=0; i= first_dof || dof_indices[i] < last_dof) && + !dof_map.is_constrained_dof(dof_indices[i])) { + + libMesh::DofConstraintRow c_row; + dof_map.add_constraint_row(dof_indices[i], c_row, true); + } + } + } + } + + // now add constriant for the nodes identified as hanging node + std::set>>::iterator + n_it = _hanging_node.begin(), + n_end = _hanging_node.end(); + + libMesh::Point + p; + + Real + d = 0.; + + unsigned int + dof_b_node1, + dof_b_node2, + dof_node; + + for ( ; n_it != n_end; n_it++) { + + const std::pair> + &v = *n_it; + + // obtain the fraction of the node from the bounding nodes + // distance of node from first + p = *v.first - *v.second.first; + d = p.norm(); + // distance between the bounding nodes + p = *v.second.second - *v.second.first; + d /= p.norm(); + + + // we iterate over the variables in the system + // and add a constraint for each node + for (unsigned int i=0; i<_system.n_vars(); i++) { + + // identify the dofs for the ith variable on each node + // first, the node for which the constraint will be added + dof_indices.clear(); + dof_map.dof_indices(v.first, dof_indices, i); + libmesh_assert_equal_to(dof_indices.size(), 1); + dof_node = dof_indices[0]; + + // next, the first bounding node + dof_indices.clear(); + dof_map.dof_indices(v.second.first, dof_indices, i); + libmesh_assert_equal_to(dof_indices.size(), 1); + dof_b_node1 = dof_indices[0]; + + // next, the second bounding node + dof_indices.clear(); + dof_map.dof_indices(v.second.second, dof_indices, i); + libmesh_assert_equal_to(dof_indices.size(), 1); + dof_b_node2 = dof_indices[0]; + + // now create and add the constraint + if ((dof_node >= first_dof || dof_node < last_dof) && + !dof_map.is_constrained_dof(dof_node)) { + + // the constraint assumes linear variation of the value + // between the bounding nodes + // the constraint reads + // un = (1-d) ub1 + d ub2 + // or, un - (1-d) ub1 - d ub2 = 0 + libMesh::DofConstraintRow c_row; + c_row[dof_b_node1] = (1.-d); + c_row[dof_b_node2] = d; + + dof_map.add_constraint_row(dof_node, c_row, true); + } + } + } +} + + + +void +MAST::SubElemMeshRefinement::_process_sub_elements(bool strong_discontinuity, + unsigned int negative_level_set_subdomain_offset, + unsigned level_set_boundary_id, + libMesh::Elem& e, + MAST::LevelSetIntersection& intersect, + bool positive_phi, + const std::vector& elems) { + + libmesh_assert(!_initialized); + + std::map + intersection_object_to_mesh_node_map; + + std::vector> + interior_node_association; + + for (unsigned int i=0; itype()).release(); + + // set nodes for this child element + for (unsigned int j=0; jn_nodes(); j++) { + + const libMesh::Node + *sub_e_node = sub_e->node_ptr(j); + + if (!intersect.if_node_is_new(*sub_e_node)) { + + // this is a node from the parent element. So, we use is as is + child->set_node(j) = const_cast(sub_e_node); + + // keep track of nodes for the addition of interior nodes + intersection_object_to_mesh_node_map[sub_e_node] = sub_e_node; + } + else if (intersect.if_interior_node(*sub_e_node)) { + + // this node will be added after all edge nodes have been + // added. This will be added as the jth node of the + // child elem + interior_node_association.push_back + (std::tuple(sub_e_node, child, j)); + } + else { + + // this is a new node. So, we ask the intersection object + // about the bounding nodes and add them to this new node + std::pair + bounding_nodes = intersect.get_bounding_nodes_for_node(*sub_e_node); + + libMesh::Node* + child_node = _add_node(*sub_e_node, + strong_discontinuity, + positive_phi, + e.processor_id(), + bounding_nodes); + child->set_node(j) = child_node; + + // identify this node to as a hanging node + if (intersect.if_hanging_node(sub_e_node)) + _hanging_node.insert(std::make_pair(child_node, bounding_nodes)); + + // keep track for nodes for the addition of interior nodes + intersection_object_to_mesh_node_map[sub_e_node] = child_node; + } + } + + // set flags for this child element + //child->set_refinement_flag(libMesh::Elem::JUST_REFINED); + child->set_p_level(e.p_level()); + child->set_p_refinement_flag(e.p_refinement_flag()); + child->set_n_systems(e.n_systems()); + // we need to offset the subdomain id for an element type that is + // not the same as the parent element since exodus output requires + // different subdomain ids for different element types. + if (child->type() == e.type()) + child->subdomain_id() = e.subdomain_id(); + else + child->subdomain_id() = e.subdomain_id()+1; + + // the negative level set is offset by the specified value so that + // the assembly routines can deal with them separately + if (!positive_phi) { + + child->subdomain_id() += negative_level_set_subdomain_offset; + _negative_level_set_ids.insert(child->subdomain_id()); + } + +#if (LIBMESH_MAJOR_VERSION == 1 && LIBMESH_MINOR_VERSION >= 5) + libmesh_assert_equal_to (child->n_extra_integers(), + e.n_extra_integers()); + for (unsigned int j=0; j != e.n_extra_integers(); ++j) + child->set_extra_integer(j, e.get_extra_integer(j)); +#endif + _mesh.add_elem(child); + + if (intersect.has_side_on_interface(*sub_e)) + _mesh.boundary_info->add_side(child, + intersect.get_side_on_interface(*sub_e), + level_set_boundary_id); + + _new_elems.push_back(child); + } + + + // now process the interior nodes + for (unsigned int i=0; i(interior_node_association[i]); + + libMesh::Elem + *child = std::get<1>(interior_node_association[i]); + + unsigned int + node_num = std::get<2>(interior_node_association[i]); + + std::pair + bounding_nodes = intersect.get_bounding_nodes_for_node(*sub_e_node); + + // since the nodes in the map are based on newly created nodes, and + // not those created by the intersection object, we replace the + // bounding_nodes pair with those that were created here. + libmesh_assert(intersection_object_to_mesh_node_map.count(bounding_nodes.first)); + libmesh_assert(intersection_object_to_mesh_node_map.count(bounding_nodes.second)); + bounding_nodes.first = intersection_object_to_mesh_node_map[bounding_nodes.first]; + bounding_nodes.second = intersection_object_to_mesh_node_map[bounding_nodes.second]; + + libMesh::Node* + child_node = _add_node(*sub_e_node, + strong_discontinuity, + positive_phi, + e.processor_id(), + bounding_nodes); + + child->set_node(node_num) = child_node; + } +} + + + + +void +MAST::SubElemMeshRefinement::_process_negative_element(unsigned int negative_level_set_subdomain_offset, + unsigned level_set_boundary_id, + libMesh::Elem& e, + MAST::LevelSetIntersection& intersect) { + + libmesh_assert(!_initialized); + + std::set + side_nodes; + + // get the nodes on the side with the interface that will be replaced + // with new nodes on the negative phi + if (intersect.get_intersection_mode() == MAST::THROUGH_NODE) + side_nodes.insert(e.node_ptr(intersect.node_on_boundary())); + else if (intersect.get_intersection_mode() == MAST::COLINEAR_EDGE) { + + unsigned int i = intersect.edge_on_boundary(); + std::unique_ptr side(e.side_ptr(i)); + for (unsigned int j=0; jn_nodes(); j++) + side_nodes.insert(side->node_ptr(j)); + } + else { + // should not get here + libmesh_assert(false); + } + + + libMesh::Elem + *child = libMesh::Elem::build(e.type()).release(); + + // set nodes for this new element + for (unsigned int j=0; jset_node(j) = e_node; + else { + + std::pair + bounding_nodes = std::make_pair(e_node, e_node); + + libMesh::Node* + child_node = _add_node(*e_node, + true, // this method only deals with strong discontinuity + false, // and with nodes on negative level set + e.processor_id(), + bounding_nodes); + child->set_node(j) = child_node; + } + } + + // set flags for this child element + //child->set_refinement_flag(libMesh::Elem::JUST_REFINED); + child->set_p_level(e.p_level()); + child->set_p_refinement_flag(e.p_refinement_flag()); + child->set_n_systems(e.n_systems()); + // we need to offset the subdomain id for an element type that is + // not the same as the parent element since exodus output requires + // different subdomain ids for different element types. + if (child->type() == e.type()) + child->subdomain_id() = e.subdomain_id() + negative_level_set_subdomain_offset; + else + child->subdomain_id() = e.subdomain_id()+1 + negative_level_set_subdomain_offset; + + _negative_level_set_ids.insert(child->subdomain_id()); + +#if (LIBMESH_MAJOR_VERSION == 1 && LIBMESH_MINOR_VERSION >= 5) + libmesh_assert_equal_to (child->n_extra_integers(), + e.n_extra_integers()); + + for (unsigned int j=0; j != e.n_extra_integers(); ++j) + child->set_extra_integer(j, e.get_extra_integer(j)); +#endif + _mesh.add_elem(child); + + _new_elems.push_back(child); +} + + + +libMesh::Node* +MAST::SubElemMeshRefinement::_add_node(const libMesh::Point& p, + bool strong_disontinuity, + bool positive_phi, + unsigned int processor_id, + const std::pair& bounding_nodes) { + + libmesh_assert(!_initialized); + + unsigned int + id1 = std::min(bounding_nodes.first->id(), bounding_nodes.second->id()), + id2 = std::max(bounding_nodes.first->id(), bounding_nodes.second->id()); + + std::pair& + node_pair = _node_map->add(id1, id2); + + // if a weak discontinuity is requested, and if the node has already been + // created, then make sure that both nodes are the same + if (!strong_disontinuity) { + + if (node_pair.first) { + libmesh_assert_equal_to(node_pair.first, node_pair.second); + return node_pair.first; + } + else { + + // for a weak discontinuity nodes on either side of the discontinuity + // are the same + node_pair.first = _mesh.add_point(p, libMesh::DofObject::invalid_id, processor_id); + _new_nodes.push_back(node_pair.first); + + libmesh_assert(node_pair.first); + + node_pair.first->processor_id() = libMesh::DofObject::invalid_processor_id; + node_pair.second = node_pair.first; + + return node_pair.first; + } + } + else { + + if (positive_phi) { + + if (node_pair.first) + return node_pair.first; + else { + + // create and store a separate node for the positive side of the + // level set + node_pair.first = _mesh.add_point(p, libMesh::DofObject::invalid_id, processor_id); + _new_nodes.push_back(node_pair.first); + + libmesh_assert(node_pair.first); + + node_pair.first->processor_id() = libMesh::DofObject::invalid_processor_id; + + return node_pair.first; + } + } + else { // negative phi + + if (node_pair.second) + return node_pair.second; + else { + + // create and store a separate node for the positive side of the + // level set + node_pair.second = _mesh.add_point(p, libMesh::DofObject::invalid_id, processor_id); + _new_nodes.push_back(node_pair.second); + + libmesh_assert(node_pair.second); + + node_pair.second->processor_id() = libMesh::DofObject::invalid_processor_id; + + return node_pair.second; + } + } + } +} + + diff --git a/src/level_set/sub_elem_mesh_refinement.h b/src/level_set/sub_elem_mesh_refinement.h new file mode 100644 index 00000000..f2252758 --- /dev/null +++ b/src/level_set/sub_elem_mesh_refinement.h @@ -0,0 +1,122 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast__sub_elem_mesh_refinement_h__ +#define __mast__sub_elem_mesh_refinement_h__ + +// MAST includes +#include "base/mast_data_types.h" + +// libMesh includes +#include "libmesh/mesh_base.h" +#include "libmesh/system.h" + + +namespace MAST { + + // Forward declerations + template class FieldFunction; + class LevelSetIntersection; + class SubElemNodeMap; + + class SubElemMeshRefinement: + public libMesh::System::Constraint { + + public: + + SubElemMeshRefinement(libMesh::MeshBase& mesh, + libMesh::System& sys); + + virtual ~SubElemMeshRefinement(); + + bool initialized() { return _initialized; } + + bool process_mesh(const MAST::FieldFunction& phi, + bool strong_discontinuity, + Real time, + unsigned int negative_level_set_subdomain_offset, + unsigned int inactive_subdomain_offset, + unsigned int level_set_boundary_id); + + bool clear_mesh(); + + /*! + * provides implementation of the libMesh::System::Constraint::constrain() + * virtual method + */ + virtual void + constrain (); + + protected: + + void _process_sub_elements(bool strong_discontinuity, + unsigned int negative_level_set_subdomain_offset, + unsigned int level_set_boundary_id, + libMesh::Elem& e, + MAST::LevelSetIntersection& intersect, + bool positive_phi, + const std::vector& elems); + + void _process_negative_element(unsigned int negative_level_set_subdomain_offset, + unsigned level_set_boundary_id, + libMesh::Elem& e, + MAST::LevelSetIntersection& intersect); + + /*! + * \returns a node between the bounding nodes at the specified + * location. If a node already exists between these bounding nodes the + * that node is returned. Else, a new node is created, added to the + * mesh and returned. + */ + libMesh::Node* + _add_node(const libMesh::Point& p, + bool strong_disontinuity, + bool positive_phi, + unsigned int processor_id, + const std::pair& bounding_nodes); + + libMesh::Elem* _add_elem(); + + bool _initialized; + + bool _strong_discontinuity; + + unsigned int _negative_level_set_subdomain_offset; + + unsigned int _inactive_subdomain_offset; + + unsigned int _level_set_boundary_id; + + libMesh::MeshBase& _mesh; + + libMesh::System& _system; + + // map for storing new nodes + MAST::SubElemNodeMap *_node_map; + + std::set _negative_level_set_ids; + std::vector _new_nodes; + std::vector _new_elems; + std::vector> _old_elems; + std::set>> _hanging_node; + }; +} + + +#endif // __mast__sub_elem_mesh_refinement_h__ diff --git a/src/level_set/sub_elem_node_map.cpp b/src/level_set/sub_elem_node_map.cpp new file mode 100644 index 00000000..f88766a1 --- /dev/null +++ b/src/level_set/sub_elem_node_map.cpp @@ -0,0 +1,59 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// MAST includes +#include "level_set/sub_elem_node_map.h" + + +MAST::SubElemNodeMap::SubElemNodeMap() { + +} + + +MAST::SubElemNodeMap::~SubElemNodeMap() { + +} + + + + +unsigned int +MAST::SubElemNodeMap::count(libMesh::dof_id_type bracket_node1, + libMesh::dof_id_type bracket_node2) const { + + return _map.count(std::make_pair(bracket_node1, bracket_node2)); +} + + +std::pair& +MAST::SubElemNodeMap::add(libMesh::dof_id_type bracket_node1, + libMesh::dof_id_type bracket_node2) { + + MAST::SubElemNodeMap::map_type::iterator + it = _map.find(std::make_pair(bracket_node1, bracket_node2)); + + if (it == _map.end()) { + + it = _map.insert(std::make_pair(std::make_pair(bracket_node1, bracket_node2), + std::make_pair(nullptr, nullptr))).first; + } + + return it->second; +} + diff --git a/src/level_set/sub_elem_node_map.h b/src/level_set/sub_elem_node_map.h new file mode 100644 index 00000000..a451ea68 --- /dev/null +++ b/src/level_set/sub_elem_node_map.h @@ -0,0 +1,89 @@ +/* + * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit + * Copyright (C) 2013-2019 Manav Bhatia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __mast_sub_elem_node_map_h__ +#define __mast_sub_elem_node_map_h__ + +// C++ includes +#include +#include // std::hash + +// libMesh includes +#include "libmesh/mesh_base.h" + +namespace MAST{ + + + class SubElemNodeMap { + + // borrowing this from libMesh::TopologyMap + struct myhash { + public: + template + std::size_t operator()(const std::pair & x) const + { + return 3 * std::hash()(x.first) + std::hash()(x.second); + } + }; + + + // first pair is the key, which identifies the bounding nodes on a + // parent edge where the node is created. + // second pair is the set of nodes used by the adjacent elements on the + // positive and negative sides of the level set function. Specifying + // both to be same will allow a weak discontinuity, while specifying + // both to be different will allow a strong discontinuity + typedef + std::unordered_map + , + std::pair, MAST::SubElemNodeMap::myhash> map_type; + + + public: + + SubElemNodeMap(); + + + virtual ~SubElemNodeMap(); + + + void clear() { _map.clear(); } + + + bool empty() const { return _map.empty(); } + + + unsigned int + count(libMesh::dof_id_type bracket_node1, libMesh::dof_id_type bracket_node2) const; + + + std::pair& + add(libMesh::dof_id_type bracket_node1, libMesh::dof_id_type bracket_node2); + + + protected: + + + MAST::SubElemNodeMap::map_type _map; + }; + +} + + +#endif // __mast_sub_elem_node_map_h__ diff --git a/src/mesh/fe_base.h b/src/mesh/fe_base.h index a374f806..b15dc372 100644 --- a/src/mesh/fe_base.h +++ b/src/mesh/fe_base.h @@ -44,6 +44,8 @@ namespace MAST { virtual ~FEBase(); + bool initialized() const { return _initialized;} + /*! * this is used, in addition to \p libMesh::System::extra_quadrature_order * to set the quadrature rule. diff --git a/src/mesh/geom_elem.cpp b/src/mesh/geom_elem.cpp index f43decb1..e29a5253 100644 --- a/src/mesh/geom_elem.cpp +++ b/src/mesh/geom_elem.cpp @@ -158,11 +158,13 @@ MAST::GeomElem::init_fe(bool init_grads, std::unique_ptr MAST::GeomElem::init_side_fe(unsigned int s, bool init_grads, + bool init_second_order_derivative, int extra_quadrature_order) const { std::unique_ptr fe(new MAST::FEBase(*_sys_init)); fe->set_extra_quadrature_order(extra_quadrature_order); - + fe->set_evaluate_second_order_derivatives(init_second_order_derivative); + fe->init_for_side(*this, s, init_grads); return fe; diff --git a/src/mesh/geom_elem.h b/src/mesh/geom_elem.h index 5a21a4fe..beae4e71 100644 --- a/src/mesh/geom_elem.h +++ b/src/mesh/geom_elem.h @@ -134,6 +134,7 @@ namespace MAST { virtual std::unique_ptr init_side_fe(unsigned int s, bool init_grads, + bool init_second_order_derivative, int extra_quadrature_order = 0) const; diff --git a/src/mesh/nastran_io.cpp b/src/mesh/nastran_io.cpp index 1be1f654..494109f2 100644 --- a/src/mesh/nastran_io.cpp +++ b/src/mesh/nastran_io.cpp @@ -12,6 +12,9 @@ #include // #include +#include "base/mast_config.h" +#if MAST_ENABLE_NASTRANIO == 1 + // MAST includes. #include "mesh/nastran_io.h" #include "libfort/fort.hpp" @@ -316,3 +319,4 @@ void MAST::printNodeCoords(std::vector> nodes) } } +#endif // MAST_ENABLE_NASTRANIO diff --git a/src/mesh/nastran_io.h b/src/mesh/nastran_io.h index 6f488783..6212fa69 100644 --- a/src/mesh/nastran_io.h +++ b/src/mesh/nastran_io.h @@ -11,6 +11,9 @@ #include #include +#include "base/mast_config.h" +#if MAST_ENABLE_NASTRANIO == 1 + // Python includes. #include @@ -201,4 +204,5 @@ class NastranIO : public libMesh::MeshInput { void printNodeCoords(std::vector> nodes); } +#endif // MAST_ENABLE_NASTRANIO #endif // __mast_nastran_io_h__ diff --git a/src/optimization/function_evaluation.cpp b/src/optimization/function_evaluation.cpp index 503dba7b..0de811bf 100644 --- a/src/optimization/function_evaluation.cpp +++ b/src/optimization/function_evaluation.cpp @@ -23,11 +23,10 @@ #include // MAST includes -#include "optimization/function_evaluation.h" +#include "optimization/function_evaluation.h" // libMesh includes -#include "libmesh/parallel_implementation.h" - +#include "libmesh/parallel.h" void MAST::FunctionEvaluation::attach_optimization_interface(MAST::OptimizationInterface& opt) { @@ -514,6 +513,7 @@ MAST::FunctionEvaluation::_output_wrapper(unsigned int iter, libmesh_assert(this->comm().verify(iter)); libmesh_assert(this->comm().verify(x)); - this->output(iter, x, obj, fval, if_write_to_optim_file); + this->output(_iter, x, obj, fval, if_write_to_optim_file); + _iter++; } diff --git a/src/optimization/function_evaluation.h b/src/optimization/function_evaluation.h index a9951582..b057b735 100644 --- a/src/optimization/function_evaluation.h +++ b/src/optimization/function_evaluation.h @@ -49,6 +49,7 @@ namespace MAST { FunctionEvaluation(const libMesh::Parallel::Communicator& comm_in): libMesh::ParallelObject (comm_in), + _iter (0), _n_vars (0), _n_eq (0), _n_ineq (0), @@ -260,6 +261,8 @@ namespace MAST { protected: + unsigned int _iter; + unsigned int _n_vars; unsigned int _n_eq; diff --git a/src/solver/first_order_newmark_transient_solver.cpp b/src/solver/first_order_newmark_transient_solver.cpp index 7d34b17e..5251cf8c 100644 --- a/src/solver/first_order_newmark_transient_solver.cpp +++ b/src/solver/first_order_newmark_transient_solver.cpp @@ -364,6 +364,15 @@ elem_shape_sensitivity_calculations(const MAST::FunctionBase& f, +void +MAST::FirstOrderNewmarkTransientSolver:: +elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec) { + libmesh_assert(false); // to be implemented +} + + + void MAST::FirstOrderNewmarkTransientSolver:: elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, diff --git a/src/solver/first_order_newmark_transient_solver.h b/src/solver/first_order_newmark_transient_solver.h index 9227a7f7..a362dc8b 100644 --- a/src/solver/first_order_newmark_transient_solver.h +++ b/src/solver/first_order_newmark_transient_solver.h @@ -192,6 +192,15 @@ namespace MAST { elem_shape_sensitivity_calculations(const MAST::FunctionBase& f, RealVectorX& vec); + /*! + * performs the element topology sensitivity calculations over \p elem, + * and returns the element residual sensitivity in \p vec . + */ + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec); + + /*! * performs the element topology sensitivity calculations over \p elem, * and returns the element residual sensitivity in \p vec . diff --git a/src/solver/second_order_newmark_transient_solver.cpp b/src/solver/second_order_newmark_transient_solver.cpp index 402c09c5..9addf780 100644 --- a/src/solver/second_order_newmark_transient_solver.cpp +++ b/src/solver/second_order_newmark_transient_solver.cpp @@ -462,6 +462,15 @@ elem_shape_sensitivity_calculations(const MAST::FunctionBase& f, +void +MAST::SecondOrderNewmarkTransientSolver:: +elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec) { + libmesh_assert(false); // to be implemented +} + + + void MAST::SecondOrderNewmarkTransientSolver:: elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, diff --git a/src/solver/second_order_newmark_transient_solver.h b/src/solver/second_order_newmark_transient_solver.h index 9c5b2ce3..2dae2bf1 100644 --- a/src/solver/second_order_newmark_transient_solver.h +++ b/src/solver/second_order_newmark_transient_solver.h @@ -164,6 +164,14 @@ namespace MAST { elem_shape_sensitivity_calculations(const MAST::FunctionBase& f, RealVectorX& vec); + /*! + * performs the element topology sensitivity calculations over \p elem, + * and returns the element residual sensitivity in \p vec . + */ + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec); + /*! * performs the element topology sensitivity calculations over \p elem, * and returns the element residual sensitivity in \p vec . diff --git a/src/solver/stabilized_first_order_transient_sensitivity_solver.h b/src/solver/stabilized_first_order_transient_sensitivity_solver.h index fc4a7bed..9952e885 100644 --- a/src/solver/stabilized_first_order_transient_sensitivity_solver.h +++ b/src/solver/stabilized_first_order_transient_sensitivity_solver.h @@ -142,6 +142,13 @@ namespace MAST { libmesh_error(); } + virtual void + elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, + RealVectorX& vec) { + libmesh_error(); + } + + virtual void elem_topology_sensitivity_calculations(const MAST::FunctionBase& f, const MAST::FieldFunction& vel, diff --git a/src/utility/plot.cpp b/src/utility/plot.cpp index 224c4016..effd888e 100644 --- a/src/utility/plot.cpp +++ b/src/utility/plot.cpp @@ -46,13 +46,13 @@ MAST::plot_elem(Gnuplot& gp, void -MAST::plot_node(Gnuplot& gp, const libMesh::Node& node) { +MAST::plot_node(Gnuplot& gp, const libMesh::Point& pt) { std::vector> xy(1); - std::get<0>(xy[0]) = node(0); - std::get<1>(xy[0]) = node(1); + std::get<0>(xy[0]) = pt(0); + std::get<1>(xy[0]) = pt(1); gp << "plot '-' with points \n"; gp.send1d(xy); diff --git a/src/utility/plot.h b/src/utility/plot.h index aa52efdf..f9a0b7d2 100644 --- a/src/utility/plot.h +++ b/src/utility/plot.h @@ -35,7 +35,7 @@ namespace MAST { void plot_elem(Gnuplot& gp, const libMesh::Elem& elem); - void plot_node(Gnuplot& gp, const libMesh::Node& node); + void plot_node(Gnuplot& gp, const libMesh::Point& point); } #endif // MAST_ENABLE_GNUPLOT diff --git a/tests/fluid/base/fluid_elem_initialization.h b/tests/fluid/base/fluid_elem_initialization.h index 5b91b649..2235a3cb 100644 --- a/tests/fluid/base/fluid_elem_initialization.h +++ b/tests/fluid/base/fluid_elem_initialization.h @@ -162,7 +162,6 @@ struct BuildFluidElem { _geom_elem = new MAST::GeomElem; _geom_elem->init(**_mesh->elements_begin(), *_sys_init); _fluid_elem = new MAST::ConservativeFluidElementBase(*_sys_init, - *_assembly, *_geom_elem, *_flight_cond);