https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MultiAppGeometricInterpolationTransfer.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 "MultiApp.h"
20
21#include "libmesh/parallel_algebra.h"
22#include "libmesh/meshfree_interpolation.h"
23#include "libmesh/system.h"
24#include "libmesh/radial_basis_interpolation.h"
25
28 MultiAppInterpolationTransfer,
29 "12/31/2023 24:00",
31
34{
37 "Transfers the value to the target domain from a combination/interpolation of the values on "
38 "the nearest nodes in the source domain, using coefficients based on the distance to each "
39 "node.");
40 params.addParam<unsigned int>(
41 "num_points", 3, "The number of nearest points to use for interpolation.");
42 params.addParam<Real>(
43 "power", 2, "The polynomial power to use for calculation of the decay in the interpolation.");
44
45 MooseEnum interp_type("inverse_distance radial_basis", "inverse_distance");
46 params.addParam<MooseEnum>("interp_type", interp_type, "The algorithm to use for interpolation.");
47
48 params.addParam<Real>("radius",
49 -1,
50 "Radius to use for radial_basis interpolation. If negative "
51 "then the radius is taken as the max distance between "
52 "points.");
53 params.addParam<Real>(
54 "shrink_gap_width",
55 0,
56 "gap width with which we want to temporarily shrink mesh in transfering solution");
57
58 MooseEnum shrink_type("SOURCE TARGET", "SOURCE");
59 params.addParam<MooseEnum>("shrink_mesh", shrink_type, "Which mesh we want to shrink");
60
61 params.addParam<std::vector<SubdomainName>>(
62 "exclude_gap_blocks",
63 {},
64 "Gap subdomains we want to exclude when constructing/using virtually translated points");
65
66 params.addParam<Real>("distance_tol",
67 1e-10,
68 "If the distance between two points is smaller than distance_tol, two "
69 "points will be considered as identical");
70
71 return params;
72}
73
75 const InputParameters & parameters)
76 : MultiAppConservativeTransfer(parameters),
77 _num_points(getParam<unsigned int>("num_points")),
78 _power(getParam<Real>("power")),
79 _interp_type(getParam<MooseEnum>("interp_type")),
80 _radius(getParam<Real>("radius")),
81 _shrink_gap_width(getParam<Real>("shrink_gap_width")),
82 _shrink_mesh(getParam<MooseEnum>("shrink_mesh")),
83 _exclude_gap_blocks(getParam<std::vector<SubdomainName>>("exclude_gap_blocks")),
84 _distance_tol(getParam<Real>("distance_tol"))
85{
86 // This transfer does not work with DistributedMesh
87 _fe_problem.mesh().errorIfDistributedMesh("MultiAppGeometricInterpolationTransfer");
88
89 if (_to_var_names.size() != 1)
90 paramError("variable", " Support single to-variable only ");
91
92 if (_from_var_names.size() != 1)
93 paramError("source_variable", " Support single from-variable only ");
94}
95
96void
98 const Node & node,
99 std::set<subdomain_id_type> & subdomainids)
100{
101 // We need this map to figure out to which subdomains a given mesh point is attached
102 // We can not make mesh const here because we may need to create a node-to-elems map
103 // if it does not exists
104 auto & node_to_elem = mesh.nodeToElemMap();
105 auto node_to_elem_pair = node_to_elem.find(node.id());
106
107 if (node_to_elem_pair == node_to_elem.end())
108 mooseError("Can not find elements for node ", node.id());
109
110 subdomainids.clear();
111 // Add all subdomain IDs that are attached to node
112 for (auto element : node_to_elem_pair->second)
113 {
114 auto & elem = mesh.getMesh().elem_ref(element);
115 auto subdomain = elem.subdomain_id();
116
117 subdomainids.insert(subdomain);
118 }
119}
120
121void
123 FEProblemBase & from_problem,
124 const MooseVariableFieldBase & from_var,
125 const MultiAppCoordTransform & from_app_transform,
127{
128 auto & from_moose_mesh = from_problem.mesh(_displaced_source_mesh);
129 const auto & from_mesh = from_moose_mesh.getMesh();
130
131 // Moose system
132 const SystemBase & from_system_base = from_var.sys();
133 // libmesh system
134 const System & from_sys = from_system_base.system();
135
136 // System number and var number
137 auto from_sys_num = from_sys.number();
138 auto from_var_num = from_sys.variable_number(from_var.name());
139
140 // Check FE type so we can figure out how to sample points
141 const auto & fe_type = from_sys.variable_type(from_var_num);
142 bool from_is_constant = fe_type.order == CONSTANT;
143 bool from_is_nodal = fe_type.family == LAGRANGE;
144
145 // Currently, for an elemental variable, we support the constant and first order
146 if (fe_type.order > FIRST && !from_is_nodal)
147 mooseError("We don't currently support second order or higher elemental variable ");
148
149 // Containers for points and values
150 // We later will push data into these containers
151 std::vector<Point> & src_pts(idi->get_source_points());
152 std::vector<Number> & src_vals(idi->get_source_vals());
153
154 // How much we want to translate mesh if users ask
155 std::unordered_map<dof_id_type, Point> from_tranforms;
156 std::set<subdomain_id_type> exclude_block_ids;
157 if (_shrink_gap_width > 0 && _shrink_mesh == "source")
158 {
159 computeTransformation(from_moose_mesh, from_tranforms);
160 auto exclude_subdomainids = from_moose_mesh.getSubdomainIDs(_exclude_gap_blocks);
161 exclude_block_ids.insert(exclude_subdomainids.begin(), exclude_subdomainids.end());
162 }
163
164 // The solution from the system with which the from_var is associated
165 const NumericVector<Number> & from_solution = *from_sys.solution;
166
167 std::set<subdomain_id_type> subdomainids;
168 std::vector<subdomain_id_type> include_block_ids;
169 if (from_is_nodal)
170 {
171 for (const auto * const from_node : from_mesh.local_node_ptr_range())
172 {
173 // Assuming LAGRANGE!
174 if (from_node->n_comp(from_sys_num, from_var_num) == 0)
175 continue;
176
177 Point translate(0);
178
179 if (from_tranforms.size() > 0)
180 {
181 subdomainIDsNode(const_cast<MooseMesh &>(from_moose_mesh), *from_node, subdomainids);
182 // Check if node is excluded
183 // Node will be excluded if it is in the interior of excluded subdomains
184 include_block_ids.clear();
185 include_block_ids.resize(std::max(subdomainids.size(), exclude_block_ids.size()));
186 auto it = std::set_difference(subdomainids.begin(),
187 subdomainids.end(),
188 exclude_block_ids.begin(),
189 exclude_block_ids.end(),
190 include_block_ids.begin());
191
192 include_block_ids.resize(it - include_block_ids.begin());
193 // Node is not excluded
194 if (include_block_ids.size())
195 translate = from_tranforms[*include_block_ids.begin()];
196 else
197 continue;
198 }
199
200 // Push value and point to KDTree
201 dof_id_type from_dof = from_node->dof_number(from_sys_num, from_var_num, 0);
202 src_vals.push_back(from_solution(from_dof));
203 src_pts.push_back(from_app_transform(*from_node) + translate);
204 }
205 }
206 else
207 {
208 std::vector<Point> points;
209 for (const auto * const from_elem :
210 as_range(from_mesh.local_elements_begin(), from_mesh.local_elements_end()))
211 {
212 // Skip this element if the variable has no dofs at it.
213 if (from_elem->n_dofs(from_sys_num, from_var_num) < 1)
214 continue;
215
216 points.clear();
217 if (from_is_constant)
218 points.push_back(from_elem->vertex_average());
219 else
220 for (const auto & node : from_elem->node_ref_range())
221 points.push_back(node);
222
223 unsigned int n_comp = from_elem->n_comp(from_sys_num, from_var_num);
224 auto n_points = points.size();
225 // We assume each point corresponds to one component of elemental variable
226 if (n_points != n_comp)
227 mooseError(" Number of points ",
228 n_points,
229 " does not equal to number of variable components ",
230 n_comp);
231
232 unsigned int offset = 0;
233
234 Point translate(0);
235
236 if (from_tranforms.size() > 0)
237 {
238 auto subdomain = from_elem->subdomain_id();
239
240 if (subdomain == Moose::INVALID_BLOCK_ID)
241 mooseError("subdomain id does not make sense", subdomain);
242
243 // subdomain is not excluded
244 if (exclude_block_ids.find(subdomain) == exclude_block_ids.end())
245 translate = from_tranforms[subdomain];
246 else
247 continue;
248 }
249
250 for (const auto & point : points)
251 {
252 dof_id_type from_dof = from_elem->dof_number(from_sys_num, from_var_num, offset++);
253 src_vals.push_back(from_solution(from_dof));
254 src_pts.push_back(from_app_transform(point) + translate);
255 }
256 }
257 }
258}
259
260void
262 FEProblemBase & to_problem,
263 MooseVariableFieldBase & to_var,
264 NumericVector<Real> & to_solution,
265 const MultiAppCoordTransform & to_app_transform,
266 const std::unique_ptr<libMesh::InverseDistanceInterpolation<LIBMESH_DIM>> & idi)
267{
268 // Moose system
269 SystemBase & to_system_base = to_var.sys();
270 // libmesh system
271 System & to_sys = to_system_base.system();
272
273 // System number and var number
274 auto to_sys_num = to_sys.number();
275 auto to_var_num = to_sys.variable_number(to_var.name());
276
277 const MooseMesh & to_moose_mesh = to_problem.mesh(_displaced_target_mesh);
278 const MeshBase & to_mesh = to_moose_mesh.getMesh();
279
280 // Compute transform info
281 std::unordered_map<dof_id_type, Point> to_tranforms;
282 std::set<subdomain_id_type> exclude_block_ids;
283 if (_shrink_gap_width > 0 && _shrink_mesh == "target")
284 {
285 computeTransformation(to_moose_mesh, to_tranforms);
286 auto exclude_subdomainids = to_moose_mesh.getSubdomainIDs(_exclude_gap_blocks);
287 exclude_block_ids.insert(exclude_subdomainids.begin(), exclude_subdomainids.end());
288 }
289
290 const auto & to_fe_type = to_sys.variable_type(to_var_num);
291 bool to_is_constant = to_fe_type.order == CONSTANT;
292 bool to_is_nodal = to_fe_type.family == LAGRANGE;
293
294 if (to_fe_type.order > FIRST && !to_is_nodal)
295 mooseError("We don't currently support second order or higher elemental variable ");
296
297 std::set<subdomain_id_type> subdomainids;
298 std::vector<subdomain_id_type> include_block_ids;
299 std::vector<Point> pts;
300 std::vector<Number> vals;
301 if (to_is_nodal)
302 {
303 for (const auto * const node : to_mesh.local_node_ptr_range())
304 {
305 if (node->n_dofs(to_sys_num, to_var_num) <= 0) // If this variable has dofs at this node
306 continue;
307
308 Point translate(0);
309 if (to_tranforms.size() > 0)
310 {
311 subdomainIDsNode(const_cast<MooseMesh &>(to_moose_mesh), *node, subdomainids);
312 // Check if node is excluded
313 // Node will be excluded if it is in the interior of excluded subdomains
314 include_block_ids.clear();
315 include_block_ids.resize(std::max(subdomainids.size(), exclude_block_ids.size()));
316 auto it = std::set_difference(subdomainids.begin(),
317 subdomainids.end(),
318 exclude_block_ids.begin(),
319 exclude_block_ids.end(),
320 include_block_ids.begin());
321 include_block_ids.resize(it - include_block_ids.begin());
322 if (include_block_ids.size())
323 translate = to_tranforms[*include_block_ids.begin()];
324 else
325 continue;
326 }
327
328 pts.clear();
329 pts.push_back(to_app_transform(*node) + translate);
330 vals.resize(1);
331
332 idi->interpolate_field_data({_to_var_name}, pts, vals);
333 dof_id_type dof = node->dof_number(to_sys_num, to_var_num, 0);
334 to_solution.set(dof, vals.front());
335 }
336 }
337 else // Elemental
338 {
339 std::vector<Point> points;
340 for (const auto * const elem :
341 as_range(to_mesh.local_elements_begin(), to_mesh.local_elements_end()))
342 {
343 // Skip this element if the variable has no dofs at it.
344 if (elem->n_dofs(to_sys_num, to_var_num) < 1)
345 continue;
346
347 points.clear();
348 if (to_is_constant)
349 points.push_back(elem->vertex_average());
350 else
351 for (const auto & node : elem->node_ref_range())
352 points.push_back(node);
353
354 auto n_points = points.size();
355 unsigned int n_comp = elem->n_comp(to_sys_num, to_var_num);
356 // We assume each point corresponds to one component of elemental variable
357 if (n_points != n_comp)
358 mooseError(" Number of points ",
359 n_points,
360 " does not equal to number of variable components ",
361 n_comp);
362
363 Point translate(0);
364
365 if (to_tranforms.size() > 0)
366 {
367 auto subdomain = elem->subdomain_id();
368
369 if (subdomain == Moose::INVALID_BLOCK_ID)
370 mooseError("subdomain id does not make sense", subdomain);
371
372 if (exclude_block_ids.find(subdomain) == exclude_block_ids.end())
373 translate = to_tranforms[subdomain];
374 else
375 continue;
376 }
377
378 unsigned int offset = 0;
379 for (const auto & point : points)
380 {
381 pts.clear();
382 pts.push_back(to_app_transform(point) + translate);
383 vals.resize(1);
384
385 idi->interpolate_field_data({_to_var_name}, pts, vals);
386 dof_id_type dof = elem->dof_number(to_sys_num, to_var_num, offset++);
387 to_solution.set(dof, vals.front());
388 } // point
389 } // auto elem
390 } // else
391
392 to_solution.close();
393 to_sys.update();
394}
395
396void
398{
399 TIME_SECTION("MultiAppGeometricInterpolationTransfer::execute()",
400 5,
401 "Transferring variables based on node interpolation");
402
403 const FEProblemBase & fe_problem =
404 hasFromMultiApp() ? getFromMultiApp()->problemBase() : getToMultiApp()->problemBase();
405 std::unique_ptr<libMesh::InverseDistanceInterpolation<LIBMESH_DIM>> idi;
406 switch (_interp_type)
407 {
408 case 0:
409 idi = std::make_unique<libMesh::InverseDistanceInterpolation<LIBMESH_DIM>>(
410 fe_problem.comm(), _num_points, _power);
411 break;
412 case 1:
413 idi = std::make_unique<libMesh::RadialBasisInterpolation<LIBMESH_DIM>>(fe_problem.comm(),
414 _radius);
415 break;
416 default:
417 mooseError("Unknown interpolation type!");
418 }
419
420 idi->set_field_variables({_to_var_name});
421
422 switch (_current_direction)
423 {
424 case TO_MULTIAPP:
425 {
426 FEProblemBase & from_problem = getToMultiApp()->problemBase();
427 const auto & from_var = from_problem.getVariable(
429
430 mooseAssert(_from_transforms.size() == 1, "This should be size 1");
431 fillSourceInterpolationPoints(from_problem, from_var, *_from_transforms[0], idi);
432
433 // We have only set local values - prepare for use by gathering remote gata
434 idi->prepare_for_use();
435
436 for (unsigned int i = 0; i < getToMultiApp()->numGlobalApps(); i++)
437 {
438 if (getToMultiApp()->hasLocalApp(i))
439 {
440 auto & to_problem = getToMultiApp()->appProblemBase(i);
441 Moose::ScopedCommSwapper swapper(to_problem.comm().get());
442 auto & to_var = to_problem.getVariable(0,
446
447 auto & to_solution = getToMultiApp()->appTransferVector(i, _to_var_name);
448
449 interpolateTargetPoints(to_problem, to_var, to_solution, *_to_transforms[i], idi);
450 }
451 }
452
453 break;
454 }
455
456 case FROM_MULTIAPP:
457 {
458 for (unsigned int i = 0; i < getFromMultiApp()->numGlobalApps(); i++)
459 {
460 if (getFromMultiApp()->hasLocalApp(i))
461 {
462 auto & from_problem = getFromMultiApp()->appProblemBase(i);
463 Moose::ScopedCommSwapper swapper(from_problem.comm().get());
464 const auto & from_var = from_problem.getVariable(0,
468
469 fillSourceInterpolationPoints(from_problem, from_var, *_from_transforms[i], idi);
470 }
471 }
472
473 idi->prepare_for_use();
474
475 FEProblemBase & to_problem = getFromMultiApp()->problemBase();
476 MooseVariableFieldBase & to_var = to_problem.getVariable(
478
479 auto & to_solution = *to_var.sys().system().solution;
480
481 mooseAssert(_to_transforms.size() == 1, "This should be size 1");
482 interpolateTargetPoints(to_problem, to_var, to_solution, *_to_transforms[0], idi);
483
484 break;
485 }
486 default:
487 {
488 mooseError("Unsupported transfer direction ", _current_direction);
489 break;
490 }
491 }
492}
493
494void
496 const MooseMesh & mesh, std::unordered_map<dof_id_type, Point> & transformation)
497{
498 auto & libmesh_mesh = mesh.getMesh();
499
500 auto & subdomainids = mesh.meshSubdomains();
501
502 subdomain_id_type max_subdomain_id = 0;
503
504 // max_subdomain_id will be used to represent the center of the entire domain
505 for (auto subdomain_id : subdomainids)
506 {
507 max_subdomain_id = max_subdomain_id > subdomain_id ? max_subdomain_id : subdomain_id;
508 }
509
510 max_subdomain_id += 1;
511
512 std::unordered_map<dof_id_type, Point> subdomain_centers;
513 std::unordered_map<dof_id_type, dof_id_type> nelems;
514
515 for (auto & elem :
516 as_range(libmesh_mesh.local_elements_begin(), libmesh_mesh.local_elements_end()))
517 {
518 // Compute center of the entire domain
519 subdomain_centers[max_subdomain_id] += elem->vertex_average();
520 nelems[max_subdomain_id] += 1;
521
522 auto subdomain = elem->subdomain_id();
523
524 if (subdomain == Moose::INVALID_BLOCK_ID)
525 mooseError("block is invalid");
526
527 // Centers for subdomains
528 subdomain_centers[subdomain] += elem->vertex_average();
529
530 nelems[subdomain] += 1;
531 }
532
533 comm().sum(subdomain_centers);
534
535 comm().sum(nelems);
536
537 subdomain_centers[max_subdomain_id] /= nelems[max_subdomain_id];
538
539 for (auto subdomain_id : subdomainids)
540 {
541 subdomain_centers[subdomain_id] /= nelems[subdomain_id];
542 }
543
544 // Compute unit vectors representing directions in which we want to shrink mesh
545 // The unit vectors is scaled by 'shrink_gap_width'
546 transformation.clear();
547 for (auto subdomain_id : subdomainids)
548 {
549 transformation[subdomain_id] =
550 subdomain_centers[max_subdomain_id] - subdomain_centers[subdomain_id];
551
552 auto norm = transformation[subdomain_id].norm();
553
554 // The current subdomain is the center of the entire domain,
555 // then we do not move this subdomain
556 if (norm > _distance_tol)
557 transformation[subdomain_id] /= norm;
558
559 transformation[subdomain_id] *= _shrink_gap_width;
560 }
561}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
registerMooseObjectRenamed("MooseApp", MultiAppInterpolationTransfer, "12/31/2023 24:00", MultiAppGeometricInterpolationTransfer)
registerMooseObject("MooseApp", MultiAppGeometricInterpolationTransfer)
void ErrorVector unsigned int
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...
virtual MooseMesh & mesh() override
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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 addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
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:457
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition MooseMesh.C:3557
std::vector< SubdomainID > getSubdomainIDs(const std::vector< SubdomainName > &subdomain_names) const
Get the associated subdomainIDs for the subdomain names that are passed in.
Definition MooseMesh.C:1729
void errorIfDistributedMesh(std::string name) const
Generate a unified error message if the underlying libMesh mesh is a DistributedMesh.
Definition MooseMesh.C:3726
SystemBase & sys()
Get the system this variable is part of.
This class provides an interface for common operations on field variables of both FE and FV types wit...
Transfers variables on possibly different meshes while conserving a user defined property (Postproces...
VariableName _from_var_name
This values are used if a derived class only supports one variable.
const std::vector< VariableName > _from_var_names
Name of variables transferring from.
const std::vector< AuxVariableName > _to_var_names
Name of variables transferring to.
This class contains transformation information that only exists in a context in which there are multi...
Interpolate variable values using geometry/mesh-based coefficients.
void interpolateTargetPoints(FEProblemBase &to_problem, MooseVariableFieldBase &to_var, NumericVector< Real > &to_solution, const MultiAppCoordTransform &to_app_transform, const std::unique_ptr< libMesh::InverseDistanceInterpolation< Moose::dim > > &idi)
void computeTransformation(const MooseMesh &mesh, std::unordered_map< dof_id_type, Point > &transformation)
void fillSourceInterpolationPoints(FEProblemBase &from_problem, const MooseVariableFieldBase &from_var, const MultiAppCoordTransform &from_app_transform, std::unique_ptr< libMesh::InverseDistanceInterpolation< Moose::dim > > &idi)
void subdomainIDsNode(MooseMesh &mesh, const Node &node, std::set< subdomain_id_type > &subdomainids)
virtual void execute() override
Execute the transfer.
MultiAppGeometricInterpolationTransfer(const InputParameters &parameters)
bool _displaced_source_mesh
True if displaced mesh is used for the source mesh, otherwise false.
std::vector< std::unique_ptr< MultiAppCoordTransform > > _from_transforms
bool _displaced_target_mesh
True if displaced mesh is used for the target mesh, otherwise false.
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.
std::vector< std::unique_ptr< MultiAppCoordTransform > > _to_transforms
bool hasFromMultiApp() const
Whether the transfer owns a non-null from_multi_app.
Base class for a system (of equations)
Definition SystemBase.h:87
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
@ FROM_MULTIAPP
Definition Transfer.h:71
@ TO_MULTIAPP
Definition Transfer.h:70
FEProblemBase & _fe_problem
Definition Transfer.h:100
MooseEnum _current_direction
Definition Transfer.h:109
const Parallel::Communicator & comm() const
std::unique_ptr< NumericVector< Number > > solution
unsigned int number() const
MeshBase & mesh
@ VAR_FIELD_STANDARD
Definition MooseTypes.h:777
@ VAR_ANY
Definition MooseTypes.h:772
const SubdomainID INVALID_BLOCK_ID
Definition MooseTypes.C:20