CSGLattice

This is an abstract class that can be used to define custom 2D lattice types for constructive solid geometry (CSG) representations. The CSGBase framework already contains definitions for Cartesian and regular hexagonal lattices, but developers of modules or MOOSE-based application can define additional custom types by deriving from this abstract class. General information about implementing CSG-related methods can be found in CSGBase.

Defining a New Lattice Type

Any custom lattice can be defined by inheriting from CSGLattice. There are several methods that must be defined for any lattice type, each of which is elaborated on in the following sections:

  • Public Methods:

    • getAttributes: returns a map of lattices attributes to their values

    • isValidIndex: checks if a given index is valid for the lattice dimensions

    • isValidUniverseMap: checks that a provided 2D set of universes is arranged in a valid form for the lattice type

    • clone: clones the instance as a unique pointer

  • Protected Methods:

    • setUniverses: sets the 2D universe arrangement for the lattice

    • compareAttributes: checks if the attributes are identical to those of another lattice instance

Defining a Valid Universe Layout

The rules of what makes a valid universe layout will vary depending on the lattice type. These rules should be defined in the isValidUniverseMap(), which takes in a 2D vector of CSGUniverse objects and returns true or false depending on whether it is valid. A recommendation for this method is to check that the number of rows and the length of each row is correct given the rules.

To set the value of the _universe_map, which is a 2D vector of CSGUniverse references that defines the layout, the setUniverses() method must be defined. In this method, it is important to check the validity of the provided set of universes with the aforementioned isValidUniverseMap() method and set any necessary attributes that could change with the setting of the universe layout. For example, in the CSGCartesianLattice example, the number of rows (_nrow) and columns (_ncol) need to be set in order to stay consistent with the lattice layout.

Each lattice should also have some sort of defined 2D indexing scheme that is used to define the layout and access items in the _universe_map. The method isValidIndex() is where these rules are defined. The isValidIndex() method is called by setUniverseAtIndex() and getUniverseAtIndex() to ensure that the provided index is valid.

Lattice Attributes

The lattice attributes at a minimum are any pieces of data that are needed for a complete definition of the lattice for any downstream connected codes that cannot be derived from the _universe_map that defines the lattice layout. For example, the pitch (flat-to-flat distance of elements) for the CSGCartesianLattice is needed for a complete definition, but cannot be determined from the universe layout directly. These pieces of data can optionally be attributes that could be derived from the _universe_map but are just more convenient to have explicitly defined (e.g., for the CSGCartesianLattice example, the number of rows (_nrow) and columns (_ncol) can be derived from the universe layout, but they are included as attributes for convenience). These attributes are what get returned in a map using the getAttributes() method, which gets called when producing the CSG JSON object in CSGBase.

commentnote:Attributes Data Type

The getAttributes() method returns a map of AttributeVariant data types (std::unordered_map<std::string, AttributeVariant>) to support flexibility in the type of data that might need to be defined. The AttributeVariant is a std::variant that can hold any of the following types: int, unsigned int, std::string, Real, bool, and vectors of these types (std::vector<int>, std::vector<unsigned int>, std::vector<std::string>, std::vector<Real>, std::vector<bool>).

These attributes are also what gets compared in the compareAttributes() method, which is used by the equality operators (== and !=) to check for lattice equality. Because this data can be of different types, this method must be explicitly defined for the lattice type, and use of std::get<type> will be necessary to compare the values.

Creating a Lattice Clone

In order to make sure that clones of CSGBase objects are created properly, each derived CSGLattice type must implement a clone() method, which returns a std::unique_ptr<CSGLattice> from the given lattice instance. This can typically be done by calling std::make_unique on the constructor for the derived lattice type.

Setting the Lattice Type

The type of lattice must be set for _lattice_type in the lattice constructor. It is recommended that this be done based on the class name using MooseUtils::prettyCppType<LatticeClassName>() so that the lattice type automatically matches the class that created it.

Example

Below shows how CSGCartesianLattice is implemented as an example.


#pragma once

#include "CSGLattice.h"

