LCOV - code coverage report
Current view: top level - src/positions - Positions.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 102 123 82.9 %
Date: 2025-07-17 01:28:37 Functions: 14 15 93.3 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      10             : #include "Positions.h"
      11             : #include "libmesh/parallel_algebra.h"
      12             : #include "MooseMeshUtils.h"
      13             : 
      14             : InputParameters
      15      188245 : Positions::validParams()
      16             : {
      17      188245 :   InputParameters params = GeneralReporter::validParams();
      18             : 
      19      188245 :   params.addParam<PositionsName>("initial_positions",
      20             :                                  "Positions at the beginning of the simulation");
      21             : 
      22             :   // This parameter should be set by each derived class depending on whether the generation of
      23             :   // positions is replicated or distributed. We want positions to be replicated across all ranks
      24      188245 :   params.addRequiredParam<bool>("auto_broadcast",
      25             :                                 "Wether Positions should be broadcasted across all ranks");
      26             :   // This parameter should be set by each derived class depending on whether positions should
      27             :   // be sorted after being communicated. Sorting is usually undesirable, as the user input order
      28             :   // should be respected. It may be only desirable for testing reproducibility
      29      188245 :   params.addRequiredParam<bool>("auto_sort", "Whether Positions should be sorted by coordinates");
      30             : 
      31             :   // In general, no need to refresh Positions unless the mesh moved
      32      188245 :   params.set<ExecFlagEnum>("execute_on") = EXEC_NONE;
      33             : 
      34      188245 :   params.addParamNamesToGroup("auto_broadcast auto_sort", "Advanced");
      35      188245 :   params.registerBase("Positions");
      36      188245 :   return params;
      37           0 : }
      38             : 
      39        1400 : Positions::Positions(const InputParameters & parameters)
      40             :   : GeneralReporter(parameters),
      41             :     // leverage reporter interface to keep track of consumers
      42        2800 :     _initial_positions(isParamValid("initial_positions")
      43        1416 :                            ? &getReporterValueByName<std::vector<Point>>(
      44        1416 :                                  getParam<PositionsName>("initial_positions") + "/positions_1d")
      45             :                            : nullptr),
      46             :     // Positions will be replicated on every rank so transfers may query positions from all ranks
      47        1400 :     _positions(declareValueByName<std::vector<Point>, ReporterVectorContext<Point>>(
      48             :         "positions_1d", REPORTER_MODE_REPLICATED)),
      49        1400 :     _need_broadcast(getParam<bool>("auto_broadcast")),
      50        1400 :     _need_sort(getParam<bool>("auto_sort")),
      51        2800 :     _initialized(false)
      52             : {
      53             : 
      54        1400 :   if (_initial_positions)
      55          16 :     _initial_positions_kd_tree = std::make_unique<KDTree>(*_initial_positions, 1);
      56        1400 : }
      57             : 
      58             : const Point &
      59      154319 : Positions::getPosition(unsigned int index, bool initial) const
      60             : {
      61             :   mooseAssert(initialized(initial), "Positions vector has not been initialized.");
      62             :   // Check sizes of inital positions
      63      154319 :   if (initial && _initial_positions && (*_initial_positions).size() < index)
      64           0 :     mooseError("Initial positions is not sized or initialized appropriately");
      65             : 
      66             :   // Check sizes of regular positions
      67      154319 :   if (!initial)
      68             :   {
      69      116131 :     if (_initial_positions && _positions.size() != (*_initial_positions).size())
      70           4 :       mooseError("Initial positions and current positions array length do not match");
      71      116127 :     else if (_positions.size() < index)
      72           0 :       mooseError("Positions retrieved with an out-of-bound index: '",
      73             :                  index,
      74             :                  "' when there are only ",
      75           0 :                  _positions.size(),
      76             :                  " positions.");
      77             :   }
      78             : 
      79      154315 :   if (initial && _initial_positions)
      80           0 :     return (*_initial_positions)[index];
      81      154315 :   if (_positions.size())
      82      154315 :     return _positions[index];
      83             :   else
      84           0 :     mooseError("Positions vector has not been initialized.");
      85             : }
      86             : 
      87             : const Point &
      88      178815 : Positions::getNearestPosition(const Point & target, const bool initial) const
      89             : {
      90             :   mooseAssert(initialized(initial), "Positions vector has not been initialized.");
      91      178815 :   const auto & positions = (initial && _initial_positions) ? *_initial_positions : _positions;
      92      178815 :   return positions[getNearestPositionIndex(target, initial)];
      93             : }
      94             : 
      95             : unsigned int
      96      326824 : Positions::getNearestPositionIndex(const Point & target, const bool initial) const
      97             : {
      98             :   mooseAssert(initialized(initial), "Positions vector has not been initialized.");
      99      326824 :   std::vector<std::size_t> return_index(1);
     100             : 
     101      326824 :   if (initial && _initial_positions)
     102             :   {
     103             :     mooseAssert(_initial_positions_kd_tree, "Should have an initial positions KDTree");
     104           0 :     _initial_positions_kd_tree->neighborSearch(target, 1, return_index);
     105             :   }
     106             :   else
     107             :   {
     108             :     mooseAssert(_positions_kd_tree, "Should have a positions KDTree");
     109      326824 :     _positions_kd_tree->neighborSearch(target, 1, return_index);
     110             :   }
     111             : 
     112      653648 :   return return_index[0];
     113      326824 : }
     114             : 
     115             : const std::vector<Point> &
     116         798 : Positions::getPositions(bool initial) const
     117             : {
     118         798 :   if (initial && _initial_positions)
     119          32 :     return *_initial_positions;
     120         766 :   if (_positions.size())
     121         761 :     return _positions;
     122             :   else
     123           5 :     mooseError("Positions vector has not been initialized.");
     124             : }
     125             : 
     126             : const std::vector<std::vector<Point>> &
     127           1 : Positions::getPositionsVector2D() const
     128             : {
     129           1 :   if (_positions_2d.size())
     130           0 :     return _positions_2d;
     131             :   else
     132           1 :     mooseError("2D positions vectors have not been initialized.");
     133             : }
     134             : 
     135             : const std::vector<std::vector<std::vector<Point>>> &
     136           1 : Positions::getPositionsVector3D() const
     137             : {
     138           1 :   if (_positions_3d.size())
     139           0 :     return _positions_3d;
     140             :   else
     141           1 :     mooseError("3D positions vectors have not been initialized.");
     142             : }
     143             : 
     144             : const std::vector<std::vector<std::vector<std::vector<Point>>>> &
     145           1 : Positions::getPositionsVector4D() const
     146             : {
     147           1 :   if (_positions_4d.size())
     148           0 :     return _positions_4d;
     149             :   else
     150           1 :     mooseError("4D positions vectors have not been initialized.");
     151             : }
     152             : 
     153             : void
     154        1278 : Positions::clearPositions()
     155             : {
     156        1278 :   _positions.clear();
     157        1278 :   _positions_2d.clear();
     158        1278 :   _positions_3d.clear();
     159        1278 :   _positions_4d.clear();
     160        1278 : }
     161             : 
     162             : void
     163          72 : Positions::unrollMultiDPositions()
     164             : {
     165             :   // Unroll in every dimension available
     166          72 :   std::vector<Point> temp_2d_unrolled;
     167          72 :   std::vector<Point> temp_3d_unrolled;
     168          72 :   std::vector<Point> temp_4d_unrolled;
     169          96 :   for (auto vec : _positions_2d)
     170          24 :     temp_2d_unrolled.insert(temp_2d_unrolled.end(), vec.begin(), vec.end());
     171         108 :   for (const auto & vec_vec : _positions_3d)
     172         108 :     for (const auto & vec : vec_vec)
     173          72 :       temp_3d_unrolled.insert(temp_3d_unrolled.end(), vec.begin(), vec.end());
     174          96 :   for (const auto & vec_vec_vec : _positions_4d)
     175          48 :     for (const auto & vec_vec : vec_vec_vec)
     176         120 :       for (const auto & vec : vec_vec)
     177          96 :         temp_4d_unrolled.insert(temp_4d_unrolled.end(), vec.begin(), vec.end());
     178             : 
     179             :   // for now we wont even tolerate a different ordering
     180             :   // 2D & 1D: check for conflicts
     181          72 :   if (temp_2d_unrolled.size() && _positions.size() && temp_2d_unrolled != _positions)
     182           0 :     mooseError("Inconsistency between the 2D and 1D position vectors detected. "
     183             :                "The 2D positions must unroll into the 1D positions");
     184             : 
     185             :   // 3D vs 2D & 1D
     186          72 :   if (temp_3d_unrolled.size() && temp_2d_unrolled.size() && temp_3d_unrolled != temp_3d_unrolled)
     187           0 :     mooseError("Inconsistency between the 3D and 2D position vectors detected. "
     188             :                "The 3D positions must unroll the same way as the 2D positions");
     189          72 :   if (temp_3d_unrolled.size() && _positions.size() && temp_3d_unrolled != _positions)
     190           0 :     mooseError("Inconsistency between the 3D and 1D position vectors detected. "
     191             :                "The 3D positions must unroll into the 1D positions");
     192             : 
     193             :   // 4D vs all lower dimensions
     194          72 :   if (temp_4d_unrolled.size() && temp_3d_unrolled.size() && temp_4d_unrolled != temp_3d_unrolled)
     195           0 :     mooseError("Inconsistency between the 4D and 3D position vectors detected. "
     196             :                "The 4D positions must unroll the same way as the 3D positions");
     197          72 :   if (temp_4d_unrolled.size() && temp_2d_unrolled.size() && temp_4d_unrolled != temp_2d_unrolled)
     198           0 :     mooseError("Inconsistency between the 4D and 2D position vectors detected. "
     199             :                "The 4D positions must unroll the same way as the 2D positions");
     200          72 :   if (temp_4d_unrolled.size() && _positions.size() && temp_4d_unrolled != _positions)
     201           0 :     mooseError("Inconsistency between the 4D and 1D position vectors detected. "
     202             :                "The 4D positions must unroll into the 1D positions");
     203             : 
     204             :   // Use any of the higher D to set the one D vector
     205          72 :   if (!_positions.size())
     206             :   {
     207          48 :     if (temp_2d_unrolled.size())
     208          12 :       _positions = temp_2d_unrolled;
     209          36 :     else if (temp_3d_unrolled.size())
     210          24 :       _positions = temp_3d_unrolled;
     211          12 :     else if (temp_4d_unrolled.size())
     212          12 :       _positions = temp_4d_unrolled;
     213             :     else
     214           0 :       mooseError("Positions::unrollMultiDPositions() may only be called if there is at least one "
     215             :                  "non-empty positions vector.");
     216             :   }
     217          72 : }
     218             : 
     219             : void
     220        1641 : Positions::finalize()
     221             : {
     222             :   // Gather up the positions vector on all ranks
     223             :   mooseAssert(initialized(false), "Positions vector has not been initialized.");
     224        1641 :   if (_need_broadcast)
     225             :     // The consumer/producer reporter interface can keep track of whether a reduction is needed
     226             :     // (for example if a consumer needs replicated data, but the producer is distributed) however,
     227             :     // we have currently made the decision that positions should ALWAYS be replicated
     228         183 :     _communicator.allgather(_positions, /* identical buffer lengths = */ false);
     229             : 
     230             :   // Sort positions by X then Y then Z
     231        1641 :   if (_need_sort)
     232         116 :     std::sort(_positions.begin(), _positions.end());
     233             : 
     234             :   // Make a KDTree with the positions
     235        1641 :   _positions_kd_tree = std::make_unique<KDTree>(_positions, 1);
     236             : 
     237             :   // For now at least, we expect positions to be the same on all ranks
     238             :   mooseAssert(comm().verify(_positions), "Positions should be the same across all MPI processes.");
     239        1641 : }
     240             : 
     241             : Real
     242          51 : Positions::getMinDistanceBetweenPositions() const
     243             : {
     244             :   mooseAssert(initialized(false), "Positions vector has not been initialized.");
     245             :   // Dumb nested loops. We can revisit this once we have a KDTree for nearest position searching
     246             :   // These nested loops are still faster than calling getNearestPositions on each position for now
     247          51 :   Real min_distance_sq = std::numeric_limits<Real>::max();
     248         114 :   for (const auto i1 : index_range(_positions))
     249          75 :     for (auto i2 = i1 + 1; i2 < _positions.size(); i2++)
     250          12 :       min_distance_sq = std::min(min_distance_sq, (_positions[i1] - _positions[i2]).norm_sq());
     251         102 :   return std::sqrt(min_distance_sq);
     252             : }
     253             : 
     254             : bool
     255           0 : Positions::arePositionsCoplanar() const
     256             : {
     257             :   mooseAssert(initialized(false), "Positions vector has not been initialized.");
     258           0 :   return MooseMeshUtils::isCoPlanar(_positions);
     259             : }
     260             : 
     261             : bool
     262         170 : Positions::initialized(bool initial) const
     263             : {
     264         170 :   if (initial && _initial_positions)
     265             :     // We do not forward the 'initial' status as we are not currently looking
     266             :     // to support two level nesting for initial positions
     267           0 :     return _fe_problem.getPositionsObject(getParam<PositionsName>("initial_positions"))
     268           0 :         .initialized(false);
     269             :   else
     270         170 :     return _initialized;
     271             : }

Generated by: LCOV version 1.14