https://mooseframework.inl.gov
ParameterMeshOptimization.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 
12 #include "AddVariableAction.h"
13 #include "ParameterMesh.h"
14 #include "OptUtils.h"
15 #include "libmesh/string_to_enum.h"
16 
17 #include "ReadExodusMeshVars.h"
18 
19 using namespace libMesh;
20 
22 
25 {
27 
28  params.addClassDescription(
29  "Computes objective function, gradient and contains reporters for communicating between "
30  "optimizeSolve and subapps using mesh-based parameter definition.");
31 
32  params.addRequiredParam<std::vector<FileName>>(
33  "parameter_meshes", "Exodus file containing meshes describing parameters.");
34 
36  MultiMooseEnum families(family.getRawNames(), "LAGRANGE");
37  params.addParam<MultiMooseEnum>(
38  "parameter_families",
39  families,
40  "Specifies the family of FE shape functions for each group of parameters. If a single value "
41  "is "
42  "specified, then that value is used for all groups of parameters.");
44  MultiMooseEnum orders(order.getRawNames(), "FIRST");
45  params.addParam<MultiMooseEnum>(
46  "parameter_orders",
47  orders,
48  "Specifies the order of FE shape functions for each group of parameters. If a single value "
49  "is "
50  "specified, then that value is used for all groups of parameters.");
51 
52  params.addParam<unsigned int>(
53  "num_parameter_times", 1, "The number of time points the parameters represent.");
54 
55  params.addParam<std::vector<std::string>>(
56  "initial_condition_mesh_variable",
57  "Name of variable on parameter mesh to use as initial condition.");
58  params.addParam<std::vector<std::string>>(
59  "lower_bound_mesh_variable", "Name of variable on parameter mesh to use as lower bound.");
60  params.addParam<std::vector<std::string>>(
61  "upper_bound_mesh_variable", "Name of variable on parameter mesh to use as upper bound.");
62  params.addParam<std::vector<unsigned int>>(
63  "exodus_timesteps_for_parameter_mesh_variable",
64  "Timesteps to read all parameter group bounds and initial conditions from Exodus mesh. The "
65  "options are to give no timestep, a single timestep or \"num_parameter_times\" timesteps. "
66  "No timestep results in the final timestep from the mesh being used. A single timestep "
67  "results in values at that timestep being used for all timesteps. \"num_parameter_times\" "
68  "timesteps results in values from the mesh at those steps being used. The same timesteps "
69  "are used for all parameter groups and all meshes, the capability to define different "
70  "timesteps for different meshes is not supported.");
71 
72  // New parameters for multiple regularization types
73  MultiMooseEnum reg_types("L2_GRADIENT");
74  params.addParam<MultiMooseEnum>(
75  "regularization_types",
76  reg_types,
77  "Types of regularization to apply. Multiple types can be specified.");
78 
79  params.addParam<std::vector<Real>>("regularization_coeffs",
80  {},
81  "Coefficients for each regularization type. Must match the "
82  "number of regularization_types specified.");
83 
84  params.addParamNamesToGroup("tikhonov_coeff regularization_types regularization_coeffs",
85  "Regularization");
86 
87  return params;
88 }
89 
91  : GeneralOptimization(parameters),
92  _regularization_coeffs(getParam<std::vector<Real>>("regularization_coeffs")),
93  _regularization_types(getParam<MultiMooseEnum>("regularization_types")
94  .getSetValueIDs<ParameterMesh::RegularizationType>())
95 {
96  // Validate that regularization coefficients match types
97  if (_regularization_coeffs.size() != _regularization_types.size())
98  paramError("regularization_coeffs",
99  "Number of regularization coefficients (",
100  _regularization_coeffs.size(),
101  ") must match number of regularization types (",
102  _regularization_types.size(),
103  ")");
104 }
105 
106 std::vector<Real>
108  const FileName mesh_file_name,
109  const std::vector<unsigned int> & exodus_timestep,
110  const std::string & mesh_var_name) const
111 {
112  // read data off Exodus mesh
113  ReadExodusMeshVars data_mesh(fetype, mesh_file_name, mesh_var_name);
114  std::vector<Real> parsed_data;
115  // read from mesh
116  for (auto const & step : exodus_timestep)
117  {
118  std::vector<Real> data = data_mesh.getParameterValues(step);
119  parsed_data.insert(parsed_data.end(), data.begin(), data.end());
120  }
121 
122  return parsed_data;
123 }
124 
125 void
127 {
128  if ((isParamValid("num_values_name") || isParamValid("num_values")))
129  paramError("num_values_name or num_values should not be used with ParameterMeshOptimization. "
130  "Instead the number of dofs is set by the parameter meshes.");
131 
132  _nvalues.resize(_nparams, 0);
133  // Fill the mesh information
134  const auto & meshes = getParam<std::vector<FileName>>("parameter_meshes");
135  const auto & families = getParam<MultiMooseEnum>("parameter_families");
136  const auto & orders = getParam<MultiMooseEnum>("parameter_orders");
137  const auto & ntimes = getParam<unsigned int>("num_parameter_times");
138 
139  // Fill exodus parameter bounds and IC information
140  std::vector<std::string> initial_condition_mesh_variable;
141  std::vector<std::string> lower_bound_mesh_variable;
142  std::vector<std::string> upper_bound_mesh_variable;
143  if (isParamValid("initial_condition_mesh_variable"))
144  initial_condition_mesh_variable =
145  getParam<std::vector<std::string>>("initial_condition_mesh_variable");
146  if (isParamValid("lower_bound_mesh_variable"))
147  lower_bound_mesh_variable = getParam<std::vector<std::string>>("lower_bound_mesh_variable");
148  if (isParamValid("upper_bound_mesh_variable"))
149  upper_bound_mesh_variable = getParam<std::vector<std::string>>("upper_bound_mesh_variable");
150 
151  std::vector<unsigned int> exodus_timestep;
152  if (isParamValid("exodus_timesteps_for_parameter_mesh_variable"))
153  exodus_timestep =
154  getParam<std::vector<unsigned int>>("exodus_timesteps_for_parameter_mesh_variable");
155  else // get last timestep in file
156  exodus_timestep = {std::numeric_limits<unsigned int>::max()};
157 
158  // now do a bunch of error checking
159  // Size checks for data
160  if (meshes.size() != _nparams)
161  paramError("parameter_meshes",
162  "There must be a mesh associated with each group of parameters.");
163  if (families.size() > 1 && families.size() != _nparams)
164  paramError("parameter_families",
165  "There must be a family associated with each group of parameters.");
166  if (orders.size() > 1 && orders.size() != _nparams)
167  paramError("parameter_orders",
168  "There must be an order associated with each group of parameters.");
169 
170  // error checking that initial conditions and bounds are only read from a single location
171  if (isParamValid("initial_condition_mesh_variable") && isParamValid("initial_condition"))
172  paramError("initial_condition_mesh_variable",
173  "Initial conditions for all parameter groups can only be defined by "
174  "initial_condition_mesh_variable or "
175  "initial_condition but not both.");
176  else if (isParamValid("lower_bound_mesh_variable") && isParamValid("lower_bounds"))
177  paramError(
178  "lower_bound_mesh_variable",
179  "Lower bounds for all parameter groups can only be defined by lower_bound_mesh_variable or "
180  "lower_bounds but not both.");
181  else if (isParamValid("upper_bound_mesh_variable") && isParamValid("upper_bounds"))
182  paramError(
183  "upper_bound_mesh_variable",
184  "Upper bounds for all parameter groups can only be defined by upper_bound_mesh_variable or "
185  "upper_bounds but not both.");
186 
187  // Make sure they did not specify too many timesteps
188  if (isParamValid("exodus_timesteps_for_parameter_mesh_variable") &&
189  (!isParamValid("lower_bound_mesh_variable") + !isParamValid("upper_bound_mesh_variable") +
190  !isParamValid("initial_condition_mesh_variable") ==
191  3))
192  paramError("\"exodus_timesteps_for_parameter_mesh_variable\" should only be specified if "
193  "reading values from a mesh.");
194  else if (exodus_timestep.size() != ntimes && exodus_timestep.size() != 1)
195  paramError("exodus_timesteps_for_parameter_mesh_variable",
196  "Number of timesteps to read mesh data specified by "
197  "\"exodus_timesteps_for_parameter_mesh_variable\" incorrect. "
198  "\"exodus_timesteps_for_parameter_mesh_variable\" can specify a single timestep or "
199  "\"num_parameter_times\" timesteps.");
200 
201  _ndof = 0;
202  _parameter_meshes.resize(_nparams);
203  for (const auto & param_id : make_range(_nparams))
204  {
205  const std::string family = families.size() > 1 ? families[param_id] : families[0];
206  const std::string order = orders.size() > 1 ? orders[param_id] : orders[0];
207  const FEType fetype(Utility::string_to_enum<Order>(order),
208  Utility::string_to_enum<FEFamily>(family));
209 
210  _parameter_meshes[param_id] = std::make_unique<ParameterMesh>(fetype, meshes[param_id]);
211  _nvalues[param_id] = _parameter_meshes[param_id]->size() * ntimes;
212  _ndof += _nvalues[param_id];
213 
214  // read and assign initial conditions
215  {
216  std::vector<Real> initial_condition;
217  if (isParamValid("initial_condition_mesh_variable"))
219  fetype, meshes[param_id], exodus_timestep, initial_condition_mesh_variable[param_id]);
220  else
221  initial_condition = parseInputData("initial_condition", 0, param_id);
222 
223  _parameters[param_id]->assign(initial_condition.begin(), initial_condition.end());
224  }
225 
226  // read and assign lower bound
227  {
228  std::vector<Real> lower_bound;
229  if (isParamValid("lower_bound_mesh_variable"))
230  lower_bound = parseExodusData(
231  fetype, meshes[param_id], exodus_timestep, lower_bound_mesh_variable[param_id]);
232  else
233  lower_bound = parseInputData("lower_bounds", std::numeric_limits<Real>::lowest(), param_id);
234 
235  _lower_bounds.insert(_lower_bounds.end(), lower_bound.begin(), lower_bound.end());
236  }
237 
238  // read and assign upper bound
239  {
240  std::vector<Real> upper_bound;
241  if (isParamValid("upper_bound_mesh_variable"))
242  upper_bound = parseExodusData(
243  fetype, meshes[param_id], exodus_timestep, upper_bound_mesh_variable[param_id]);
244  else
245  upper_bound = parseInputData("upper_bounds", std::numeric_limits<Real>::max(), param_id);
246 
247  _upper_bounds.insert(_upper_bounds.end(), upper_bound.begin(), upper_bound.end());
248  }
249 
250  // resize gradient vector to be filled later
251  _gradients[param_id]->resize(_nvalues[param_id]);
252  }
253 }
254 
255 Real
257 {
259 
260  // Apply each regularization type with its coefficient
261  for (const auto reg_idx : index_range(_regularization_types))
262  {
263  if (_regularization_coeffs[reg_idx] > 0.0)
264  {
265  Real regularization_value = 0.0;
266 
267  // Convert MultiMooseEnum to RegularizationType using get() method
269 
270  for (const auto & param_id : make_range(_nparams))
271  {
272  // Get current parameter values for this group
273  const auto & param_values = *_parameters[param_id];
274 
275  // Compute regularization objective for this type
276  regularization_value +=
277  _parameter_meshes[param_id]->computeRegularizationObjective(param_values, reg_type);
278  }
279 
280  val += _regularization_coeffs[reg_idx] * regularization_value;
281  }
282  }
283 
284  return val;
285 }
286 
287 void
289 {
290  // Add regularization gradient contributions to the reporter gradients before base computation
291  for (const auto reg_idx : index_range(_regularization_types))
292  {
293  if (_regularization_coeffs[reg_idx] > 0.0)
294  {
295  // Convert MultiMooseEnum to RegularizationType using get() method
297 
298  for (const auto & param_id : make_range(_nparams))
299  {
300  // Get current parameter values for this group
301  const auto & param_values = *_parameters[param_id];
302  auto grad_values = _gradients[param_id];
303 
304  // Compute regularization gradient for this type
305  std::vector<Real> reg_grad =
306  _parameter_meshes[param_id]->computeRegularizationGradient(param_values, reg_type);
307 
308  // Add to gradient with coefficient
309  for (unsigned int i = 0; i < param_values.size(); ++i)
310  (*grad_values)[i] += _regularization_coeffs[reg_idx] * reg_grad[i];
311  }
312  }
313  }
314 
315  // Now call base class method which includes Tikhonov and copies to PETSc vector
317 }
std::vector< Real > getParameterValues(const unsigned int timestep) const
Initializes parameter data and sets bounds in the main optmiization application getParameterValues is...
virtual void computeGradient(libMesh::PetscVector< Number > &gradient) const override
Function to compute gradient.
RegularizationType
Enumerations for regularization computations.
Definition: ParameterMesh.h:52
virtual Real computeObjective() override
Function to compute objective.
Utility class to use an Exodus mesh to define controllable parameters for optimization problems This ...
Definition: ParameterMesh.h:43
void paramError(const std::string &param, Args... args) const
const T & getParam(const std::string &name) const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
const std::vector< ParameterMesh::RegularizationType > _regularization_types
Regularization types to apply.
static InputParameters validParams()
Utility function to read a single variable off an Exodus mesh for optimization problem This class wil...
dof_id_type _ndof
Total number of parameters.
registerMooseObject("OptimizationApp", ParameterMeshOptimization)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
std::string getRawNames() const
std::vector< std::unique_ptr< ParameterMesh > > _parameter_meshes
Store parameter meshes for regularization computation.
void addRequiredParam(const std::string &name, const std::string &doc_string)
static MooseEnum getNonlinearVariableFamilies()
const unsigned int _nparams
Number of parameter vectors.
static MooseEnum getNonlinearVariableOrders()
ParameterMeshOptimization(const InputParameters &parameters)
std::vector< dof_id_type > _nvalues
Number of values for each parameter.
Number initial_condition(const Point &p, const Parameters &parameters, const std::string &, const std::string &)
Mesh-based parameter optimization.
std::vector< Real > parseInputData(std::string type, Real default_value, unsigned int param_id) const
Function to to parse bounds and initial conditions from input file.
std::vector< Real > _lower_bounds
Bounds of the parameters.
std::vector< std::vector< Real > * > _parameters
Parameter values declared as reporter data.
const std::vector< Real > _regularization_coeffs
Vector of regularization coefficients corresponding to each type.
std::vector< Real > parseExodusData(const FEType fetype, const FileName mesh_file_name, const std::vector< unsigned int > &exodus_timestep, const std::string &mesh_var_name) const
Read initialization data off of parameter mesh and error check.
static InputParameters validParams()
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void computeGradient(libMesh::PetscVector< Number > &gradient) const
Function to compute gradient.
std::vector< std::vector< Real > * > _gradients
Gradient values declared as reporter data.
IntRange< T > make_range(T beg, T end)
virtual Real computeObjective() override
Function to compute objective.
void addClassDescription(const std::string &doc_string)
bool isParamValid(const std::string &name) const
auto index_range(const T &sizable)
virtual void setICsandBounds() override
Sets the initial conditions and bounds right before it is needed.
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
Optimization reporter that interfaces with TAO.