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 "MultiAppVariableValueSamplePostprocessorTransfer.h"
11 :
12 : // MOOSE includes
13 : #include "FEProblem.h"
14 : #include "MooseMesh.h"
15 : #include "MooseTypes.h"
16 : #include "MooseVariableFE.h"
17 : #include "MultiApp.h"
18 : #include "AuxiliarySystem.h"
19 : #include "MooseUtils.h"
20 : #include "MooseAppCoordTransform.h"
21 :
22 : #include "libmesh/meshfree_interpolation.h"
23 : #include "libmesh/system.h"
24 :
25 : #include "timpi/parallel_sync.h"
26 :
27 : using namespace libMesh;
28 :
29 : registerMooseObject("MooseApp", MultiAppVariableValueSamplePostprocessorTransfer);
30 :
31 : InputParameters
32 14941 : MultiAppVariableValueSamplePostprocessorTransfer::validParams()
33 : {
34 14941 : InputParameters params = MultiAppTransfer::validParams();
35 14941 : params.addClassDescription(
36 : "Samples the value of a variable within the main application at each sub-application "
37 : "position and transfers the value to a postprocessor on the sub-application(s) when "
38 : "performing the to-multiapp transfer. Reconstructs the value of a CONSTANT MONOMIAL "
39 : "variable associating the value of each element to the value of the postprocessor "
40 : "in the closest sub-application whem performing the from-multiapp transfer.");
41 14941 : params.addRequiredParam<PostprocessorName>(
42 : "postprocessor",
43 : "The name of the postprocessor in the MultiApp to transfer the value to. "
44 : "This should most likely be a Reciever Postprocessor.");
45 14941 : params.addRequiredParam<VariableName>("source_variable", "The variable to transfer from.");
46 44823 : params.addParam<unsigned int>(
47 : "source_variable_component",
48 29882 : 0,
49 : "The component of source variable, may be non-zero for array variables.");
50 44823 : params.addParam<bool>(
51 : "map_array_variable_components_to_child_apps",
52 29882 : false,
53 : "When true, groups of sub-applications will be associated with different components of the "
54 : "supplied array variable in 'source_variable'. For instance, if there are 9 sub-applications "
55 : "and 3 components in the variable, sub-apps 0-2 will go to component 0, 3-5 will go to 1, "
56 : "and 6-8 will go to 2.");
57 14941 : return params;
58 0 : }
59 :
60 340 : MultiAppVariableValueSamplePostprocessorTransfer::MultiAppVariableValueSamplePostprocessorTransfer(
61 340 : const InputParameters & parameters)
62 : : MultiAppTransfer(parameters),
63 : MeshChangedInterface(parameters),
64 340 : _postprocessor_name(getParam<PostprocessorName>("postprocessor")),
65 340 : _var_name(getParam<VariableName>("source_variable")),
66 340 : _comp(getParam<unsigned int>("source_variable_component")),
67 340 : _var(_fe_problem.getVariable(0, _var_name)),
68 680 : _map_comp_to_child(getParam<bool>("map_array_variable_components_to_child_apps"))
69 : {
70 340 : if (_directions.size() != 1)
71 0 : paramError("direction", "This transfer is only unidirectional");
72 :
73 340 : if (_directions.isValueSet("from_multiapp"))
74 : {
75 : // Check that the variable is a CONSTANT MONOMIAL.
76 116 : auto & fe_type = _var.feType();
77 116 : if (fe_type.order != CONSTANT || fe_type.family != MONOMIAL)
78 0 : paramError("source_variable",
79 : "Variable must be in CONSTANT MONOMIAL when transferring from a postprocessor "
80 : "from sub-apps.");
81 :
82 116 : if (!_fe_problem.getAuxiliarySystem().hasVariable(_var_name))
83 4 : paramError("source_variable", "Variable must be an auxiliary variable");
84 : }
85 224 : else if (_directions.isValueSet("between_multiapp"))
86 0 : mooseError("MultiAppVariableValueSamplePostprocessorTransfer has not been made to support "
87 : "sibling transfers");
88 :
89 336 : if (_map_comp_to_child && !_var.isArray())
90 4 : paramError("map_array_variable_components_to_child_apps",
91 : "'source_variable' must be an array variable when mapping array variable components "
92 : "to child applications.");
93 332 : if (_map_comp_to_child && parameters.isParamSetByUser("source_variable_component"))
94 4 : paramError("map_array_variable_components_to_child_apps",
95 : "'source_variable_component' is invalid when mapping array variable components to "
96 : "child applications.");
97 328 : }
98 :
99 : void
100 304 : MultiAppVariableValueSamplePostprocessorTransfer::setupPostprocessorCommunication()
101 : {
102 304 : if (!_directions.isValueSet("from_multiapp"))
103 204 : return;
104 :
105 100 : const auto num_global_apps = getFromMultiApp()->numGlobalApps();
106 :
107 : // Setup the communication pattern
108 200 : _postprocessor_to_processor_id.resize(num_global_apps,
109 100 : std::numeric_limits<processor_id_type>::max());
110 1380 : for (const auto i : make_range(num_global_apps))
111 1280 : if (getFromMultiApp()->hasLocalApp(i))
112 968 : _postprocessor_to_processor_id[i] = this->processor_id();
113 :
114 100 : _communicator.min(_postprocessor_to_processor_id);
115 : #ifdef DEBUG
116 : for (const auto i : make_range(num_global_apps))
117 : {
118 : mooseAssert(_postprocessor_to_processor_id[i] != std::numeric_limits<processor_id_type>::max(),
119 : "Every element in the vector should have been set.");
120 : if (getFromMultiApp()->hasLocalApp(i))
121 : mooseAssert(_postprocessor_to_processor_id[i] == this->processor_id(),
122 : "If I owned this app, then the processor id value should be my own");
123 : }
124 : #endif
125 : }
126 :
127 : void
128 304 : MultiAppVariableValueSamplePostprocessorTransfer::cacheElemToPostprocessorData()
129 : {
130 304 : if (!_directions.isValueSet("from_multiapp"))
131 204 : return;
132 :
133 : // Cache the Multiapp position ID for every element.
134 100 : auto & mesh = _fe_problem.mesh().getMesh();
135 100 : unsigned int multiapp_pos_id = 0;
136 3924 : for (auto & elem : as_range(mesh.active_local_elements_begin(), mesh.active_local_elements_end()))
137 : // Exclude the elements without dofs.
138 1916 : if (_var.hasBlocks(elem->subdomain_id()))
139 : {
140 : // The next two loops will loop through all the sub-applications
141 : // The first loop is over each component of the source variable we are transferring to/from
142 1772 : unsigned int j = 0; // Indicates sub-app index
143 4584 : for (unsigned int g = 0; g < getFromMultiApp()->numGlobalApps() / _apps_per_component; ++g)
144 : {
145 2816 : Real distance = std::numeric_limits<Real>::max();
146 2816 : unsigned int count = 0;
147 : // The second loop is over all the sub-apps the given component is associated with
148 22752 : for (unsigned int c = 0; c < _apps_per_component; ++c, ++j)
149 : {
150 19936 : Real current_distance = (getFromMultiApp()->position(j) - elem->true_centroid()).norm();
151 19936 : if (MooseUtils::absoluteFuzzyLessThan(current_distance, distance))
152 : {
153 8014 : distance = current_distance;
154 8014 : multiapp_pos_id = j;
155 8014 : count = 0;
156 : }
157 11922 : else if (MooseUtils::absoluteFuzzyEqual(current_distance, distance))
158 530 : ++count;
159 : }
160 2816 : if (count > 0)
161 8 : mooseWarning(
162 : "The distances of an element to more than one sub-applications are too close "
163 : " in transfer '",
164 4 : name(),
165 : "'. The code chooses the sub-application with the smallest ID to set "
166 : "the variable on the element, which may created undesired variable solutions."
167 : "\nHaving different positions for sub-applications, "
168 : "a centroid-based MultiApp or adding block restriction to the variable can "
169 : "be used to resolve this warning.");
170 :
171 : // Note: in case of count>0, the sub-application with smallest id will be used for the
172 : // transfer.
173 2812 : _cached_multiapp_pos_ids.push_back(multiapp_pos_id);
174 2812 : _needed_postprocessors.insert(multiapp_pos_id);
175 : }
176 96 : }
177 : }
178 :
179 : void
180 312 : MultiAppVariableValueSamplePostprocessorTransfer::initialSetup()
181 : {
182 312 : MultiAppTransfer::initialSetup();
183 :
184 616 : unsigned int num_apps = _directions.isValueSet("from_multiapp")
185 616 : ? getFromMultiApp()->numGlobalApps()
186 308 : : getToMultiApp()->numGlobalApps();
187 308 : if (_map_comp_to_child && num_apps % _var.count() != 0)
188 4 : paramError("map_array_variable_components_to_child_apps",
189 : "The number of sub-applications (",
190 : num_apps,
191 : ") is not divisible by the number of components in '",
192 4 : _var_name,
193 : "' (",
194 4 : _var.count(),
195 : ").");
196 304 : _apps_per_component = _map_comp_to_child ? num_apps / _var.count() : num_apps;
197 :
198 304 : setupPostprocessorCommunication();
199 304 : cacheElemToPostprocessorData();
200 300 : }
201 :
202 : void
203 0 : MultiAppVariableValueSamplePostprocessorTransfer::meshChanged()
204 : {
205 0 : cacheElemToPostprocessorData();
206 0 : }
207 :
208 : void
209 512 : MultiAppVariableValueSamplePostprocessorTransfer::execute()
210 : {
211 512 : TIME_SECTION("MultiAppVariableValueSamplePostprocessorTransfer::execute()",
212 : 5,
213 : "Transferring a variable to a postprocessor through sampling");
214 :
215 512 : switch (_current_direction)
216 : {
217 292 : case TO_MULTIAPP:
218 : {
219 292 : const ArrayMooseVariable * array_var = nullptr;
220 292 : const MooseVariableField<Real> * standard_var = nullptr;
221 292 : if (_var.isArray())
222 44 : array_var = &_fe_problem.getArrayVariable(0, _var_name);
223 248 : else if (!_var.isVector())
224 248 : standard_var = static_cast<MooseVariableField<Real> *>(&_var);
225 : else
226 0 : mooseError("MultiAppVariableValueSamplePostprocessorTransfer does not support transfer of "
227 : "vector variables");
228 :
229 292 : auto active_tags = _fe_problem.getActiveFEVariableCoupleableVectorTags(/*thread_id=*/0);
230 292 : std::set<unsigned int> solution_tag = {_fe_problem.getVectorTagID(Moose::SOLUTION_TAG)};
231 :
232 292 : _fe_problem.setActiveFEVariableCoupleableVectorTags(solution_tag, /*thread_id=*/0);
233 :
234 292 : MooseMesh & from_mesh = _fe_problem.mesh();
235 :
236 292 : std::unique_ptr<PointLocatorBase> pl = from_mesh.getPointLocator();
237 :
238 292 : pl->enable_out_of_mesh_mode();
239 :
240 3074 : for (unsigned int i = 0; i < getToMultiApp()->numGlobalApps(); i++)
241 : {
242 2782 : Real value = -std::numeric_limits<Real>::max();
243 :
244 : { // Get the value of the variable at the point where this multiapp is in the master domain
245 :
246 2782 : Point multi_app_position = getToMultiApp()->position(i);
247 :
248 2782 : std::vector<Point> point_vec(1, multi_app_position);
249 :
250 : // First find the element the hit lands in
251 2782 : const Elem * elem = (*pl)(multi_app_position);
252 :
253 2782 : if (elem && elem->processor_id() == from_mesh.processor_id())
254 : {
255 1966 : _fe_problem.setCurrentSubdomainID(elem, 0);
256 1966 : _fe_problem.reinitElemPhys(elem, point_vec, 0);
257 :
258 1966 : if (array_var)
259 : {
260 352 : value = array_var->sln()[0](getVariableComponent(i));
261 : mooseAssert(
262 : getVariableComponent(i) < array_var->count(),
263 : "Component must be smaller than the number of components of array variable!");
264 : mooseAssert(array_var->sln().size() == 1, "No values in u!");
265 : }
266 : else
267 : {
268 1614 : value = standard_var->sln()[0];
269 : mooseAssert(standard_var->sln().size() == 1, "No values in u!");
270 : }
271 : }
272 :
273 2782 : _communicator.max(value);
274 2782 : }
275 :
276 2782 : if (getToMultiApp()->hasLocalApp(i))
277 1966 : getToMultiApp()->appProblemBase(i).setPostprocessorValueByName(_postprocessor_name,
278 : value);
279 : }
280 :
281 292 : _fe_problem.setActiveFEVariableCoupleableVectorTags(active_tags, /*thread_id=*/0);
282 :
283 292 : break;
284 292 : }
285 220 : case FROM_MULTIAPP:
286 : {
287 220 : auto & mesh = _fe_problem.mesh().getMesh();
288 220 : auto & solution = _var.sys().solution();
289 :
290 : // Get the required postprocessor values
291 220 : const unsigned int n_subapps = getFromMultiApp()->numGlobalApps();
292 220 : std::vector<Real> pp_values(n_subapps, std::numeric_limits<Real>::max());
293 2772 : for (const auto i : make_range(n_subapps))
294 2552 : if (getFromMultiApp()->hasLocalApp(i))
295 1856 : pp_values[i] = getFromMultiApp()->appPostprocessorValue(i, _postprocessor_name);
296 :
297 : // Gather all the multiapps postprocessor values that we need
298 220 : std::unordered_map<processor_id_type, std::vector<unsigned int>> postprocessor_queries;
299 2156 : for (const auto needed_postprocessor : _needed_postprocessors)
300 : {
301 1936 : const auto proc_id = _postprocessor_to_processor_id[needed_postprocessor];
302 1936 : if (proc_id != this->processor_id())
303 454 : postprocessor_queries[proc_id].push_back(needed_postprocessor);
304 : }
305 :
306 96 : auto gather_data = [&pp_values
307 : #ifndef NDEBUG
308 : ,
309 : this
310 : #endif
311 : ](processor_id_type libmesh_dbg_var(pid),
312 : const std::vector<unsigned int> & postprocessor_ids,
313 454 : std::vector<Real> & postprocessor_values)
314 : {
315 : mooseAssert(pid != this->processor_id(), "Should not be pulling from self");
316 96 : postprocessor_values.resize(postprocessor_ids.size());
317 550 : for (const auto i : index_range(postprocessor_ids))
318 : {
319 454 : const auto pp_id = postprocessor_ids[i];
320 454 : const auto pp_value = pp_values[pp_id];
321 : mooseAssert(
322 : pp_value != std::numeric_limits<Real>::max(),
323 : "If we are getting queried for postprocessor data, then we better have a valid"
324 : "postprocesor value.");
325 454 : postprocessor_values[i] = pp_value;
326 : }
327 96 : };
328 :
329 96 : auto act_on_data = [&pp_values
330 : #ifndef NDEBUG
331 : ,
332 : this
333 : #endif
334 : ](processor_id_type libmesh_dbg_var(pid),
335 : const std::vector<unsigned int> & postprocessor_ids,
336 454 : const std::vector<Real> & postprocessor_values)
337 : {
338 : mooseAssert(pid != this->processor_id(), "Should not be returning a query from self");
339 : mooseAssert(postprocessor_ids.size() == postprocessor_values.size(),
340 : "should be a 1-to-1 query-to-response");
341 550 : for (const auto i : index_range(postprocessor_ids))
342 : {
343 454 : const auto pp_id = postprocessor_ids[i];
344 454 : const auto pp_value = postprocessor_values[i];
345 : mooseAssert(pp_value != std::numeric_limits<Real>::max(),
346 : "If we are returning postprocessor data, then we better have a valid"
347 : "postprocesor value.");
348 454 : pp_values[pp_id] = pp_value;
349 : }
350 96 : };
351 :
352 220 : constexpr Real example = 0;
353 220 : TIMPI::pull_parallel_vector_data(
354 220 : _communicator, postprocessor_queries, gather_data, act_on_data, &example);
355 :
356 : // Assign the multiapps postprocessor values to the local elements.
357 220 : unsigned int i = 0;
358 220 : for (auto & elem :
359 6904 : as_range(mesh.active_local_elements_begin(), mesh.active_local_elements_end()))
360 : {
361 : // Exclude the elements without dofs
362 3232 : if (_var.hasBlocks(elem->subdomain_id()))
363 : {
364 2848 : std::vector<dof_id_type> dof_indices;
365 2848 : _var.getDofIndices(elem, dof_indices);
366 : mooseAssert(dof_indices.size() == 1,
367 : "The variable must be a constant monomial with one DoF on an element");
368 : mooseAssert(pp_values[_cached_multiapp_pos_ids[i]] != std::numeric_limits<Real>::max(),
369 : "We should have pulled all the data we needed.");
370 6624 : for (unsigned int c = 0; c < n_subapps / _apps_per_component; ++c)
371 : {
372 3776 : solution.set(dof_indices[0] + getVariableComponent(_cached_multiapp_pos_ids[i]),
373 3776 : pp_values[_cached_multiapp_pos_ids[i]]);
374 3776 : ++i;
375 : }
376 2848 : }
377 220 : }
378 220 : solution.close();
379 220 : break;
380 220 : }
381 : }
382 512 : }
|