#ifndef OPENMC_CELL_H #define OPENMC_CELL_H #include #include // for hash #include #include #include #include #include "hdf5.h" #include "pugixml.hpp" #include "openmc/bounding_box.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/neighbor_list.h" #include "openmc/position.h" #include "openmc/surface.h" #include "openmc/universe.h" #include "openmc/vector.h" namespace openmc { //============================================================================== // Constants //============================================================================== enum class Fill { MATERIAL, UNIVERSE, LATTICE }; constexpr int32_t OP_LEFT_PAREN {std::numeric_limits::max()}; constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits::max() - 1}; constexpr int32_t OP_COMPLEMENT {std::numeric_limits::max() - 2}; constexpr int32_t OP_INTERSECTION {std::numeric_limits::max() - 3}; constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; //============================================================================== // Global variables //============================================================================== class Cell; class GeometryState; class ParentCell; class CellInstance; class Universe; class UniversePartitioner; namespace model { extern std::unordered_map cell_map; extern vector> cells; } // namespace model //============================================================================== class Region { public: //---------------------------------------------------------------------------- // Constructors Region() {} explicit Region(std::string region_spec, int32_t cell_id); //---------------------------------------------------------------------------- // Methods //! \brief Determine if a cell contains the particle at a given location. //! //! The bounds of the cell are determined by a logical expression involving //! surface half-spaces. The expression used is given in infix notation //! //! The function is split into two cases, one for simple cells (those //! involving only the intersection of half-spaces) and one for complex cells. //! Both cases use short circuiting; however, in the case fo complex cells, //! the complexity increases with the binary operators involved. //! \param r The 3D Cartesian coordinate to check. //! \param u A direction used to "break ties" the coordinates are very //! close to a surface. //! \param on_surface The signed index of a surface that the coordinate is //! known to be on. This index takes precedence over surface sense //! calculations. bool contains(Position r, Direction u, int32_t on_surface) const; //! Find the oncoming boundary of this cell. std::pair distance( Position r, Direction u, int32_t on_surface) const; //! Get the BoundingBox for this cell. BoundingBox bounding_box(int32_t cell_id) const; //! Get the CSG expression as a string std::string str() const; //! Get a vector containing all the surfaces in the region expression vector surfaces() const; //---------------------------------------------------------------------------- // Accessors //! Get Boolean of if the cell is simple or not bool is_simple() const { return simple_; } private: //---------------------------------------------------------------------------- // Private Methods //! Get a vector of the region expression in postfix notation vector generate_postfix(int32_t cell_id) const; //! Determine if a particle is inside the cell for a simple cell (only //! intersection operators) bool contains_simple(Position r, Direction u, int32_t on_surface) const; //! Determine if a particle is inside the cell for a complex cell. //! //! Uses the comobination of half-spaces and binary operators to determine //! if short circuiting can be used. Short cicuiting uses the relative and //! absolute depth of parentheses in the expression. bool contains_complex(Position r, Direction u, int32_t on_surface) const; //! BoundingBox if the paritcle is in a simple cell. BoundingBox bounding_box_simple() const; //! BoundingBox if the particle is in a complex cell. BoundingBox bounding_box_complex(vector postfix) const; //! Enfource precedence: Parenthases, Complement, Intersection, Union void add_precedence(); //! Add parenthesis to enforce precedence int64_t add_parentheses(int64_t start); //! Remove complement operators from the expression void remove_complement_ops(); //! Remove complement operators by using DeMorgan's laws void apply_demorgan( vector::iterator start, vector::iterator stop); //---------------------------------------------------------------------------- // Private Data //! Definition of spatial region as Boolean expression of half-spaces // TODO: Should this be a vector of some other type vector expression_; bool simple_; //!< Does the region contain only intersections? }; //============================================================================== class Cell { public: //---------------------------------------------------------------------------- // Constructors, destructors, factory functions explicit Cell(pugi::xml_node cell_node); Cell() {}; virtual ~Cell() = default; //---------------------------------------------------------------------------- // Methods //! \brief Determine if a cell contains the particle at a given location. //! //! The bounds of the cell are detemined by a logical expression involving //! surface half-spaces. At initialization, the expression was converted //! to RPN notation. //! //! The function is split into two cases, one for simple cells (those //! involving only the intersection of half-spaces) and one for complex cells. //! Simple cells can be evaluated with short circuit evaluation, i.e., as soon //! as we know that one half-space is not satisfied, we can exit. This //! provides a performance benefit for the common case. In //! contains_complex, we evaluate the RPN expression using a stack, similar to //! how a RPN calculator would work. //! \param r The 3D Cartesian coordinate to check. //! \param u A direction used to "break ties" the coordinates are very //! close to a surface. //! \param on_surface The signed index of a surface that the coordinate is //! known to be on. This index takes precedence over surface sense //! calculations. virtual bool contains(Position r, Direction u, int32_t on_surface) const = 0; //! Find the oncoming boundary of this cell. virtual std::pair distance( Position r, Direction u, int32_t on_surface, GeometryState* p) const = 0; //! Write all information needed to reconstruct the cell to an HDF5 group. //! \param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; virtual void to_hdf5_inner(hid_t group_id) const = 0; //! Export physical properties to HDF5 //! \param[in] group HDF5 group to read from void export_properties_hdf5(hid_t group) const; //! Import physical properties from HDF5 //! \param[in] group HDF5 group to write to void import_properties_hdf5(hid_t group); //! Get the BoundingBox for this cell. virtual BoundingBox bounding_box() const = 0; //! Get a vector of surfaces in the cell virtual vector surfaces() const { return vector(); } //! Check if the cell region expression is simple virtual bool is_simple() const { return true; } //---------------------------------------------------------------------------- // Accessors //! Get the temperature of a cell instance //! \param[in] instance Instance index. If -1 is given, the temperature for //! the first instance is returned. //! \return Temperature in [K] double temperature(int32_t instance = -1) const; //! Get the density multiplier of a cell instance //! \param[in] instance Instance index. If -1 is given, the density multiplier //! for the first instance is returned. //! \return Density multiplier double density_mult(int32_t instance = -1) const; //! Get the density of a cell instance in g/cm3 //! \param[in] instance Instance index. If -1 is given, the density //! for the first instance is returned. //! \return Density in [g/cm3] double density(int32_t instance = -1) const; //! Set the temperature of a cell instance //! \param[in] T Temperature in [K] //! \param[in] instance Instance index. If -1 is given, the temperature for //! all instances is set. //! \param[in] set_contained If this cell is not filled with a material, //! collect all contained cells with material fills and set their //! temperatures. void set_temperature( double T, int32_t instance = -1, bool set_contained = false); //! Set the density of a cell instance //! \param[in] density Density [g/cm3] //! \param[in] instance Instance index. If -1 is given, the density //! for all instances is set. //! \param[in] set_contained If this cell is not filled with a material, //! collect all contained cells with material fills and set their //! densities. void set_density( double density, int32_t instance = -1, bool set_contained = false); int32_t n_instances() const; //! Set the rotation matrix of a cell instance //! \param[in] rot The rotation matrix of length 3 or 9 void set_rotation(const vector& rot); //! Get the name of a cell //! \return Cell name const std::string& name() const { return name_; }; //! Set the temperature of a cell instance //! \param[in] name Cell name void set_name(const std::string& name) { name_ = name; }; //! Get all cell instances contained by this cell //! \param[in] instance Instance of the cell for which to get contained cells //! (default instance is zero) //! \param[in] hint positional hint for determining the parent cells //! \return Map with cell indexes as keys and //! instances as values std::unordered_map> get_contained_cells( int32_t instance = 0, Position* hint = nullptr) const; //! Determine the material index corresponding to a specific cell instance, //! taking into account presence of distribcell material //! \param[in] instance of the cell //! \return material index int32_t material(int32_t instance) const { // If distributed materials are used, then each instance has its own // material definition. If distributed materials are not used, then // all instances used the same material stored at material_[0]. The // presence of distributed materials is inferred from the size of // the material_ vector being greater than one. if (material_.size() > 1) { return material_[instance]; } else { return material_[0]; } } //! Determine the temperature index corresponding to a specific cell instance, //! taking into account presence of distribcell temperature //! \param[in] instance of the cell //! \return temperature index double sqrtkT(int32_t instance) const { // If distributed materials are used, then each instance has its own // temperature definition. If distributed materials are not used, then // all instances used the same temperature stored at sqrtkT_[0]. The // presence of distributed materials is inferred from the size of // the sqrtkT_ vector being greater than one. if (sqrtkT_.size() > 1) { return sqrtkT_[instance]; } else { return sqrtkT_[0]; } } protected: //! Determine the path to this cell instance in the geometry hierarchy //! \param[in] instance of the cell to find parent cells for //! \param[in] r position used to do a fast search for parent cells //! \return parent cells vector find_parent_cells( int32_t instance, const Position& r) const; //! Determine the path to this cell instance in the geometry hierarchy //! \param[in] instance of the cell to find parent cells for //! \param[in] p particle used to do a fast search for parent cells //! \return parent cells vector find_parent_cells( int32_t instance, GeometryState& p) const; //! Determine the path to this cell instance in the geometry hierarchy //! \param[in] instance of the cell to find parent cells for //! \return parent cells vector exhaustive_find_parent_cells(int32_t instance) const; //! Inner function for retrieving contained cells void get_contained_cells_inner( std::unordered_map>& contained_cells, vector& parent_cells) const; public: //---------------------------------------------------------------------------- // Data members int32_t id_; //!< Unique ID std::string name_; //!< User-defined name Fill type_; //!< Material, universe, or lattice int32_t universe_; //!< Universe # this cell is in int32_t fill_; //!< Universe # filling this cell //! \brief Index corresponding to this cell in distribcell arrays int distribcell_index_ {C_NONE}; //! \brief Material(s) within this cell. //! //! May be multiple materials for distribcell. vector material_; //! \brief Temperature(s) within this cell. //! //! The stored values are actually sqrt(k_Boltzmann * T) for each temperature //! T. The units are sqrt(eV). vector sqrtkT_; //! \brief Unitless density multiplier(s) within this cell. vector density_mult_; //! \brief Neighboring cells in the same universe. NeighborList neighbors_; Position translation_ {0, 0, 0}; //!< Translation vector for filled universe //! \brief Rotational tranfsormation of the filled universe. // //! The vector is empty if there is no rotation. Otherwise, the first 9 values //! give the rotation matrix in row-major order. When the user specifies //! rotation angles about the x-, y- and z- axes in degrees, these values are //! also present at the end of the vector, making it of length 12. vector rotation_; vector offset_; //!< Distribcell offset table // Right now, either CSG or DAGMC cells are used. virtual GeometryType geom_type() const = 0; }; struct CellInstanceItem { int32_t index {-1}; //! Index into global cells array int lattice_indx {-1}; //! Flat index value of the lattice cell }; //============================================================================== class CSGCell : public Cell { public: //---------------------------------------------------------------------------- // Constructors CSGCell() = default; explicit CSGCell(pugi::xml_node cell_node); //---------------------------------------------------------------------------- // Methods vector surfaces() const override { return region_.surfaces(); } std::pair distance(Position r, Direction u, int32_t on_surface, GeometryState* p) const override { return region_.distance(r, u, on_surface); } bool contains(Position r, Direction u, int32_t on_surface) const override { return region_.contains(r, u, on_surface); } BoundingBox bounding_box() const override { return region_.bounding_box(id_); } void to_hdf5_inner(hid_t group_id) const override; bool is_simple() const override { return region_.is_simple(); } virtual GeometryType geom_type() const override { return GeometryType::CSG; } protected: //! Returns the beginning position of a parenthesis block (immediately before //! two surface tokens) in the RPN given a starting position at the end of //! that block (immediately after two surface tokens) //! \param start Starting position of the search //! \param rpn The rpn being searched static vector::iterator find_left_parenthesis( vector::iterator start, const vector& rpn); private: Region region_; }; //============================================================================== //! Define an instance of a particular cell //============================================================================== //! Stores information used to identify a unique cell in the model struct CellInstance { //! Check for equality bool operator==(const CellInstance& other) const { return index_cell == other.index_cell && instance == other.instance; } int64_t index_cell; int64_t instance; }; //! Structure necessary for inserting CellInstance into hashed STL data //! structures struct CellInstanceHash { std::size_t operator()(const CellInstance& k) const { return 4096 * k.index_cell + k.instance; } }; //============================================================================== // Non-member functions //============================================================================== void read_cells(pugi::xml_node node); //! Add cells to universes void populate_universes(); } // namespace openmc #endif // OPENMC_CELL_H