#ifdef MOOSE_UNIT_TEST
#include "gtest/gtest.h"
#endif

namespace CSG
{

/**
 * CSGCartesianLattice is the class for constructing regular Cartesian lattices of CSGUniverses.
 */
class CSGCartesianLattice : public CSGLattice
{
public:
  /**
   * @brief Construct a new CSGCartesianLattice object from the map of universes provided.
   *
   * @param name unique identifying name of lattice
   * @param pitch pitch of lattice elements
   * @param universes list of list of universes to set as the lattice map
   * @param outer optional outer universe or material name that fills space around lattice elements.
   *              If not provided, outer is assumed to be VOID.
   */
  CSGCartesianLattice(const std::string & name,
                      const Real pitch,
                      std::vector<std::vector<std::reference_wrapper<const CSGUniverse>>> universes,
                      const std::optional<OuterVariant> & outer = std::nullopt);

  /**
   * @brief Construct a new empty CSGCartesianLattice object with specified pitch.
   * NOTE: must call setLatticeUniverses to populate the universe map.
   *
   * @param name unique identifying name of lattice
   * @param pitch pitch of lattice elements
   * @param outer optional outer universe or material name that fills space around lattice elements.
   *              If not provided, outer is assumed to be VOID.
   */
  CSGCartesianLattice(const std::string & name,
                      const Real pitch,
                      const std::optional<OuterVariant> & outer = std::nullopt);

  /**
   * Destructor
   */
  virtual ~CSGCartesianLattice() = default;

  /**
   * @brief clone this Cartesian lattice
   *
   * @return std::unique_ptr<CSGLattice> unique pointer to cloned Cartesian lattice
   */
  std::unique_ptr<CSGLattice> clone() const override
  {
    return std::make_unique<CSGCartesianLattice>(*this);
  }

  /**
   * @brief Get attributes that define the lattice (excluding the universe map).
   *  - nrow: number of rows (int)
   *  - ncol: number of columns (int)
   *  - pitch: pitch of the lattice element (Real)
   *
   * @return map of string dimension name to value of that dimension
   */
  virtual std::unordered_map<std::string, AttributeVariant> getAttributes() const override;

  /**
   * @brief Checks if the given index location (row, column) is a valid index for the
   * lattice. Allowable indices are: 0 <= row < _nrow and 0 <= column < _ncol.
   *
   * @param index location in (row, column) form
   * @return true if index is valid for the lattice
   */
  virtual bool isValidIndex(const std::pair<int, int> index) const override;

  /**
   * @brief check that any provided list of list of CSGUniverses are the correct dimensions. Must
   * have number of lists within universes equal to _nrow. And each sublist must be size _ncol.
   *
   * @param universes list of list of universes to be used to define the lattice structure
   * @return true if universe dimensions are valid
   */
  virtual bool isValidUniverseMap(
      std::vector<std::vector<std::reference_wrapper<const CSGUniverse>>> universes) const override;

  /**
   * @brief get the number of rows
   *
   * @return number of rows
   */
  unsigned int getNRows() const { return _nrow; }

  /**
   * @brief get number of columns
   *
   * @return number of columns
   */
  unsigned int getNCols() const { return _ncol; }

  /**
   * @brief get lattice pitch
   *
   * @return pitch
   */
  Real getPitch() const { return _pitch; }

  /**
   * @brief set the pitch of the lattice
   *
   * @param pitch new pitch value
   */
  void setPitch(Real pitch);

protected:
  /// compare the attributes returned in getAttributes of this lattice to another lattice
  virtual bool compareAttributes(const CSGLattice & other) const override;

  /**
   * @brief set the universes that define the lattice layout
   *
   * @param universes list of list of universes to set as the lattice map
   */
  virtual void setUniverses(
      std::vector<std::vector<std::reference_wrapper<const CSGUniverse>>> universes) override;

  /// pitch
  Real _pitch;

  /// number of elements in the first dimension (rows)
  unsigned int _nrow;

  /// number of elements in the second direction (columns)
  unsigned int _ncol;

