LCOV - code coverage report
Current view: top level - src/positions - FunctorExtremaPositions.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 78 86 90.7 %
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 "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       14289 : FunctorExtremaPositions::validParams()
      19             : {
      20       14289 :   InputParameters params = Positions::validParams();
      21       14289 :   params += NonADFunctorInterface::validParams();
      22       14289 :   params += BlockRestrictable::validParams();
      23             : 
      24       14289 :   params.addRequiredParam<MooseFunctorName>(
      25             :       "functor", "Functor of which the extremas are going to form the list of positions");
      26       42867 :   params.addParam<MooseEnum>("extrema_type",
      27       28578 :                              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       14289 :   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       14289 :   params.set<bool>("auto_sort") = false;
      40       14289 :   params.suppressParameter<bool>("auto_sort");
      41             :   // The broadcasting has to be custom to preserve the order of the extremas
      42       14289 :   params.set<bool>("auto_broadcast") = false;
      43       14289 :   params.suppressParameter<bool>("auto_broadcast");
      44             : 
      45             :   // Keep as up-to-date as possible given the generality of functors
      46       42867 :   params.set<ExecFlagEnum>("execute_on") = {EXEC_LINEAR, EXEC_TIMESTEP_BEGIN};
      47             : 
      48       14289 :   params.addClassDescription("Searches the geometry for the extrema of the functors");
      49       14289 :   return params;
      50       14289 : }
      51             : 
      52          12 : FunctorExtremaPositions::FunctorExtremaPositions(const InputParameters & parameters)
      53             :   : Positions(parameters),
      54             :     NonADFunctorInterface(this),
      55             :     BlockRestrictable(this),
      56          12 :     _functor(getFunctor<Real>("functor")),
      57          12 :     _n_extrema(getParam<unsigned int>("num_extrema")),
      58          12 :     _type(getParam<MooseEnum>("extrema_type").getEnum<ExtremeType>()),
      59          12 :     _positions_values(declareValueByName<std::vector<Real>, ReporterVectorContext<Real>>(
      60          12 :         "functor_extrema", REPORTER_MODE_REPLICATED))
      61             : {
      62             :   // Constants and postprocessors are not interesting here
      63          12 :   const auto & functor_name = getParam<MooseFunctorName>("functor");
      64          24 :   if (_fe_problem.hasPostprocessorValueByName(functor_name) ||
      65          12 :       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          12 :   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          12 : }
      75             : 
      76             : void
      77          44 : FunctorExtremaPositions::initialize()
      78             : {
      79          44 :   clearPositions();
      80          44 :   _positions.resize(_n_extrema);
      81          44 :   _positions_values.resize(_n_extrema);
      82             : 
      83          44 :   std::list<Real> extrema;
      84          44 :   std::list<Point> extrema_locs;
      85          44 :   if (_type == ExtremeType::MAX || _type == ExtremeType::MAX_ABS)
      86          44 :     extrema.resize(_n_extrema, -std::numeric_limits<Real>::max());
      87             :   else
      88           0 :     extrema.resize(_n_extrema, std::numeric_limits<Real>::max());
      89          44 :   extrema_locs.resize(_n_extrema);
      90             : 
      91          44 :   const auto time_arg = determineState();
      92             : 
      93             :   // Loop over the local mesh, keep track of all extrema
      94          44 :   for (const auto & elem :
      95        6488 :        _fe_problem.mesh().getMesh().active_local_subdomain_set_element_ptr_range(blockIDs()))
      96             :   {
      97        3200 :     const Moose::ElemArg elem_arg = {elem, false};
      98        3200 :     auto value = _functor(elem_arg, time_arg);
      99             : 
     100        3200 :     auto extremum = extrema.begin();
     101        3200 :     auto extremum_loc = extrema_locs.begin();
     102             : 
     103        8952 :     for (const auto i : make_range(_n_extrema))
     104             :     {
     105        8952 :       libmesh_ignore(i);
     106        8952 :       bool extrema_found = false;
     107        8952 :       switch (_type)
     108             :       {
     109        8952 :         case ExtremeType::MAX_ABS:
     110        8952 :           value = std::abs(value);
     111             :           // fallthrough
     112        8952 :         case ExtremeType::MAX:
     113             :         {
     114        8952 :           if (value > *extremum)
     115        3200 :             extrema_found = true;
     116        8952 :           break;
     117             :         }
     118           0 :         case ExtremeType::MIN:
     119             :         {
     120           0 :           if (value < *extremum)
     121           0 :             extrema_found = true;
     122           0 :           break;
     123             :         }
     124             :       }
     125        8952 :       if (extrema_found)
     126             :       {
     127             :         // Move the extrema down the list of extrema
     128        3200 :         extrema.insert(extremum, value);
     129        3200 :         extrema_locs.insert(extremum_loc, elem->true_centroid());
     130        3200 :         extrema.pop_back();
     131        3200 :         extrema_locs.pop_back();
     132             :         // Found an extremum, filled the extrema vector, done
     133        3200 :         break;
     134             :       }
     135             :       // Consider the next extremum
     136        5752 :       std::advance(extremum, 1);
     137        5752 :       std::advance(extremum_loc, 1);
     138             :     }
     139          44 :   }
     140             : 
     141             :   // Synchronize across all ranks
     142          44 :   auto current_candidate = extrema.begin();
     143          44 :   auto current_candidate_loc = extrema_locs.begin();
     144         484 :   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         440 :     auto copy = *current_candidate;
     149         440 :     if (_type == ExtremeType::MAX || _type == ExtremeType::MAX_ABS)
     150         440 :       comm().maxloc(copy, rank);
     151             :     else
     152           0 :       comm().minloc(copy, rank);
     153             : 
     154         440 :     RealVectorValue extreme_point(0., 0., 0.);
     155         440 :     if (rank == processor_id())
     156             :     {
     157         320 :       extreme_point = *current_candidate_loc;
     158             :       // Our candidate for ith max got accepted, move on to offering the next extremal one
     159         320 :       std::advance(current_candidate, 1);
     160         320 :       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         440 :     comm().sum(extreme_point);
     166         440 :     _positions[i] = extreme_point;
     167         440 :     _positions_values[i] = copy;
     168             :   }
     169             :   mooseAssert(comm().verify(_positions_values),
     170             :               "Positions extrema should be the same across all MPI processes.");
     171          44 :   _initialized = true;
     172          44 : }

Generated by: LCOV version 1.14