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  // Use mesh function to compute interpolation values
292  vals_ids_for_incoming_points[i_pt].first = (local_meshfuns[i_from])(
293  getPointInSourceAppFrame(pt, i_from, "Shape evaluation transfer"));
294  // Record problem ID as well
295  switch (_current_direction)
296  {
297  case FROM_MULTIAPP:
298  vals_ids_for_incoming_points[i_pt].second = _from_local2global_map[i_from];
299  break;
300  case TO_MULTIAPP:
301  vals_ids_for_incoming_points[i_pt].second = _to_local2global_map[i_from];
302  break;
303  default:
304  mooseError("Unsupported direction");
305  }
306  }
307  }
308  }
309  };
310 
311  // Incoming values and APP ids for outgoing points
312  std::map<processor_id_type, std::vector<std::pair<Real, unsigned int>>> incoming_vals_ids;
313  // Copy data out to incoming_vals_ids
314  auto action_functor =
315  [&incoming_vals_ids](
316  processor_id_type pid,
317  const std::vector<Point> & /*my_outgoing_points*/,
318  const std::vector<std::pair<Real, unsigned int>> & vals_ids_for_outgoing_points)
319  {
320  // This lambda function might be called multiple times
321  incoming_vals_ids[pid].reserve(vals_ids_for_outgoing_points.size());
322  // Copy data for processor 'pid'
323  std::copy(vals_ids_for_outgoing_points.begin(),
324  vals_ids_for_outgoing_points.end(),
325  std::back_inserter(incoming_vals_ids[pid]));
326  };
327 
328  // We assume incoming_vals_ids is ordered in the same way as outgoing_points
329  // Hopefully, pull_parallel_vector_data will not mess up this
330  const std::pair<Real, unsigned int> * ex = nullptr;
331  libMesh::Parallel::pull_parallel_vector_data(
332  comm(), outgoing_points, gather_functor, action_functor, ex);
333 
334  for (unsigned int i_to = 0; i_to < _to_problems.size(); ++i_to)
335  {
336  const auto to_global_num = _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
337  System * to_sys = find_sys(*_to_es[i_to], _to_var_names[i]);
338 
339  unsigned int sys_num = to_sys->number();
340  unsigned int var_num = to_sys->variable_number(_to_var_names[i]);
341 
342  NumericVector<Real> * solution = nullptr;
343  switch (_current_direction)
344  {
345  case TO_MULTIAPP:
346  solution = &getTransferVector(i_to, _to_var_names[i]);
347  break;
348  case FROM_MULTIAPP:
349  solution = to_sys->solution.get();
350  break;
351  default:
352  mooseError("Unknown direction");
353  }
354 
355  MeshBase * to_mesh = &_to_meshes[i_to]->getMesh();
356  auto & fe_type = to_sys->variable_type(var_num);
357  bool is_constant = fe_type.order == CONSTANT;
358  bool is_nodal = fe_type.family == LAGRANGE;
359 
360  if (is_nodal)
361  {
362  for (const auto & node : to_mesh->local_node_ptr_range())
363  {
364  // Skip this node if the variable has no dofs at it.
365  if (node->n_dofs(sys_num, var_num) < 1)
366  continue;
367 
368  unsigned int lowest_app_rank = libMesh::invalid_uint;
369  Real best_val = 0.;
370  bool point_found = false;
371  for (auto & group : incoming_vals_ids)
372  {
373  // Skip this proc if the node wasn't in it's bounding boxes.
374  std::pair<unsigned int, dof_id_type> key(i_to, node->id());
375  // Make sure point_index_map has data for corresponding pid
376  mooseAssert(point_index_map.find(group.first) != point_index_map.end(),
377  "Point index map does not have data for processor group.first");
378  if (point_index_map[group.first].find(key) == point_index_map[group.first].end())
379  continue;
380 
381  auto i_pt = point_index_map[group.first][key];
382 
383  // Ignore this proc if it's app has a higher rank than the
384  // previously found lowest app rank.
386  {
387  if (group.second[i_pt].second >= lowest_app_rank)
388  continue;
389  }
390 
391  // Ignore this proc if the point was actually outside its meshes.
392  if (group.second[i_pt].first == OutOfMeshValue)
393  continue;
394 
395  best_val = group.second[i_pt].first;
396  point_found = true;
397  }
398 
399  if (_error_on_miss && !point_found)
400  mooseError("Point not found in the reference space! ",
401  (*_to_transforms[to_global_num])(*node));
402 
403  dof_id_type dof = node->dof_number(sys_num, var_num, 0);
404  solution->set(dof, best_val);
405  }
406  }
407  else // Elemental
408  {
409  std::vector<Point> points;
410  std::vector<dof_id_type> point_ids;
411  for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
412  {
413  // Skip this element if the variable has no dofs at it.
414  if (elem->n_dofs(sys_num, var_num) < 1)
415  continue;
416 
417  points.clear();
418  point_ids.clear();
419  // grab sample points
420  // for constant shape function, we take the element centroid
421  if (is_constant)
422  {
423  points.push_back(elem->vertex_average());
424  point_ids.push_back(elem->id());
425  }
426  // for higher order method, we take all nodes of element
427  // this works for the first order L2 Lagrange. Might not work
428  // with something higher than the first order
429  else
430  {
431  for (auto & node : elem->node_ref_range())
432  {
433  points.push_back(node);
434  point_ids.push_back(node.id());
435  }
436  }
437 
438  auto n_points = points.size();
439  unsigned int n_comp = elem->n_comp(sys_num, var_num);
440  // We assume each point corresponds to one component of elemental variable
441  if (n_points != n_comp)
442  mooseError(" Number of points ",
443  n_points,
444  " does not equal to number of variable components ",
445  n_comp);
446  for (unsigned int offset = 0; offset < n_points; offset++)
447  {
448  unsigned int lowest_app_rank = libMesh::invalid_uint;
449  Real best_val = 0;
450  bool point_found = false;
451  for (auto & group : incoming_vals_ids)
452  {
453  // Skip this proc if the elem wasn't in it's bounding boxes.
454  std::pair<unsigned int, dof_id_type> key(i_to, point_ids[offset]);
455  if (point_index_map[group.first].find(key) == point_index_map[group.first].end())
456  continue;
457 
458  unsigned int i_pt = point_index_map[group.first][key];
459 
460  // Ignore this proc if it's app has a higher rank than the
461  // previously found lowest app rank.
463  {
464  if (group.second[i_pt].second >= lowest_app_rank)
465  continue;
466  }
467 
468  // Ignore this proc if the point was actually outside its meshes.
469  if (group.second[i_pt].first == OutOfMeshValue)
470  continue;
471 
472  best_val = group.second[i_pt].first;
473  point_found = true;
474  }
475 
476  if (_error_on_miss && !point_found)
477  mooseError("Point not found in the reference space! ",
478  (*_to_transforms[to_global_num])(elem->vertex_average()));
479 
480  // Get the value for a dof
481  dof_id_type dof = elem->dof_number(sys_num, var_num, offset);
482  solution->set(dof, best_val);
483  } // point
484  } // element
485  }
486  solution->close();
487  to_sys->update();
488  }
489 }
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
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
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:467
MooseEnum _current_direction
Definition: Transfer.h:106
Point getPointInSourceAppFrame(const Point &p, unsigned int local_i_from, const std::string &phase) const
Get the source app point from a point in the reference frame.
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:103
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.
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.
void mooseDeprecated(Args &&... args) const
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)
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:281
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