https://mooseframework.inl.gov
MultiAppShapeEvaluationTransfer.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 
11 
12 // MOOSE includes
13 #include "DisplacedProblem.h"
14 #include "FEProblem.h"
15 #include "MooseMesh.h"
16 #include "MooseTypes.h"
17 #include "MooseVariableFE.h"
18 #include "MooseAppCoordTransform.h"
19 
20 #include "libmesh/meshfree_interpolation.h"
21 #include "libmesh/system.h"
22 #include "libmesh/mesh_function.h"
23 #include "libmesh/mesh_tools.h"
24 #include "libmesh/parallel_algebra.h" // for communicator send and receive stuff
25 
26 // TIMPI includes
27 #include "timpi/communicator.h"
28 #include "timpi/parallel_sync.h"
29 
30 using namespace libMesh;
31 
32 registerMooseObjectDeprecated("MooseApp", MultiAppShapeEvaluationTransfer, "12/31/2024 24:00");
34  MultiAppMeshFunctionTransfer,
35  "12/31/2023 24:00",
37 
40 {
42  params.addClassDescription(
43  "Transfers field data at the MultiApp position using solution the finite element function "
44  "from the main/parent application, via a 'libMesh::MeshFunction' object.");
45 
46  params.addParam<bool>(
47  "error_on_miss",
48  false,
49  "Whether or not to error in the case that a target point is not found in the source domain.");
51  return params;
52 }
53 
55  : MultiAppConservativeTransfer(parameters), _error_on_miss(getParam<bool>("error_on_miss"))
56 {
57  mooseDeprecated("MultiAppShapeEvaluationTransfer is deprecated. Use "
58  "MultiAppGeneralFieldShapeEvaluationTransfer instead and adapt the parameters");
59 
60  if (_to_var_names.size() == _from_var_names.size())
61  _var_size = _to_var_names.size();
62  else
63  paramError("variable", "The number of variables to transfer to and from should be equal");
64 }
65 
66 void
68 {
69  TIME_SECTION("MultiAppShapeEvaluationTransfer::execute()",
70  5,
71  "Transferring variables via finite element interpolation");
72 
73  // loop over the vector of variables and make the transfer one by one
74  for (unsigned int i = 0; i < _var_size; ++i)
76 
77  postExecute();
78 }
79 
80 void
82 {
83  mooseAssert(i < _var_size, "The variable of index " << i << " does not exist");
84 
93  // Get the bounding boxes for the "from" domains.
94  std::vector<BoundingBox> bboxes = getFromBoundingBoxes();
95 
96  // Figure out how many "from" domains each processor owns.
97  std::vector<unsigned int> froms_per_proc = getFromsPerProc();
98 
99  // Point locations needed to send to from-domain
100  // processor to points
101  std::map<processor_id_type, std::vector<Point>> outgoing_points;
102  // <processor, <system_id, node_i>> --> point_id
103  std::map<processor_id_type, std::map<std::pair<unsigned int, dof_id_type>, dof_id_type>>
104  point_index_map;
105 
106  for (unsigned int i_to = 0; i_to < _to_problems.size(); ++i_to)
107  {
108  System * to_sys = find_sys(*_to_es[i_to], _to_var_names[i]);
109  unsigned int sys_num = to_sys->number();
110  unsigned int var_num = to_sys->variable_number(_to_var_names[i]);
111  MeshBase * to_mesh = &_to_meshes[i_to]->getMesh();
112  auto & fe_type = to_sys->variable_type(var_num);
113  bool is_constant = fe_type.order == CONSTANT;
114  bool is_nodal = fe_type.family == LAGRANGE;
115  const auto to_global_num = _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
116  const auto & to_transform = *_to_transforms[to_global_num];
117 
118  if (fe_type.order > FIRST && !is_nodal)
119  mooseError("We don't currently support second order or higher elemental variable.");
120 
121  if (is_nodal)
122  {
123  for (const auto & node : to_mesh->local_node_ptr_range())
124  {
125  // Skip this node if the variable has no dofs at it.
126  if (node->n_dofs(sys_num, var_num) < 1)
127  continue;
128 
129  // Loop over the "froms" on processor i_proc. If the node is found in
130  // any of the "froms", add that node to the vector that will be sent to
131  // i_proc.
132  unsigned int from0 = 0;
133  for (processor_id_type i_proc = 0; i_proc < n_processors();
134  from0 += froms_per_proc[i_proc], ++i_proc)
135  {
136  bool point_found = false;
137  for (unsigned int i_from = from0; i_from < from0 + froms_per_proc[i_proc] && !point_found;
138  ++i_from)
139  {
140  auto transformed_node = to_transform(*node);
141  if (bboxes[i_from].contains_point(transformed_node))
142  {
143  // <system id, node id>
144  std::pair<unsigned int, dof_id_type> key(i_to, node->id());
145  // map a tuple of pid, problem id and node id to point id
146  // point id is counted from zero
147  point_index_map[i_proc][key] = outgoing_points[i_proc].size();
148  // map pid to points
149  outgoing_points[i_proc].push_back(std::move(transformed_node));
150  point_found = true;
151  }
152  }
153  }
154  }
155  }
156  else // Elemental
157  {
158  std::vector<Point> points;
159  std::vector<dof_id_type> point_ids;
160  for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
161  {
162  // Skip this element if the variable has no dofs at it.
163  if (elem->n_dofs(sys_num, var_num) < 1)
164  continue;
165 
166  points.clear();
167  point_ids.clear();
168  // grab sample points
169  // for constant shape function, we take the element centroid
170  if (is_constant)
171  {
172  points.push_back(elem->vertex_average());
173  point_ids.push_back(elem->id());
174  }
175 
176  // for higher order method, we take all nodes of element
177  // this works for the first order L2 Lagrange.
178  else
179  for (auto & node : elem->node_ref_range())
180  {
181  points.push_back(node);
182  point_ids.push_back(node.id());
183  }
184 
185  unsigned int offset = 0;
186  for (auto & point : points)
187  {
188  // Loop over the "froms" on processor i_proc. If the elem is found in
189  // any of the "froms", add that elem to the vector that will be sent to
190  // i_proc.
191  unsigned int from0 = 0;
192  for (processor_id_type i_proc = 0; i_proc < n_processors();
193  from0 += froms_per_proc[i_proc], ++i_proc)
194  {
195  bool point_found = false;
196  for (unsigned int i_from = from0;
197  i_from < from0 + froms_per_proc[i_proc] && !point_found;
198  ++i_from)
199  {
200  auto transformed_point = to_transform(point);
201  if (bboxes[i_from].contains_point(transformed_point))
202  {
203  std::pair<unsigned int, dof_id_type> key(i_to, point_ids[offset]);
204  if (point_index_map[i_proc].find(key) != point_index_map[i_proc].end())
205  continue;
206 
207  point_index_map[i_proc][key] = outgoing_points[i_proc].size();
208  outgoing_points[i_proc].push_back(std::move(transformed_point));
209  point_found = true;
210  } // if
211  } // i_from
212  } // i_proc
213  offset++;
214  } // point
215 
216  } // else
217  }
218  }
219 
220  // Get the local bounding boxes for current processor.
221  // There could be more than one box because of the number of local apps
222  // can be larger than one
223  std::vector<BoundingBox> local_bboxes(froms_per_proc[processor_id()]);
224  {
225  // Find the index to the first of this processor's local bounding boxes.
226  unsigned int local_start = 0;
227  for (processor_id_type i_proc = 0; i_proc < n_processors() && i_proc != processor_id();
228  ++i_proc)
229  {
230  local_start += froms_per_proc[i_proc];
231  }
232 
233  // Extract the local bounding boxes.
234  for (unsigned int i_from = 0; i_from < froms_per_proc[processor_id()]; ++i_from)
235  {
236  local_bboxes[i_from] = bboxes[local_start + i_from];
237  }
238  }
239 
240  // Setup the local mesh functions.
241  std::vector<MeshFunction> local_meshfuns;
242  local_meshfuns.reserve(_from_problems.size());
243  for (unsigned int i_from = 0; i_from < _from_problems.size(); ++i_from)
244  {
245  FEProblemBase & from_problem = *_from_problems[i_from];
246  MooseVariableFEBase & from_var =
247  from_problem.getVariable(0,
248  _from_var_names[i],
251  System & from_sys = from_var.sys().system();
252  unsigned int from_var_num = from_sys.variable_number(from_var.name());
253 
254  local_meshfuns.emplace_back(getEquationSystem(from_problem, _displaced_source_mesh),
255  *from_sys.current_local_solution,
256  from_sys.get_dof_map(),
257  from_var_num);
258  local_meshfuns.back().init();
259  local_meshfuns.back().enable_out_of_mesh_mode(OutOfMeshValue);
260  }
261 
269  // Fill values and app ids for incoming points
270  // We are responsible to compute values for these incoming points
271  auto gather_functor =
272  [this, &local_meshfuns, &local_bboxes](
273  processor_id_type /*pid*/,
274  const std::vector<Point> & incoming_points,
275  std::vector<std::pair<Real, unsigned int>> & vals_ids_for_incoming_points)
276  {
277  vals_ids_for_incoming_points.resize(incoming_points.size(), std::make_pair(OutOfMeshValue, 0));
278  for (MooseIndex(incoming_points.size()) i_pt = 0; i_pt < incoming_points.size(); ++i_pt)
279  {
280  Point pt = incoming_points[i_pt];
281 
282  // Loop until we've found the lowest-ranked app that actually contains
283  // the quadrature point.
284  for (MooseIndex(_from_problems.size()) i_from = 0;
285  i_from < _from_problems.size() &&
286  vals_ids_for_incoming_points[i_pt].first == OutOfMeshValue;
287  ++i_from)
288  {
289  if (local_bboxes[i_from].contains_point(pt))
290  {
291  const auto from_global_num =
293  // Use mesh function to compute interpolation values
294  vals_ids_for_incoming_points[i_pt].first =
295  (local_meshfuns[i_from])(_from_transforms[from_global_num]->mapBack(pt));
296  // Record problem ID as well
297  switch (_current_direction)
298  {
299  case FROM_MULTIAPP:
300  vals_ids_for_incoming_points[i_pt].second = _from_local2global_map[i_from];
301  break;
302  case TO_MULTIAPP:
303  vals_ids_for_incoming_points[i_pt].second = _to_local2global_map[i_from];
304  break;
305  default:
306  mooseError("Unsupported direction");
307  }
308  }
309  }
310  }
311  };
312 
313  // Incoming values and APP ids for outgoing points
314  std::map<processor_id_type, std::vector<std::pair<Real, unsigned int>>> incoming_vals_ids;
315  // Copy data out to incoming_vals_ids
316  auto action_functor =
317  [&incoming_vals_ids](
318  processor_id_type pid,
319  const std::vector<Point> & /*my_outgoing_points*/,
320  const std::vector<std::pair<Real, unsigned int>> & vals_ids_for_outgoing_points)
321  {
322  // This lambda function might be called multiple times
323  incoming_vals_ids[pid].reserve(vals_ids_for_outgoing_points.size());
324  // Copy data for processor 'pid'
325  std::copy(vals_ids_for_outgoing_points.begin(),
326  vals_ids_for_outgoing_points.end(),
327  std::back_inserter(incoming_vals_ids[pid]));
328  };
329 
330  // We assume incoming_vals_ids is ordered in the same way as outgoing_points
331  // Hopefully, pull_parallel_vector_data will not mess up this
332  const std::pair<Real, unsigned int> * ex = nullptr;
333  libMesh::Parallel::pull_parallel_vector_data(
334  comm(), outgoing_points, gather_functor, action_functor, ex);
335 
336  for (unsigned int i_to = 0; i_to < _to_problems.size(); ++i_to)
337  {
338  const auto to_global_num = _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
339  System * to_sys = find_sys(*_to_es[i_to], _to_var_names[i]);
340 
341  unsigned int sys_num = to_sys->number();
342  unsigned int var_num = to_sys->variable_number(_to_var_names[i]);
343 
344  NumericVector<Real> * solution = nullptr;
345  switch (_current_direction)
346  {
347  case TO_MULTIAPP:
348  solution = &getTransferVector(i_to, _to_var_names[i]);
349  break;
350  case FROM_MULTIAPP:
351  solution = to_sys->solution.get();
352  break;
353  default:
354  mooseError("Unknown direction");
355  }
356 
357  MeshBase * to_mesh = &_to_meshes[i_to]->getMesh();
358  auto & fe_type = to_sys->variable_type(var_num);
359  bool is_constant = fe_type.order == CONSTANT;
360  bool is_nodal = fe_type.family == LAGRANGE;
361 
362  if (is_nodal)
363  {
364  for (const auto & node : to_mesh->local_node_ptr_range())
365  {
366  // Skip this node if the variable has no dofs at it.
367  if (node->n_dofs(sys_num, var_num) < 1)
368  continue;
369 
370  unsigned int lowest_app_rank = libMesh::invalid_uint;
371  Real best_val = 0.;
372  bool point_found = false;
373  for (auto & group : incoming_vals_ids)
374  {
375  // Skip this proc if the node wasn't in it's bounding boxes.
376  std::pair<unsigned int, dof_id_type> key(i_to, node->id());
377  // Make sure point_index_map has data for corresponding pid
378  mooseAssert(point_index_map.find(group.first) != point_index_map.end(),
379  "Point index map does not have data for processor group.first");
380  if (point_index_map[group.first].find(key) == point_index_map[group.first].end())
381  continue;
382 
383  auto i_pt = point_index_map[group.first][key];
384 
385  // Ignore this proc if it's app has a higher rank than the
386  // previously found lowest app rank.
388  {
389  if (group.second[i_pt].second >= lowest_app_rank)
390  continue;
391  }
392 
393  // Ignore this proc if the point was actually outside its meshes.
394  if (group.second[i_pt].first == OutOfMeshValue)
395  continue;
396 
397  best_val = group.second[i_pt].first;
398  point_found = true;
399  }
400 
401  if (_error_on_miss && !point_found)
402  mooseError("Point not found in the reference space! ",
403  (*_to_transforms[to_global_num])(*node));
404 
405  dof_id_type dof = node->dof_number(sys_num, var_num, 0);
406  solution->set(dof, best_val);
407  }
408  }
409  else // Elemental
410  {
411  std::vector<Point> points;
412  std::vector<dof_id_type> point_ids;
413  for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
414  {
415  // Skip this element if the variable has no dofs at it.
416  if (elem->n_dofs(sys_num, var_num) < 1)
417  continue;
418 
419  points.clear();
420  point_ids.clear();
421  // grab sample points
422  // for constant shape function, we take the element centroid
423  if (is_constant)
424  {
425  points.push_back(elem->vertex_average());
426  point_ids.push_back(elem->id());
427  }
428  // for higher order method, we take all nodes of element
429  // this works for the first order L2 Lagrange. Might not work
430  // with something higher than the first order
431  else
432  {
433  for (auto & node : elem->node_ref_range())
434  {
435  points.push_back(node);
436  point_ids.push_back(node.id());
437  }
438  }
439 
440  auto n_points = points.size();
441  unsigned int n_comp = elem->n_comp(sys_num, var_num);
442  // We assume each point corresponds to one component of elemental variable
443  if (n_points != n_comp)
444  mooseError(" Number of points ",
445  n_points,
446  " does not equal to number of variable components ",
447  n_comp);
448  for (unsigned int offset = 0; offset < n_points; offset++)
449  {
450  unsigned int lowest_app_rank = libMesh::invalid_uint;
451  Real best_val = 0;
452  bool point_found = false;
453  for (auto & group : incoming_vals_ids)
454  {
455  // Skip this proc if the elem wasn't in it's bounding boxes.
456  std::pair<unsigned int, dof_id_type> key(i_to, point_ids[offset]);
457  if (point_index_map[group.first].find(key) == point_index_map[group.first].end())
458  continue;
459 
460  unsigned int i_pt = point_index_map[group.first][key];
461 
462  // Ignore this proc if it's app has a higher rank than the
463  // previously found lowest app rank.
465  {
466  if (group.second[i_pt].second >= lowest_app_rank)
467  continue;
468  }
469 
470  // Ignore this proc if the point was actually outside its meshes.
471  if (group.second[i_pt].first == OutOfMeshValue)
472  continue;
473 
474  best_val = group.second[i_pt].first;
475  point_found = true;
476  }
477 
478  if (_error_on_miss && !point_found)
479  mooseError("Point not found in the reference space! ",
480  (*_to_transforms[to_global_num])(elem->vertex_average()));
481 
482  // Get the value for a dof
483  dof_id_type dof = elem->dof_number(sys_num, var_num, offset);
484  solution->set(dof, best_val);
485  } // point
486  } // element
487  }
488  solution->close();
489  to_sys->update();
490  }
491 }
libMesh::NumericVector< Real > & getTransferVector(unsigned int i_local, std::string var_name)
If we are transferring to a multiapp, return the appropriate solution vector.
LAGRANGE
std::vector< std::unique_ptr< MultiAppCoordTransform > > _to_transforms
std::vector< libMesh::EquationSystems * > _to_es
virtual void execute() override
Execute the transfer.
const unsigned int invalid_uint
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:435
MooseEnum _current_direction
Definition: Transfer.h:106
FIRST
MultiAppShapeEvaluationTransfer(const InputParameters &parameters)
registerMooseObjectDeprecated("MooseApp", MultiAppShapeEvaluationTransfer, "12/31/2024 24:00")
std::vector< libMesh::BoundingBox > getFromBoundingBoxes()
Return the bounding boxes of all the "from" domains, including all the domains not local to this proc...
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
const std::vector< VariableName > _from_var_names
Name of variables transferring from.
std::vector< FEProblemBase * > _to_problems
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().
OrderWrapper order
This class provides an interface for common operations on field variables of both FE and FV types wit...
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
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...
std::vector< unsigned int > _to_local2global_map
Given local app index, returns global app index.
unsigned int variable_number(std::string_view var) const
uint8_t processor_id_type
processor_id_type n_processors() const
CONSTANT
unsigned int number() const
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:99
libMesh::EquationSystems & getEquationSystem(FEProblemBase &problem, bool use_displaced) const
Returns the Problem&#39;s equation system, displaced or not Be careful! If you transfer TO a displaced sy...
bool _error_on_miss
Whether to error if the target point is not found in the source domain.
void mooseDeprecated(Args &&... args) const
Definition: MooseBase.h:310
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
const std::vector< AuxVariableName > _to_var_names
Name of variables transferring to.
static libMesh::System * find_sys(libMesh::EquationSystems &es, const std::string &var_name)
Small helper function for finding the system containing the variable.
Definition: Transfer.C:91
std::unique_ptr< NumericVector< Number > > solution
std::vector< unsigned int > getFromsPerProc()
Return the number of "from" domains that each processor owns.
Transfers variables on possibly different meshes while conserving a user defined property (Postproces...
static const libMesh::Number OutOfMeshValue
Definition: Transfer.h:113
virtual void close()=0
virtual void update()
Transfers a vector of variables.
const FEType & variable_type(const unsigned int i) const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void transferVariable(unsigned int i)
Performs the transfer for the variable of index i.
unsigned int _var_size
The number of variables to transfer.
registerMooseObjectRenamed("MooseApp", MultiAppMeshFunctionTransfer, "12/31/2023 24:00", MultiAppShapeEvaluationTransfer)
std::vector< std::unique_ptr< MultiAppCoordTransform > > _from_transforms
bool _displaced_source_mesh
True if displaced mesh is used for the source mesh, otherwise false.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:267
std::vector< unsigned int > _from_local2global_map
Given local app index, returns global app index.
std::unique_ptr< NumericVector< Number > > current_local_solution
static void addBBoxFactorParam(InputParameters &params)
Add the bounding box factor parameter to the supplied input parameters.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
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...
virtual void set(const numeric_index_type i, const T value)=0
processor_id_type processor_id() const
SystemBase & sys()
Get the system this variable is part of.
const DofMap & get_dof_map() const
std::vector< FEProblemBase * > _from_problems
std::vector< MooseMesh * > _to_meshes
uint8_t dof_id_type