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

Generated by: LCOV version 1.14