https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MultiAppGeneralFieldKDTreeTransferBase.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 "FEProblem.h"
14#include "MooseMesh.h"
15#include "MooseTypes.h"
16#include "MooseVariableFE.h"
17#include "SystemBase.h"
18#include "Positions.h"
20
21#include "libmesh/system.h"
22
25{
27
28 params.addParam<unsigned int>("num_nearest_points",
29 1,
30 "Number of nearest source (from) points will be chosen to "
31 "construct a value for the target point. All points will be "
32 "selected from the same origin mesh!");
33
34 // choose whether to include data from multiple apps when performing nearest-position/
35 // mesh-divisions based transfers
36 params.addParam<bool>("group_subapps",
37 false,
38 "Whether to group source locations and values from all subapps "
39 "when working with a nearest-position or source mesh-division");
40
41 return params;
42}
43
45 const InputParameters & parameters)
46 : MultiAppGeneralFieldTransfer(parameters),
47 _num_nearest_points(getParam<unsigned int>("num_nearest_points")),
48 _group_subapps(getParam<bool>("group_subapps"))
49{
51 paramError("use_nearest_position",
52 "We do not support using both nearest positions matching and checking if target "
53 "points are within an app domain because the KDTrees for nearest-positions matching "
54 "are (currently) built with data from multiple applications.");
56 (isParamValid("from_mesh_divisions") || isParamValid("to_mesh_divisions")))
57 paramError("use_nearest_position", "Cannot use nearest positions with mesh divisions");
58
59 // Parameter checks on grouping subapp values
62 "group_subapps",
63 "This option is only available for using mesh divisions or nearest positions regions");
64 else if (_group_subapps &&
67 paramError("group_subapps",
68 "Cannot group subapps when considering nearest-location data as we would lose "
69 "track of the division index of the source locations");
72 "group_subapps",
73 "When using the 'nearest child application' data, the source data (positions and values) "
74 "are grouped on a per-application basis, so it cannot be agglomerated over all child "
75 "applications.\nNote that the option to use nearest applications for source restrictions, "
76 "but further split each child application's domain by regions closest to each position "
77 "(here the the child application's centroid), which could be conceived when "
78 "'group_subapps' = false, is also not available.");
79}
80
81void
83{
85
86 // We need to improve the indexing if we are to allow this
87 if (!_from_mesh_divisions.empty())
88 for (const auto mesh_div : _from_mesh_divisions)
89 if (mesh_div->getNumDivisions() != _from_mesh_divisions[0]->getNumDivisions())
90 paramError("from_mesh_division",
91 "This transfer has only been implemented with a uniform number of source mesh "
92 "divisions across all source applications");
93}
94
95void
97 const unsigned int var_index)
98{
99 _local_kdtrees.clear();
100 _local_points.clear();
101 _local_values.clear();
102 buildKDTrees(var_index);
103}
104
105bool
107 const MooseMesh & mesh,
108 const Elem * elem) const
109{
110 // We need to override the definition of block restriction for an element
111 // because we have to consider whether each node of an element is adjacent to a block
112 for (const auto & i_node : make_range(elem->n_nodes()))
113 {
114 const auto & node = elem->node_ptr(i_node);
115 const auto & node_blocks = mesh.getNodeBlockIds(*node);
116 std::set<SubdomainID> u;
117 std::set_intersection(blocks.begin(),
118 blocks.end(),
119 node_blocks.begin(),
120 node_blocks.end(),
121 std::inserter(u, u.begin()));
122 if (!u.empty())
123 return true;
124 }
125 return false;
126}
127
128void
130{
131 // Number of source = number of KDTrees.
132 // Using mesh divisions or nearest-positions, for every app we use 1 tree per division
133 if (!_from_mesh_divisions.empty() ||
136 // If we group apps, then we only use one tree per division (nearest-position region)
140 // Regular case: 1 KDTree per app
141 // Also if use_nearest_app = true, the number of problems is better than the number of positions,
142 // because some of the positions are positions of child applications that are not local
143 else
145}
146
147unsigned int
149 unsigned int nested_loop_on_app_index) const
150{
151 // Each app is mapped to a single KD Tree
153 return kdtree_index;
154 // We are looping over all the apps that are grouped together
155 else if (_group_subapps)
156 return nested_loop_on_app_index;
157 // There are num_divisions trees for each app, inner ordering is divisions, so dividing by the
158 // number of divisions gets us the index of the application
159 else
160 return kdtree_index / getNumDivisions();
161}
162
163unsigned int
165{
167 return 1;
168 else if (_group_subapps)
169 return _from_meshes.size();
170 else
171 return 1;
172}
173
174unsigned int
176{
177 // This is not used currently, but conceptually it is better to only divide the domain with the
178 // local of local applications rather than the global number of positions (# global applications
179 // here)
181 return _from_meshes.size();
182 // Each nearest-position region is a division
186 // Assume all mesh divisions (on each sub-app) has the same number of divisions. This is checked
187 else if (!_from_mesh_divisions.empty())
188 return _from_mesh_divisions[0]->getNumDivisions();
189 // Grouping subapps or no special mode, we do not subdivide
190 else
191 return 1;
192}
193
194Point
196 const Point & pt) const
197{
199 // KD-trees are built in global coords when grouping subapps with nearest-positions
200 return pt;
201 else
202 return getPointInSourceAppFrame(pt, i_source, "KD-tree neighbor search");
203}
204
205bool
207 const unsigned int mesh_div,
208 const unsigned int i_from) const
209{
210 // Only use the KDTree from the closest position if in "nearest-position" mode
212 {
213 // See computeNumSources for the number of sources. i_from is the index in the source loop
214 // i_from is local if looping on _from_problems as sources, positions are indexed globally
215 // i_from is already indexing in positions if using group_subapps
216 auto position_index = i_from; // if _group_subapps
218 position_index = getGlobalSourceAppIndex(i_from);
219 else if (!_group_subapps)
220 position_index = i_from % getNumDivisions();
221
222 // NOTE: if two positions are equi-distant to the point, this will chose one
223 // This problem is detected if using search_value_conflicts in this call
224 if (!closestToPosition(position_index, pt))
225 return false;
226 }
227
228 // Application index depends on which source/grouping mode we are using
229 const unsigned int app_index = getAppIndex(i_from, i_from / getNumDivisions());
230
231 // Check mesh restriction before anything
233 {
235 mooseError("Nearest-positions + source_app_must_contain_point not implemented");
236 // Transform the point to place it in the local coordinate system
237 const auto local_pt = getPointInSourceAppFrame(pt, app_index, "Source mesh containment check");
238 if (!inMesh(_from_point_locators[app_index].get(), local_pt))
239 return false;
240 }
241
242 // Check the mesh division. We have handled the restriction of the source locations when
243 // building the nearest-neighbor trees. We only need to check that we meet the required
244 // source division index.
245 if (!_from_mesh_divisions.empty())
246 {
247 mooseAssert(mesh_div != MooseMeshDivision::INVALID_DIVISION_INDEX,
248 "We should not be receiving point requests with an invalid "
249 "source mesh division index");
250 const unsigned int kd_div_index = i_from % getNumDivisions();
251
252 // If matching source mesh divisions to target apps, we check that the index of the target
253 // application, which was passed in the point request, is equal to the current mesh division
255 mesh_div != kd_div_index)
256 return false;
257 // If matching source mesh divisions to target mesh divisions, we check that the index of the
258 // target mesh division, which was passed in the point request, is equal to the current mesh
259 // division
262 mesh_div != kd_div_index)
263 return false;
264 }
265
266 // If matching target apps to source mesh divisions, we check that the global index of the
267 // application is equal to the target mesh division index, which was passed in the point request
269 mesh_div != getGlobalSourceAppIndex(app_index))
270 return false;
271
272 return true;
273}
274
275void
277 const Point & pt,
278 unsigned int source_index,
279 std::pair<Real, Real> & outgoing_val,
280 bool & point_found)
281{
282 // First pass: find the nearest neighbor value across all local sources
283 for (const auto i_from : make_range(_num_sources))
284 {
285 if (!checkRestrictionsForSource(pt, source_index, i_from))
286 continue;
287
288 // TODO: Pre-allocate these two work arrays. They will be regularly resized by the searches
289 std::vector<std::size_t> return_index(_num_nearest_points);
290 std::vector<Real> return_dist_sqr(_num_nearest_points);
291
292 // KD-Tree can be empty if no points are within block/boundary/bounding box restrictions
293 if (_local_kdtrees[i_from]->numberCandidatePoints())
294 {
295 point_found = true;
296 // Note that we do not need to use the transformed_pt (in the source app frame)
297 // because the KDTree has been created in the reference frame
298 _local_kdtrees[i_from]->neighborSearch(
299 pt, _num_nearest_points, return_index, return_dist_sqr);
300 Real val_sum = 0, dist_sum = 0;
301 for (const auto index : return_index)
302 {
303 val_sum += _local_values[i_from][index];
304 dist_sum += (_local_points[i_from][index] - pt).norm();
305 }
306
307 // If the new value found is closer than for other sources, use it
308 const auto new_distance = dist_sum / return_dist_sqr.size();
309 if (new_distance < outgoing_val.second)
310 outgoing_val = {val_sum / return_index.size(), new_distance};
311 }
312 }
313
314 // Second pass: search-value-conflict detection
315 if (point_found && _search_value_conflicts)
316 {
317 unsigned int num_equidistant_problems = 0;
318
319 for (const auto i_from : make_range(_num_sources))
320 {
321 if (!checkRestrictionsForSource(pt, source_index, i_from))
322 continue;
323
324 // TODO: Pre-allocate these two work arrays. They will be regularly resized by the searches
325 std::vector<std::size_t> return_index(_num_nearest_points + 1);
326 std::vector<Real> return_dist_sqr(_num_nearest_points + 1);
327
328 const auto app_index = getAppIndex(i_from, i_from / getNumDivisions());
329 const auto num_search = _num_nearest_points + 1;
330
331 if (_local_kdtrees[i_from]->numberCandidatePoints())
332 {
333 _local_kdtrees[i_from]->neighborSearch(pt, num_search, return_index, return_dist_sqr);
334 auto num_found = return_dist_sqr.size();
335
336 // Local coordinates only accessible when not using nearest-position,
337 // as we did not keep the index of the source app, only the position index
338 const Point local_pt = getPointInSourceKDTreeFrame(app_index, pt);
339
341 _from_transforms[getGlobalSourceAppIndex(app_index)]->hasCoordinateSystemTypeChange())
343 mooseInfo("Search value conflict cannot find the origin point due to the "
344 "non-uniqueness of the coordinate collapsing reverse mapping");
345
346 // Look for too many equidistant nodes within a problem. First zip then sort by distance
347 std::vector<std::pair<Real, std::size_t>> zipped_nearest_points;
348 for (const auto i : make_range(num_found))
349 zipped_nearest_points.push_back(std::make_pair(return_dist_sqr[i], return_index[i]));
350 std::sort(zipped_nearest_points.begin(), zipped_nearest_points.end());
351
352 // If two furthest are equally far from target point, then we have an indetermination in
353 // what is sent in this communication round from this process. However, it may not
354 // materialize to an actual conflict, as values sent from another process for the
355 // desired target point could be closer (nearest). There is no way to know at this point
356 // in the communication that a closer value exists somewhere else
357 if (num_found > 1 && num_found == num_search &&
358 MooseUtils::absoluteFuzzyEqual(zipped_nearest_points[num_found - 1].first,
359 zipped_nearest_points[num_found - 2].first))
360 {
362 registerConflict(app_index, 0, pt, outgoing_val.second, true);
363 else
364 registerConflict(app_index, 0, local_pt, outgoing_val.second, true);
365 }
366
367 // Recompute the distance for this problem. If it matches the cached value more than
368 // once it means multiple problems provide equidistant values for this point
369 Real dist_sum = 0;
370 for (const auto i : make_range(num_search - 1))
371 {
372 auto index = zipped_nearest_points[i].second;
373 dist_sum += (_local_points[i_from][index] - pt).norm();
374 }
375
376 // Compare to the selected value found after looking at all the problems
377 if (MooseUtils::absoluteFuzzyEqual(dist_sum / return_dist_sqr.size(), outgoing_val.second))
378 {
379 num_equidistant_problems++;
380 if (num_equidistant_problems > 1)
381 {
383 registerConflict(app_index, 0, pt, outgoing_val.second, true);
384 else
385 registerConflict(app_index, 0, local_pt, outgoing_val.second, true);
386 }
387 }
388 }
389 }
390 }
391}
void mooseInfo(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition MooseError.h:401
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
const ExecFlagType EXEC_INITIAL
Definition Moose.C:31
char ** blocks
void ErrorVector unsigned int
const ExecFlagType & getCurrentExecuteOnFlag() const
Return/set the current execution flag.
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 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
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
virtual void prepareEvaluationOfInterpValues(const unsigned int var_index) override
bool checkRestrictionsForSource(const Point &pt, const unsigned int valid_mesh_div, const unsigned int i_from) const
Examine all spatial restrictions that could preclude this source from being a valid source for this p...
unsigned int getNumAppsPerTree() const
Number of applications which contributed nearest-locations to each KD-tree.
std::vector< std::vector< Point > > _local_points
All the nodes that meet the spatial restrictions in all the local source apps.
std::vector< std::shared_ptr< KDTree > > _local_kdtrees
KD-Trees for all the local source apps.
Point getPointInSourceKDTreeFrame(unsigned int i_source, const Point &pt) const
Transform a point into the frame used for KD-tree queries.
MultiAppGeneralFieldKDTreeTransferBase(const InputParameters &parameters)
unsigned int _num_sources
Number of KD-Trees to create.
unsigned int getAppIndex(unsigned int kdtree_index, unsigned int app_index_in_tree) const
Get the index of the app when inside of a KD-Tree source loop, where multiple applications could be l...
std::vector< std::vector< Real > > _local_values
Values of the variable being transferred at all the points in _local_points.
virtual void buildKDTrees(const unsigned int var_index)=0
void computeNumSources()
Pre-compute the number of sources Number of KDTrees used to hold the locations and variable value dat...
unsigned int _num_nearest_points
Number of points to consider.
const bool _group_subapps
Whether to group data when creating the nearest-point regions.
bool inBlocks(const std::set< SubdomainID > &blocks, const MooseMesh &mesh, const Elem *elem) const override
unsigned int getNumDivisions() const
Number of divisions (nearest-positions or source mesh divisions) used when building KD-Trees.
void evaluateNearestNodeFromKDTrees(const Point &pt, unsigned int source_index, std::pair< Real, Real > &outgoing_val, bool &point_found)
Search all local KD-trees for the nearest node/element and update outgoing_val.
void initialSetup() override
Method called at the beginning of the simulation for checking integrity or doing one-time setup.
It is a general field transfer.
const bool _use_nearest_app
Whether to keep track of the distance from the requested point to the app position.
const MooseEnum & _from_mesh_division_behavior
How to use the origin mesh divisions to restrict the transfer.
const MooseEnum & _to_mesh_division_behavior
How to use the target mesh divisions to restrict the transfer.
bool _source_app_must_contain_point
Whether the source app mesh must actually contain the points for them to be considered or whether the...
std::vector< std::unique_ptr< libMesh::PointLocatorBase > > _from_point_locators
Point locators, useful to examine point location with regards to domain restriction.
void registerConflict(unsigned int problem, dof_id_type dof_id, Point p, Real dist, bool local)
Register a potential value conflict, e.g.
bool closestToPosition(unsigned int pos_index, const Point &pt) const
Whether a point is closest to a position at the index specified than any other position.
virtual void initialSetup() override
Method called at the beginning of the simulation for checking integrity or doing one-time setup.
std::vector< const MeshDivision * > _from_mesh_divisions
Division of the origin mesh.
bool inMesh(const libMesh::PointLocatorBase *const pl, const Point &pt) const
bool _search_value_conflicts
Whether to look for conflicts between origin points, multiple valid values for a target point.
unsigned int getGlobalSourceAppIndex(unsigned int i_from) const
Return the global app index from the local index in the "from-multiapp" transfer direction.
std::vector< MooseMesh * > _from_meshes
std::vector< FEProblemBase * > _from_problems
std::vector< std::unique_ptr< MultiAppCoordTransform > > _from_transforms
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.
const bool _skip_coordinate_collapsing
Whether to skip coordinate collapsing (transformations of coordinates between applications using diff...
unsigned int getNumPositions(bool initial=false) const
}
Definition Positions.h:37
FEProblemBase & _fe_problem
Definition Transfer.h:100
MeshBase & mesh
unsigned int INVALID_DIVISION_INDEX
Invalid subdomain id to return when outside the mesh division.