https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MooseAppCoordTransform.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#include "InputParameters.h"
12#include "MultiMooseEnum.h"
13#include "MooseEnum.h"
14#include "MooseMesh.h"
15#include "libmesh/mesh_modification.h"
16
17using namespace libMesh;
18
24
25void
27{
28 Real alpha = 0, beta = 0, gamma = 0;
29
30 const bool must_rotate_axes =
32 // Don't error immediately for unit testing purposes
33 bool negative_radii = false;
34
35 if (up_direction == X)
36 {
37 alpha = 90, beta = 0, gamma = 0;
38 if (must_rotate_axes)
39 {
40 if (_r_axis == X)
41 {
42 _r_axis = Y;
44 }
45 else if (_r_axis == Y)
46 {
47 negative_radii = true;
48 _r_axis = X;
50 }
51 else
52 mooseError("Bad r-axis value");
53 }
54 }
55 else if (up_direction == Y)
56 alpha = 0, beta = 0, gamma = 0;
57 else if (up_direction == Z)
58 {
59 alpha = 0, beta = -90, gamma = 0;
60 if (must_rotate_axes)
61 {
62 if (_r_axis == X)
63 {
64 _r_axis = X;
66 }
67 else if (_r_axis == Y)
68 {
69 negative_radii = true;
70 _r_axis = Z;
72 }
73 else
74 mooseError("Bad r-axis value");
75 }
76 }
77 else
78 mooseError("Bad up direction value");
79
80 _euler_angles = {{alpha, beta, gamma}};
81
82 _rotate = std::make_unique<RealTensorValue>(
84 computeRS();
85
86 if (negative_radii)
87 mooseError("Rotation yields negative radial values");
88}
89
90void
91MooseAppCoordTransform::setRotation(const Real alpha, const Real beta, const Real gamma)
92{
93 const bool must_rotate_axes =
95 bool axes_rotated = false;
96 if (must_rotate_axes)
97 {
98 const auto angles = std::make_tuple(alpha, beta, gamma);
99 if (angles == std::make_tuple(0, 90, 0))
100 {
101 if (_r_axis == X)
102 {
103 mooseAssert((_coord_type == Moose::COORD_RZ && _z_axis == Y) ||
105 "'_z_axis' is not an expected value");
106 _r_axis = X;
108 }
109 else if (_r_axis == Y)
110 {
111 mooseAssert((_coord_type == Moose::COORD_RZ && _z_axis == X) ||
113 "'_z_axis' is not an expected value");
114 _r_axis = Z;
116 }
117 axes_rotated = true;
118 }
119 }
120
121 _euler_angles = {{alpha, beta, gamma}};
122 _rotate = std::make_unique<RealTensorValue>(
124 computeRS();
125
126 if (must_rotate_axes && !axes_rotated)
127 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}
131
132void
134{
135 _length_unit = length_unit;
136 const auto scale = Real(_length_unit / MooseUnits("m"));
137 _scale =
138 std::make_unique<RealTensorValue>(RealTensorValue(scale, 0, 0, 0, scale, 0, 0, 0, scale));
139 computeRS();
140}
141
142void
144 const Direction rz_symmetry_axis)
145{
146 _coord_type = coord_type;
147
149 {
150 if (rz_symmetry_axis == INVALID)
151 mooseError("For RZ coordinate systems, the 'rz_symmetry_axis' parameter must be provided to "
152 "'MooseAppCoordTransform::setCoordinateSystem'");
153
154 _z_axis = rz_symmetry_axis;
155 _r_axis = _z_axis == X ? Y : X;
156 }
158 _r_axis = X;
159}
160
161void
163{
164 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 const auto & coord_sys = mesh.getCoordSystem();
170 std::unordered_set<Moose::CoordinateSystemType> coord_types;
171 auto map_it = coord_sys.begin();
172 // It's possible that the mesh is not in a complete state
173 if (map_it == coord_sys.end())
175 else
177 map_it->second,
178 Direction(static_cast<unsigned int>(int(params.get<MooseEnum>("rz_coord_axis")))));
179 for (; map_it != coord_sys.end(); ++map_it)
180 coord_types.insert(map_it->second);
181
182 _has_different_coord_sys = coord_types.size() > 1;
183
184 if (mesh.usingGeneralAxisymmetricCoordAxes())
186}
187
190{
191 auto params = emptyInputParameters();
197 params.addDeprecatedParam<std::vector<SubdomainName>>(
198 "block",
199 "Block IDs for the coordinate systems.",
200 "Please use the 'coord_block' parameter instead.");
201 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 MultiMooseEnum coord_types("XYZ RZ RSPHERICAL", "XYZ");
206 MooseEnum rz_coord_axis("X=0 Y=1", "Y");
207 params.addParam<MultiMooseEnum>(
208 "coord_type", coord_types, "Type of the coordinate system per block param");
209 params.addParam<MooseEnum>(
210 "rz_coord_axis", rz_coord_axis, "The rotation axis (X | Y) for axisymmetric coordinates");
211 params.addParam<std::vector<SubdomainName>>(
212 "rz_coord_blocks", "Blocks using general axisymmetric coordinate systems");
213 params.addParam<std::vector<Point>>("rz_coord_origins",
214 "Axis origin points for each block in 'rz_coord_blocks'");
215 params.addParam<std::vector<RealVectorValue>>(
216 "rz_coord_directions", "Axis directions for each block in 'rz_coord_blocks'");
217 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 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 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 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 MooseEnum up_direction("X=0 Y=1 Z=2");
239 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 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 params.addParamNamesToGroup(
254 "length_unit alpha_rotation beta_rotation gamma_rotation up_direction",
255 "Transformations relative to parent application frame of reference");
256 return params;
257}
258
260 : _coord_type(Moose::COORD_XYZ),
261 _r_axis(INVALID),
262 _z_axis(INVALID),
263 _has_different_coord_sys(false),
264 _using_general_rz_coord_axes(false),
265 _length_unit(std::string("1*m")),
266 _euler_angles(),
267 _mesh_transformed(false)
268{
269 //
270 // Coordinate system transformation
271 //
273
274 const auto & params = mesh.parameters();
275
276 //
277 // Rotation
278 //
279 const bool has_alpha = params.isParamValid("alpha_rotation");
280 const bool has_beta = params.isParamValid("beta_rotation");
281 const bool has_gamma = params.isParamValid("gamma_rotation");
282 const auto & up_direction = params.get<MooseEnum>("up_direction");
283
284 if (has_alpha || has_beta || has_gamma)
285 {
286 if (up_direction.isValid())
287 mooseError("Cannot simultaneously set rotation angles as well as an up direction");
288
289 const auto alpha = (has_alpha ? params.get<Real>("alpha_rotation") : Real(0));
290 const auto beta = (has_beta ? params.get<Real>("beta_rotation") : Real(0));
291 const auto gamma = (has_gamma ? params.get<Real>("gamma_rotation") : Real(0));
292
293 setRotation(alpha, beta, gamma);
294 }
295 else if (up_direction.isValid())
296 setUpDirection(Direction(static_cast<unsigned int>(int(up_direction))));
297
298 //
299 // Scaling
300 //
301 if (params.isParamValid("length_unit"))
302 setLengthUnit(MooseUnits(params.get<std::string>("length_unit")));
303}
304
306 : _coord_type(Moose::COORD_XYZ),
307 _r_axis(INVALID),
308 _z_axis(INVALID),
309 _has_different_coord_sys(false),
310 _using_general_rz_coord_axes(false),
311 _length_unit(std::string("1*m")),
312 _euler_angles(),
313 _mesh_transformed(false)
314{
315}
316
318 : _coord_type(other._coord_type),
319 _r_axis(other._r_axis),
320 _z_axis(other._z_axis),
321 _has_different_coord_sys(other._has_different_coord_sys),
322 _using_general_rz_coord_axes(other._using_general_rz_coord_axes),
323 _length_unit(other._length_unit),
324 _euler_angles(other._euler_angles),
325 _mesh_transformed(other._mesh_transformed)
326{
327 if (other._scale)
328 _scale = std::make_unique<RealTensorValue>(*other._scale);
329 if (other._rotate)
330 _rotate = std::make_unique<RealTensorValue>(*other._rotate);
331 computeRS();
332}
333
335 : _coord_type(static_cast<Moose::CoordinateSystemType>(std::get<4>(minimal_data))),
336 _r_axis(static_cast<Direction>(std::get<5>(minimal_data))),
337 _z_axis(static_cast<Direction>(std::get<6>(minimal_data))),
338 _has_different_coord_sys(std::get<7>(minimal_data)),
339 _using_general_rz_coord_axes(std::get<8>(minimal_data)),
340 _length_unit(std::string("1*m")),
341 _euler_angles(std::get<3>(minimal_data)),
342 _mesh_transformed(std::get<9>(minimal_data))
343{
344 if (std::get<0>(minimal_data))
345 setLengthUnit(MooseUnits(std::to_string(std::get<1>(minimal_data)) + "*m"));
346 if (std::get<2>(minimal_data))
347 _rotate = std::make_unique<RealTensorValue>(RealTensorValue::extrinsic_rotation_matrix(
349 computeRS();
350}
351
354{
355 const Real scale_factor = _scale ? (*_scale)(0, 0) : 1;
356 return {static_cast<short int>(bool(_scale)),
357 scale_factor,
358 static_cast<short int>(bool(_rotate)),
360 static_cast<int>(_coord_type),
361 static_cast<unsigned int>(_r_axis),
362 static_cast<unsigned int>(_z_axis),
363 static_cast<short int>(_has_different_coord_sys),
364 static_cast<short int>(_using_general_rz_coord_axes),
365 static_cast<short int>(_mesh_transformed)};
366}
367
370{
371 _coord_type = other._coord_type;
372 _r_axis = other._r_axis;
373 _z_axis = other._z_axis;
379
380 if (other._scale)
381 _scale = std::make_unique<RealTensorValue>(*other._scale);
382 else
383 _scale.reset();
384 if (other._rotate)
385 _rotate = std::make_unique<RealTensorValue>(*other._rotate);
386 else
387 _rotate.reset();
388
389 computeRS();
390
391 return *this;
392}
393
394void
396{
397 if (_scale || _rotate)
398 {
399 _rs = std::make_unique<RealTensorValue>(RealTensorValue(1, 0, 0, 0, 1, 0, 0, 0, 1));
400
401 if (_scale)
402 *_rs = *_scale * *_rs;
403 if (_rotate)
404 *_rs = *_rotate * *_rs;
405
406 _rs_inverse = std::make_unique<RealTensorValue>(_rs->inverse());
407 }
408 else
409 {
410 _rs.reset();
411 _rs_inverse.reset();
412 }
413}
414
415void
417{
418 // Transforming a RZ or R-spherical mesh doesnt always make sense, disallow it
420 mooseError("Running MultiApps 'in position' is only supported for XYZ coordinate systems");
421
423 mooseError("Scaling and rotation are currently not supported for general axisymmetric "
424 "coordinate systems.");
425
427 mooseError("App mesh is being transformed twice");
428
429 // Apply all the transformation to the mesh
430 if (_scale)
431 MeshTools::Modification::scale(mesh, (*_scale)(0, 0), (*_scale)(1, 1), (*_scale)(2, 2));
432 if (_rotate)
434 if (translation != Point(0, 0, 0))
435 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 _mesh_transformed = true;
440}
441
442bool
444{
445 if (_rs)
446 for (const auto i : make_range(Moose::dim))
447 for (const auto j : make_range(Moose::dim))
448 {
449 const auto matrix_elem = (*_rs)(i, j);
450 if (i == j)
451 {
452 if (!MooseUtils::absoluteFuzzyEqual(matrix_elem, 1))
453 return true;
454 }
455 else if (!MooseUtils::absoluteFuzzyEqual(matrix_elem, 0))
456 return true;
457 }
458
459 return false;
460}
461
463 : _our_app_transform(our_app_transform),
464 _destination_app_transform(nullptr),
465 _skip_coordinate_collapsing(false)
466{
467}
468
469Point
471{
472 mooseAssert(_destination_app_transform, "The destination application transform must be set");
473
474 Point ret(point);
475
476 // Translation, rotation and scaling already applied, coordinate system conversion not supported
478 return ret;
479
480 // Apply scaling and then rotation
482 ret = (*_our_app_transform._rs) * ret;
483
484 // If this shows up in profiling we can make _translation a pointer
485 ret += _translation;
486
488 return ret;
489
490 // Finally, coordinate system conversions
493 {
494 Real r_squared = 0;
495 for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
497 r_squared += ret(i) * ret(i);
498
499 const auto r = std::sqrt(r_squared);
500 const auto z = ret(_destination_app_transform->_z_axis);
501 ret = 0;
504 }
507 {
508 Real r_squared = 0;
509 for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
510 r_squared += ret(i) * ret(i);
511
512 const auto r = std::sqrt(r_squared);
513 ret = 0;
515 }
518 {
519 Real r_squared = 0;
520 for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
521 {
522 mooseAssert(
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 r_squared += ret(i) * ret(i);
527 }
528
529 const auto r = std::sqrt(r_squared);
530 ret = 0;
532 }
533
534 return ret;
535}
536
537Point
539{
540 Point ret(point);
541
542 // Translation, rotation and scaling already applied, coordinate system conversion not supported
544 return ret;
545
546 // inverse translate
547 ret -= _translation;
548
549 // inverse rotate and then inverse scale
551 ret = (*_our_app_transform._rs_inverse) * ret;
552
554 return ret;
555
556 // Finally, coordinate system conversions
562 mooseError("Coordinate collapsing occurred in going to the reference space. There is no unique "
563 "return mapping");
564
565 return ret;
566}
567
568void
570 const MooseAppCoordTransform & destination_app_transform)
571{
572 _destination_app_transform = &destination_app_transform;
573
576 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
582 return;
583
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
596 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
604 mooseError("If either this app or the destination app uses general axisymmetric axes, "
605 "coordinate collapsing must be skipped.");
606}
607
608bool
610{
612 return true;
613
615 return false;
616
618 return true;
619
620 return false;
621}
622
623bool
638
639bool
641{
643 return false;
644
645 for (const auto i : make_range(Moose::dim))
646 if (!MooseUtils::absoluteFuzzyEqual(_translation(i), 0))
647 return false;
648
649 return true;
650}
651
652void
653MultiAppCoordTransform::skipCoordinateCollapsing(const bool skip_coordinate_collapsing)
654{
655 _skip_coordinate_collapsing = skip_coordinate_collapsing;
656}
657
658void
660{
661 _translation = translation;
662}
InputParameters emptyInputParameters()
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
Real scale
Definition MortarUtils.C:62
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
MooseUnits _length_unit
How much distance one mesh length unit represents, e.g. 1 cm, 1 nm, 1 ft, 5 inches.
std::array< Real, 3 > _euler_angles
The Euler angles describing rotation.
void setCoordinateSystem(Moose::CoordinateSystemType system_type, Direction rz_symmetry_axis=INVALID)
Set our coordinate system.
std::unique_ptr< libMesh::RealTensorValue > _rotate
Represents a forward rotation transformation from our domain to the reference frame domain.
MinimalData minimalDataDescription() const
bool _has_different_coord_sys
Whether we have different coordinate systems within our single domain.
bool hasScalingOrRotationTransformation() const
Returns true if the app has scaling and/or rotation transformation.
Direction processZAxis(Direction z_axis)
If the coordinate system type is RZ, then we return the provided argument.
std::unique_ptr< libMesh::RealTensorValue > _scale
Represents a forward scaling transformation from our units to reference frame units of meters.
Moose::CoordinateSystemType _coord_type
Our coordinate system.
MooseAppCoordTransform()
Default constructor.
void setUpDirection(Direction up_direction)
Will setup a rotation transformation.
Direction _r_axis
If we are RZ or RSPHERICAL, the Cartesian axis corresponding to the radial coordinate.
std::unique_ptr< libMesh::RealTensorValue > _rs_inverse
Represents the inverse of the product of rotation and scaling transformations.
std::unique_ptr< libMesh::RealTensorValue > _rs
Represents the product of rotation and scaling transformations.
void computeRS()
Compute the RS and (RS)^{-1} matrices.
Direction
A class scope enumeration for conveniently denoting X, Y, and Z axis directions.
bool _using_general_rz_coord_axes
Whether general axisymmetric coordinate axes are being used.
Direction _z_axis
If we are RZ, the Cartesian axis corresponding to the axial/axis-of-symmetry coordinate.
static InputParameters validParams()
Describes the parameters this object can take to setup transformations.
MooseAppCoordTransform & operator=(const MooseAppCoordTransform &other)
void setRotation(Real alpha, Real beta, Real gamma)
Setup an \emph extrinsic rotation defined in the following way:
void setLengthUnit(const MooseUnits &length_unit)
Set the scaling transformation.
std::tuple< short int, Real, short int, std::array< Real, 3 >, int, unsigned int, unsigned int, short int, short int, short int > MinimalData
A typedef for conveniency that describes the minimal data necessary to broadcast and build a MooseApp...
bool _mesh_transformed
Whether the mesh has been translated and rotated.
void transformMesh(MooseMesh &mesh, const libMesh::Point &translation)
Transforms the entire mesh with the coordinate transform This can be done to output in position,...
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
Physical unit management class with runtime unit string parsing, unit checking, unit conversion,...
Definition Units.h:33
libMesh::Point mapBack(const libMesh::Point &point) const
Inverse transform from the reference space to our space.
MultiAppCoordTransform(const MooseAppCoordTransform &our_app_transform)
libMesh::Point operator()(const libMesh::Point &point) const
Transforms a point from our domain into the reference domain.
libMesh::Point _translation
Describes a forward translation transformation from our domain to the reference frame domain.
void setDestinationCoordTransform(const MooseAppCoordTransform &destination_coord_transform)
Set the destination coordinate system and destination radial and symmetry axes as appropriate for RZ ...
const MooseAppCoordTransform & _our_app_transform
A reference to the MooseAppCoordTransform object that describes scaling, rotation,...
void setTranslationVector(const libMesh::Point &translation)
Set how much our domain should be translated in order to match a reference frame.
bool skipCoordinateCollapsing() const
whether coordinate collapsing operations should be skipped
const MooseAppCoordTransform * _destination_app_transform
A pointer to the MooseAppCoordTransform object that describes scaling, rotation, and coordinate syste...
bool _skip_coordinate_collapsing
whether coordinate collapsing operations should be skipped
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type.
static TensorValue< Real > extrinsic_rotation_matrix(Real angle1_deg, Real angle2_deg, Real angle3_deg)
MeshBase & mesh
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition Moose.h:165
CoordinateSystemType
Definition MooseTypes.h:864
@ COORD_RZ
Definition MooseTypes.h:866
@ COORD_RSPHERICAL
Definition MooseTypes.h:867
@ COORD_XYZ
Definition MooseTypes.h:865
void scale(MeshBase &mesh, const Real xs, const Real ys=0., const Real zs=0.)
RealTensorValue rotate(MeshBase &mesh, const Real phi, const Real theta=0., const Real psi=0.)
void translate(MeshBase &mesh, const Real xt=0., const Real yt=0., const Real zt=0.)
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
TensorValue< Real > RealTensorValue
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)