LCOV - code coverage report
Current view: top level - src/positions - FunctorExtremaPositions.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 74 82 90.2 %
Date: 2026-05-29 20:35:17 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 "FunctorExtremaPositions.h"
      11             : #include "libmesh/parallel.h"
      12             : #include "libmesh/parallel_algebra.h"
      13             : #include "libmesh/vector_value.h"
      14             : 
      15             : registerMooseObject("MooseApp", FunctorExtremaPositions);
      16             : 
      17             : InputParameters
      18        3133 : FunctorExtremaPositions::validParams()
      19             : {
      20        3133 :   InputParameters params = Positions::validParams();
      21        3133 :   params += NonADFunctorInterface::validParams();
      22        3133 :   params += BlockRestrictable::validParams();
      23             : 
      24       12532 :   params.addRequiredParam<MooseFunctorName>(
      25             :       "functor", "Functor of which the extremas are going to form the list of positions");
      26        9399 :   params.addParam<MooseEnum>("extrema_type",
      27       15665 :                              MooseEnum("max=0 min=1 max_abs=2", "max"),
      28             :                              "Type of extreme value to return. 'max' "
      29             :                              "returns the location(s) of the maximum value(s). 'min' returns "
      30             :                              "the location(s) of the minimum value(s). 'max_abs' returns the "
      31             :                              "locations of the maximum(a) of the absolute value.");
      32       12532 :   params.addParam<unsigned int>("num_extrema", 1, "Number of extrema to look for");
      33             : 
      34             :   // Future arguments we could conceive:
      35             :   // - min_dist to force a certain distance between extrema
      36             :   // - functor argument type. We use elements, we could do nodes, qps, sides, ...
      37             : 
      38             :   // Use user-provided ordering
      39        6266 :   params.set<bool>("auto_sort") = false;
      40        6266 :   params.suppressParameter<bool>("auto_sort");
      41             :   // The broadcasting has to be custom to preserve the order of the extremas
      42        6266 :   params.set<bool>("auto_broadcast") = false;
      43        3133 :   params.suppressParameter<bool>("auto_broadcast");
      44             : 
      45             :   // Keep as up-to-date as possible given the generality of functors
      46       12532 :   params.set<ExecFlagEnum>("execute_on") = {EXEC_LINEAR, EXEC_TIMESTEP_BEGIN};
      47             : 
      48        3133 :   params.addClassDescription("Searches the geometry for the extrema of the functors");
      49        3133 :   return params;
      50        3133 : }
      51             : 
      52          36 : FunctorExtremaPositions::FunctorExtremaPositions(const InputParameters & parameters)
      53             :   : Positions(parameters),
      54             :     NonADFunctorInterface(this),
      55             :     BlockRestrictable(this),
      56          36 :     _functor(getFunctor<Real>("functor")),
      57          72 :     _n_extrema(getParam<unsigned int>("num_extrema")),
      58          72 :     _type(getParam<MooseEnum>("extrema_type").getEnum<ExtremeType>()),
      59          36 :     _positions_values(declareValueByName<std::vector<Real>, ReporterVectorContext<Real>>(
      60          36 :         "functor_extrema", REPORTER_MODE_REPLICATED))
      61             : {
      62             :   // Constants and postprocessors are not interesting here
      63          72 :   const auto & functor_name = getParam<MooseFunctorName>("functor");
      64          72 :   if (_fe_problem.hasPostprocessorValueByName(functor_name) ||
      65          36 :       MooseUtils::parsesToReal(functor_name))
      66           0 :     paramError(
      67             :         "functor",
      68             :         "Postprocessors and constants do not have extrema, they are constant over the domain.");
      69             : 
      70         108 :   if (getParam<ExecFlagEnum>("execute_on").contains(EXEC_NONE))
      71           0 :     paramError("execute_on",
      72             :                "NONE execution flag not supported. Most functors (functions, variables, spatial "
      73             :                "user objects for example) are not initialized at construction.");
      74          36 : }
      75             : 
      76             : void
      77         224 : FunctorExtremaPositions::initialize()
      78             : {
      79         224 :   clearPositions();
      80         224 :   _positions.resize(_n_extrema);
      81         224 :   _positions_values.resize(_n_extrema);
      82             : 
      83         224 :   std::list<Real> extrema;
      84         224 :   std::list<Point> extrema_locs;
      85         224 :   if (_type == ExtremeType::MAX || _type == ExtremeType::MAX_ABS)
      86         224 :     extrema.resize(_n_extrema, -std::numeric_limits<Real>::max());
      87             :   else
      88           0 :     extrema.resize(_n_extrema, std::numeric_limits<Real>::max());
      89         224 :   extrema_locs.resize(_n_extrema);
      90             : 
      91         224 :   const auto time_arg = determineState();
      92             : 
      93             :   // Loop over the local mesh, keep track of all extrema
      94         224 :   for (const auto & elem :
      95        4960 :        _subproblem.mesh().getMesh().active_local_subdomain_set_element_ptr_range(blockIDs()))
      96             :   {
      97        4512 :     const Moose::ElemArg elem_arg = {elem, false};
      98        4512 :     auto value = _functor(elem_arg, time_arg);
      99             : 
     100        4512 :     auto extremum = extrema.begin();
     101        4512 :     auto extremum_loc = extrema_locs.begin();
     102             : 
     103       13208 :     for (const auto i : make_range(_n_extrema))
     104             :     {
     105       13048 :       libmesh_ignore(i);
     106       13048 :       bool extrema_found = false;
     107       13048 :       switch (_type)
     108             :       {
     109        8952 :         case ExtremeType::MAX_ABS:
     110        8952 :           value = std::abs(value);
     111             :           // fallthrough
     112       13048 :         case ExtremeType::MAX:
     113             :         {
     114       13048 :           if (value > *extremum)
     115        4352 :             extrema_found = true;
     116       13048 :           break;
     117             :         }
     118           0 :         case ExtremeType::MIN:
     119             :         {
     120           0 :           if (value < *extremum)
     121           0 :             extrema_found = true;
     122           0 :           break;
     123             :         }
     124             :       }
     125       13048 :       if (extrema_found)
     126             :       {
     127             :         // Move the extrema down the list of extrema
     128        4352 :         extrema.insert(extremum, value);
     129        4352 :         extrema_locs.insert(extremum_loc, elem->true_centroid());
     130        4352 :         extrema.pop_back();
     131        4352 :         extrema_locs.pop_back();
     132             :         // Found an extremum, filled the extrema vector, done
     133        4352 :         break;
     134             :       }
     135             :       // Consider the next extremum
     136             :       std::advance(extremum, 1);
     137             :       std::advance(extremum_loc, 1);
     138             :     }
     139         224 :   }
     140             : 
     141             :   // Synchronize across all ranks
     142         224 :   auto current_candidate = extrema.begin();
     143         224 :   auto current_candidate_loc = extrema_locs.begin();
     144        2104 :   for (const auto i : make_range(_n_extrema))
     145             :   {
     146             :     unsigned int rank;
     147             :     // dont modify the extremum, it might be the global n-th extremum in a later call
     148        1880 :     auto copy = *current_candidate;
     149        1880 :     if (_type == ExtremeType::MAX || _type == ExtremeType::MAX_ABS)
     150        1880 :       comm().maxloc(copy, rank);
     151             :     else
     152           0 :       comm().minloc(copy, rank);
     153             : 
     154        1880 :     RealVectorValue extreme_point(0., 0., 0.);
     155        1880 :     if (rank == processor_id())
     156             :     {
     157        1376 :       extreme_point = *current_candidate_loc;
     158             :       // Our candidate for ith max got accepted, move on to offering the next extremal one
     159             :       std::advance(current_candidate, 1);
     160             :       std::advance(current_candidate_loc, 1);
     161             :     }
     162             : 
     163             :     // Send the position
     164             :     // The broadcast and scatter are for root 0 sending to all. The sum works just as well
     165        1880 :     comm().sum(extreme_point);
     166        1880 :     _positions[i] = extreme_point;
     167        1880 :     _positions_values[i] = copy;
     168             :   }
     169             :   mooseAssert(comm().verify(_positions_values),
     170             :               "Positions extrema should be the same across all MPI processes.");
     171         224 :   _initialized = true;
     172         224 : }

Generated by: LCOV version 1.14