LCOV - code coverage report
Current view: top level - src/positions - ParsedDownSelectionPositions.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 94 103 91.3 %
Date: 2025-07-17 01:28:37 Functions: 3 3 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 "ParsedDownSelectionPositions.h"
      11             : #include "libmesh/parallel.h"
      12             : #include "libmesh/parallel_algebra.h"
      13             : #include "libmesh/vector_value.h"
      14             : 
      15             : registerMooseObject("MooseApp", ParsedDownSelectionPositions);
      16             : 
      17             : InputParameters
      18       14289 : ParsedDownSelectionPositions::validParams()
      19             : {
      20       14289 :   InputParameters params = Positions::validParams();
      21       14289 :   params += NonADFunctorInterface::validParams();
      22             :   // Might as well offer block restriction as an additional down-selection criterion
      23       14289 :   params += BlockRestrictable::validParams();
      24       14289 :   params += FunctionParserUtils<false>::validParams();
      25             : 
      26       14289 :   params.addRequiredParam<std::vector<PositionsName>>(
      27             :       "input_positions",
      28             :       "Positions object(s) that will be down-selected by this object. The order of the "
      29             :       "down-selected positions is kept the same");
      30             : 
      31             :   // Parsed expression parameters
      32       14289 :   params.addRequiredCustomTypeParam<std::string>(
      33             :       "expression",
      34             :       "FunctionExpression",
      35             :       "Parsed function expression to compute the selection criterion. Note that x,y,z and t can be "
      36             :       "used in the expression without being declared as functors.");
      37       14289 :   params.addParam<std::vector<MooseFunctorName>>(
      38             :       "functor_names", {}, "Functors to use in the parsed expression");
      39       14289 :   params.addParam<std::vector<std::string>>(
      40             :       "functor_symbols",
      41             :       {},
      42             :       "Symbolic name to use for each functor in 'functor_names' in the parsed expression. If not "
      43             :       "provided, then the actual functor names will be used in the parsed expression.");
      44             : 
      45             :   // We need to preserve the order of the positions from the input_positions objects
      46       14289 :   params.set<bool>("auto_sort") = false;
      47             :   // We already perform a custom synchronization, because the functors cannot be evaluated on every
      48             :   // domain
      49       14289 :   params.set<bool>("auto_broadcast") = false;
      50             : 
      51             :   // Keep as up-to-date as possible given the generality of functors
      52       42867 :   params.set<ExecFlagEnum>("execute_on") = {EXEC_LINEAR, EXEC_TIMESTEP_BEGIN};
      53             : 
      54       14289 :   params.addClassDescription("Examines input positions object(s) to find all positions matching a "
      55             :                              "user-specifed parsed expression criterion. The order of the "
      56             :                              "positions in the input is kept.");
      57       14289 :   return params;
      58       14289 : }
      59             : 
      60          12 : ParsedDownSelectionPositions::ParsedDownSelectionPositions(const InputParameters & parameters)
      61             :   : Positions(parameters),
      62             :     NonADFunctorInterface(this),
      63             :     BlockRestrictable(this),
      64             :     FunctionParserUtils(parameters),
      65          12 :     _expression(getParam<std::string>("expression")),
      66          72 :     _xyzt({"x", "y", "z", "t"}),
      67          12 :     _functor_names(getParam<std::vector<MooseFunctorName>>("functor_names")),
      68          12 :     _n_functors(_functor_names.size()),
      69          36 :     _functor_symbols(getParam<std::vector<std::string>>("functor_symbols"))
      70             : {
      71          12 :   if (getParam<ExecFlagEnum>("execute_on").contains(EXEC_NONE))
      72           0 :     paramError("execute_on",
      73             :                "NONE execution flag not supported. Most functors (functions, variables, spatial "
      74             :                "user objects for example) are not initialized at construction.");
      75             : 
      76             :   // sanity checks
      77          12 :   if (!_functor_symbols.empty() && (_functor_symbols.size() != _n_functors))
      78           0 :     paramError("functor_symbols", "functor_symbols must be the same length as functor_names.");
      79             : 
      80             :   // Make sure functors are not x, y, z, or t
      81          36 :   for (const auto & name : _functor_symbols)
      82          24 :     if (std::find(_xyzt.begin(), _xyzt.end(), name) != _xyzt.end())
      83           0 :       paramError("functor_symbols", "x, y, z, and t cannot be used in 'functor_symbols'.");
      84          36 :   for (const auto & name : _functor_names)
      85          24 :     if (std::find(_xyzt.begin(), _xyzt.end(), name) != _xyzt.end())
      86           0 :       paramError("functor_names",
      87             :                  "x, y, z, and t cannot be used in 'functor_names'. Use functor_symbols to "
      88             :                  "disambiguate by using a different symbol in the expression.");
      89             : 
      90             :   // build variables argument
      91          12 :   std::string variables;
      92             : 
      93             :   // adding functors to the expression
      94          12 :   if (_functor_symbols.size())
      95          36 :     for (const auto & symbol : _functor_symbols)
      96          24 :       variables += (variables.empty() ? "" : ",") + symbol;
      97             :   else
      98           0 :     for (const auto & name : _functor_names)
      99           0 :       variables += (variables.empty() ? "" : ",") + name;
     100             : 
     101             :   // xyz and t are likely useful here
     102          60 :   for (auto & v : _xyzt)
     103          48 :     variables += (variables.empty() ? "" : ",") + v;
     104             : 
     105             :   // Create parsed function
     106          12 :   _func_F = std::make_shared<SymFunction>();
     107          12 :   parsedFunctionSetup(_func_F, _expression, variables, {}, {}, comm());
     108             : 
     109             :   // reserve storage for parameter passing buffer
     110          12 :   _func_params.resize(_n_functors + 4);
     111             : 
     112             :   // keep pointers to the functors
     113          36 :   for (const auto & name : _functor_names)
     114          24 :     _functors.push_back(&getFunctor<Real>(name));
     115          36 : }
     116             : 
     117             : void
     118          44 : ParsedDownSelectionPositions::initialize()
     119             : {
     120          44 :   if (!_initialized)
     121             :   {
     122             :     // Retrieve the input positions
     123          12 :     const auto & base_names = getParam<std::vector<PositionsName>>("input_positions");
     124          24 :     for (const auto & base_name : base_names)
     125          12 :       if (_fe_problem.hasUserObject(base_name))
     126          12 :         _positions_ptrs.push_back(&_fe_problem.getPositionsObject(base_name));
     127             : 
     128             :     // Check execute-ons
     129          24 :     for (const auto pos_ptr : _positions_ptrs)
     130          12 :       if (!pos_ptr->getExecuteOnEnum().contains(_fe_problem.getCurrentExecuteOnFlag()))
     131          36 :         mooseInfo("Positions '",
     132          12 :                   pos_ptr->name(),
     133             :                   "' is not executing on ",
     134          24 :                   Moose::stringify(_fe_problem.getCurrentExecuteOnFlag()),
     135             :                   ". This could mean this position is not updated when down-selecting.");
     136             :   }
     137             : 
     138          44 :   clearPositions();
     139          44 :   const bool initial = _fe_problem.getCurrentExecuteOnFlag() == EXEC_INITIAL;
     140             : 
     141             :   // Pre-allocate for performance
     142          44 :   unsigned int n_points = 0;
     143          88 :   for (const auto pos_ptr : _positions_ptrs)
     144          44 :     n_points += pos_ptr->getNumPositions(initial);
     145          44 :   _positions.reserve(n_points);
     146             :   mooseAssert(comm().verify(n_points), "Input positions should be synchronized");
     147             : 
     148             :   // Rather than synchronize all ranks at every point, we will figure out whether to keep (2),
     149             :   // discard (1) or error (0, due to no ranks having made the decision) for each position
     150          44 :   std::vector<short> keep_positions(n_points, PositionSelection::Error);
     151             : 
     152          44 :   const auto state = determineState();
     153          44 :   auto pl = _fe_problem.mesh().getMesh().sub_point_locator();
     154          44 :   pl->enable_out_of_mesh_mode();
     155             : 
     156             :   // Loop over the positions, find them in the mesh to form the adequate functor arguments
     157             :   // Note that every positions object is assumed replicated over every rank already
     158          44 :   unsigned int i_pos, counter = 0;
     159          88 :   for (const auto & pos_ptr : _positions_ptrs)
     160         748 :     for (const auto & pos : pos_ptr->getPositions(initial))
     161             :     {
     162         704 :       counter++;
     163         704 :       i_pos = counter - 1;
     164             :       // Get all possible elements the position may be in
     165         704 :       std::set<const Elem *> candidate_elements;
     166         704 :       (*pl)(pos, candidate_elements);
     167             : 
     168        1376 :       for (const auto elem : candidate_elements)
     169             :       {
     170             :         // Check block restriction
     171             :         // Dont exclude a point we already chose to keep. This 'inclusivity' means that a position
     172             :         // at a node can be included if at least one element it borders is in the block restriction
     173         672 :         if (!hasBlocks(elem->subdomain_id()) && (keep_positions[i_pos] != PositionSelection::Keep))
     174             :         {
     175           0 :           keep_positions[i_pos] = PositionSelection::Discard;
     176         160 :           continue;
     177             :         }
     178             : 
     179             :         // We can't guarantee we have enough algebraic ghosting for variable functors. Might as well
     180             :         // skip, another process will take care of it
     181         672 :         if (elem->processor_id() != processor_id())
     182         160 :           continue;
     183             : 
     184             :         // Form a functor argument
     185         512 :         const Moose::ElemPointArg elem_arg = {elem, pos, false};
     186             : 
     187             :         // Fill arguments to the parsed expression
     188             :         // Functors
     189        1536 :         for (const auto i : index_range(_functors))
     190        1024 :           _func_params[i] = (*_functors[i])(elem_arg, state);
     191             : 
     192             :         // Positions and time
     193        2048 :         for (const auto j : make_range(Moose::dim))
     194        1536 :           _func_params[_n_functors + j] = pos(j);
     195         512 :         _func_params[_n_functors + 3] = _t;
     196             : 
     197             :         // Evaluate parsed expression
     198         512 :         const auto value = evaluate(_func_F);
     199             : 
     200             :         // Keep points matching the criterion
     201         512 :         if (value > 0)
     202          32 :           keep_positions[i_pos] = PositionSelection::Keep;
     203             :         // Dont exclude a point we already chose to keep. This 'inclusivity'
     204             :         // means that a position at an interface between elements can get included if the functor
     205             :         // evaluates greater than 0 for any of the elements used in forming ElemPointArgs
     206         480 :         else if (keep_positions[i_pos] != PositionSelection::Keep)
     207         480 :           keep_positions[i_pos] = PositionSelection::Discard;
     208             :       }
     209         704 :     }
     210             : 
     211             :   // Synchronize which positions to keep across all ranks
     212          44 :   comm().max(keep_positions);
     213          44 :   i_pos = 0;
     214          88 :   for (const auto & pos_ptr : _positions_ptrs)
     215         748 :     for (const auto & pos : pos_ptr->getPositions(initial))
     216             :     {
     217         704 :       if (keep_positions[i_pos] == PositionSelection::Keep)
     218          44 :         _positions.push_back(pos);
     219         660 :       else if (keep_positions[i_pos] == PositionSelection::Error)
     220           0 :         mooseError(
     221             :             "No process has made a decision on whether position '",
     222             :             pos,
     223           0 :             "' from Positions object '" + pos_ptr->name() +
     224             :                 "' should be discarded or kept during down-selection. This usually means this "
     225             :                 "position is outside the mesh!");
     226         704 :       i_pos++;
     227             :     }
     228             : 
     229          44 :   _initialized = true;
     230          44 : }

Generated by: LCOV version 1.14