LCOV - code coverage report
Current view: top level - src/vectorpostprocessors - SamplerBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 107 112 95.5 %
Date: 2026-05-29 20:35:17 Functions: 9 9 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 "SamplerBase.h"
      11             : #include "IndirectSort.h"
      12             : #include "InputParameters.h"
      13             : #include "MooseEnum.h"
      14             : #include "MooseError.h"
      15             : #include "VectorPostprocessor.h"
      16             : #include "MooseVariableFieldBase.h"
      17             : #include "FEProblemBase.h"
      18             : #include "MooseApp.h"
      19             : #include "TransientBase.h"
      20             : 
      21             : #include "libmesh/point.h"
      22             : 
      23             : InputParameters
      24       31294 : SamplerBase::validParams()
      25             : {
      26       31294 :   InputParameters params = emptyInputParameters();
      27             : 
      28      125176 :   params.addRequiredParam<std::string>(
      29             :       "sort_by",
      30             :       "What to sort the samples by. Options include 'x', 'y', 'z', 'id', and the name of any of "
      31             :       "the sampled quantities (which each create a vector of the same name).");
      32             : 
      33             :   // The value from this VPP is naturally already on every processor
      34             :   // TODO: Make this not the case!  See #11415
      35       31294 :   params.set<bool>("_auto_broadcast") = false;
      36             : 
      37       31294 :   return params;
      38           0 : }
      39             : 
      40        1897 : SamplerBase::SamplerBase(const InputParameters & parameters,
      41             :                          VectorPostprocessor * vpp,
      42        1897 :                          const libMesh::Parallel::Communicator & comm)
      43        1897 :   : _sampler_params(parameters),
      44        1897 :     _vpp(vpp),
      45        1897 :     _comm(comm),
      46        1897 :     _sampler_transient(dynamic_cast<TransientBase *>(
      47        7588 :         parameters.getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")
      48        1897 :             ->getMooseApp()
      49        1897 :             .getExecutioner())),
      50        1897 :     _sort_by_index(libMesh::invalid_uint),
      51        3794 :     _x(vpp->declareVector("x")),
      52        3794 :     _y(vpp->declareVector("y")),
      53        3794 :     _z(vpp->declareVector("z")),
      54        3794 :     _id(vpp->declareVector("id")),
      55        5691 :     _sort_by(parameters.get<std::string>("sort_by"))
      56             : {
      57        1897 :   if (_sort_by == "x")
      58         881 :     _sort_by_index = 0;
      59        1016 :   else if (_sort_by == "y")
      60         126 :     _sort_by_index = 1;
      61         890 :   else if (_sort_by == "z")
      62           0 :     _sort_by_index = 2;
      63         890 :   else if (_sort_by == "id")
      64         877 :     _sort_by_index = 3;
      65             :   // Sort by index for variables determined in setupVariables()
      66        1897 : }
      67             : 
      68             : void
      69        1879 : SamplerBase::setupVariables(const std::vector<std::string> & variable_names)
      70             : {
      71        1879 :   _variable_names = variable_names;
      72        1879 :   _values.reserve(variable_names.size());
      73             : 
      74        4509 :   for (const auto & variable_name : variable_names)
      75        2630 :     _values.push_back(&_vpp->declareVector(variable_name));
      76             : 
      77             :   // Find the index of the column to sort by for variables
      78        1879 :   if (_sort_by_index == libMesh::invalid_uint)
      79             :   {
      80          13 :     const auto it = std::find(_variable_names.begin(), _variable_names.end(), _sort_by);
      81          13 :     if (it != _variable_names.end())
      82          13 :       _sort_by_index = 4 + it - _variable_names.begin();
      83             :     else
      84           0 :       mooseError(
      85           0 :           "The 'sort_by' parameter must be one of x/y/z/id or one of the sampled variable names: " +
      86           0 :           Moose::stringify(_variable_names));
      87             :   }
      88        1879 : }
      89             : 
      90             : void
      91      220695 : SamplerBase::addSample(const Point & p, const Real & id, const std::vector<Real> & values)
      92             : {
      93      220695 :   _x.push_back(p(0));
      94      220695 :   _y.push_back(p(1));
      95      220695 :   _z.push_back(p(2));
      96             : 
      97      220695 :   _id.push_back(id);
      98             : 
      99             :   mooseAssert(values.size() == _variable_names.size(), "Mismatch of variable names to vector size");
     100      447907 :   for (MooseIndex(values) i = 0; i < values.size(); ++i)
     101      227212 :     _values[i]->emplace_back(values[i]);
     102             : 
     103      220695 :   _curr_num_samples++;
     104      220695 : }
     105             : 
     106             : void
     107        9430 : SamplerBase::initialize()
     108             : {
     109             :   // Don't reset the vectors if we want to retain history
     110        9430 :   if (_vpp->containsCompleteHistory() && _comm.rank() == 0)
     111             :   {
     112             :     // If we are repeating the timestep due to an aborted solve, we want to throw away the last
     113             :     // values
     114         203 :     if (_sampler_transient && !_sampler_transient->lastSolveConverged())
     115             :     {
     116             :       // Convenient to allocate a single variable for all the vpp values
     117           6 :       std::vector<VectorPostprocessorValue *> vec_ptrs = {{&_x, &_y, &_z, &_id}};
     118           3 :       vec_ptrs.insert(vec_ptrs.end(), _values.begin(), _values.end());
     119             :       // Erase the elements from the last execution
     120          18 :       for (auto vec_ptr : vec_ptrs)
     121             :       {
     122             :         // Vector may have already been restored, if so, skip the erasure
     123          15 :         if (_curr_total_samples > vec_ptr->size())
     124             :         {
     125             :           mooseAssert(vec_ptr->size() == (_curr_total_samples - _curr_num_samples),
     126             :                       "Number of samples is not what is expected.");
     127           1 :           continue;
     128             :         }
     129          28 :         for (auto ind : _curr_indices)
     130             :         {
     131             :           mooseAssert(ind < vec_ptr->size(), "Trying to remove a sample that doesn't exist.");
     132          14 :           vec_ptr->erase(vec_ptr->begin() + ind);
     133             :         }
     134             :       }
     135           3 :     }
     136         203 :     _curr_indices.clear();
     137             :   }
     138             :   else
     139             :   {
     140        9227 :     _x.clear();
     141        9227 :     _y.clear();
     142        9227 :     _z.clear();
     143        9227 :     _id.clear();
     144             : 
     145        9227 :     std::for_each(
     146       10158 :         _values.begin(), _values.end(), [](VectorPostprocessorValue * vec) { vec->clear(); });
     147             :   }
     148             : 
     149        9430 :   _curr_num_samples = 0;
     150        9430 : }
     151             : 
     152             : void
     153        2224 : SamplerBase::checkForStandardFieldVariableType(const MooseVariableFieldBase * const var_ptr,
     154             :                                                const std::string & var_param_name) const
     155             : {
     156             :   // A pointer to a MooseVariableFieldBase should never be SCALAR
     157             :   mooseAssert(var_ptr->feType().family != SCALAR,
     158             :               "Scalar variable '" + var_ptr->name() + "' cannot be sampled.");
     159             :   mooseAssert(dynamic_cast<const MooseObject *>(_vpp), "Should have succeeded");
     160        2224 :   if (var_ptr->isVector())
     161          12 :     dynamic_cast<const MooseObject *>(_vpp)->paramError(
     162             :         var_param_name,
     163             :         "The variable '",
     164           6 :         var_ptr->name(),
     165             :         "' is a vector variable. Sampling those is not currently supported in the "
     166             :         "framework. It may be supported using a dedicated object in your application. Use "
     167             :         "'VectorVariableComponentAux' auxkernel to copy those values into a regular field "
     168             :         "variable");
     169        2218 :   if (var_ptr->isArray())
     170          12 :     dynamic_cast<const MooseObject *>(_vpp)->paramError(
     171             :         var_param_name,
     172             :         "The variable '",
     173           6 :         var_ptr->name(),
     174             :         "' is an array variable. Sampling those is not currently supported in the "
     175             :         "framework. It may be supported using a dedicated object in your application. Use "
     176             :         "'ArrayVariableComponent' auxkernel to copy those values into a regular field variable");
     177        2212 : }
     178             : 
     179             : void
     180        9005 : SamplerBase::finalize()
     181             : {
     182             :   /**
     183             :    * We have several vectors that all need to be processed in the same way.
     184             :    * Rather than enumerate them all, let's just create a vector of pointers
     185             :    * and work on them that way.
     186             :    */
     187        9005 :   constexpr auto NUM_ID_VECTORS = 4;
     188             : 
     189        9005 :   std::vector<VectorPostprocessorValue *> vec_ptrs;
     190        9005 :   vec_ptrs.reserve(_values.size() + NUM_ID_VECTORS);
     191             :   // Initialize the pointer vector with the position and ID vectors
     192       18010 :   vec_ptrs = {{&_x, &_y, &_z, &_id}};
     193             :   // Now extend the vector by all the remaining values vector before processing
     194        9005 :   vec_ptrs.insert(vec_ptrs.end(), _values.begin(), _values.end());
     195             : 
     196             :   // Gather up each of the partial vectors
     197       54946 :   for (auto vec_ptr : vec_ptrs)
     198       45941 :     _comm.allgather(*vec_ptr, /* identical buffer lengths = */ false);
     199             : 
     200             :   // Now create an index vector by using an indirect sort
     201        9005 :   std::vector<std::size_t> sorted_indices;
     202       18010 :   Moose::indirectSort(
     203       18010 :       vec_ptrs[_sort_by_index]->begin(), vec_ptrs[_sort_by_index]->end(), sorted_indices);
     204             : 
     205             :   /**
     206             :    * We now have one sorted vector. The remaining vectors need to be sorted according to that
     207             :    * vector.
     208             :    * We'll need a temporary vector to hold values as we map them according to the sorted indices.
     209             :    * After that, we'll swap the vector contents with the sorted vector to get the values
     210             :    * back into the original vector.
     211             :    */
     212             :   // This vector is used as temp storage to sort each of the remaining vectors according to the
     213             :   // first
     214        9005 :   auto vector_length = sorted_indices.size();
     215        9005 :   VectorPostprocessorValue tmp_vector(vector_length);
     216             : 
     217             : #ifndef NDEBUG
     218             :   for (const auto vec_ptr : vec_ptrs)
     219             :     if (vec_ptr->size() != vector_length)
     220             :       mooseError("Vector length mismatch");
     221             : #endif
     222             : 
     223             :   // Sort each of the vectors using the same sorted indices
     224       54946 :   for (auto & vec_ptr : vec_ptrs)
     225             :   {
     226     1605002 :     for (MooseIndex(sorted_indices) j = 0; j < sorted_indices.size(); ++j)
     227     1559061 :       tmp_vector[j] = (*vec_ptr)[sorted_indices[j]];
     228             : 
     229             :     // Swap vector storage with sorted vector
     230       45941 :     vec_ptr->swap(tmp_vector);
     231             :   }
     232             : 
     233             :   // Gather the indices of samples from the last execution
     234             :   // Used to determine which parts of the vector need to be erased if a solve fails
     235        9005 :   if (_vpp->containsCompleteHistory())
     236             :   {
     237         278 :     _comm.sum(_curr_num_samples);
     238         278 :     if (_comm.rank() == 0)
     239             :     {
     240         203 :       _curr_indices.insert(sorted_indices.end() - _curr_num_samples, sorted_indices.end());
     241         203 :       _curr_total_samples = vec_ptrs[0]->size();
     242             :     }
     243             :   }
     244        9005 : }
     245             : 
     246             : void
     247         422 : SamplerBase::threadJoin(const SamplerBase & y)
     248             : {
     249         422 :   _x.insert(_x.end(), y._x.begin(), y._x.end());
     250         422 :   _y.insert(_y.end(), y._y.begin(), y._y.end());
     251         422 :   _z.insert(_z.end(), y._z.begin(), y._z.end());
     252             : 
     253         422 :   _id.insert(_id.end(), y._id.begin(), y._id.end());
     254             : 
     255         856 :   for (MooseIndex(_variable_names) i = 0; i < _variable_names.size(); i++)
     256         434 :     _values[i]->insert(_values[i]->end(), y._values[i]->begin(), y._values[i]->end());
     257             : 
     258         422 :   _curr_num_samples += y._curr_num_samples;
     259         422 : }

Generated by: LCOV version 1.14