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
23using namespace libMesh;
24
27{
29
30 params.addParam<unsigned int>("num_nearest_points",
31 1,
32 "Number of nearest source (from) points will be chosen to "
33 "construct a value for the target point. All points will be "
34 "selected from the same origin mesh!");
35
36 // choose whether to include data from multiple apps when performing nearest-position/
37 // mesh-divisions based transfers
38 params.addParam<bool>("group_subapps",
39 false,
40 "Whether to group source locations and values from all subapps "
41 "when working with a nearest-position or source mesh-division");
42
43 return params;
44}
45
47 const InputParameters & parameters)
48 : MultiAppGeneralFieldTransfer(parameters),
49 _num_nearest_points(getParam<unsigned int>("num_nearest_points")),
50 _group_subapps(getParam<bool>("group_subapps"))
51{
53 paramError("use_nearest_position",
54 "We do not support using both nearest positions matching and checking if target "
55 "points are within an app domain because the KDTrees for nearest-positions matching "
56 "are (currently) built with data from multiple applications.");
58 (isParamValid("from_mesh_divisions") || isParamValid("to_mesh_divisions")))
59 paramError("use_nearest_position", "Cannot use nearest positions with mesh divisions");
60
61 // Parameter checks on grouping subapp values
64 "group_subapps",
65 "This option is only available for using mesh divisions or nearest positions regions");
66 else if (_group_subapps &&
69 paramError("group_subapps",
70 "Cannot group subapps when considering nearest-location data as we would lose "
71 "track of the division index of the source locations");
74 "group_subapps",
75 "When using the 'nearest child application' data, the source data (positions and values) "
76 "are grouped on a per-application basis, so it cannot be agglomerated over all child "
77 "applications.\nNote that the option to use nearest applications for source restrictions, "
78 "but further split each child application's domain by regions closest to each position "
79 "(here the the child application's centroid), which could be conceived when "
80 "'group_subapps' = false, is also not available.");
81}
82
83void
85{
87
88 // We need to improve the indexing if we are to allow this
89 if (!_from_mesh_divisions.empty())
90 for (const auto mesh_div : _from_mesh_divisions)
91 if (mesh_div->getNumDivisions() != _from_mesh_divisions[0]->getNumDivisions())
92 paramError("from_mesh_division",
93 "This transfer has only been implemented with a uniform number of source mesh "
94 "divisions across all source applications");
95}
96
97void
99 const unsigned int var_index)
100{
101 _local_kdtrees.clear();
102 _local_points.clear();
103 _local_values.clear();
104 buildKDTrees(var_index);
105}
106
107bool
109 const MooseMesh & mesh,
110 const Elem * elem) const
111{
112 // We need to override the definition of block restriction for an element
113 // because we have to consider whether each node of an element is adjacent to a block
114 for (const auto & i_node : make_range(elem->n_nodes()))
115 {
116 const auto & node = elem->node_ptr(i_node);
117 const auto & node_blocks = mesh.getNodeBlockIds(*node);
118 std::set<SubdomainID> u;
119 std::set_intersection(blocks.begin(),
120 blocks.end(),
121 node_blocks.begin(),
122 node_blocks.end(),
123 std::inserter(u, u.begin()));
124 if (!u.empty())
125 return true;
126 }
127 return false;
128}
129
130void
132{
133 // Number of source = number of KDTrees.
134 // Using mesh divisions or nearest-positions, for every app we use 1 tree per division
135 if (!_from_mesh_divisions.empty() ||
138 // If we group apps, then we only use one tree per division (nearest-position region)
142 // Regular case: 1 KDTree per app
143 // Also if use_nearest_app = true, the number of problems is better than the number of positions,
144 // because some of the positions are positions of child applications that are not local
145 else
147}
148
149unsigned int
151 unsigned int nested_loop_on_app_index) const
152{
153 // Each app is mapped to a single KD Tree
155 return kdtree_index;
156 // We are looping over all the apps that are grouped together
157 else if (_group_subapps)
158 return nested_loop_on_app_index;
159 // There are num_divisions trees for each app, inner ordering is divisions, so dividing by the
160 // number of divisions gets us the index of the application
161 else
162 return kdtree_index / getNumDivisions();
163}
164
165unsigned int
167{
169 return 1;
170 else if (_group_subapps)
171 return _from_meshes.size();
172 else
173 return 1;
174}
175
176unsigned int
178{
179 // This is not used currently, but conceptually it is better to only divide the domain with the
180 // local of local applications rather than the global number of positions (# global applications
181 // here)
183 return _from_meshes.size();
184 // Each nearest-position region is a division
188 // Assume all mesh divisions (on each sub-app) has the same number of divisions. This is checked
189 else if (!_from_mesh_divisions.empty())
190 return _from_mesh_divisions[0]->getNumDivisions();
191 // Grouping subapps or no special mode, we do not subdivide
192 else
193 return 1;
194}
195
196Point
198 const Point & pt) const
199{
201 // KD-trees are built in global coords when grouping subapps with nearest-positions
202 return pt;
203 else
204 return getPointInSourceAppFrame(pt, i_source, "KD-tree neighbor search");
205}
206
207bool
209 const unsigned int mesh_div,
210 const unsigned int i_from) const
211{
212 // Only use the KDTree from the closest position if in "nearest-position" mode
214 {
215 // See computeNumSources for the number of sources. i_from is the index in the source loop
216 // i_from is local if looping on _from_problems as sources, positions are indexed globally
217 // i_from is already indexing in positions if using group_subapps
218 auto position_index = i_from; // if _group_subapps
220 position_index = getGlobalSourceAppIndex(i_from);
221 else if (!_group_subapps)
222 position_index = i_from % getNumDivisions();
223
224 // NOTE: if two positions are equi-distant to the point, this will chose one
225 // This problem is detected if using search_value_conflicts in this call
226 if (!closestToPosition(position_index, pt))
227 return false;
228 }
229
230 // Application index depends on which source/grouping mode we are using
231 const unsigned int app_index = getAppIndex(i_from, i_from / getNumDivisions());
232
233 // Check mesh restriction before anything
235 {
237 mooseError("Nearest-positions + source_app_must_contain_point not implemented");
238 // Transform the point to place it in the local coordinate system
239 const auto local_pt = getPointInSourceAppFrame(pt, app_index, "Source mesh containment check");
240 if (!inMesh(_from_point_locators[app_index].get(), local_pt))
241 return false;
242 }
243
244 // Check the mesh division. We have handled the restriction of the source locations when
245 // building the nearest-neighbor trees. We only need to check that we meet the required
246 // source division index.
247 if (!_from_mesh_divisions.empty())
248 {
249 mooseAssert(mesh_div != MooseMeshDivision::INVALID_DIVISION_INDEX,
250 "We should not be receiving point requests with an invalid "
251 "source mesh division index");
252 const unsigned int kd_div_index = i_from % getNumDivisions();
253
254 // If matching source mesh divisions to target apps, we check that the index of the target
255 // application, which was passed in the point request, is equal to the current mesh division
257 mesh_div != kd_div_index)
258 return false;
259 // If matching source mesh divisions to target mesh divisions, we check that the index of the
260 // target mesh division, which was passed in the point request, is equal to the current mesh
261 // division
264 mesh_div != kd_div_index)
265 return false;
266 }
267
268 // If matching target apps to source mesh divisions, we check that the global index of the
269 // application is equal to the target mesh division index, which was passed in the point request
271 mesh_div != getGlobalSourceAppIndex(app_index))
272 return false;
273
274 return true;
275}
276
277void
279 const Point & pt,
280 unsigned int source_index,
281 std::pair<Real, Real> & outgoing_val,
282 bool & point_found)
283{
284 // First pass: find the nearest neighbor value across all local sources
285 for (const auto i_from : make_range(_num_sources))
286 {
287 if (!checkRestrictionsForSource(pt, source_index, i_from))
288 continue;
289
290 // TODO: Pre-allocate these two work arrays. They will be regularly resized by the searches
291 std::vector<std::size_t> return_index(_num_nearest_points);
292 std::vector<Real> return_dist_sqr(_num_nearest_points);
293
294 // KD-Tree can be empty if no points are within block/boundary/bounding box restrictions
295 if (_local_kdtrees[i_from]->numberCandidatePoints())
296 {
297 point_found = true;
298 // Note that we do not need to use the transformed_pt (in the source app frame)
299 // because the KDTree has been created in the reference frame
300 _local_kdtrees[i_from]->neighborSearch(
301 pt, _num_nearest_points, return_index, return_dist_sqr);
302 Real val_sum = 0, dist_sum = 0;
303 for (const auto index : return_index)
304 {
305 val_sum += _local_values[i_from][index];
306 dist_sum += (_local_points[i_from][index] - pt).norm();
307 }
308
309 // If the new value found is closer than for other sources, use it
310 const auto new_distance = dist_sum / return_dist_sqr.size();
311 if (new_distance < outgoing_val.second)
312 outgoing_val = {val_sum / return_index.size(), new_distance};
313 }
314 }
315
316 // Second pass: search-value-conflict detection
317 if (point_found && _search_value_conflicts)
318 {
319 unsigned int num_equidistant_problems = 0;
320
321 for (const auto i_from : make_range(_num_sources))
322 {
323 if (!checkRestrictionsForSource(pt, source_index, i_from))
324 continue;
325
326 // TODO: Pre-allocate these two work arrays. They will be regularly resized by the searches
327 std::vector<std::size_t> return_index(_num_nearest_points + 1);
328 std::vector<Real> return_dist_sqr(_num_nearest_points + 1);
329
330 const auto app_index = getAppIndex(i_from, i_from / getNumDivisions());
331 const auto num_search = _num_nearest_points + 1;
332
333 if (_local_kdtrees[i_from]->numberCandidatePoints())
334 {
335 _local_kdtrees[i_from]->neighborSearch(pt, num_search, return_index, return_dist_sqr);
336 auto num_found = return_dist_sqr.size();
337
338 // Local coordinates only accessible when not using nearest-position,
339 // as we did not keep the index of the source app, only the position index
340 const Point local_pt = getPointInSourceKDTreeFrame(app_index, pt);
341
343 _from_transforms[getGlobalSourceAppIndex(app_index)]->hasCoordinateSystemTypeChange())
345 mooseInfo("Search value conflict cannot find the origin point due to the "
346 "non-uniqueness of the coordinate collapsing reverse mapping");
347
348 // Look for too many equidistant nodes within a problem. First zip then sort by distance
349 std::vector<std::pair<Real, std::size_t>> zipped_nearest_points;
350 for (const auto i : make_range(num_found))
351 zipped_nearest_points.push_back(std::make_pair(return_dist_sqr[i], return_index[i]));
352 std::sort(zipped_nearest_points.begin(), zipped_nearest_points.end());
353
354 // If two furthest are equally far from target point, then we have an indetermination in
355 // what is sent in this communication round from this process. However, it may not
356 // materialize to an actual conflict, as values sent from another process for the
357 // desired target point could be closer (nearest). There is no way to know at this point
358 // in the communication that a closer value exists somewhere else
359 if (num_found > 1 && num_found == num_search &&
360 MooseUtils::absoluteFuzzyEqual(zipped_nearest_points[num_found - 1].first,
361 zipped_nearest_points[num_found - 2].first))
362 {
364 registerConflict(app_index, 0, pt, outgoing_val.second, true);
365 else
366 registerConflict(app_index, 0, local_pt, outgoing_val.second, true);
367 }
368
369 // Recompute the distance for this problem. If it matches the cached value more than
370 // once it means multiple problems provide equidistant values for this point
371 Real dist_sum = 0;
372 for (const auto i : make_range(num_search - 1))
373 {
374 auto index = zipped_nearest_points[i].second;
375 dist_sum += (_local_points[i_from][index] - pt).norm();
376 }
377
378 // Compare to the selected value found after looking at all the problems
379 if (MooseUtils::absoluteFuzzyEqual(dist_sum / return_dist_sqr.size(), outgoing_val.second))
380 {
381 num_equidistant_problems++;
382 if (num_equidistant_problems > 1)
383 {
385 registerConflict(app_index, 0, pt, outgoing_val.second, true);
386 else
387 registerConflict(app_index, 0, local_pt, outgoing_val.second, true);
388 }
389 }
390 }
391 }
392 }
393}
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:30
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:97
virtual unsigned int n_nodes() const=0
const Node * node_ptr(const unsigned int i) const
MeshBase & mesh
unsigned int INVALID_DIVISION_INDEX
Invalid subdomain id to return when outside the mesh division.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)