Loading [MathJax]/extensions/tex2jax.js
https://mooseframework.inl.gov
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends
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 
116 void
118 {
120  if (_preserve_transfer)
121  {
123  {
124  FEProblemBase & from_problem = getToMultiApp()->problemBase();
125  auto * pps = dynamic_cast<const NearestPointIntegralVariablePostprocessor *>(
127  if (pps)
128  _use_nearestpoint_pps = true;
129  else
130  {
131  _use_nearestpoint_pps = false;
132  if (getToMultiApp()->numGlobalApps() > 1)
133  mooseError(
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)
146  _use_nearestpoint_pps = true;
147  else
148  {
149  _use_nearestpoint_pps = false;
150  if (getFromMultiApp()->numGlobalApps() > 1)
151  mooseError(
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))
175  mooseError(
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))
201  mooseError(
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 
214 void
216 {
217  if (_preserve_transfer)
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 
247  else if (_current_direction == FROM_MULTIAPP)
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 
279 void
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 
397 void
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 
525 bool
527  const PostprocessorValue & to) const
528 {
529  if (from * to > 0)
530  return true;
531  else if (_allow_skipped_adjustment)
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 }
LAGRANGE
const ExecFlagType EXEC_TRANSFER
Definition: Moose.C:48
const std::shared_ptr< MultiApp > getFromMultiApp() const
Get the MultiApp to transfer data from.
MooseEnum _current_direction
Definition: Transfer.h:106
bool _use_nearestpoint_pps
Whether to use a nearest point UserObject to obtain the conservation factor.
void scale(MeshBase &mesh, const Real xs, const Real ys=0., const Real zs=0.)
Given a list of points this object computes the variable integral closest to each one of those points...
virtual void initialSetup()
Method called at the beginning of the simulation for checking integrity or doing one-time setup...
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
void adjustTransferredSolutionNearestPoint(unsigned int i, FEProblemBase *from_problem, PostprocessorName &from_postprocessor, FEProblemBase &to_problem, PostprocessorName &to_postprocessor)
bool _allow_skipped_adjustment
Whether the adjustment may be skipped when the postprocessor values are 0 / of different signs...
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
const Parallel::Communicator & comm() const
virtual void postExecute()
Add some extra work if necessary after execute().
const std::shared_ptr< MultiApp > getToMultiApp() const
Get the MultiApp to transfer data to.
bool hasFromMultiApp() const
Whether the transfer owns a non-null from_multi_app.
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
void adjustTransferredSolution(FEProblemBase *from_problem, PostprocessorName &from_postprocessor, FEProblemBase &to_problem, PostprocessorName &to_postprocessor)
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...
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...
unsigned int variable_number(std::string_view var) const
static InputParameters validParams()
virtual void initialSetup() override
Method called at the beginning of the simulation for checking integrity or doing one-time setup...
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3417
void min(const T &r, T &o, Request &req) const
MultiAppConservativeTransfer(const InputParameters &parameters)
Intermediary class that allows variable names as inputs.
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Real PostprocessorValue
various MOOSE typedefs
Definition: MooseTypes.h:198
const ExecFlagEnum & getExecuteOnEnum() const
Return the execute on MultiMooseEnum for this object.
const std::string & type() const
Get the type of this class.
Definition: MooseBase.h:51
bool performAdjustment(const PostprocessorValue &from, const PostprocessorValue &to) const
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.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< PostprocessorName > _to_postprocessors_to_be_preserved
Postprocessor evaluates an adjuster for the target physics.
void max(const T &r, T &o, Request &req) const
bool absoluteFuzzyLessEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether a variable is less than or equal to another variable within an absolute tol...
Definition: MooseUtils.h:444
An interface that restricts an object to subdomains via the &#39;blocks&#39; input parameter.
virtual MooseMesh & mesh() override
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
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...
const UserObject & getUserObjectBase(const std::string &name, const THREAD_ID tid=0) const
Get the user object by its name.
virtual void computeUserObjectByName(const ExecFlagType &type, const Moose::AuxGroup &group, const std::string &name)
Compute an user object with the given name.
std::vector< PostprocessorName > _from_postprocessors_to_be_preserved
Postprocessor evaluates an adjuster for the source physics.
SystemBase & sys()
Get the system this variable is part of.
uint8_t dof_id_type
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...
bool _preserve_transfer
If this transfer is going to conserve the physics.