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 "MooseAppCoordTransform.h"
11 : #include "InputParameters.h"
12 : #include "MultiMooseEnum.h"
13 : #include "MooseEnum.h"
14 : #include "MooseMesh.h"
15 : #include "libmesh/mesh_modification.h"
16 :
17 : using namespace libMesh;
18 :
19 : MooseAppCoordTransform::Direction
20 4 : MooseAppCoordTransform::processZAxis(const Direction z_axis)
21 : {
22 4 : return _coord_type == Moose::COORD_RZ ? z_axis : INVALID;
23 : }
24 :
25 : void
26 6 : MooseAppCoordTransform::setUpDirection(const Direction up_direction)
27 : {
28 6 : Real alpha = 0, beta = 0, gamma = 0;
29 :
30 6 : const bool must_rotate_axes =
31 6 : _coord_type == Moose::COORD_RZ || _coord_type == Moose::COORD_RSPHERICAL;
32 : // Don't error immediately for unit testing purposes
33 6 : bool negative_radii = false;
34 :
35 6 : if (up_direction == X)
36 : {
37 2 : alpha = 90, beta = 0, gamma = 0;
38 2 : if (must_rotate_axes)
39 : {
40 2 : if (_r_axis == X)
41 : {
42 1 : _r_axis = Y;
43 1 : _z_axis = processZAxis(X);
44 : }
45 1 : else if (_r_axis == Y)
46 : {
47 1 : negative_radii = true;
48 1 : _r_axis = X;
49 1 : _z_axis = processZAxis(Y);
50 : }
51 : else
52 0 : mooseError("Bad r-axis value");
53 : }
54 : }
55 4 : else if (up_direction == Y)
56 2 : alpha = 0, beta = 0, gamma = 0;
57 2 : else if (up_direction == Z)
58 : {
59 2 : alpha = 0, beta = -90, gamma = 0;
60 2 : if (must_rotate_axes)
61 : {
62 2 : if (_r_axis == X)
63 : {
64 1 : _r_axis = X;
65 1 : _z_axis = processZAxis(Z);
66 : }
67 1 : else if (_r_axis == Y)
68 : {
69 1 : negative_radii = true;
70 1 : _r_axis = Z;
71 1 : _z_axis = processZAxis(X);
72 : }
73 : else
74 0 : mooseError("Bad r-axis value");
75 : }
76 : }
77 : else
78 0 : mooseError("Bad up direction value");
79 :
80 6 : _euler_angles = {{alpha, beta, gamma}};
81 :
82 12 : _rotate = std::make_unique<RealTensorValue>(
83 18 : RealTensorValue::extrinsic_rotation_matrix(alpha, beta, gamma));
84 6 : computeRS();
85 :
86 6 : if (negative_radii)
87 2 : mooseError("Rotation yields negative radial values");
88 4 : }
89 :
90 : void
91 726 : MooseAppCoordTransform::setRotation(const Real alpha, const Real beta, const Real gamma)
92 : {
93 726 : const bool must_rotate_axes =
94 726 : _coord_type == Moose::COORD_RZ || _coord_type == Moose::COORD_RSPHERICAL;
95 726 : bool axes_rotated = false;
96 726 : if (must_rotate_axes)
97 : {
98 28 : const auto angles = std::make_tuple(alpha, beta, gamma);
99 28 : if (angles == std::make_tuple(0, 90, 0))
100 : {
101 27 : if (_r_axis == X)
102 : {
103 : mooseAssert((_coord_type == Moose::COORD_RZ && _z_axis == Y) ||
104 : (_coord_type == Moose::COORD_RSPHERICAL && _z_axis == INVALID),
105 : "'_z_axis' is not an expected value");
106 26 : _r_axis = X;
107 26 : _z_axis = _coord_type == Moose::COORD_RZ ? Z : INVALID;
108 : }
109 1 : else if (_r_axis == Y)
110 : {
111 : mooseAssert((_coord_type == Moose::COORD_RZ && _z_axis == X) ||
112 : (_coord_type == Moose::COORD_RSPHERICAL && _z_axis == INVALID),
113 : "'_z_axis' is not an expected value");
114 1 : _r_axis = Z;
115 1 : _z_axis = _coord_type == Moose::COORD_RZ ? X : INVALID;
116 : }
117 27 : axes_rotated = true;
118 : }
119 : }
120 :
121 726 : _euler_angles = {{alpha, beta, gamma}};
122 1452 : _rotate = std::make_unique<RealTensorValue>(
123 2178 : RealTensorValue::extrinsic_rotation_matrix(alpha, beta, gamma));
124 726 : computeRS();
125 :
126 726 : if (must_rotate_axes && !axes_rotated)
127 1 : mooseError("Unsupported manual angle prescription in 'MooseAppCoordTransform::setRotation'. "
128 : "For non-Cartesian coordinate systems, the only currently supported rotation is "
129 : "(alpha, beta, gamma) = (0, 90, 0)");
130 725 : }
131 :
132 : void
133 196 : MooseAppCoordTransform::setLengthUnit(const MooseUnits & length_unit)
134 : {
135 196 : _length_unit = length_unit;
136 196 : const auto scale = Real(_length_unit / MooseUnits("m"));
137 : _scale =
138 196 : std::make_unique<RealTensorValue>(RealTensorValue(scale, 0, 0, 0, scale, 0, 0, 0, scale));
139 196 : computeRS();
140 196 : }
141 :
142 : void
143 68504 : MooseAppCoordTransform::setCoordinateSystem(const Moose::CoordinateSystemType coord_type,
144 : const Direction rz_symmetry_axis)
145 : {
146 68504 : _coord_type = coord_type;
147 :
148 68504 : if (_coord_type == Moose::COORD_RZ)
149 : {
150 594 : if (rz_symmetry_axis == INVALID)
151 0 : mooseError("For RZ coordinate systems, the 'rz_symmetry_axis' parameter must be provided to "
152 : "'MooseAppCoordTransform::setCoordinateSystem'");
153 :
154 594 : _z_axis = rz_symmetry_axis;
155 594 : _r_axis = _z_axis == X ? Y : X;
156 : }
157 67910 : else if (_coord_type == Moose::COORD_RSPHERICAL)
158 57 : _r_axis = X;
159 68504 : }
160 :
161 : void
162 68493 : MooseAppCoordTransform::setCoordinateSystem(const MooseMesh & mesh)
163 : {
164 68493 : const auto & params = mesh.parameters();
165 :
166 : // If we have multiple different coordinate system types in our problem, we
167 : // take note of it because that can cause issues if there is a non-Cartesian
168 : // destination coordinate system
169 68493 : const auto & coord_sys = mesh.getCoordSystem();
170 68493 : std::unordered_set<Moose::CoordinateSystemType> coord_types;
171 68493 : auto map_it = coord_sys.begin();
172 : // It's possible that the mesh is not in a complete state
173 68493 : if (map_it == coord_sys.end())
174 2248 : setCoordinateSystem(Moose::COORD_XYZ);
175 : else
176 132490 : setCoordinateSystem(
177 66245 : map_it->second,
178 132490 : Direction(static_cast<unsigned int>(int(params.get<MooseEnum>("rz_coord_axis")))));
179 159975 : for (; map_it != coord_sys.end(); ++map_it)
180 91482 : coord_types.insert(map_it->second);
181 :
182 68493 : _has_different_coord_sys = coord_types.size() > 1;
183 :
184 68493 : if (mesh.usingGeneralAxisymmetricCoordAxes())
185 21 : _using_general_rz_coord_axes = true;
186 68493 : }
187 :
188 : InputParameters
189 342048 : MooseAppCoordTransform::validParams()
190 : {
191 342048 : auto params = emptyInputParameters();
192 : /// One entry of coord system per block, the size of _blocks and _coord_sys has to match, except:
193 : /// 1. _blocks.size() == 0, then there needs to be just one entry in _coord_sys, which will
194 : /// be set for the whole domain
195 : /// 2. _blocks.size() > 0 and no coordinate system was specified, then the whole domain will be XYZ.
196 : /// 3. _blocks.size() > 0 and one coordinate system was specified, then the whole domain will be that system.
197 342048 : params.addDeprecatedParam<std::vector<SubdomainName>>(
198 : "block",
199 : "Block IDs for the coordinate systems.",
200 : "Please use the 'coord_block' parameter instead.");
201 342048 : params.addParam<std::vector<SubdomainName>>(
202 : "coord_block",
203 : "Block IDs for the coordinate systems. If this parameter is specified, then it must "
204 : "encompass all the subdomains on the mesh.");
205 342048 : MultiMooseEnum coord_types("XYZ RZ RSPHERICAL", "XYZ");
206 342048 : MooseEnum rz_coord_axis("X=0 Y=1", "Y");
207 342048 : params.addParam<MultiMooseEnum>(
208 : "coord_type", coord_types, "Type of the coordinate system per block param");
209 342048 : params.addParam<MooseEnum>(
210 : "rz_coord_axis", rz_coord_axis, "The rotation axis (X | Y) for axisymmetric coordinates");
211 342048 : params.addParam<std::vector<SubdomainName>>(
212 : "rz_coord_blocks", "Blocks using general axisymmetric coordinate systems");
213 342048 : params.addParam<std::vector<Point>>("rz_coord_origins",
214 : "Axis origin points for each block in 'rz_coord_blocks'");
215 342048 : params.addParam<std::vector<RealVectorValue>>(
216 : "rz_coord_directions", "Axis directions for each block in 'rz_coord_blocks'");
217 342048 : params.addParam<std::string>(
218 : "length_unit",
219 : "How much distance one mesh length unit represents, e.g. 1 cm, 1 nm, 1 ft, 5inches");
220 342048 : params.addRangeCheckedParam<Real>(
221 : "alpha_rotation",
222 : "-180<alpha_rotation<=180",
223 : "The number of degrees that the domain should be alpha-rotated using the Euler "
224 : "angle ZXZ convention from https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix in "
225 : "order to align with a canonical physical space of your choosing.");
226 342048 : params.addRangeCheckedParam<Real>(
227 : "beta_rotation",
228 : "-180<beta_rotation<=180",
229 : "The number of degrees that the domain should be beta-rotated using the Euler "
230 : "angle ZXZ convention from https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix in "
231 : "order to align with a canonical physical space of your choosing.");
232 342048 : params.addRangeCheckedParam<Real>(
233 : "gamma_rotation",
234 : "-180<gamma_rotation<=180",
235 : "The number of degrees that the domain should be gamma-rotated using the Euler "
236 : "angle ZXZ convention from https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix in "
237 : "order to align with a canonical physical space of your choosing.");
238 342048 : MooseEnum up_direction("X=0 Y=1 Z=2");
239 342048 : params.addParam<MooseEnum>(
240 : "up_direction",
241 : up_direction,
242 : "Specify what axis corresponds to the up direction in physical space (the opposite of the "
243 : "gravity vector if you will). If this parameter is provided, we will perform a single 90 "
244 : "degree rotation of the domain--if the provided axis is 'x' or 'z', we will not rotate if "
245 : "the axis is 'y'--such that a point which was on the provided axis will now lie on the "
246 : "y-axis, e.g. the y-axis is our canonical up direction. If you want finer grained control "
247 : "than this, please use the 'alpha_rotation', 'beta_rotation', and 'gamma_rotation' "
248 : "parameters.");
249 342048 : params.addParamNamesToGroup(
250 : "block coord_type coord_block rz_coord_axis rz_coord_blocks rz_coord_origins "
251 : "rz_coord_directions",
252 : "Coordinate system");
253 342048 : params.addParamNamesToGroup(
254 : "length_unit alpha_rotation beta_rotation gamma_rotation up_direction",
255 : "Transformations relative to parent application frame of reference");
256 684096 : return params;
257 342048 : }
258 :
259 68256 : MooseAppCoordTransform::MooseAppCoordTransform(const MooseMesh & mesh)
260 68256 : : _coord_type(Moose::COORD_XYZ),
261 68256 : _r_axis(INVALID),
262 68256 : _z_axis(INVALID),
263 68256 : _has_different_coord_sys(false),
264 68256 : _using_general_rz_coord_axes(false),
265 68256 : _length_unit(std::string("1*m")),
266 204768 : _euler_angles(),
267 68256 : _mesh_transformed(false)
268 : {
269 : //
270 : // Coordinate system transformation
271 : //
272 68256 : setCoordinateSystem(mesh);
273 :
274 68256 : const auto & params = mesh.parameters();
275 :
276 : //
277 : // Rotation
278 : //
279 68256 : const bool has_alpha = params.isParamValid("alpha_rotation");
280 68256 : const bool has_beta = params.isParamValid("beta_rotation");
281 68256 : const bool has_gamma = params.isParamValid("gamma_rotation");
282 68256 : const auto & up_direction = params.get<MooseEnum>("up_direction");
283 :
284 68256 : if (has_alpha || has_beta || has_gamma)
285 : {
286 724 : if (up_direction.isValid())
287 0 : mooseError("Cannot simultaneously set rotation angles as well as an up direction");
288 :
289 724 : const auto alpha = (has_alpha ? params.get<Real>("alpha_rotation") : Real(0));
290 724 : const auto beta = (has_beta ? params.get<Real>("beta_rotation") : Real(0));
291 724 : const auto gamma = (has_gamma ? params.get<Real>("gamma_rotation") : Real(0));
292 :
293 724 : setRotation(alpha, beta, gamma);
294 724 : }
295 67532 : else if (up_direction.isValid())
296 0 : setUpDirection(Direction(static_cast<unsigned int>(int(up_direction))));
297 :
298 : //
299 : // Scaling
300 : //
301 68256 : if (params.isParamValid("length_unit"))
302 90 : setLengthUnit(MooseUnits(params.get<std::string>("length_unit")));
303 68256 : }
304 :
305 4 : MooseAppCoordTransform::MooseAppCoordTransform()
306 4 : : _coord_type(Moose::COORD_XYZ),
307 4 : _r_axis(INVALID),
308 4 : _z_axis(INVALID),
309 4 : _has_different_coord_sys(false),
310 4 : _using_general_rz_coord_axes(false),
311 4 : _length_unit(std::string("1*m")),
312 12 : _euler_angles(),
313 4 : _mesh_transformed(false)
314 : {
315 4 : }
316 :
317 1 : MooseAppCoordTransform::MooseAppCoordTransform(const MooseAppCoordTransform & other)
318 1 : : _coord_type(other._coord_type),
319 1 : _r_axis(other._r_axis),
320 1 : _z_axis(other._z_axis),
321 1 : _has_different_coord_sys(other._has_different_coord_sys),
322 1 : _using_general_rz_coord_axes(other._using_general_rz_coord_axes),
323 1 : _length_unit(other._length_unit),
324 1 : _euler_angles(other._euler_angles),
325 1 : _mesh_transformed(other._mesh_transformed)
326 : {
327 1 : if (other._scale)
328 0 : _scale = std::make_unique<RealTensorValue>(*other._scale);
329 1 : if (other._rotate)
330 1 : _rotate = std::make_unique<RealTensorValue>(*other._rotate);
331 1 : computeRS();
332 1 : }
333 :
334 149060 : MooseAppCoordTransform::MooseAppCoordTransform(const MinimalData & minimal_data)
335 149060 : : _coord_type(static_cast<Moose::CoordinateSystemType>(std::get<4>(minimal_data))),
336 149060 : _r_axis(static_cast<Direction>(std::get<5>(minimal_data))),
337 149060 : _z_axis(static_cast<Direction>(std::get<6>(minimal_data))),
338 149060 : _has_different_coord_sys(std::get<7>(minimal_data)),
339 149060 : _using_general_rz_coord_axes(std::get<8>(minimal_data)),
340 149060 : _length_unit(std::string("1*m")),
341 149060 : _euler_angles(std::get<3>(minimal_data)),
342 298120 : _mesh_transformed(std::get<9>(minimal_data))
343 : {
344 149060 : if (std::get<0>(minimal_data))
345 106 : setLengthUnit(MooseUnits(std::to_string(std::get<1>(minimal_data)) + "*m"));
346 149060 : if (std::get<2>(minimal_data))
347 2732 : _rotate = std::make_unique<RealTensorValue>(RealTensorValue::extrinsic_rotation_matrix(
348 2732 : _euler_angles[0], _euler_angles[1], _euler_angles[2]));
349 149060 : computeRS();
350 149060 : }
351 :
352 : MooseAppCoordTransform::MinimalData
353 112104 : MooseAppCoordTransform::minimalDataDescription() const
354 : {
355 112104 : const Real scale_factor = _scale ? (*_scale)(0, 0) : 1;
356 112104 : return {static_cast<short int>(bool(_scale)),
357 : scale_factor,
358 112104 : static_cast<short int>(bool(_rotate)),
359 112104 : _euler_angles,
360 224208 : static_cast<int>(_coord_type),
361 224208 : static_cast<unsigned int>(_r_axis),
362 224208 : static_cast<unsigned int>(_z_axis),
363 224208 : static_cast<short int>(_has_different_coord_sys),
364 224208 : static_cast<short int>(_using_general_rz_coord_axes),
365 112104 : static_cast<short int>(_mesh_transformed)};
366 : }
367 :
368 : MooseAppCoordTransform &
369 1 : MooseAppCoordTransform::operator=(const MooseAppCoordTransform & other)
370 : {
371 1 : _coord_type = other._coord_type;
372 1 : _r_axis = other._r_axis;
373 1 : _z_axis = other._z_axis;
374 1 : _has_different_coord_sys = other._has_different_coord_sys;
375 1 : _using_general_rz_coord_axes = other._using_general_rz_coord_axes;
376 1 : _length_unit = other._length_unit;
377 1 : _euler_angles = other._euler_angles;
378 1 : _mesh_transformed = other._mesh_transformed;
379 :
380 1 : if (other._scale)
381 0 : _scale = std::make_unique<RealTensorValue>(*other._scale);
382 : else
383 1 : _scale.reset();
384 1 : if (other._rotate)
385 1 : _rotate = std::make_unique<RealTensorValue>(*other._rotate);
386 : else
387 0 : _rotate.reset();
388 :
389 1 : computeRS();
390 :
391 1 : return *this;
392 : }
393 :
394 : void
395 149990 : MooseAppCoordTransform::computeRS()
396 : {
397 149990 : if (_scale || _rotate)
398 : {
399 2296 : _rs = std::make_unique<RealTensorValue>(RealTensorValue(1, 0, 0, 0, 1, 0, 0, 0, 1));
400 :
401 2296 : if (_scale)
402 302 : *_rs = *_scale * *_rs;
403 2296 : if (_rotate)
404 2186 : *_rs = *_rotate * *_rs;
405 :
406 2296 : _rs_inverse = std::make_unique<RealTensorValue>(_rs->inverse());
407 : }
408 : else
409 : {
410 147694 : _rs.reset();
411 147694 : _rs_inverse.reset();
412 : }
413 149990 : }
414 :
415 : void
416 274 : MooseAppCoordTransform::transformMesh(MooseMesh & mesh, const Point & translation)
417 : {
418 : // Transforming a RZ or R-spherical mesh doesnt always make sense, disallow it
419 274 : if (_coord_type != Moose::COORD_XYZ)
420 4 : mooseError("Running MultiApps 'in position' is only supported for XYZ coordinate systems");
421 :
422 270 : if (_using_general_rz_coord_axes && hasScalingOrRotationTransformation())
423 0 : mooseError("Scaling and rotation are currently not supported for general axisymmetric "
424 : "coordinate systems.");
425 :
426 270 : if (_mesh_transformed)
427 0 : mooseError("App mesh is being transformed twice");
428 :
429 : // Apply all the transformation to the mesh
430 270 : if (_scale)
431 13 : MeshTools::Modification::scale(mesh, (*_scale)(0, 0), (*_scale)(1, 1), (*_scale)(2, 2));
432 270 : if (_rotate)
433 13 : MeshTools::Modification::rotate(mesh, _euler_angles[0], _euler_angles[1], _euler_angles[2]);
434 270 : if (translation != Point(0, 0, 0))
435 270 : MeshTools::Modification::translate(mesh, translation(0), translation(1), translation(2));
436 :
437 : // Translation, scaling and rotation need not be applied anymore when performing coordinate
438 : // transforms
439 270 : _mesh_transformed = true;
440 270 : }
441 :
442 : bool
443 209518 : MooseAppCoordTransform::hasScalingOrRotationTransformation() const
444 : {
445 209518 : if (_rs)
446 2184 : for (const auto i : make_range(Moose::dim))
447 2592 : for (const auto j : make_range(Moose::dim))
448 : {
449 2490 : const auto matrix_elem = (*_rs)(i, j);
450 2490 : if (i == j)
451 : {
452 2184 : if (!MooseUtils::absoluteFuzzyEqual(matrix_elem, 1))
453 2082 : return true;
454 : }
455 306 : else if (!MooseUtils::absoluteFuzzyEqual(matrix_elem, 0))
456 0 : return true;
457 : }
458 :
459 207436 : return false;
460 : }
461 :
462 175523 : MultiAppCoordTransform::MultiAppCoordTransform(const MooseAppCoordTransform & our_app_transform)
463 175523 : : _our_app_transform(our_app_transform),
464 175523 : _destination_app_transform(nullptr),
465 175523 : _skip_coordinate_collapsing(false)
466 : {
467 175523 : }
468 :
469 : Point
470 54647050 : MultiAppCoordTransform::operator()(const Point & point) const
471 : {
472 : mooseAssert(_destination_app_transform, "The destination application transform must be set");
473 :
474 54647050 : Point ret(point);
475 :
476 : // Translation, rotation and scaling already applied, coordinate system conversion not supported
477 54647050 : if (_our_app_transform._mesh_transformed)
478 961122 : return ret;
479 :
480 : // Apply scaling and then rotation
481 53685928 : if (_our_app_transform._rs)
482 1033140 : ret = (*_our_app_transform._rs) * ret;
483 :
484 : // If this shows up in profiling we can make _translation a pointer
485 53685928 : ret += _translation;
486 :
487 53685928 : if (_skip_coordinate_collapsing)
488 53623360 : return ret;
489 :
490 : // Finally, coordinate system conversions
491 62568 : if (_our_app_transform._coord_type == Moose::COORD_XYZ &&
492 33710 : _destination_app_transform->_coord_type == Moose::COORD_RZ)
493 : {
494 33709 : Real r_squared = 0;
495 134836 : for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
496 101127 : if (i != _destination_app_transform->_z_axis)
497 67418 : r_squared += ret(i) * ret(i);
498 :
499 33709 : const auto r = std::sqrt(r_squared);
500 33709 : const auto z = ret(_destination_app_transform->_z_axis);
501 33709 : ret = 0;
502 33709 : ret(_destination_app_transform->_r_axis) = r;
503 33709 : ret(_destination_app_transform->_z_axis) = z;
504 33709 : }
505 28859 : else if (_our_app_transform._coord_type == Moose::COORD_XYZ &&
506 1 : _destination_app_transform->_coord_type == Moose::COORD_RSPHERICAL)
507 : {
508 1 : Real r_squared = 0;
509 4 : for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
510 3 : r_squared += ret(i) * ret(i);
511 :
512 1 : const auto r = std::sqrt(r_squared);
513 1 : ret = 0;
514 1 : ret(_destination_app_transform->_r_axis) = r;
515 1 : }
516 28858 : else if (_our_app_transform._coord_type == Moose::COORD_RZ &&
517 28858 : _destination_app_transform->_coord_type == Moose::COORD_RSPHERICAL)
518 : {
519 1 : Real r_squared = 0;
520 4 : for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
521 : {
522 : mooseAssert(
523 : i == _our_app_transform._r_axis || i == _our_app_transform._z_axis ||
524 : MooseUtils::absoluteFuzzyEqual(ret(i), 0),
525 : "Our point should be 0 if we are evaluating at an index that is neither our r or z-axis");
526 3 : r_squared += ret(i) * ret(i);
527 : }
528 :
529 1 : const auto r = std::sqrt(r_squared);
530 1 : ret = 0;
531 1 : ret(_destination_app_transform->_r_axis) = r;
532 : }
533 :
534 62568 : return ret;
535 : }
536 :
537 : Point
538 1595910 : MultiAppCoordTransform::mapBack(const Point & point) const
539 : {
540 1595910 : Point ret(point);
541 :
542 : // Translation, rotation and scaling already applied, coordinate system conversion not supported
543 1595910 : if (_our_app_transform._mesh_transformed)
544 0 : return ret;
545 :
546 : // inverse translate
547 1595910 : ret -= _translation;
548 :
549 : // inverse rotate and then inverse scale
550 1595910 : if (_our_app_transform._rs_inverse)
551 34438 : ret = (*_our_app_transform._rs_inverse) * ret;
552 :
553 1595910 : if (_skip_coordinate_collapsing)
554 1595907 : return ret;
555 :
556 : // Finally, coordinate system conversions
557 3 : if ((_our_app_transform._coord_type == Moose::COORD_XYZ &&
558 2 : (_destination_app_transform->_coord_type == Moose::COORD_RZ ||
559 1 : _destination_app_transform->_coord_type == Moose::COORD_RSPHERICAL)) ||
560 1 : (_our_app_transform._coord_type == Moose::COORD_RZ &&
561 1 : _destination_app_transform->_coord_type == Moose::COORD_RSPHERICAL))
562 3 : mooseError("Coordinate collapsing occurred in going to the reference space. There is no unique "
563 : "return mapping");
564 :
565 0 : return ret;
566 : }
567 :
568 : void
569 175119 : MultiAppCoordTransform::setDestinationCoordTransform(
570 : const MooseAppCoordTransform & destination_app_transform)
571 : {
572 175119 : _destination_app_transform = &destination_app_transform;
573 :
574 175119 : if (_our_app_transform._using_general_rz_coord_axes &&
575 0 : _our_app_transform.hasScalingOrRotationTransformation())
576 0 : mooseError("Scaling and rotation are currently not supported for general axisymmetric "
577 : "coordinate systems.");
578 :
579 : // Don't error check mismatching coordinate system types if we've been asked to skip coordinate
580 : // collapsing since in that case the mismatch doesn't matter
581 175119 : if (_skip_coordinate_collapsing)
582 174963 : return;
583 :
584 156 : if (_destination_app_transform->_has_different_coord_sys &&
585 0 : (_our_app_transform._has_different_coord_sys ||
586 0 : _our_app_transform._coord_type != Moose::COORD_RSPHERICAL))
587 0 : mooseError(
588 : "The destination coordinate system has different coordinate systems and we have coordinate "
589 : "system(s) that could require coordinate collapsing when transforming from our coordinate "
590 : "system to the destination coordinate system. Because our transform method only takes a "
591 : "point argument, and not subdomain arguments, the transform is ambiguous");
592 :
593 156 : if ((_destination_app_transform->_coord_type == Moose::COORD_RZ ||
594 79 : _destination_app_transform->_coord_type == Moose::COORD_RSPHERICAL) &&
595 79 : _our_app_transform._has_different_coord_sys)
596 0 : mooseError("When the destination coordinate system is RZ or RSPHERICAL, we have to perform "
597 : "coordinate collapsing based on *our* coordinate system. However, we have multiple "
598 : "coordinate systems, and since when evaluating transformations, we are only "
599 : "called with a Point argument, we do not know what subdomain we are on and "
600 : "consequently we do not know what coordinate collapse to apply.");
601 :
602 156 : if (_our_app_transform._using_general_rz_coord_axes ||
603 156 : _destination_app_transform->_using_general_rz_coord_axes)
604 0 : mooseError("If either this app or the destination app uses general axisymmetric axes, "
605 : "coordinate collapsing must be skipped.");
606 : }
607 :
608 : bool
609 149055 : MultiAppCoordTransform::hasNonTranslationTransformation() const
610 : {
611 149055 : if (_our_app_transform.hasScalingOrRotationTransformation())
612 1358 : return true;
613 :
614 147697 : if (_skip_coordinate_collapsing)
615 147618 : return false;
616 :
617 79 : if (hasCoordinateSystemTypeChange())
618 79 : return true;
619 :
620 0 : return false;
621 : }
622 :
623 : bool
624 10577 : MultiAppCoordTransform::hasCoordinateSystemTypeChange() const
625 : {
626 10577 : if (_skip_coordinate_collapsing)
627 10498 : return false;
628 :
629 79 : if ((_our_app_transform._coord_type == Moose::COORD_XYZ &&
630 78 : (_destination_app_transform->_coord_type == Moose::COORD_RZ ||
631 1 : _destination_app_transform->_coord_type == Moose::COORD_RSPHERICAL)) ||
632 1 : (_our_app_transform._coord_type == Moose::COORD_RZ &&
633 1 : _destination_app_transform->_coord_type == Moose::COORD_RSPHERICAL))
634 79 : return true;
635 :
636 0 : return false;
637 : }
638 :
639 : bool
640 0 : MultiAppCoordTransform::isIdentity() const
641 : {
642 0 : if (hasNonTranslationTransformation())
643 0 : return false;
644 :
645 0 : for (const auto i : make_range(Moose::dim))
646 0 : if (!MooseUtils::absoluteFuzzyEqual(_translation(i), 0))
647 0 : return false;
648 :
649 0 : return true;
650 : }
651 :
652 : void
653 175519 : MultiAppCoordTransform::skipCoordinateCollapsing(const bool skip_coordinate_collapsing)
654 : {
655 175519 : _skip_coordinate_collapsing = skip_coordinate_collapsing;
656 175519 : }
657 :
658 : void
659 108230 : MultiAppCoordTransform::setTranslationVector(const Point & translation)
660 : {
661 108230 : _translation = translation;
662 108230 : }
|