Thermomechanical SIMP Optimization with Multi-Apps
In this example, we setup two different physics problems defined in the same domain. The first model is a mechanical small deformation problem with two loads using symmetric boundary conditions. Such a model yields the typical SIMP "bridge-like" structure. The problem is set up regularly with the mechanical compliance sensitivity computed and adequately filtered.
Listing 1: MBB User objects in structural subapp
[UserObjects]
[rad_avg]
type = RadialAverage
radius = 1.2
weights = linear
prop_name = sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
[calc_sense]
type = SensitivityFilter
density_sensitivity = Dc
design_density = mat_den
filter_UO = rad_avg
execute_on = TIMESTEP_END
force_postaux = true
[]
[]
(modules/combined/examples/optimization/thermomechanical/structural_sub.i)The second model is a thermal, heat conduction problem with one heat generation boundary (on the left) with the rest of boundaries defined as insulating material; i.e. zero Neumann boundary condition on temperature. Such a model often generates a dendritic structure to maximize the reduction in the temperature gradients. In a way analogous to the structural problem, the thermal compliance sensitivities are generated and filtered in the subapp:
Listing 2: MBB User objects in thermal subapp
[UserObjects]
[rad_avg]
type = RadialAverage
radius = 0.1
weights = linear
prop_name = sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
[rad_avg_cost]
type = RadialAverage
radius = 0.1
weights = linear
prop_name = cost_sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
[rad_avg_thermal]
type = RadialAverage
radius = 0.1
weights = linear
prop_name = thermal_sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
# Provides Dc
[calc_sense]
type = SensitivityFilter
density_sensitivity = Dc
design_density = mat_den
filter_UO = rad_avg
execute_on = TIMESTEP_END
force_postaux = true
[]
# Provides Cc
[calc_sense_cost]
type = SensitivityFilter
density_sensitivity = Cc
design_density = mat_den
filter_UO = rad_avg_cost
execute_on = TIMESTEP_END
force_postaux = true
[]
# Provides Tc
[calc_sense_thermal]
type = SensitivityFilter
density_sensitivity = Tc
design_density = mat_den
filter_UO = rad_avg_thermal
execute_on = TIMESTEP_END
force_postaux = true
[]
[]
(modules/combined/examples/optimization/thermomechanical/thermal_sub.i)Finally, the main app obtain the sensitivities from the subapps and sends the computed pseudo-densities to the subapps. This process tends to reduce the objective function of both problems. The extent to which the one particular subapp drives the optimization process depends on the weights used to obtain a total or overall sensitivity, which is used in the density update process.
The result of the optimization, which heavily depends on the many simulation and optimization parameters, is shown in what follows:

