LCOV - code coverage report
Current view: top level - src/optimizationreporters - ParameterMeshOptimization.C (source / functions) Hit Total Coverage
Test: idaholab/moose optimization: #32971 (54bef8) with base c6cf66 Lines: 120 123 97.6 %
Date: 2026-05-29 20:38:04 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      10             : #include "ParameterMeshOptimization.h"
      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             : 
      21             : registerMooseObject("OptimizationApp", ParameterMeshOptimization);
      22             : 
      23             : InputParameters
      24         241 : ParameterMeshOptimization::validParams()
      25             : {
      26         241 :   InputParameters params = GeneralOptimization::validParams();
      27             : 
      28         241 :   params.addClassDescription(
      29             :       "Computes objective function, gradient and contains reporters for communicating between "
      30             :       "optimizeSolve and subapps using mesh-based parameter definition.");
      31             : 
      32         482 :   params.addRequiredParam<std::vector<FileName>>(
      33             :       "parameter_meshes", "Exodus file containing meshes describing parameters.");
      34             : 
      35         241 :   const auto family = AddVariableAction::getNonlinearVariableFamilies();
      36         241 :   MultiMooseEnum families(family.getRawNames(), "LAGRANGE");
      37         482 :   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.");
      43         241 :   const auto order = AddVariableAction::getNonlinearVariableOrders();
      44         241 :   MultiMooseEnum orders(order.getRawNames(), "FIRST");
      45         482 :   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         482 :   params.addParam<unsigned int>(
      53         482 :       "num_parameter_times", 1, "The number of time points the parameters represent.");
      54             : 
      55         482 :   params.addParam<std::vector<std::string>>(
      56             :       "initial_condition_mesh_variable",
      57             :       "Name of variable on parameter mesh to use as initial condition.");
      58         482 :   params.addParam<std::vector<std::string>>(
      59             :       "lower_bound_mesh_variable", "Name of variable on parameter mesh to use as lower bound.");
      60         482 :   params.addParam<std::vector<std::string>>(
      61             :       "upper_bound_mesh_variable", "Name of variable on parameter mesh to use as upper bound.");
      62         482 :   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         241 :   MultiMooseEnum reg_types("L2_GRADIENT");
      74         482 :   params.addParam<MultiMooseEnum>(
      75             :       "regularization_types",
      76             :       reg_types,
      77             :       "Types of regularization to apply. Multiple types can be specified.");
      78             : 
      79         482 :   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         482 :   params.addParamNamesToGroup("tikhonov_coeff regularization_types regularization_coeffs",
      85             :                               "Regularization");
      86             : 
      87         241 :   return params;
      88         241 : }
      89             : 
      90         121 : ParameterMeshOptimization::ParameterMeshOptimization(const InputParameters & parameters)
      91             :   : GeneralOptimization(parameters),
      92         242 :     _regularization_coeffs(getParam<std::vector<Real>>("regularization_coeffs")),
      93         363 :     _regularization_types(getParam<MultiMooseEnum>("regularization_types")
      94         121 :                               .getSetValueIDs<ParameterMesh::RegularizationType>())
      95             : {
      96             :   // Validate that regularization coefficients match types
      97         121 :   if (_regularization_coeffs.size() != _regularization_types.size())
      98           2 :     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         119 : }
     105             : 
     106             : std::vector<Real>
     107         163 : ParameterMeshOptimization::parseExodusData(const FEType fetype,
     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         324 :   ReadExodusMeshVars data_mesh(fetype, mesh_file_name, mesh_var_name);
     114             :   std::vector<Real> parsed_data;
     115             :   // read from mesh
     116         450 :   for (auto const & step : exodus_timestep)
     117             :   {
     118         291 :     std::vector<Real> data = data_mesh.getParameterValues(step);
     119         289 :     parsed_data.insert(parsed_data.end(), data.begin(), data.end());
     120         289 :   }
     121             : 
     122         159 :   return parsed_data;
     123         159 : }
     124             : 
     125             : void
     126         119 : ParameterMeshOptimization::setICsandBounds()
     127             : {
     128         476 :   if ((isParamValid("num_values_name") || isParamValid("num_values")))
     129           0 :     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         119 :   _nvalues.resize(_nparams, 0);
     133             :   // Fill the mesh information
     134         119 :   const auto & meshes = getParam<std::vector<FileName>>("parameter_meshes");
     135         119 :   const auto & families = getParam<MultiMooseEnum>("parameter_families");
     136         119 :   const auto & orders = getParam<MultiMooseEnum>("parameter_orders");
     137         238 :   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         238 :   if (isParamValid("initial_condition_mesh_variable"))
     144             :     initial_condition_mesh_variable =
     145         141 :         getParam<std::vector<std::string>>("initial_condition_mesh_variable");
     146         238 :   if (isParamValid("lower_bound_mesh_variable"))
     147          87 :     lower_bound_mesh_variable = getParam<std::vector<std::string>>("lower_bound_mesh_variable");
     148         238 :   if (isParamValid("upper_bound_mesh_variable"))
     149         117 :     upper_bound_mesh_variable = getParam<std::vector<std::string>>("upper_bound_mesh_variable");
     150             : 
     151             :   std::vector<unsigned int> exodus_timestep;
     152         238 :   if (isParamValid("exodus_timesteps_for_parameter_mesh_variable"))
     153             :     exodus_timestep =
     154          78 :         getParam<std::vector<unsigned int>>("exodus_timesteps_for_parameter_mesh_variable");
     155             :   else // get last timestep in file
     156          93 :     exodus_timestep = {std::numeric_limits<unsigned int>::max()};
     157             : 
     158             :   // now do a bunch of error checking
     159             :   // Size checks for data
     160         119 :   if (meshes.size() != _nparams)
     161           1 :     paramError("parameter_meshes",
     162             :                "There must be a mesh associated with each group of parameters.");
     163         118 :   if (families.size() > 1 && families.size() != _nparams)
     164           1 :     paramError("parameter_families",
     165             :                "There must be a family associated with each group of parameters.");
     166         117 :   if (orders.size() > 1 && orders.size() != _nparams)
     167           1 :     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         326 :   if (isParamValid("initial_condition_mesh_variable") && isParamValid("initial_condition"))
     172           0 :     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         290 :   else if (isParamValid("lower_bound_mesh_variable") && isParamValid("lower_bounds"))
     177           2 :     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         302 :   else if (isParamValid("upper_bound_mesh_variable") && isParamValid("upper_bounds"))
     182           0 :     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         342 :   if (isParamValid("exodus_timesteps_for_parameter_mesh_variable") &&
     189         192 :       (!isParamValid("lower_bound_mesh_variable") + !isParamValid("upper_bound_mesh_variable") +
     190         190 :            !isParamValid("initial_condition_mesh_variable") ==
     191             :        3))
     192           2 :     paramError("\"exodus_timesteps_for_parameter_mesh_variable\" should only be specified if "
     193             :                "reading values from a mesh.");
     194         112 :   else if (exodus_timestep.size() != ntimes && exodus_timestep.size() != 1)
     195           2 :     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         110 :   _ndof = 0;
     202         110 :   _parameter_meshes.resize(_nparams);
     203         243 :   for (const auto & param_id : make_range(_nparams))
     204             :   {
     205         138 :     const std::string family = families.size() > 1 ? families[param_id] : families[0];
     206         138 :     const std::string order = orders.size() > 1 ? orders[param_id] : orders[0];
     207         138 :     const FEType fetype(Utility::string_to_enum<Order>(order),
     208         138 :                         Utility::string_to_enum<FEFamily>(family));
     209             : 
     210         276 :     _parameter_meshes[param_id] = std::make_unique<ParameterMesh>(fetype, meshes[param_id]);
     211         138 :     _nvalues[param_id] = _parameter_meshes[param_id]->size() * ntimes;
     212         138 :     _ndof += _nvalues[param_id];
     213             : 
     214             :     // read and assign initial conditions
     215             :     {
     216             :       std::vector<Real> initial_condition;
     217         276 :       if (isParamValid("initial_condition_mesh_variable"))
     218         204 :         initial_condition = parseExodusData(
     219             :             fetype, meshes[param_id], exodus_timestep, initial_condition_mesh_variable[param_id]);
     220             :       else
     221         208 :         initial_condition = parseInputData("initial_condition", 0, param_id);
     222             : 
     223         137 :       _parameters[param_id]->assign(initial_condition.begin(), initial_condition.end());
     224         137 :     }
     225             : 
     226             :     // read and assign lower bound
     227             :     {
     228             :       std::vector<Real> lower_bound;
     229         274 :       if (isParamValid("lower_bound_mesh_variable"))
     230         109 :         lower_bound = parseExodusData(
     231             :             fetype, meshes[param_id], exodus_timestep, lower_bound_mesh_variable[param_id]);
     232             :       else
     233         294 :         lower_bound = parseInputData("lower_bounds", std::numeric_limits<Real>::lowest(), param_id);
     234             : 
     235         133 :       _lower_bounds.insert(_lower_bounds.end(), lower_bound.begin(), lower_bound.end());
     236         133 :     }
     237             : 
     238             :     // read and assign upper bound
     239             :     {
     240             :       std::vector<Real> upper_bound;
     241         266 :       if (isParamValid("upper_bound_mesh_variable"))
     242         168 :         upper_bound = parseExodusData(
     243             :             fetype, meshes[param_id], exodus_timestep, upper_bound_mesh_variable[param_id]);
     244             :       else
     245         231 :         upper_bound = parseInputData("upper_bounds", std::numeric_limits<Real>::max(), param_id);
     246             : 
     247         133 :       _upper_bounds.insert(_upper_bounds.end(), upper_bound.begin(), upper_bound.end());
     248         133 :     }
     249             : 
     250             :     // resize gradient vector to be filled later
     251         133 :     _gradients[param_id]->resize(_nvalues[param_id]);
     252             :   }
     253         105 : }
     254             : 
     255             : Real
     256        1447 : ParameterMeshOptimization::computeObjective()
     257             : {
     258        1447 :   Real val = GeneralOptimization::computeObjective();
     259             : 
     260             :   // Apply each regularization type with its coefficient
     261        2515 :   for (const auto reg_idx : index_range(_regularization_types))
     262             :   {
     263        1068 :     if (_regularization_coeffs[reg_idx] > 0.0)
     264             :     {
     265             :       Real regularization_value = 0.0;
     266             : 
     267             :       // Convert MultiMooseEnum to RegularizationType using get() method
     268        1068 :       ParameterMesh::RegularizationType reg_type = _regularization_types[reg_idx];
     269             : 
     270        2136 :       for (const auto & param_id : make_range(_nparams))
     271             :       {
     272             :         // Get current parameter values for this group
     273        1068 :         const auto & param_values = *_parameters[param_id];
     274             : 
     275             :         // Compute regularization objective for this type
     276        1068 :         regularization_value +=
     277        1068 :             _parameter_meshes[param_id]->computeRegularizationObjective(param_values, reg_type);
     278             :       }
     279             : 
     280        1068 :       val += _regularization_coeffs[reg_idx] * regularization_value;
     281             :     }
     282             :   }
     283             : 
     284        1447 :   return val;
     285             : }
     286             : 
     287             : void
     288        1447 : ParameterMeshOptimization::computeGradient(libMesh::PetscVector<Number> & gradient) const
     289             : {
     290             :   // Add regularization gradient contributions to the reporter gradients before base computation
     291        2515 :   for (const auto reg_idx : index_range(_regularization_types))
     292             :   {
     293        1068 :     if (_regularization_coeffs[reg_idx] > 0.0)
     294             :     {
     295             :       // Convert MultiMooseEnum to RegularizationType using get() method
     296        1068 :       ParameterMesh::RegularizationType reg_type = _regularization_types[reg_idx];
     297             : 
     298        2136 :       for (const auto & param_id : make_range(_nparams))
     299             :       {
     300             :         // Get current parameter values for this group
     301        1068 :         const auto & param_values = *_parameters[param_id];
     302        1068 :         auto grad_values = _gradients[param_id];
     303             : 
     304             :         // Compute regularization gradient for this type
     305             :         std::vector<Real> reg_grad =
     306        1068 :             _parameter_meshes[param_id]->computeRegularizationGradient(param_values, reg_type);
     307             : 
     308             :         // Add to gradient with coefficient
     309      186900 :         for (unsigned int i = 0; i < param_values.size(); ++i)
     310      185832 :           (*grad_values)[i] += _regularization_coeffs[reg_idx] * reg_grad[i];
     311        1068 :       }
     312             :     }
     313             :   }
     314             : 
     315             :   // Now call base class method which includes Tikhonov and copies to PETSc vector
     316        1447 :   OptimizationReporterBase::computeGradient(gradient);
     317        1446 : }

Generated by: LCOV version 1.14