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 : }