https://mooseframework.inl.gov
DensityUpdateTwoConstraints.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
11 #include "Transient.h"
12 #include <algorithm>
13 
15 
18 {
20  params.addClassDescription(
21  "Compute updated densities based on sensitivities using an optimality criteria method to "
22  "keep the volume and cost constraints satisified.");
23  params.addParam<Real>(
24  "relative_tolerance",
25  1.0e-3,
26  "Relative tolerance on both compliance and cost to end the bisection method.");
27  params.addRequiredCoupledVar("design_density", "Design density variable name.");
28  params.addRequiredParam<VariableName>("density_sensitivity",
29  "Name of the density_sensitivity variable.");
30  params.addRequiredParam<VariableName>("cost_density_sensitivity",
31  "Name of the cost density sensitivity variable.");
32  params.addParam<VariableName>("thermal_sensitivity",
33  "Name of the thermal density sensitivity variable.");
34  params.addRequiredParam<Real>("volume_fraction", "Volume fraction.");
35  params.addRequiredParam<Real>("cost_fraction", "Cost fraction.");
36  params.addParam<Real>("bisection_move", 0.01, "Bisection move for the updated solution.");
37  params.addParam<bool>("adaptive_move",
38  false,
39  "Whether incremental moves in the bisection algorithm will be reduced as "
40  "the number of iterations increases. Note that the time must correspond to "
41  "iteration number for better results.");
42  params.addRequiredParam<VariableName>("cost", "Name of the cost variable.");
43 
44  params.addParam<Real>("bisection_lower_bound", 0, "Lower bound for the bisection algorithm.");
45  params.addParam<Real>("bisection_upper_bound", 1e16, "Upper bound for the bisection algorithm.");
46 
47  params.addParam<std::vector<Real>>(
48  "weight_mechanical_thermal",
49  "List of values between 0 and 1 to weight the stiffness and thermal sensitivities");
50 
51  params.addParam<bool>("use_thermal_compliance",
52  false,
53  "Whether to include the thermal compliance in the sensitivities to "
54  "minimize in conjunction with stiffness compliance.");
55 
56  return params;
57 }
58 
60  : ElementUserObject(parameters),
61  _mesh(_subproblem.mesh()),
62  _density_sensitivity_name(getParam<VariableName>("density_sensitivity")),
63  _cost_density_sensitivity_name(getParam<VariableName>("cost_density_sensitivity")),
64  _cost_name(getParam<VariableName>("cost")),
65  _design_density(&writableVariable("design_density")),
66  _density_sensitivity(&_subproblem.getStandardVariable(_tid, _density_sensitivity_name)),
67  _cost_density_sensitivity(
68  &_subproblem.getStandardVariable(_tid, _cost_density_sensitivity_name)),
69  _cost(_subproblem.getStandardVariable(_tid, _cost_name)),
70  _volume_fraction(getParam<Real>("volume_fraction")),
71  _cost_fraction(getParam<Real>("cost_fraction")),
72  _relative_tolerance(getParam<Real>("relative_tolerance")),
73  _lower_bound(getParam<Real>("bisection_lower_bound")),
74  _upper_bound(getParam<Real>("bisection_upper_bound")),
75  _bisection_move(getParam<Real>("bisection_move")),
76  _adaptive_move(getParam<bool>("adaptive_move")),
77  _thermal_sensitivity_name(
78  isParamValid("thermal_sensitivity") ? getParam<VariableName>("thermal_sensitivity") : ""),
79  _thermal_sensitivity(isParamValid("thermal_sensitivity")
80  ? &_subproblem.getStandardVariable(_tid, _thermal_sensitivity_name)
81  : nullptr)
82 {
83  if (isParamValid("thermal_sensitivity"))
84  {
85  if (!isParamValid("thermal_sensitivity"))
86  paramError("thermal_sensitivity",
87  "The thermal_sensitivity variable name must be provided by the user if "
88  "include_thermal_compliance is chosen to be true.");
89 
90  if (isParamValid("weight_mechanical_thermal"))
91  {
92  _weight_values = getParam<std::vector<Real>>("weight_mechanical_thermal");
93  if (_weight_values.size() != 2)
94  paramError("weight_mechanical_thermal",
95  "Weighing of sensitivities is only available for the mechanical compliance and "
96  "the thermal compliances problems, respectively.");
97  }
98  else
99  paramError("weight_mechanical_thermal",
100  "This parameter needs to be provided when including thermal sensitivity.");
101  }
102 
103  auto transient = dynamic_cast<TransientBase *>(_app.getExecutioner());
104 
105  if (!transient && _adaptive_move)
106  paramError("adaptive_move", "Cannot find a transient executioner for adaptive bisection move.");
107 
108  if (!dynamic_cast<MooseVariableFE<Real> *>(_design_density))
109  paramError("design_density", "Design density must be a finite element variable");
110 
111  if (!dynamic_cast<const MooseVariableFE<Real> *>(_density_sensitivity))
112  paramError("density_sensitivity", "Design sensitivity must be a finite element variable");
113 
114  if (!dynamic_cast<const MooseVariableFE<Real> *>(_cost_density_sensitivity))
115  paramError("cost_density_sensitivity",
116  "Cost density sensitivity must be a finite element variable");
117 }
118 
119 void
121 {
124 }
125 
126 void
128 {
129  // Grab the element data for each id
130  auto elem_data_iter = _elem_data_map.find(_current_elem->id());
131 
132  // Check if the element data is not null
133  if (elem_data_iter != _elem_data_map.end())
134  {
135  ElementData & elem_data = elem_data_iter->second;
137  }
138  else
139  {
140  mooseError("Element data not found for the current element id.");
141  }
142 }
143 
144 void
146 {
147  // Create parallel-consistent data structures constraining compliance and cost sensitivities.
148  _elem_data_map.clear();
151 
152  for (const auto & sub_id : blockIDs())
153  for (const auto & elem : _mesh.getMesh().active_local_subdomain_elements_ptr_range(sub_id))
154  {
155  dof_id_type elem_id = elem->id();
156  ElementData data = ElementData(
157  dynamic_cast<MooseVariableFE<Real> *>(_design_density)->getElementalValue(elem),
158  dynamic_cast<const MooseVariableFE<Real> *>(_density_sensitivity)
159  ->getElementalValue(elem),
160  dynamic_cast<const MooseVariableFE<Real> *>(_cost_density_sensitivity)
161  ->getElementalValue(elem),
162  isParamValid("thermal_sensitivity") ? _thermal_sensitivity->getElementalValue(elem) : 0.0,
163  _cost.getElementalValue(elem),
164  elem->volume(),
165  0);
166  _elem_data_map[elem_id] = data;
167  _total_allowable_volume += elem->volume();
168  }
169 
172 
175 }
176 
177 void
179 {
180  // Initialize the lower and upper bounds for the bisection method
181  Real l1 = _lower_bound;
182  Real l2 = _upper_bound;
183 
184  // Initialize the lower and upper bounds for cost solution
185  Real c1 = _lower_bound;
186  Real c2 = _upper_bound;
187 
188  bool perform_loop = true;
189  // Loop until the relative difference between l1 and l2 is less than a small tolerance
190 
191  while (perform_loop)
192  {
193  // Compute the midpoint between l1 and l2
194  Real lmid = 0.5 * (l2 + l1);
195 
196  // Compute the midpoint between c1 and c2
197  Real cmid = 0.5 * (c2 + c1);
198 
199  // Initialize the current total volume
200  Real curr_total_volume = 0;
201 
202  // Initialize the current total cost
203  Real curr_total_cost = 0;
204 
205  // Loop over all elements
206  for (auto && [id, elem_data] : _elem_data_map)
207  {
208  // Compute the updated density for the current element
209  Real new_density = computeUpdatedDensity(
210  elem_data.old_density,
211  elem_data.sensitivity * (isParamValid("thermal_sensitivity") ? _weight_values[0] : 1.0),
212  elem_data.cost_sensitivity,
213  elem_data.thermal_sensitivity *
214  (isParamValid("thermal_sensitivity") ? _weight_values[1] : 1.0),
215  elem_data.cost,
216  lmid,
217  cmid);
218 
219  // Update the current filtered density for the current element
220  elem_data.new_density = new_density;
221  // Update the current total volume
222  curr_total_volume += new_density * elem_data.volume;
223  // Update the current total cost
224  curr_total_cost += new_density * elem_data.volume * elem_data.cost;
225  }
226 
227  // Sum the current total volume across all processors
228  _communicator.sum(curr_total_volume);
229  _communicator.sum(curr_total_cost);
230 
231  // Update l1 or l2 based on whether the current total volume is greater than the total allowable
232  // volume
233  if (curr_total_volume > _total_allowable_volume)
234  l1 = lmid;
235  else
236  l2 = lmid;
237 
238  if (curr_total_cost > _total_allowable_cost)
239  c1 = cmid;
240  else
241  c2 = cmid;
242 
243  // Determine whether to continue the loop based on the relative difference between l1 and l2
244  perform_loop =
245  (l2 - l1) / (l1 + l2) > _relative_tolerance || (c2 - c1) / (c1 + c2) > _relative_tolerance;
246  }
247 }
248 
249 // Method to compute the updated density for an element
250 Real
252  Real dc,
253  Real cost_sensitivity,
254  Real temp_sensitivity,
255  Real cost,
256  Real lmid,
257  Real cmid)
258 {
259  Real move = _bisection_move;
260 
261  // Minimum move
262  const Real move_min = 1.0e-3;
263  // Control adaptivity
264  const Real alpha = 0.96;
265 
266  // Adaptive move (takes the transient time step as SIMP iteration number)
267  if (_adaptive_move)
268  move = std::max(MathUtils::pow(alpha, _t) * move, move_min);
269 
270  Real denominator = lmid + cmid * cost + cmid * current_density * cost_sensitivity;
271 
272  // Effect of damping to be assessed
273  const Real damping = 1.0;
274 
275  Real updated_density = std::max(
276  0.0,
277  std::max(
278  current_density - move,
279  std::min(1.0,
280  std::min(current_density + move,
281  current_density *
282  MathUtils::pow(std::sqrt((-(dc + temp_sensitivity) / denominator)),
283  damping)))));
284 
285  // Return the updated density
286  return updated_density;
287 }
Real computeUpdatedDensity(Real current_density, Real dc, Real cost_sensitivity, Real thermal_sensitivity, Real cost, Real lmid, Real cmid)
std::map< dof_id_type, ElementData > _elem_data_map
Data structure to hold old density, sensitivity, volume, current density.
const MooseWritableVariable * _cost_density_sensitivity
The filtered density sensitivity variable (cost)
Element user object that performs SIMP optimization using a bisection algorithm applying a volume con...
const MooseWritableVariable * _density_sensitivity
The filtered density sensitivity variable (elasticity)
const Real _cost_fraction
The cost fraction to be enforced.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
static InputParameters validParams()
virtual void setNodalValue(const Real &value, unsigned int idx=0)=0
Real _total_allowable_volume
Total volume allowed for volume contraint.
MeshBase & mesh
static InputParameters validParams()
const Parallel::Communicator & _communicator
virtual const std::set< SubdomainID > & blockIDs() const
void addRequiredParam(const std::string &name, const std::string &doc_string)
OutputData getElementalValue(const Elem *elem, unsigned int idx=0) const
const Real _bisection_move
Bisection algorithm move.
bool isParamValid(const std::string &name) const
void performOptimCritLoop()
Performs the optimality criterion loop (bisection)
const Real _upper_bound
Upper bound for bisection algorithm.
const Real _lower_bound
Lower bound for bisection algorithm.
Real _total_allowable_cost
Total volume allowed for cost contraint.
MeshBase & getMesh()
MooseVariable * _thermal_sensitivity
Thermal compliance variable.
void paramError(const std::string &param, Args... args) const
Executioner * getExecutioner() const
const MooseVariable & _cost
The cost variable.
MooseWritableVariable * _design_density
The pseudo-density variable.
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
const MooseMesh & _mesh
The system mesh.
DensityUpdateTwoConstraints(const InputParameters &parameters)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Elem *const & _current_elem
static const std::string alpha
Definition: NS.h:134
const bool _adaptive_move
Whether bisection moves are adaptive.
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
T pow(T x, int e)
const Real _volume_fraction
The volume fraction to be enforced.
registerMooseObject("OptimizationApp", DensityUpdateTwoConstraints)
uint8_t dof_id_type
void gatherElementData()
Gathers element date necessary to perform the bisection algorithm for optimization.