https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MultiAppConservativeTransfer.C
Go to the documentation of this file.
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// MOOSE includes
12#include "MooseTypes.h"
13#include "FEProblem.h"
14#include "MultiApp.h"
15#include "MooseMesh.h"
16#include "UserObject.h"
18#include "SystemBase.h"
19
22{
24 params.addRequiredParam<std::vector<AuxVariableName>>(
25 "variable", "The auxiliary variable to store the transferred values in.");
26 params.addRequiredParam<std::vector<VariableName>>("source_variable",
27 "The variable to transfer from.");
28
29 params.addParam<std::vector<PostprocessorName>>(
30 "from_postprocessors_to_be_preserved",
31 "The name of the Postprocessor in the from-app to evaluate an adjusting factor.");
32
33 params.addParam<std::vector<PostprocessorName>>(
34 "to_postprocessors_to_be_preserved",
35 {},
36 "The name of the Postprocessor in the to-app to evaluate an adjusting factor.");
37 params.addParam<bool>("allow_skipped_adjustment",
38 false,
39 "If set to true, the transfer skips adjustment when from or to "
40 "postprocessor values are either zero or have different signs. If set to "
41 "false, an error is thrown when encountering these conditions.");
42 params.addParamNamesToGroup("from_postprocessors_to_be_preserved "
43 "to_postprocessors_to_be_preserved allow_skipped_adjustment",
44 "Conservative transfer");
45
46 return params;
47}
48
50 : MultiAppFieldTransfer(parameters),
51 _from_var_names(isParamValid("source_variable")
52 ? getParam<std::vector<VariableName>>("source_variable")
53 : std::vector<VariableName>()),
54 _to_var_names(getParam<std::vector<AuxVariableName>>("variable")),
55 _preserve_transfer(isParamValid("from_postprocessors_to_be_preserved")),
56 _from_postprocessors_to_be_preserved(
57 _preserve_transfer
58 ? getParam<std::vector<PostprocessorName>>("from_postprocessors_to_be_preserved")
59 : std::vector<PostprocessorName>{}),
60 _to_postprocessors_to_be_preserved(
61 getParam<std::vector<PostprocessorName>>("to_postprocessors_to_be_preserved")),
62 _use_nearestpoint_pps(false),
63 _allow_skipped_adjustment(getParam<bool>("allow_skipped_adjustment"))
64{
65 if (_directions.size() != 1)
66 paramError("direction", "This transfer is only unidirectional");
67
68 if (_preserve_transfer)
69 {
70 /*
71 * Not sure how important to support multi variables
72 * Let us handle the single variable case only right now if the conservative capability is on
73 */
74 if (_to_var_names.size() != 1)
75 paramError("variable",
76 " Support single variable only when the conservative capability is on ");
77
78 if (_current_direction == TO_MULTIAPP)
79 {
80 if (_from_postprocessors_to_be_preserved.size() != getToMultiApp()->numGlobalApps() &&
81 _from_postprocessors_to_be_preserved.size() != 1)
82 paramError("from_postprocessors_to_be_preserved",
83 "Number of from-postprocessors should equal to the number of subapps, or use "
84 "NearestPointIntegralVariablePostprocessor");
85 if (_to_postprocessors_to_be_preserved.size() != 1)
86 paramError("to_postprocessors_to_be_preserved",
87 "Number of to-postprocessors should equal to 1");
88 }
89 else if (_current_direction == FROM_MULTIAPP)
90 {
91 if (_from_postprocessors_to_be_preserved.size() != 1)
92 paramError("from_postprocessors_to_be_preserved",
93 "Number of from Postprocessors should equal to 1");
94
95 if (_to_postprocessors_to_be_preserved.size() != getFromMultiApp()->numGlobalApps() &&
96 _to_postprocessors_to_be_preserved.size() != 1)
97 paramError("to_postprocessors_to_be_preserved",
98 "_to_postprocessors_to_be_preserved",
99 "Number of to Postprocessors should equal to the number of subapps, or use "
100 "NearestPointIntegralVariablePostprocessor ");
101 }
102 }
103
104 /* Have to specify at least one to-variable */
105 if (_to_var_names.size() == 0)
106 paramError("variable", "You need to specify at least one variable");
107
108 /* Right now, most of transfers support one variable only */
109 if (_to_var_names.size() == 1)
110 _to_var_name = _to_var_names[0];
111
112 if (_from_var_names.size() == 1)
113 _from_var_name = _from_var_names[0];
114}
115
116void
118{
121 {
123 {
124 FEProblemBase & from_problem = getToMultiApp()->problemBase();
125 auto * pps = dynamic_cast<const NearestPointIntegralVariablePostprocessor *>(
127 if (pps)
129 else
130 {
131 _use_nearestpoint_pps = false;
132 if (getToMultiApp()->numGlobalApps() > 1)
134 " You have to specify ",
135 getToMultiApp()->numGlobalApps(),
136 " regular from-postprocessors, or use NearestPointIntegralVariablePostprocessor ");
137 }
138 }
139
141 {
142 FEProblemBase & to_problem = getFromMultiApp()->problemBase();
143 auto * pps = dynamic_cast<const NearestPointIntegralVariablePostprocessor *>(
145 if (pps)
147 else
148 {
149 _use_nearestpoint_pps = false;
150 if (getFromMultiApp()->numGlobalApps() > 1)
152 " You have to specify ",
153 getFromMultiApp()->numGlobalApps(),
154 " regular to-postprocessors, or use NearestPointIntegralVariablePostprocessor ");
155 }
156 }
157
158 const auto multi_app = hasFromMultiApp() ? getFromMultiApp() : getToMultiApp();
159
160 // Let us check execute_on here. Users need to specify execute_on='transfer' in their input
161 // files for the postprocessors that are used to compute the quantities to conserve in the
162 // Parent app
163 FEProblemBase & parent_problem = multi_app->problemBase();
164 std::vector<PostprocessorName> pps_empty;
165 // PPs for parent app
166 auto & parent_app_pps =
168 for (auto & pp : parent_app_pps)
169 {
170 // Get out all execute_on options for parent app source pp
171 auto & execute_on = parent_problem.getUserObjectBase(pp).getExecuteOnEnum();
172 const auto & type = parent_problem.getUserObjectBase(pp).type();
173 // Check if parent app has transfer execute_on
174 if (!execute_on.isValueSet(EXEC_TRANSFER))
176 "execute_on='transfer' is required in the conservative transfer for " + type + " '",
177 pp,
178 "' computed in the parent application.\n"
179 "Please add execute_on='transfer' to this postprocessor in the input file.\n"
180 "For a custom postprocessor, make sure that execute_on options are not hardcoded.");
181 }
182
183 // Sub apps
184 for (unsigned int i = 0; i < multi_app->numGlobalApps(); i++)
185 {
186 // If we do not have this app, we skip
187 if (!multi_app->hasLocalApp(i))
188 continue;
189 // Sub problem for
190 FEProblemBase & sub_problem = multi_app->appProblemBase(i);
191 // PPs for this subapp
192 auto & sub_pps =
194 for (auto & sub_pp : sub_pps)
195 {
196 // Get out of all execute_on options for sub pp
197 auto & execute_on = sub_problem.getUserObjectBase(sub_pp).getExecuteOnEnum();
198 const auto & type = sub_problem.getUserObjectBase(sub_pp).type();
199 // Check if sub pp has transfer execute_on
200 if (!execute_on.isValueSet(EXEC_TRANSFER))
202 "execute_on='transfer' is required in the conservative transfer for " + type + " '",
203 sub_pp,
204 "' in child application '" + multi_app->name() +
205 "'. \n"
206 "Please add execute_on='transfer' to this postprocessor in the input file.\n"
207 "For a custom postprocessor, make sure that execute_on options are not "
208 "hardcoded.");
209 }
210 }
211 }
212}
213
214void
216{
218 {
219 TIME_SECTION("MultiAppConservativeTransfer::execute()",
220 5,
221 "Post transfer to preserve postprocessor values");
222
224 {
225 FEProblemBase & from_problem = getToMultiApp()->problemBase();
227 from_problem.computeUserObjectByName(
229
230 for (unsigned int i = 0; i < getToMultiApp()->numGlobalApps(); i++)
231 if (getToMultiApp()->hasLocalApp(i))
232 {
235 &from_problem,
237 getToMultiApp()->appProblemBase(i),
239 else
240 adjustTransferredSolution(&from_problem,
242 getToMultiApp()->appProblemBase(i),
244 }
245 }
246
248 {
249 FEProblemBase & to_problem = getFromMultiApp()->problemBase();
251 to_problem.computeUserObjectByName(
253
254 for (unsigned int i = 0; i < getFromMultiApp()->numGlobalApps(); i++)
255 {
258 i,
259 getFromMultiApp()->hasLocalApp(i) ? &getFromMultiApp()->appProblemBase(i) : nullptr,
261 to_problem,
263 else
265 getFromMultiApp()->hasLocalApp(i) ? &getFromMultiApp()->appProblemBase(i) : nullptr,
267 to_problem,
269 }
270
271 // Compute the to-postprocessor again so that it has the right value with the updated solution
273 to_problem.computeUserObjectByName(
275 }
276 }
277}
278
279void
281 unsigned int i,
282 FEProblemBase * from_problem,
283 PostprocessorName & from_postprocessor,
284 FEProblemBase & to_problem,
285 PostprocessorName & to_postprocessor)
286{
287 PostprocessorValue from_adjuster = 0;
288 if (from_problem && _current_direction == FROM_MULTIAPP)
289 from_adjuster = from_problem->getPostprocessorValueByName(from_postprocessor);
290 else
291 from_adjuster = 0;
292
293 /* Everyone on the parent application side should know this value; use it to scale the solution */
295 {
296 /* In this case, only one subapp has value, and other subapps' must be zero.
297 * We should see the maximum value.
298 */
299 PostprocessorValue from_adjuster_tmp = from_adjuster;
300 comm().max(from_adjuster);
301
302 /* We may have a negative value */
303 if (MooseUtils::absoluteFuzzyLessEqual(from_adjuster, 0.))
304 {
305 comm().min(from_adjuster_tmp);
306 from_adjuster = from_adjuster_tmp;
307 }
308 }
309
310 PostprocessorValue to_adjuster = 0;
311 // Compute to-postprocessor to have the adjuster
313 {
314 to_problem.computeUserObjectByName(EXEC_TRANSFER, Moose::POST_AUX, to_postprocessor);
315 to_adjuster = to_problem.getPostprocessorValueByName(to_postprocessor);
316 }
317
318 auto & to_var = to_problem.getVariable(
320 auto & to_sys = to_var.sys().system();
321 auto var_num = to_sys.variable_number(_to_var_name);
322 auto sys_num = to_sys.number();
323 auto & pps = static_cast<const NearestPointIntegralVariablePostprocessor &>(
324 _current_direction == FROM_MULTIAPP ? (to_problem.getUserObjectBase(to_postprocessor))
325 : (from_problem->getUserObjectBase(from_postprocessor)));
326 auto & to_solution = to_var.sys().solution();
327 auto & to_mesh = to_problem.mesh().getMesh();
328 bool is_nodal = to_sys.variable_type(var_num).family == LAGRANGE;
329 if (is_nodal)
330 {
331 for (const auto & node : to_mesh.local_node_ptr_range())
332 {
333 // Skip this node if the variable has no dofs at it.
334 if (node->n_dofs(sys_num, var_num) < 1)
335 continue;
336
337 Real scale = 1;
339 {
340 auto ii = pps.nearestPointIndex(*node);
341 if (ii != i || !performAdjustment(from_adjuster, pps.userObjectValue(i)))
342 continue;
343
344 scale = from_adjuster / pps.userObjectValue(i);
345 }
346 else
347 {
348 if (!performAdjustment(pps.userObjectValue(i), to_adjuster))
349 continue;
350
351 scale = pps.userObjectValue(i) / to_adjuster;
352 }
353
354 /* Need to scale this node */
355 dof_id_type dof = node->dof_number(sys_num, var_num, 0);
356 to_solution.set(dof, scale * to_solution(dof));
357 }
358 }
359 else
360 {
361 for (auto & elem : as_range(to_mesh.local_elements_begin(), to_mesh.local_elements_end()))
362 {
363 // Skip this element if the variable has no dofs at it.
364 if (elem->n_dofs(sys_num, var_num) < 1)
365 continue;
366
367 Real scale = 1;
369 {
370 unsigned int ii = pps.nearestPointIndex(elem->vertex_average());
371 if (ii != i || !performAdjustment(from_adjuster, pps.userObjectValue(i)))
372 continue;
373
374 scale = from_adjuster / pps.userObjectValue(i);
375 }
376 else
377 {
378 if (!performAdjustment(pps.userObjectValue(i), to_adjuster))
379 continue;
380
381 scale = pps.userObjectValue(i) / to_adjuster;
382 }
383
384 dof_id_type dof = elem->dof_number(sys_num, var_num, 0);
385 to_solution.set(dof, scale * to_solution(dof));
386 }
387 }
388
389 to_solution.close();
390 to_sys.update();
391
392 // Compute the to-postprocessor again so that it has the right value with the updated solution
394 to_problem.computeUserObjectByName(EXEC_TRANSFER, Moose::POST_AUX, to_postprocessor);
395}
396
397void
399 PostprocessorName & from_postprocessor,
400 FEProblemBase & to_problem,
401 PostprocessorName & to_postprocessor)
402{
403 PostprocessorValue from_adjuster = 0;
404 if (from_problem)
405 from_adjuster = from_problem->getPostprocessorValueByName(from_postprocessor);
406 else
407 from_adjuster = 0;
408
409 /* Everyone on the parent side should know this value; use it to scale the solution */
411 {
412 /* In this case, only one subapp has value, and other subapps' must be zero.
413 * We should see the maximum value.
414 */
415 PostprocessorValue from_adjuster_tmp = from_adjuster;
416 comm().max(from_adjuster);
417
418 /* We may have a negative value, and let us try it again */
419 if (MooseUtils::absoluteFuzzyLessEqual(from_adjuster, 0.))
420 {
421 comm().min(from_adjuster_tmp);
422 from_adjuster = from_adjuster_tmp;
423 }
424 }
425
426 // Compute to-postprocessor to have the adjuster
427 to_problem.computeUserObjectByName(EXEC_TRANSFER, Moose::POST_AUX, to_postprocessor);
428
429 // Now we should have the right adjuster based on the transferred solution
430 const auto to_adjuster = to_problem.getPostprocessorValueByName(to_postprocessor);
431
432 // decide if the adjustment should be performed
433 if (!performAdjustment(from_adjuster, to_adjuster))
434 return;
435
436 auto & to_var = to_problem.getVariable(
438 auto & to_sys = to_var.sys().system();
439 auto var_num = to_sys.variable_number(_to_var_name);
440 auto sys_num = to_sys.number();
441 auto * pps =
442 dynamic_cast<const BlockRestrictable *>(&(to_problem.getUserObjectBase(to_postprocessor)));
443 auto & to_solution = to_var.sys().solution();
444 auto & to_mesh = to_problem.mesh().getMesh();
445 auto & moose_mesh = to_problem.mesh();
446 bool is_nodal = to_sys.variable_type(var_num).family == LAGRANGE;
447 if (is_nodal)
448 {
449 for (const auto & node : to_mesh.local_node_ptr_range())
450 {
451 // Skip this node if the variable has no dofs at it.
452 if (node->n_dofs(sys_num, var_num) < 1)
453 continue;
454
455 bool scale_current_node = false;
456 /* If we care about block IDs */
457 if (pps)
458 {
459 auto & blockids = pps->blockIDs();
460 auto & node_to_elem_map = moose_mesh.nodeToElemMap();
461 auto neighbor_elements = node_to_elem_map.find(node->id());
462 for (auto element : neighbor_elements->second)
463 {
464 auto & elem = to_mesh.elem_ref(element);
465 if (blockids.find(elem.subdomain_id()) != blockids.end())
466 {
467 scale_current_node = true;
468 break;
469 }
470 }
471 }
472 else
473 {
474 scale_current_node = true;
475 }
476 /* Need to scale this node */
477 if (scale_current_node)
478 {
479 dof_id_type dof = node->dof_number(sys_num, var_num, 0);
480 to_solution.set(dof, (from_adjuster / to_adjuster) * to_solution(dof));
481 }
482 }
483 }
484 else
485 {
486 for (auto & elem : as_range(to_mesh.local_elements_begin(), to_mesh.local_elements_end()))
487 {
488 // Skip this element if the variable has no dofs at it.
489 if (elem->n_dofs(sys_num, var_num) < 1)
490 continue;
491
492 bool scale_current_element = false;
493 if (pps)
494 {
495 auto & blockids = pps->blockIDs();
496 if (blockids.find(elem->subdomain_id()) != blockids.end())
497 {
498 scale_current_element = true;
499 }
500 }
501 else
502 {
503 scale_current_element = true;
504 }
505 if (scale_current_element)
506 {
507 unsigned int n_comp = elem->n_comp(sys_num, var_num);
508
509 for (unsigned int offset = 0; offset < n_comp; offset++)
510 {
511 dof_id_type dof = elem->dof_number(sys_num, var_num, offset);
512 to_solution.set(dof, (from_adjuster / to_adjuster) * to_solution(dof));
513 }
514 }
515 }
516 }
517
518 to_solution.close();
519 to_sys.update();
520
521 // Compute again so that the post-processor has the value with the updated solution
522 to_problem.computeUserObjectByName(EXEC_TRANSFER, Moose::POST_AUX, to_postprocessor);
523}
524
525bool
527 const PostprocessorValue & to) const
528{
529 if (from * to > 0)
530 return true;
532 return false;
533 else
534 mooseError("Adjustment postprocessors from: ",
535 from,
536 " to: ",
537 to,
538 " must both have the same sign and be different from 0");
539}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
Real PostprocessorValue
various MOOSE typedefs
Definition MooseTypes.h:230
const ExecFlagType EXEC_TRANSFER
Definition Moose.C:57
Real scale
Definition MortarUtils.C:62
An interface that restricts an object to subdomains via the 'blocks' input parameter.
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
const PostprocessorValue & getPostprocessorValueByName(const PostprocessorName &name, std::size_t t_index=0) const
Get a read-only reference to the value associated with a Postprocessor that exists.
virtual void computeUserObjectByName(const ExecFlagType &type, const Moose::AuxGroup &group, const std::string &name)
Compute an user object with the given name.
virtual const MooseVariableFieldBase & getVariable(const THREAD_ID tid, const std::string &var_name, Moose::VarKindType expected_var_type=Moose::VarKindType::VAR_ANY, Moose::VarFieldType expected_var_field_type=Moose::VarFieldType::VAR_FIELD_ANY) const override
Returns the variable reference for requested variable which must be of the expected_var_type (Nonline...
virtual MooseMesh & mesh() override
const UserObject & getUserObjectBase(const std::string &name, const THREAD_ID tid=0) const
Get the user object by its name.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
const std::string & type() const
Get the type of this class.
Definition MooseBase.h:93
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition MooseMesh.C:3549
SystemBase & sys()
Get the system this variable is part of.
bool performAdjustment(const PostprocessorValue &from, const PostprocessorValue &to) const
std::vector< PostprocessorName > _to_postprocessors_to_be_preserved
Postprocessor evaluates an adjuster for the target physics.
MultiAppConservativeTransfer(const InputParameters &parameters)
bool _use_nearestpoint_pps
Whether to use a nearest point UserObject to obtain the conservation factor.
std::vector< PostprocessorName > _from_postprocessors_to_be_preserved
Postprocessor evaluates an adjuster for the source physics.
virtual void postExecute()
Add some extra work if necessary after execute().
bool _allow_skipped_adjustment
Whether the adjustment may be skipped when the postprocessor values are 0 / of different signs.
bool _preserve_transfer
If this transfer is going to conserve the physics.
virtual void initialSetup() override
Method called at the beginning of the simulation for checking integrity or doing one-time setup.
void adjustTransferredSolution(FEProblemBase *from_problem, PostprocessorName &from_postprocessor, FEProblemBase &to_problem, PostprocessorName &to_postprocessor)
void adjustTransferredSolutionNearestPoint(unsigned int i, FEProblemBase *from_problem, PostprocessorName &from_postprocessor, FEProblemBase &to_problem, PostprocessorName &to_postprocessor)
Intermediary class that allows variable names as inputs.
virtual void initialSetup()
Method called at the beginning of the simulation for checking integrity or doing one-time setup.
static InputParameters validParams()
const std::shared_ptr< MultiApp > getToMultiApp() const
Get the MultiApp to transfer data to.
const std::shared_ptr< MultiApp > getFromMultiApp() const
Get the MultiApp to transfer data from.
bool hasFromMultiApp() const
Whether the transfer owns a non-null from_multi_app.
Given a list of points this object computes the variable integral closest to each one of those points...
const ExecFlagEnum & getExecuteOnEnum() const
Return the execute on MultiMooseEnum for this object.
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
void max(const T &r, T &o, Request &req) const
void min(const T &r, T &o, Request &req) const
@ FROM_MULTIAPP
Definition Transfer.h:71
@ TO_MULTIAPP
Definition Transfer.h:70
MooseEnum _current_direction
Definition Transfer.h:106
const Parallel::Communicator & comm() const
unsigned int variable_number(std::string_view var) const
@ VAR_FIELD_STANDARD
Definition MooseTypes.h:777
@ POST_AUX
Definition MooseTypes.h:761
@ VAR_ANY
Definition MooseTypes.h:772