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