Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e129cd2
separate the checking of surface presence in base
kkiesling Feb 4, 2026
76789a5
allow for transformations to be applied to any CSG Object type
kkiesling Feb 4, 2026
b77d2c2
include transformation lists in the json output using string notation
kkiesling Feb 4, 2026
3891b2c
add some convenience transform methods
kkiesling Feb 6, 2026
c66af2e
getTransformation methods can and should be public
kkiesling Feb 17, 2026
8ed5e55
add convenience methods for translation
kkiesling Feb 17, 2026
2d2b032
unit tests for transformation methods
kkiesling Feb 17, 2026
2b8e7b2
monodirectional convenience functions don't add much value and just t…
kkiesling Feb 18, 2026
daa3fd2
csg translation documentation
kkiesling Feb 18, 2026
ae942e8
add rotation to CSG MG test for example purposes
kkiesling Feb 18, 2026
90622da
add examples to docs
kkiesling Feb 19, 2026
9f11c18
minor doc fixes
kkiesling Feb 26, 2026
229c104
simplified check
kkiesling Feb 26, 2026
97bdc17
negative scales are valid, but 0 is not
kkiesling Feb 26, 2026
0bf54e6
use tuple of reals instead of vector to avoid having to check length
kkiesling Feb 26, 2026
14862c9
failsafe assert to make sure objs are correct
kkiesling Feb 26, 2026
b2751c1
use setup function for less redundancy
kkiesling Feb 27, 2026
e36ef7c
remove unnecessary includes
kkiesling Feb 27, 2026
e2e24ac
move transfromations for surfaces to private for consistency with oth…
kkiesling Feb 27, 2026
847d014
clang format
kkiesling Feb 27, 2026
a38a4d9
include not actually needed
kkiesling Feb 27, 2026
ce517fe
fix scaling restrictions in docs
kkiesling Feb 27, 2026
c07e899
typo
kkiesling Feb 27, 2026
6c341a1
correct documentation wording
kkiesling Mar 6, 2026
735736a
rename apply->add for more accurate nomenclature
kkiesling Mar 6, 2026
4e2558e
check transformations in equality too
kkiesling Mar 6, 2026
9b7461f
typo
kkiesling Mar 6, 2026
6210b19
add note about using rotations for y-oriented hex lattices
kkiesling Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions framework/doc/content/source/csg/CSGBase.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ An example of the $(i, j)$ indices for a 3-ring hexagonal lattice is shown in [!
id=fig:hex_lat_row
caption=Example of a 3-ring hexagonal lattice that has the location indices labeled in the $(i, j)$ form.

!alert! note title=Y-Oriented Hexagonal Lattices

The `CSGHexagonalLattice` class assumes an x-oriented lattice layout. To define a y-oriented lattice, the recommendation is to first define an x-oriented lattice and apply a [rotation transformation](#transformations). All indexing in this case will still remain consistent with the x-oriented indexing as described below.

!alert-end!

Convenience methods are provided for `CSGHexagonalLattice` objects to convert between the required $(i, j)$ indices and a ring-based $(r, p)$ indexing scheme.
In the ring-based scheme, the outermost ring is the 0th ring, and the right-most element in the ring is the 0th position of the ring with indices increasing counter-clockwise around the ring.
For the 3-ring lattice above in [!ref](fig:hex_lat_row), the corresponding $(r, p)$ indices would be as shown in [!ref](fig:hex_lat_ring).
Expand Down Expand Up @@ -318,6 +324,60 @@ The `setUniverseAtLatticeIndex` method is not meant to be used to change a latti

!alert-end!

### Transformations

Three types of object transformations are supported in the `CSGBase` class: rotation, translation, and scaling.
These transformations can be applied to any `CSGBase` object (`CSGSurface`, `CSGRegion`, `CSGCell`, `CSGUniverse`, and `CSGLattice`).
When a transformation is applied, existing attributes for the object (such as surface coefficients) are not directly altered.
Instead, information about the transformation applied is stored as vector of pairs on the object, where the first item in the pair is the type of transformation and the second item is the value of the transformation (stored as a size 3 tuple).
The order of the transformation vector is the order in which the transformations are applied.
The types of transformations and the form of their values are shown in the table below.
These transformations do not alter other information about the object because it is expected that the downstream connected code will process the transformations in a way that makes most sense for that code.

| Transformation Type | Vector Values | Value Restrictions |
|---------------------|----------------------------------------------|--------------------|
| `TRANSLATION` | $\{$ x-distance, y-distance, z-distance $\}$ | none |
| `ROTATION` | $\{ \phi, \theta, \psi \}$ | none |
| `SCALE` | $\{$ x-scale, y-scale, z-scale $\}$ | non-zero |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

positive no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I had this as a positive, non-zero restriction initially, but @shikhar413 pointed out that negative values were allowed in the TransformGenerator. I did look the source code there and did not see a restriction on any of the values for scaling, including the fact that the values could be zero. We decided to allow negative values to be consistent with that generator, but restrict zero values because that wouldn't make any sense from a CSG/MC standpoint. I am fine with going back to restricting it to positive values only if we think it makes most sense even though the TransformGenerator allows negative.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok seems fine.
TransformGenerator negative scaling needs an extra step to flip the elements back to a positive volume.

I guess it's a way to do a central symmetry


!alert! note title=Rotation Angles

Rotation angle is stored in Euler notation ($\phi$, $\theta$, $\psi$) using degrees, which is consistent with the [source/meshgenerators/TransformGenerator.md]. A convenience method is provided in `CSGBase` to specify a simple axis rotation (`applyAxisRotation()`) which will automatically convert the single angle rotation around a specified axis into the correct Euler notation.

!alert-end!

!alert! note title=CSGRegion Transformations

If applying a transformation to a `CSGRegion` object, the `CSGRegion` object will not store the transformation information. Rather, the transformation will be applied to each `CSGSurface` associated with that `CSGRegion`. This means that if one `CSGSurface` object is used in multiple `CSGRegion` objects, all regions with that surface may be affected by the transformation. If the intention is to apply a transformation to just one use case of the shared `CSGSurface`, then a clone of that surface should be made prior to applying the transformation.

!alert-end!

Below are several examples of applying transformations:

*Surface rotation using a vector of Euler angles:*

!listing CSGBaseTest.C start=euler rotation end=euler_angles include-end=true

!listing CSGBaseTest.C start=applyRotation(*surf end=applyRotation(*surf include-end=true

*Rotation of a cell around the z-axis:*

!listing TestCSGAxialSurfaceMeshGenerator.C start=apply rotation end=applyAxisRotation include-end=true

*Translation of a Universe:*

!listing CSGBaseTest.C start=apply multidirectional end=dists2

!listing CSGBaseTest.C start=>applyTranslation(*univ end=>applyTranslation(*univ include-end=true

*Scaling of a cell in the x and z directions:*

!listing CSGBaseTest.C start=scaling vector end=scales include-end=true

!listing CSGBaseTest.C start=>applyScaling(*cell end=>applyScaling(*cell include-end=true

Any transformation that is applied to an object must be done through the methods available from `CSGBase`, but information about the transformations applied can be retrieved directly from each object with `getTransformations()`.

## Updating Existing CSGBase Objects

An empty `CSGBase` object can be [initialized](#initialization) on its own in each `generateCSG` method for each mesh generator.
Expand Down Expand Up @@ -409,6 +469,7 @@ The code snippets provided here correspond to the `.C` file.
!listing ExampleCSGInfiniteSquareMeshGenerator.C start=InputParameters

The next example mesh generator builds on the infinite prism example above by taking a `MeshGeneratorName` for an existing `ExampleCSGInfiniteSquareMeshGenerator` as input and adds planes to create a finite rectangular prism.
This will also optionally rotate the generated cell a specified angle around the z-axis.

!listing TestCSGAxialSurfaceMeshGenerator.C start=InputParameters

Expand Down
70 changes: 70 additions & 0 deletions framework/include/csg/CSGBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "CSGCellList.h"
#include "CSGUniverseList.h"
#include "CSGLatticeList.h"
#include "CSGTransformation.h"
#include "nlohmann/json.h"

#ifdef MOOSE_UNIT_TEST
Expand All @@ -23,6 +24,16 @@
namespace CSG
{

/**
* Define a variant type that can hold references to different CSG object types
*/
typedef std::variant<std::reference_wrapper<const CSGSurface>,
std::reference_wrapper<const CSGCell>,
std::reference_wrapper<const CSGUniverse>,
std::reference_wrapper<const CSGRegion>,
std::reference_wrapper<const CSGLattice>>
CSGObjectVariant;

/**
* CSGBase creates an internal representation of a Constructive Solid Geometry (CSG)
* model.
Expand Down Expand Up @@ -462,6 +473,62 @@ class CSGBase
/// Operator overload for checking if two CSGBase objects are not equal
bool operator!=(const CSGBase & other) const;

/**
* @brief Apply a transformation to a CSG object
*
* @param csg_object The CSG object to transform (Surface, Cell, Universe, Region, or Lattice)
* @param type The type of transformation to apply (TRANSLATION, ROTATION, SCALE)
* @param values tuple of transformation values (3 values for any transformation type)
*/
void addTransformation(const CSGObjectVariant & csg_object,
TransformationType type,
const std::tuple<Real, Real, Real> & values);

/**
* @brief Apply a translation to a CSG object in the specified x, y, and z directions.
*
* @param csg_object The CSG object to translate (Surface, Cell, Universe, Region, or Lattice)
* @param distances size 3 tuple with translation distances in x, y, and z directions {x, y, z}
*/
void applyTranslation(const CSGObjectVariant & csg_object,
const std::tuple<Real, Real, Real> & distances)
{
addTransformation(csg_object, TransformationType::TRANSLATION, distances);
}

/**
* @brief Apply a rotation to a CSG object using (phi, theta, psi) angle notation (in degrees).
*
* @param csg_object The CSG object to rotate (Surface, Cell, Universe, Region, or Lattice)
* @param angles size 3 tuple {phi, theta, psi} with rotation angles in degrees
*/
void applyRotation(const CSGObjectVariant & csg_object,
const std::tuple<Real, Real, Real> & angles)
{
addTransformation(csg_object, TransformationType::ROTATION, angles);
}

/**
* @brief Apply a rotation to a CSG object about a specified axis (x, y, z).
*
* @param csg_object The CSG object to rotate (Surface, Cell, Universe, Region, or Lattice)
* @param axis x, y, or z axis about which to rotate
* @param angle angle in degrees to rotate about the specified axis
*/
void applyAxisRotation(const CSGObjectVariant & csg_object, std::string axis, const Real angle);

/**
* @brief Scale a CSG object in the specified x, y, and z directions.
*
* @param csg_object The CSG object to scale (Surface, Cell, Universe, Region, or Lattice)
* @param values size 3 tuple with scaling values in x, y, and z directions {x, y, z}
*/
void applyScaling(const CSGObjectVariant & csg_object,
const std::tuple<Real, Real, Real> & values)
{
addTransformation(csg_object, TransformationType::SCALE, values);
}

private:
/**
* @brief Get a Surface object by name.
Expand Down Expand Up @@ -600,6 +667,9 @@ class CSGBase
// check that surfaces used in this region are a part of this CSGBase instance
void checkRegionSurfaces(const CSGRegion & region) const;

// check that surface being accessed is a part of this CSGBase instance
bool checkSurfaceInBase(const CSGSurface & surface) const;

// check that cell being accessed is a part of this CSGBase instance
bool checkCellInBase(const CSGCell & cell) const;

Expand Down
19 changes: 19 additions & 0 deletions framework/include/csg/CSGCell.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#endif

#include "CSGRegion.h"
#include "CSGTransformation.h"

namespace CSG
{
Expand Down Expand Up @@ -117,6 +118,17 @@ class CSGCell
*/
const CSGRegion & getRegion() const { return _region; }

/**
* @brief Get the list of transformations applied to this cell
*
* @return const reference to the list of transformations
*/
const std::vector<std::pair<TransformationType, std::tuple<Real, Real, Real>>> &
getTransformations() const
{
return _transformations;
}

/// Operator overload for checking if two CSGCell objects are equal
bool operator==(const CSGCell & other) const;

Expand All @@ -132,6 +144,9 @@ class CSGCell
// it needs to be called from CSGBase so that the surfaces can be checked first.
void updateRegion(const CSGRegion & region) { _region = region; }

/// add a transformation to the cell (accessed through CSGBase)
void addTransformation(TransformationType type, const std::tuple<Real, Real, Real> & values);

/// Name of surface
std::string _name;

Expand All @@ -150,6 +165,9 @@ class CSGCell
/// Fill object if fill is CSGLattice
const CSGLattice * _fill_lattice;

/// list of transformations applied to the cell (type, value) in the order they are applied
std::vector<std::pair<TransformationType, std::tuple<Real, Real, Real>>> _transformations;

friend class CSGCellList; // needed for setName() access
friend class CSGBase; // needed for updateRegion() access

Expand All @@ -158,6 +176,7 @@ class CSGCell
///@{
FRIEND_TEST(CSGCellTest, testSetName);
FRIEND_TEST(CSGCellTest, testUpdateRegion);
FRIEND_TEST(CSGCellTest, testCellEquality);
///@}
#endif
};
Expand Down
19 changes: 19 additions & 0 deletions framework/include/csg/CSGLattice.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#pragma once

#include "CSGUniverse.h"
#include "CSGTransformation.h"
#include "JsonIO.h"
#include <variant>
#include <optional>
Expand Down Expand Up @@ -170,6 +171,17 @@ class CSGLattice
*/
const std::vector<std::reference_wrapper<const CSGUniverse>> getUniqueUniverses();

/**
* @brief Get the list of transformations applied to this lattice
*
* @return const reference to the list of transformations
*/
const std::vector<std::pair<TransformationType, std::tuple<Real, Real, Real>>> &
getTransformations() const
{
return _transformations;
}

/// Operator overload for checking if two CSGLattice objects are equal
bool operator==(const CSGLattice & other) const;

Expand Down Expand Up @@ -214,6 +226,9 @@ class CSGLattice
*/
void updateOuter(const CSGUniverse & outer_universe);

/// Apply a transformation to the lattice (accessed through CSGBase)
void addTransformation(TransformationType type, const std::tuple<Real, Real, Real> & values);

/// Name of lattice
std::string _name;

Expand All @@ -232,6 +247,9 @@ class CSGLattice
/// outer object if fill is CSGUniverse
const CSGUniverse * _outer_universe;

/// list of transformations applied to the lattice (type, value) in the order they are applied
std::vector<std::pair<TransformationType, std::tuple<Real, Real, Real>>> _transformations;

// CSGLatticeList needs to be friend to access setName()
friend class CSGLatticeList;
// CSGBase needed for access updateAttributes()
Expand All @@ -243,6 +261,7 @@ class CSGLattice
FRIEND_TEST(CSGLatticeTest, testSetName);
FRIEND_TEST(CSGLatticeTest, testUpdateOuter);
FRIEND_TEST(CSGBaseTest, testAddLattice);
FRIEND_TEST(CSGLatticeTest, testCartLatticeEquality);
///@}
#endif
};
Expand Down
25 changes: 25 additions & 0 deletions framework/include/csg/CSGSurface.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#endif

#include "MooseApp.h"
#include "CSGTransformation.h"

namespace CSG
{
Expand Down Expand Up @@ -93,6 +94,17 @@ class CSGSurface
*/
const std::string & getName() const { return _name; }

/**
* @brief Get the list of transformations applied to this surface
*
* @return const reference to the list of transformations
*/
const std::vector<std::pair<TransformationType, std::tuple<Real, Real, Real>>> &
getTransformations() const
{
return _transformations;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right my question is more that transformations are not usually applied to surfaces in MC codes?
At least in OpenMC if I recall correctly

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surfaces are the primary entities that can have transformations applied to them across all MC codes, various codes allow for cells and universes too under different circumstances (these are less consistent across codes). But pretty much all codes support surface transformations directly with built-in methods.

At the end of the day, it is really all the surfaces that need to be transformed to alter the geometry with a transformation.


/// Operator overload for checking if two CSGSurface objects are equal
bool operator==(const CSGSurface & other) const;

Expand All @@ -111,20 +123,33 @@ class CSGSurface
// name needs to be managed at the CSGSurfaceList level
void setName(const std::string & name) { _name = name; }

/**
* @brief add a transformation to a surface
*
* @param type type of transformation to apply
* @param values values for the transformation (3 values for any transformation type)
*/
void addTransformation(TransformationType type, const std::tuple<Real, Real, Real> & values);

/// Name of surface
std::string _name;

/// Type of surface that is being represented
/// string is taken directly from the surface class name
const std::string _surface_type;

/// list of transformations applied to the surface (type, value) in the order they are applied
std::vector<std::pair<TransformationType, std::tuple<Real, Real, Real>>> _transformations;

// CSGSurfaceList needs to be friend to access setName()
friend class CSGSurfaceList;
friend class CSGBase; // needed for addTransformation() access

#ifdef MOOSE_UNIT_TEST
/// Friends for unit testing
///@{
FRIEND_TEST(CSGSurfaceTest, testSetName);
FRIEND_TEST(CSGSurfaceTest, testSurfaceEquality);
///@}
#endif
};
Expand Down
Loading