(modules/combined/examples/optimization/thermomechanical/structural_sub.i)
vol_frac = 0.4
power = 2.0
E0 = 1.0e-6
E1 = 1.0
rho0 = 0.0
rho1 = 1.0
[GlobalParams]
displacements = 'disp_x disp_y'
[]
[Mesh]
[MeshGenerator]
type = GeneratedMeshGenerator
dim = 2
nx = 40
ny = 40
xmin = 0
xmax = 40
ymin = 0
ymax = 40
[]
[node]
type = ExtraNodesetGenerator
input = MeshGenerator
new_boundary = hold
nodes = 0
[]
[push_left]
type = ExtraNodesetGenerator
input = node
new_boundary = push_left
coord = '16 0 0'
[]
[push_center]
type = ExtraNodesetGenerator
input = push_left
new_boundary = push_center
coord = '24 0 0'
[]
[extra]
type = SideSetsFromBoundingBoxGenerator
input = push_center
bottom_left = '-0.01 17.999 0'
top_right = '5 22.001 0'
boundary_new = n1
boundaries_old = left
[]
[dirichlet_bc]
type = SideSetsFromNodeSetsGenerator
input = extra
[]
[]
[Variables]
[disp_x]
[]
[disp_y]
[]
[]
[AuxVariables]
[Dc]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[mat_den]
family = MONOMIAL
order = FIRST
initial_condition = ${vol_frac}
[]
[]
[Physics/SolidMechanics/QuasiStatic]
[all]
strain = SMALL
add_variables = true
incremental = false
[]
[]
[BCs]
[no_y]
type = DirichletBC
variable = disp_y
boundary = hold
value = 0.0
[]
[no_x_symm]
type = DirichletBC
variable = disp_x
boundary = right
value = 0.0
[]
[]
[NodalKernels]
[push_left]
type = NodalGravity
variable = disp_y
boundary = push_left
gravity_value = -1.0e-3
mass = 1
[]
[push_center]
type = NodalGravity
variable = disp_y
boundary = push_center
gravity_value = -1.0e-3
mass = 1
[]
[]
[Materials]
[elasticity_tensor]
type = ComputeVariableIsotropicElasticityTensor
youngs_modulus = E_phys
poissons_ratio = poissons_ratio
args = 'mat_den'
[]
[E_phys]
type = DerivativeParsedMaterial
expression = "A1:=(${E0}-${E1})/(${rho0}^${power}-${rho1}^${power}); "
"B1:=${E0}-A1*${rho0}^${power}; E1:=A1*mat_den^${power}+B1; E1"
coupled_variables = 'mat_den'
property_name = E_phys
[]
[poissons_ratio]
type = GenericConstantMaterial
prop_names = poissons_ratio
prop_values = 0.3
[]
[stress]
type = ComputeLinearElasticStress
[]
[dc]
type = ComplianceSensitivity
design_density = mat_den
youngs_modulus = E_phys
[]
[]
[Preconditioning]
[smp]
type = SMP
full = true
[]
[]
[UserObjects]
[rad_avg]
type = RadialAverage
radius = 1.2
weights = linear
prop_name = sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
[calc_sense]
type = SensitivityFilter
density_sensitivity = Dc
design_density = mat_den
filter_UO = rad_avg
execute_on = TIMESTEP_END
force_postaux = true
[]
[]
[Executioner]
type = Transient
solve_type = PJFNK
petsc_options_iname = '-pc_type -pc_factor_mat_solver_package'
petsc_options_value = 'lu superlu_dist'
nl_abs_tol = 1e-12
dt = 1.0
num_steps = 500
[]
[Outputs]
exodus = true
[out]
type = CSV
execute_on = 'TIMESTEP_END'
[]
print_linear_residuals = false
[]
[Postprocessors]
[mesh_volume]
type = VolumePostprocessor
execute_on = 'INITIAL TIMESTEP_END'
[]
[total_vol]
type = ElementIntegralVariablePostprocessor
variable = mat_den
execute_on = 'INITIAL TIMESTEP_END'
[]
[vol_frac]
type = ParsedPostprocessor
expression = 'total_vol / mesh_volume'
pp_names = 'total_vol mesh_volume'
[]
[sensitivity]
type = ElementIntegralMaterialProperty
mat_prop = sensitivity
[]
[objective]
type = ElementIntegralMaterialProperty
mat_prop = strain_energy_density
execute_on = 'INITIAL TIMESTEP_END'
[]
[]
(modules/combined/examples/optimization/thermomechanical/thermal_sub.i)
vol_frac = 0.4
power = 2.0
E0 = 1.0e-6
E1 = 1.0
rho0 = 0.0
rho1 = 1.0
C0 = 1.0e-6
C1 = 1.0
TC0 = 1.0e-16
TC1 = 1.0
[GlobalParams]
displacements = 'disp_x disp_y'
[]
[Mesh]
[MeshGenerator]
type = GeneratedMeshGenerator
dim = 2
nx = 40
ny = 40
xmin = 0
xmax = 40
ymin = 0
ymax = 40
[]
[node]
type = ExtraNodesetGenerator
input = MeshGenerator
new_boundary = hold
nodes = 0
[]
[push_left]
type = ExtraNodesetGenerator
input = node
new_boundary = push_left
coord = '16 0 0'
[]
[push_center]
type = ExtraNodesetGenerator
input = push_left
new_boundary = push_center
coord = '24 0 0'
[]
[extra]
type = SideSetsFromBoundingBoxGenerator
input = push_center
bottom_left = '-0.01 17.999 0'
top_right = '5 22.001 0'
boundary_new = n1
boundaries_old = left
[]
[dirichlet_bc]
type = SideSetsFromNodeSetsGenerator
input = extra
[]
[]
[Variables]
[disp_x]
[]
[disp_y]
[]
[temp]
initial_condition = 100.0
[]
[]
[AuxVariables]
[Dc]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[Cc]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[Tc]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[Cost]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[mat_den]
family = MONOMIAL
order = FIRST
initial_condition = ${vol_frac}
[]
[]
[AuxKernels]
[Cost]
type = MaterialRealAux
variable = Cost
property = Cost_mat
[]
[]
[Kernels]
[heat_conduction]
type = HeatConduction
variable = temp
diffusion_coefficient = thermal_cond
[]
[heat_source]
type = HeatSource
value = 1e-2 # W/m^3
variable = temp
[]
[]
[Physics/SolidMechanics/QuasiStatic]
[all]
strain = SMALL
add_variables = true
incremental = false
[]
[]
[BCs]
[no_y]
type = DirichletBC
variable = disp_y
boundary = hold
value = 0.0
[]
[no_x_symm]
type = DirichletBC
variable = disp_x
boundary = right
value = 0.0
[]
[left_n1]
type = DirichletBC
variable = temp
boundary = n1
value = 0.0
[]
[top]
type = NeumannBC
variable = temp
boundary = top
value = 0
[]
[bottom]
type = NeumannBC
variable = temp
boundary = bottom
value = 0
[]
[right]
type = NeumannBC
variable = temp
boundary = right
value = 0
[]
[left]
type = NeumannBC
variable = temp
boundary = left
value = 0
[]
[]
[NodalKernels]
[push_left]
type = NodalGravity
variable = disp_y
boundary = push_left
gravity_value = 0.0 # -1e-8
mass = 1
[]
[push_center]
type = NodalGravity
variable = disp_y
boundary = push_center
gravity_value = 0.0 # -1e-8
mass = 1
[]
[]
[Materials]
[thermal_cond]
type = DerivativeParsedMaterial
# ordered multimaterial simp
expression = "A1:=(${TC0}-${TC1})/(${rho0}^${power}-${rho1}^${power}); "
"B1:=${TC0}-A1*${rho0}^${power}; TC1:=A1*mat_den^${power}+B1; TC1"
coupled_variables = 'mat_den'
property_name = thermal_cond
outputs = 'exodus'
[]
[thermal_compliance]
type = ThermalCompliance
temperature = temp
thermal_conductivity = thermal_cond
outputs = 'exodus'
[]
[elasticity_tensor]
type = ComputeVariableIsotropicElasticityTensor
youngs_modulus = E_phys
poissons_ratio = poissons_ratio
args = 'mat_den'
[]
[E_phys]
type = DerivativeParsedMaterial
# ordered multimaterial simp
expression = "A1:=(${E0}-${E1})/(${rho0}^${power}-${rho1}^${power}); "
"B1:=${E0}-A1*${rho0}^${power}; E1:=A1*mat_den^${power}+B1; E1"
coupled_variables = 'mat_den'
property_name = E_phys
[]
[Cost_mat]
type = DerivativeParsedMaterial
# ordered multimaterial simp
expression = "A1:=(${C0}-${C1})/(${rho0}^(1/${power})-${rho1}^(1/${power})); "
"B1:=${C0}-A1*${rho0}^(1/${power}); C1:=A1*mat_den^(1/${power})+B1; C1"
coupled_variables = 'mat_den'
property_name = Cost_mat
[]
[CostDensity]
type = ParsedMaterial
property_name = CostDensity
coupled_variables = 'mat_den Cost'
expression = 'mat_den*Cost'
[]
[poissons_ratio]
type = GenericConstantMaterial
prop_names = poissons_ratio
prop_values = 0.3
[]
[stress]
type = ComputeLinearElasticStress
[]
[dc]
type = ComplianceSensitivity
design_density = mat_den
youngs_modulus = E_phys
[]
[cc]
type = CostSensitivity
design_density = mat_den
cost = Cost_mat
outputs = 'exodus'
[]
[tc]
type = ThermalSensitivity
design_density = mat_den
thermal_conductivity = thermal_cond
temperature = temp
outputs = 'exodus'
[]
[]
[Preconditioning]
[smp]
type = SMP
full = true
[]
[]
[UserObjects]
[rad_avg]
type = RadialAverage
radius = 0.1
weights = linear
prop_name = sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
[rad_avg_cost]
type = RadialAverage
radius = 0.1
weights = linear
prop_name = cost_sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
[rad_avg_thermal]
type = RadialAverage
radius = 0.1
weights = linear
prop_name = thermal_sensitivity
execute_on = TIMESTEP_END
force_preaux = true
[]
# Provides Dc
[calc_sense]
type = SensitivityFilter
density_sensitivity = Dc
design_density = mat_den
filter_UO = rad_avg
execute_on = TIMESTEP_END
force_postaux = true
[]
# Provides Cc
[calc_sense_cost]
type = SensitivityFilter
density_sensitivity = Cc
design_density = mat_den
filter_UO = rad_avg_cost
execute_on = TIMESTEP_END
force_postaux = true
[]
# Provides Tc
[calc_sense_thermal]
type = SensitivityFilter
density_sensitivity = Tc
design_density = mat_den
filter_UO = rad_avg_thermal
execute_on = TIMESTEP_END
force_postaux = true
[]
[]
[Executioner]
type = Transient
solve_type = PJFNK
petsc_options_iname = '-pc_type -pc_factor_mat_solver_package'
petsc_options_value = 'lu superlu_dist'
nl_abs_tol = 1e-12
dt = 1.0
num_steps = 500
[]
[Outputs]
exodus = true
[out]
type = CSV
execute_on = 'TIMESTEP_END'
[]
print_linear_residuals = false
[]
[Postprocessors]
[right_flux]
type = SideDiffusiveFluxAverage
variable = temp
boundary = right
diffusivity = 10
[]
[mesh_volume]
type = VolumePostprocessor
execute_on = 'initial timestep_end'
[]
[total_vol]
type = ElementIntegralVariablePostprocessor
variable = mat_den
execute_on = 'INITIAL TIMESTEP_END'
[]
[vol_frac]
type = ParsedPostprocessor
expression = 'total_vol / mesh_volume'
pp_names = 'total_vol mesh_volume'
[]
[sensitivity]
type = ElementIntegralMaterialProperty
mat_prop = sensitivity
[]
[cost_sensitivity]
type = ElementIntegralMaterialProperty
mat_prop = cost_sensitivity
[]
[cost]
type = ElementIntegralMaterialProperty
mat_prop = CostDensity
[]
[cost_frac]
type = ParsedPostprocessor
expression = 'cost / mesh_volume'
pp_names = 'cost mesh_volume'
[]
[objective]
type = ElementIntegralMaterialProperty
mat_prop = strain_energy_density
execute_on = 'INITIAL TIMESTEP_END'
[]
[objective_thermal]
type = ElementIntegralMaterialProperty
mat_prop = thermal_compliance
execute_on = 'INITIAL TIMESTEP_END'
[]
[]
(modules/combined/examples/optimization/thermomechanical/thermomechanical_main.i)
vol_frac = 0.4
power = 2.0
E0 = 1.0e-6
E1 = 1.0
[GlobalParams]
displacements = 'disp_x disp_y'
[]
[Mesh]
[MeshGenerator]
type = GeneratedMeshGenerator
dim = 2
nx = 40
ny = 40
xmin = 0
xmax = 40
ymin = 0
ymax = 40
[]
[node]
type = ExtraNodesetGenerator
input = MeshGenerator
new_boundary = hold
nodes = 0
[]
[push_left]
type = ExtraNodesetGenerator
input = node
new_boundary = push_left
coord = '16 0 0'
[]
[push_center]
type = ExtraNodesetGenerator
input = push_left
new_boundary = push_center
coord = '24 0 0'
[]
[extra]
type = SideSetsFromBoundingBoxGenerator
input = push_center
bottom_left = '-0.01 17.999 0'
top_right = '5 22.001 0'
boundary_new = n1
boundaries_old = left
[]
[dirichlet_bc]
type = SideSetsFromNodeSetsGenerator
input = extra
[]
[]
[Variables]
[disp_x]
[]
[disp_y]
[]
[]
[AuxVariables]
[mat_den]
family = MONOMIAL
order = FIRST
initial_condition = 0.02
[]
[sensitivity_one]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[sensitivity_two]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[total_sensitivity]
family = MONOMIAL
order = FIRST
initial_condition = -1.0
[]
[]
[AuxKernels]
[total_sensitivity]
type = ParsedAux
variable = total_sensitivity
expression = '(1-1.0e-7)*sensitivity_one + 1.0e-7*sensitivity_two'
coupled_variables = 'sensitivity_one sensitivity_two'
execute_on = 'LINEAR TIMESTEP_END'
[]
[]
[Physics/SolidMechanics/QuasiStatic]
[all]
strain = SMALL
add_variables = true
incremental = false
[]
[]
[BCs]
[no_y]
type = DirichletBC
variable = disp_y
boundary = hold
value = 0.0
[]
[no_x_symm]
type = DirichletBC
variable = disp_x
boundary = right
value = 0.0
[]
[]
[Materials]
[elasticity_tensor]
type = ComputeVariableIsotropicElasticityTensor
youngs_modulus = E_phys
poissons_ratio = poissons_ratio
args = 'mat_den'
[]
[E_phys]
type = DerivativeParsedMaterial
# Emin + (density^penal) * (E0 - Emin)
expression = '${E1} + (mat_den ^ ${power}) * (${E1}-${E0})'
coupled_variables = 'mat_den'
property_name = E_phys
[]
[poissons_ratio]
type = GenericConstantMaterial
prop_names = poissons_ratio
prop_values = 0.3
[]
[stress]
type = ComputeLinearElasticStress
[]
[]
[Preconditioning]
[smp]
type = SMP
full = true
[]
[]
[UserObjects]
# We do filtering in the subapps
[update]
type = DensityUpdate
density_sensitivity = total_sensitivity
design_density = mat_den
volume_fraction = ${vol_frac}
execute_on = MULTIAPP_FIXED_POINT_BEGIN
[]
[]
[Executioner]
type = Transient
solve_type = NEWTON
petsc_options_iname = '-pc_type -pc_factor_mat_solver_package'
petsc_options_value = 'lu superlu_dist'
nl_abs_tol = 1e-8
dt = 1.0
num_steps = 2
[]
[Outputs]
[out]
type = CSV
execute_on = 'TIMESTEP_END'
[]
print_linear_residuals = false
exodus = true
[]
[Postprocessors]
[mesh_volume]
type = VolumePostprocessor
execute_on = 'initial timestep_end'
[]
[total_vol]
type = ElementIntegralVariablePostprocessor
variable = mat_den
execute_on = 'INITIAL TIMESTEP_END'
[]
[vol_frac]
type = ParsedPostprocessor
expression = 'total_vol / mesh_volume'
pp_names = 'total_vol mesh_volume'
[]
[sensitivity]
type = ElementIntegralVariablePostprocessor
variable = total_sensitivity
[]
[]
[MultiApps]
[sub_app_one]
type = TransientMultiApp
input_files = structural_sub.i
[]
[sub_app_two]
type = TransientMultiApp
input_files = thermal_sub.i
[]
[]
[Transfers]
# First SUB-APP: STRUCTURAL
# To subapp densities
[subapp_one_density]
type = MultiAppCopyTransfer
to_multi_app = sub_app_one
source_variable = mat_den # Here
variable = mat_den
[]
# From subapp sensitivity
[subapp_one_sensitivity]
type = MultiAppCopyTransfer
from_multi_app = sub_app_one
source_variable = Dc # sensitivity_var
variable = sensitivity_one # Here
[]
# Second SUB-APP: HEAT CONDUCTIVITY
# To subapp densities
[subapp_two_density]
type = MultiAppCopyTransfer
to_multi_app = sub_app_two
source_variable = mat_den # Here
variable = mat_den
[]
# From subapp sensitivity
[subapp_two_sensitivity]
type = MultiAppCopyTransfer
from_multi_app = sub_app_two
source_variable = Tc # sensitivity_var
variable = sensitivity_two # Here
[]
[]