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 188483 : Positions::validParams() 16 : { 17 188483 : InputParameters params = GeneralReporter::validParams(); 18 : 19 188483 : 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 188483 : 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 188483 : 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 188483 : params.set<ExecFlagEnum>("execute_on") = EXEC_NONE; 33 : 34 188483 : params.addParamNamesToGroup("auto_broadcast auto_sort", "Advanced"); 35 188483 : params.registerBase("Positions"); 36 188483 : return params; 37 0 : } 38 : 39 1519 : Positions::Positions(const InputParameters & parameters) 40 : : GeneralReporter(parameters), 41 : // leverage reporter interface to keep track of consumers 42 3038 : _initial_positions(isParamValid("initial_positions") 43 1536 : ? &getReporterValueByName<std::vector<Point>>( 44 1536 : 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 1519 : _positions(declareValueByName<std::vector<Point>, ReporterVectorContext<Point>>( 48 : "positions_1d", REPORTER_MODE_REPLICATED)), 49 1519 : _need_broadcast(getParam<bool>("auto_broadcast")), 50 1519 : _need_sort(getParam<bool>("auto_sort")), 51 3038 : _initialized(false) 52 : { 53 : 54 1519 : if (_initial_positions) 55 17 : _initial_positions_kd_tree = std::make_unique<KDTree>(*_initial_positions, 1); 56 1519 : } 57 : 58 : const Point & 59 169930 : 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 169930 : 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 169930 : if (!initial) 68 : { 69 126862 : if (_initial_positions && _positions.size() != (*_initial_positions).size()) 70 4 : mooseError("Initial positions and current positions array length do not match"); 71 126858 : 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 169926 : if (initial && _initial_positions) 80 0 : return (*_initial_positions)[index]; 81 169926 : if (_positions.size()) 82 169926 : return _positions[index]; 83 : else 84 0 : mooseError("Positions vector has not been initialized."); 85 : } 86 : 87 : const Point & 88 198117 : Positions::getNearestPosition(const Point & target, const bool initial) const 89 : { 90 : mooseAssert(initialized(initial), "Positions vector has not been initialized."); 91 198117 : const auto & positions = (initial && _initial_positions) ? *_initial_positions : _positions; 92 198117 : return positions[getNearestPositionIndex(target, initial)]; 93 : } 94 : 95 : unsigned int 96 365423 : Positions::getNearestPositionIndex(const Point & target, const bool initial) const 97 : { 98 : mooseAssert(initialized(initial), "Positions vector has not been initialized."); 99 365423 : std::vector<std::size_t> return_index(1); 100 : 101 365423 : 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 365423 : _positions_kd_tree->neighborSearch(target, 1, return_index); 110 : } 111 : 112 730846 : return return_index[0]; 113 365423 : } 114 : 115 : const std::vector<Point> & 116 876 : Positions::getPositions(bool initial) const 117 : { 118 876 : if (initial && _initial_positions) 119 34 : return *_initial_positions; 120 842 : if (_positions.size()) 121 837 : 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 1386 : Positions::clearPositions() 155 : { 156 1386 : _positions.clear(); 157 1386 : _positions_2d.clear(); 158 1386 : _positions_3d.clear(); 159 1386 : _positions_4d.clear(); 160 1386 : } 161 : 162 : void 163 78 : Positions::unrollMultiDPositions() 164 : { 165 : // Unroll in every dimension available 166 78 : std::vector<Point> temp_2d_unrolled; 167 78 : std::vector<Point> temp_3d_unrolled; 168 78 : std::vector<Point> temp_4d_unrolled; 169 104 : for (auto vec : _positions_2d) 170 26 : temp_2d_unrolled.insert(temp_2d_unrolled.end(), vec.begin(), vec.end()); 171 117 : for (const auto & vec_vec : _positions_3d) 172 117 : for (const auto & vec : vec_vec) 173 78 : temp_3d_unrolled.insert(temp_3d_unrolled.end(), vec.begin(), vec.end()); 174 104 : for (const auto & vec_vec_vec : _positions_4d) 175 52 : for (const auto & vec_vec : vec_vec_vec) 176 130 : for (const auto & vec : vec_vec) 177 104 : 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 78 : 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 78 : 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 78 : 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 78 : 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 78 : 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 78 : 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 78 : if (!_positions.size()) 206 : { 207 52 : if (temp_2d_unrolled.size()) 208 13 : _positions = temp_2d_unrolled; 209 39 : else if (temp_3d_unrolled.size()) 210 26 : _positions = temp_3d_unrolled; 211 13 : else if (temp_4d_unrolled.size()) 212 13 : _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 78 : } 218 : 219 : void 220 1786 : Positions::finalize() 221 : { 222 : // Gather up the positions vector on all ranks 223 : mooseAssert(initialized(false), "Positions vector has not been initialized."); 224 1786 : 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 197 : _communicator.allgather(_positions, /* identical buffer lengths = */ false); 229 : 230 : // Sort positions by X then Y then Z 231 1786 : if (_need_sort) 232 125 : std::sort(_positions.begin(), _positions.end()); 233 : 234 : // Make a KDTree with the positions 235 1786 : _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 1786 : } 240 : 241 : Real 242 54 : 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 54 : Real min_distance_sq = std::numeric_limits<Real>::max(); 248 120 : for (const auto i1 : index_range(_positions)) 249 78 : 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 108 : 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 185 : Positions::initialized(bool initial) const 263 : { 264 185 : 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 185 : return _initialized; 271 : }