  friend class CSGBase;

#ifdef MOOSE_UNIT_TEST
  /// Friends for unit testing
  ///@{
  FRIEND_TEST(CSGLatticeTest, testCartSetUniverses);
  FRIEND_TEST(CSGLatticeTest, testCartSetUniverseAtIndex);
  FRIEND_TEST(CSGLatticeTest, testEmptyToFilled);
  FRIEND_TEST(CSGLatticeTest, testCartLatticeEquality);
  ///@}
#endif
};
}
(moose/framework/include/csg/CSGCartesianLattice.h)

#include "CSGCartesianLattice.h"

namespace CSG
{

CSGCartesianLattice::CSGCartesianLattice(
    const std::string & name,
    const Real pitch,
    std::vector<std::vector<std::reference_wrapper<const CSGUniverse>>> universes,
    const std::optional<OuterVariant> & outer)
  : CSGLattice(name, MooseUtils::prettyCppType<CSGCartesianLattice>(), outer), _pitch(pitch)
{
  setUniverses(universes); // this will set _nrow and _ncol
  if (_pitch < 0)
    mooseError("Lattice " + getName() + " must have pitch greater than 0.");
}

CSGCartesianLattice::CSGCartesianLattice(const std::string & name,
                                         const Real pitch,
                                         const std::optional<OuterVariant> & outer)
  : CSGLattice(name, MooseUtils::prettyCppType<CSGCartesianLattice>(), outer),
    _pitch(pitch),
    _nrow(0),
    _ncol(0)
{
  if (_pitch < 0)
    mooseError("Lattice " + getName() + " must have pitch greater than 0.");
}

void
CSGCartesianLattice::setPitch(const Real pitch)
{
  if (pitch < 0)
    mooseError("Lattice " + getName() + " must have pitch greater than 0.");
  _pitch = pitch;
}

std::unordered_map<std::string, AttributeVariant>
CSGCartesianLattice::getAttributes() const
{
  return {{"nrow", static_cast<unsigned int>(_nrow)},
          {"ncol", static_cast<unsigned int>(_ncol)},
          {"pitch", _pitch}};
}

bool
CSGCartesianLattice::isValidUniverseMap(
    std::vector<std::vector<std::reference_wrapper<const CSGUniverse>>> universes) const
{
  // need at least one row
  if (universes.size() < 1)
    return false;

  // check that each row is same size
  auto row_size = universes[0].size();
  for (auto univ_list : universes)
  {
    if (univ_list.size() != row_size)
      return false;
  }
  return true;
}

void
CSGCartesianLattice::setUniverses(
    std::vector<std::vector<std::reference_wrapper<const CSGUniverse>>> universes)
{
  // check for valid map arrangment
  if (!isValidUniverseMap(universes))
    mooseError("Cannot set lattice " + getName() +
               " with universes. Does not have valid dimensions for lattice type " + getType());
  // set attributes based on universe map
  _nrow = universes.size();
  _ncol = universes[0].size();
  _universe_map = universes;
}

bool
CSGCartesianLattice::isValidIndex(const std::pair<int, int> index) const
{
  auto row = index.first;  // must be (0 <= row < _nrow); rows
  auto col = index.second; // must be (0 <= col < _ncol); cols
  return ((0 <= row && row < (int)_nrow) && (0 <= col && col < (int)_ncol));
}

bool
CSGCartesianLattice::compareAttributes(const CSGLattice & other) const
{
  if (other.getType() != this->getType())
    return false;

  auto this_dims = this->getAttributes();
  auto other_dims = other.getAttributes();
  if (std::get<unsigned int>(this_dims["nrow"]) != std::get<unsigned int>(other_dims["nrow"]))
    return false;
  if (std::get<unsigned int>(this_dims["ncol"]) != std::get<unsigned int>(other_dims["ncol"]))
    return false;
  if (std::get<Real>(this_dims["pitch"]) != std::get<Real>(other_dims["pitch"]))
    return false;
  return true;
}

} // namespace CSG
(moose/framework/src/csg/CSGCartesianLattice.C)