diff --git a/.travis.yml b/.travis.yml index 00f0370218..aacf9fa726 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ cache: directories: - $HOME/nndc_hdf5 - $HOME/endf-b-vii.1 + - $HOME/WMP_Library env: global: - FC=gfortran @@ -25,7 +26,7 @@ env: - OMP_NUM_THREADS=2 - OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml - OPENMC_ENDF_DATA=$HOME/endf-b-vii.1 - - OPENMC_MULTIPOLE_LIBRARY=$HOME/multipole_lib + - OPENMC_MULTIPOLE_LIBRARY=$HOME/WMP_Library - PATH=$PATH:$HOME/NJOY2016/build - DISPLAY=:99.0 - COVERALLS_PARALLEL=true diff --git a/CMakeLists.txt b/CMakeLists.txt index fd3c58631b..57d2c6dacb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -388,6 +388,7 @@ add_library(libopenmc SHARED src/endf.cpp src/initialize.cpp src/finalize.cpp + src/geometry.cpp src/geometry_aux.cpp src/hdf5_interface.cpp src/lattice.cpp @@ -397,6 +398,7 @@ add_library(libopenmc SHARED src/mgxs.cpp src/mgxs_interface.cpp src/nuclide.cpp + src/output.cpp src/particle.cpp src/plot.cpp src/position.cpp @@ -414,6 +416,7 @@ add_library(libopenmc SHARED src/source.cpp src/state_point.cpp src/string_functions.cpp + src/summary.cpp src/surface.cpp src/thermal.cpp src/xml_interface.cpp diff --git a/docs/source/io_formats/data_wmp.rst b/docs/source/io_formats/data_wmp.rst index 6174e85ff7..25916c5229 100644 --- a/docs/source/io_formats/data_wmp.rst +++ b/docs/source/io_formats/data_wmp.rst @@ -5,7 +5,7 @@ Windowed Multipole Library Format ================================= **/version** (*char[]*) - The format version of the file. The current version is "v0.2" + The format version of the file. The current version is "v1.0" **/nuclide/** - **broaden_poly** (*int[]*) @@ -23,55 +23,25 @@ Windowed Multipole Library Format \text{data}[:,i] = [\text{pole},~\text{residue}_1,~\text{residue}_2, ~\ldots] - The residues are in the order: total, competitive if present, - absorption, fission. Complex numbers are stored by forming a type with - ":math:`r`" and ":math:`i`" identifiers, similar to how `h5py`_ does it. - - **end_E** (*double*) + The residues are in the order: scattering, absorption, fission. Complex + numbers are stored by forming a type with ":math:`r`" and ":math:`i`" + identifiers, similar to how `h5py`_ does it. + - **E_max** (*double*) Highest energy the windowed multipole part of the library is valid for. - - **formalism** (*int*) - The formalism of the underlying data. Uses the `ENDF-6`_ format - formalism numbers. - - .. table:: Table of supported formalisms. - - +-------------+------------------+ - | Formalism | Formalism number | - +=============+==================+ - | MLBW | 2 | - +-------------+------------------+ - | Reich-Moore | 3 | - +-------------+------------------+ - - - **l_value** (*int[]*) - The index for a corresponding pole. Equivalent to the :math:`l` quantum - number of the resonance the pole comes from :math:`+1`. - - **pseudo_K0RS** (*double[]*) - :math:`l` dependent value of - - .. math:: - \sqrt{\frac{2 m_n}{\hbar}}\frac{AWR}{AWR + 1} r_{s,l} - - Where :math:`m_n` is mass of neutron, :math:`AWR` is the atomic weight - ratio of the target to the neutron, and :math:`r_{s,l}` is the - scattering radius for a given :math:`l`. + - **E_min** (*double*) + Lowest energy the windowed multipole part of the library is valid for. - **spacing** (*double*) .. math:: - \frac{\sqrt{E_{max}}- \sqrt{E_{min}}}{n_w} + \frac{\sqrt{E_{max}} - \sqrt{E_{min}}}{n_w} - Where :math:`E_{max}` is the maximum energy the windows go up to. This - is not equivalent to the maximum energy for which the windowed multipole - data is valid for. It is slightly higher to ensure an integer number of - windows. :math:`E_{min}` is the minimum energy and equivalent to - ``start_E``, and :math:`n_w` is the number of windows, given by - ``windows``. + Where :math:`E_{max}` is the maximum energy the windows go up to. + :math:`E_{min}` is the minimum energy, and :math:`n_w` is the number of + windows, given by ``windows``. - **sqrtAWR** (*double*) Square root of the atomic weight ratio. - - **start_E** (*double*) - Lowest energy the windowed multipole part of the library is valid for. - - **w_start** (*int[]*) - The pole to start from for each window. - - **w_end** (*int[]*) - The pole to end at for each window. + - **windows** (*int[][]*) + The poles to start from and end at for each window. windows[i, 0] and + windows[i, 1] are, respectively, the indexes (1-based) of the first and + last pole in window i. .. _h5py: http://docs.h5py.org/en/latest/ -.. _ENDF-6: https://www.oecd-nea.org/dbdata/data/manual-endf/endf102.pdf diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index 16ce8e6553..94511842dc 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -201,7 +201,7 @@ Each ```` element can have the following attributes or sub-elements: .. math:: - \left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\theta \sin\psi + + \left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\phi \sin\psi + \sin\phi \sin\theta \cos\psi & \sin\phi \sin\psi + \cos\phi \sin\theta \cos\psi \\ \cos\theta \sin\psi & \cos\phi \cos\psi + \sin\phi \sin\theta \sin\psi & -\sin\phi \cos\psi + \cos\phi \sin\theta \sin\psi \\ diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 9f677bac43..4940275d3a 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -90,7 +90,7 @@ Assuming free-gas thermal motion, cross sections in the multipole form can be analytically Doppler broadened to give the form: .. math:: - \sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[i r_j + \sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[r_j \sqrt{\pi} W_i(z) - \frac{r_j}{\sqrt{\pi}} C \left(\frac{p_j}{\sqrt{\xi}}, \frac{u}{2 \sqrt{\xi}}\right)\right] .. math:: @@ -141,7 +141,7 @@ scattering does not occur in the resolved resonance region. This is usually, but not always the case. Future library versions may eliminate this issue. The data format used by OpenMC to represent windowed multipole data is specified -in :ref:`io_data_wmp`. +in :ref:`io_data_wmp` with a publicly available `WMP library`_. .. _temperature_treatment: @@ -270,6 +270,7 @@ or even isotropic scattering. https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf .. _Hwang: http://www.ans.org/pubs/journals/nse/a_16381 .. _Josey: http://dx.doi.org/10.1016/j.jcp.2015.08.013 +.. _WMP Library: https://github.com/mit-crpg/WMP_Library .. _MCNP: http://mcnp.lanl.gov .. _Serpent: http://montecarlo.vtt.fi .. _NJOY: http://t2.lanl.gov/codes.shtml diff --git a/examples/jupyter/nuclear-data.ipynb b/examples/jupyter/nuclear-data.ipynb index 7087681a04..726de6f3e2 100644 --- a/examples/jupyter/nuclear-data.ipynb +++ b/examples/jupyter/nuclear-data.ipynb @@ -1466,7 +1466,7 @@ }, "outputs": [], "source": [ - "url = 'https://anl.box.com/shared/static/ulhcoohm12gduwdalknmf8dpnepzkxj0.h5'\n", + "url = 'https://github.com/mit-crpg/WMP_Library/releases/download/v1.0/092238.h5'\n", "filename, headers = urllib.request.urlretrieve(url, '092238.h5')" ] }, @@ -1485,7 +1485,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `WindowedMultipole` object can be called with energy and temperature values. Calling the object gives a tuple of 3 cross sections: total, radiative capture, and fission." + "The `WindowedMultipole` object can be called with energy and temperature values. Calling the object gives a tuple of 3 cross sections: elastic scattering, radiative capture, and fission." ] }, { @@ -1498,9 +1498,7 @@ { "data": { "text/plain": [ - "(array(9.638243132516015),\n", - " array(0.5053244245010787),\n", - " array(2.931753364280356e-06))" + "(array(9.13284265), array(0.50530278), array(2.9316765e-06))" ] }, "execution_count": 43, diff --git a/include/openmc/cell.h b/include/openmc/cell.h index 2b7534b582..fa5134b60f 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -10,6 +10,7 @@ #include "hdf5.h" #include "pugixml.hpp" +#include "openmc/constants.h" #include "openmc/position.h" @@ -24,6 +25,13 @@ extern "C" int FILL_MATERIAL; extern "C" int FILL_UNIVERSE; extern "C" int FILL_LATTICE; +// TODO: Convert to enum +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 //============================================================================== @@ -31,11 +39,11 @@ extern "C" int FILL_LATTICE; extern "C" int32_t n_cells; class Cell; -extern std::vector global_cells; +extern std::vector cells; extern std::unordered_map cell_map; class Universe; -extern std::vector global_universes; +extern std::vector universes; extern std::unordered_map universe_map; //============================================================================== @@ -45,9 +53,12 @@ extern std::unordered_map universe_map; class Universe { public: - int32_t id; //!< Unique ID - std::vector cells; //!< Cells within this universe - //double x0, y0, z0; //!< Translation coordinates. + int32_t id_; //!< Unique ID + std::vector cells_; //!< Cells within this universe + + //! \brief Write universe information to an HDF5 group. + //! \param group_id An HDF5 group id. + void to_hdf5(hid_t group_id) const; }; //============================================================================== @@ -57,25 +68,44 @@ public: class Cell { public: - int32_t id; //!< Unique ID - std::string name; //!< User-defined name - int type; //!< Material, universe, or lattice - int32_t universe; //!< Universe # this cell is in - int32_t fill; //!< Universe # filling this cell - int32_t n_instances{0}; //!< Number of instances of this cell + int32_t id_; //!< Unique ID + std::string name_; //!< User-defined name + int type_; //!< Material, universe, or lattice + int32_t universe_; //!< Universe # this cell is in + int32_t fill_; //!< Universe # filling this cell + int32_t n_instances_{0}; //!< Number of instances of 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. C_NONE signifies a universe. - std::vector material; + //! May be multiple materials for distribcell. + std::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). + std::vector sqrtkT_; //! Definition of spatial region as Boolean expression of half-spaces - std::vector region; + std::vector region_; //! Reverse Polish notation for region expression - std::vector rpn; - bool simple; //!< Does the region contain only intersections? + std::vector rpn_; + bool simple_; //!< Does the region contain only intersections? - std::vector offset; //!< Distribcell offset table + 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 three + //! values are the rotation angles respectively about the x-, y-, and z-, axes + //! in degrees. The next 9 values give the rotation matrix in row-major + //! order. + std::vector rotation_; + + std::vector offset_; //!< Distribcell offset table Cell() {}; diff --git a/include/openmc/geometry.h b/include/openmc/geometry.h index 09e7eda8eb..f045209ee8 100644 --- a/include/openmc/geometry.h +++ b/include/openmc/geometry.h @@ -1,10 +1,60 @@ #ifndef OPENMC_GEOMETRY_H #define OPENMC_GEOMETRY_H +#include +#include + +#include "openmc/particle.h" + + namespace openmc { +//============================================================================== +// Global variables +//============================================================================== + extern "C" int openmc_root_universe; +extern std::vector overlap_check_count; + +//============================================================================== +//! Check for overlapping cells at a particle's position. +//============================================================================== + +extern "C" bool +check_cell_overlap(Particle* p); + +//============================================================================== +//! Locate a particle in the geometry tree and set its geometry data fields. +//! +//! \param p A particle to be located. This function will populate the +//! geometry-dependent data fields of the particle. +//! \param search_surf A surface that the particle is expected to be on. This +//! value should be the signed, 1-based index of a surface. If positive, the +//! cells on the positive half-space of the surface will be searched. If +//! negative, the negative half-space will be searched. +//! \return True if the particle's location could be found and ascribed to a +//! valid geometry coordinate stack. +//============================================================================== + +extern "C" bool +find_cell(Particle* p, int search_surf); + +//============================================================================== +//! Move a particle into a new lattice tile. +//============================================================================== + +extern "C" void +cross_lattice(Particle* p, int lattice_translation[3]); + +//============================================================================== +//! Find the next boundary a particle will intersect. +//============================================================================== + +extern "C" void +distance_to_boundary(Particle* p, double* dist, int* surface_crossed, + int lattice_translation[3], int* next_level); + } // namespace openmc #endif // OPENMC_GEOMETRY_H diff --git a/include/openmc/geometry_aux.h b/include/openmc/geometry_aux.h index a99cadc902..ac68f5b00e 100644 --- a/include/openmc/geometry_aux.h +++ b/include/openmc/geometry_aux.h @@ -15,6 +15,12 @@ namespace openmc { extern "C" void adjust_indices(); +//============================================================================== +//! Assign defaults to cells with undefined temperatures. +//============================================================================== + +extern "C" void assign_temperatures(); + //============================================================================== //! Figure out which Universe is the root universe. //! @@ -25,11 +31,17 @@ extern "C" void adjust_indices(); extern "C" int32_t find_root_universe(); +//!============================================================================= +//! Build a list of neighboring cells to each surface to speed up tracking. +//!============================================================================= + +extern "C" void neighbor_lists(); + //============================================================================== -//! Allocate storage in Lattice and Cell objects for distribcell offset tables. +//! Populate all data structures needed for distribcells. //============================================================================== -extern "C" void allocate_offset_tables(int n_maps); +extern "C" void prepare_distribcell(int32_t* filter_cell_list, int n); //============================================================================== //! Recursively search through the geometry and count cell instances. @@ -53,15 +65,6 @@ extern "C" void count_cell_instances(int32_t univ_indx); extern "C" int count_universe_instances(int32_t search_univ, int32_t target_univ_id); -//============================================================================== -//! Populate Cell and Lattice distribcell offset tables. -//! @param target_univ_id The ID of the universe to be counted. -//! @param map The index of the distribcell map that defines the offsets for the -//! target universe. -//============================================================================== - -extern "C" void fill_offset_tables(int32_t target_univ_id, int map); - //============================================================================== //! Find the length necessary for a string to contain a distribcell path. //! @param target_cell The index of the Cell in the global Cell array. diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 0191d4ffb5..48ce41d0e9 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -36,7 +36,13 @@ bool using_mpio_device(hid_t obj_id); //============================================================================== hid_t create_group(hid_t parent_id, const std::string& name); + +inline hid_t create_group(hid_t parent_id, const std::stringstream& name) +{return create_group(parent_id, name.str());} + + hid_t file_open(const std::string& filename, char mode, bool parallel=false); + void write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep); @@ -295,7 +301,7 @@ void read_dataset(hid_t obj_id, const char* name, xt::xarray& arr, bool indep template inline void write_attribute(hid_t obj_id, const char* name, T buffer) { - write_attr(obj_id, name, 0, nullptr, H5TypeMap::type_id, &buffer); + write_attr(obj_id, 0, nullptr, name, H5TypeMap::type_id, &buffer); } inline void @@ -334,6 +340,13 @@ write_dataset(hid_t obj_id, const char* name, const std::array& buffer) write_dataset(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data(), false); } +template inline void +write_dataset(hid_t obj_id, const char* name, const std::vector& buffer) +{ + hsize_t dims[] {buffer.size()}; + write_dataset(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data(), false); +} + inline void write_dataset(hid_t obj_id, const char* name, Position r) { diff --git a/include/openmc/lattice.h b/include/openmc/lattice.h index 47544938d4..cacb87abea 100644 --- a/include/openmc/lattice.h +++ b/include/openmc/lattice.h @@ -22,12 +22,16 @@ namespace openmc { constexpr int32_t NO_OUTER_UNIVERSE{-1}; +enum class LatticeType { + rect, hex +}; + //============================================================================== // Global variables //============================================================================== class Lattice; -extern std::vector lattices_c; +extern std::vector lattices; extern std::unordered_map lattice_map; @@ -42,17 +46,18 @@ class ReverseLatticeIter; class Lattice { public: - int32_t id; //!< Universe ID number - std::string name; //!< User-defined name - std::vector universes; //!< Universes filling each lattice tile - int32_t outer {NO_OUTER_UNIVERSE}; //!< Universe tiled outside the lattice - std::vector offsets; //!< Distribcell offset table + int32_t id_; //!< Universe ID number + std::string name_; //!< User-defined name + LatticeType type_; + std::vector universes_; //!< Universes filling each lattice tile + int32_t outer_ {NO_OUTER_UNIVERSE}; //!< Universe tiled outside the lattice + std::vector offsets_; //!< Distribcell offset table explicit Lattice(pugi::xml_node lat_node); virtual ~Lattice() {} - virtual int32_t& operator[](const int i_xyz[3]) = 0; + virtual int32_t& operator[](std::array i_xyz) = 0; virtual LatticeIter begin(); LatticeIter end(); @@ -65,7 +70,7 @@ public: //! Allocate offset table for distribcell. void allocate_offset_table(int n_maps) - {offsets.resize(n_maps * universes.size(), C_NONE);} + {offsets_.resize(n_maps * universes_.size(), C_NONE);} //! Populate the distribcell offset tables. int32_t fill_offset_table(int32_t offset, int32_t target_univ_id, int map); @@ -76,14 +81,21 @@ public: //! otherwise. virtual bool are_valid_indices(const int i_xyz[3]) const = 0; + bool + are_valid_indices(std::array i_xyz) const + { + int i_xyz_[3] {i_xyz[0], i_xyz[1], i_xyz[2]}; + return are_valid_indices(i_xyz_); + } + //! \brief Find the next lattice surface crossing //! \param r A 3D Cartesian coordinate. //! \param u A 3D Cartesian direction. - //! \param i_xyz[3] The indices for a lattice tile. + //! \param i_xyz The indices for a lattice tile. //! \return The distance to the next crossing and an array indicating how the //! lattice indices would change after crossing that boundary. virtual std::pair> - distance(Position r, Direction u, const int i_xyz[3]) const + distance(Position r, Direction u, const std::array& i_xyz) const = 0; //! \brief Find the lattice tile indices for a given point. @@ -93,17 +105,17 @@ public: //! \brief Get coordinates local to a lattice tile. //! \param r A 3D Cartesian coordinate. - //! \param i_xyz[3] The indices for a lattice tile. + //! \param i_xyz The indices for a lattice tile. //! \return Local 3D Cartesian coordinates. virtual Position - get_local_position(Position r, const int i_xyz[3]) const = 0; + get_local_position(Position r, const std::array i_xyz) const = 0; //! \brief Check flattened lattice index. //! \param indx The index for a lattice tile. //! \return true if the given index fit within the lattice bounds. False //! otherwise. virtual bool is_valid_index(int indx) const - {return (indx >= 0) && (indx < universes.size());} + {return (indx >= 0) && (indx < universes_.size());} //! \brief Get the distribcell offset for a lattice tile. //! \param The map index for the target cell. @@ -122,7 +134,7 @@ public: void to_hdf5(hid_t group_id) const; protected: - bool is_3d; //!< Has divisions along the z-axis? + bool is_3d_; //!< Has divisions along the z-axis? virtual void to_hdf5_inner(hid_t group_id) const = 0; }; @@ -134,31 +146,31 @@ protected: class LatticeIter { public: - int indx; //!< An index to a Lattice universes or offsets array. + int indx_; //!< An index to a Lattice universes or offsets array. - LatticeIter(Lattice &lat_, int indx_) - : lat(lat_), - indx(indx_) + LatticeIter(Lattice &lat, int indx) + : lat_(lat), + indx_(indx) {} - bool operator==(const LatticeIter &rhs) {return (indx == rhs.indx);} + bool operator==(const LatticeIter &rhs) {return (indx_ == rhs.indx_);} bool operator!=(const LatticeIter &rhs) {return !(*this == rhs);} - int32_t& operator*() {return lat.universes[indx];} + int32_t& operator*() {return lat_.universes_[indx_];} LatticeIter& operator++() { - while (indx < lat.universes.size()) { - ++indx; - if (lat.is_valid_index(indx)) return *this; + while (indx_ < lat_.universes_.size()) { + ++indx_; + if (lat_.is_valid_index(indx_)) return *this; } - indx = lat.universes.size(); + indx_ = lat_.universes_.size(); return *this; } protected: - Lattice ⪫ + Lattice& lat_; }; //============================================================================== @@ -168,17 +180,17 @@ protected: class ReverseLatticeIter : public LatticeIter { public: - ReverseLatticeIter(Lattice &lat_, int indx_) - : LatticeIter {lat_, indx_} + ReverseLatticeIter(Lattice &lat, int indx) + : LatticeIter {lat, indx} {} ReverseLatticeIter& operator++() { - while (indx > -1) { - --indx; - if (lat.is_valid_index(indx)) return *this; + while (indx_ > -1) { + --indx_; + if (lat_.is_valid_index(indx_)) return *this; } - indx = -1; + indx_ = -1; return *this; } }; @@ -190,17 +202,17 @@ class RectLattice : public Lattice public: explicit RectLattice(pugi::xml_node lat_node); - int32_t& operator[](const int i_xyz[3]); + int32_t& operator[](std::array i_xyz); bool are_valid_indices(const int i_xyz[3]) const; std::pair> - distance(Position r, Direction u, const int i_xyz[3]) const; + distance(Position r, Direction u, const std::array& i_xyz) const; std::array get_indices(Position r) const; Position - get_local_position(Position r, const int i_xyz[3]) const; + get_local_position(Position r, const std::array i_xyz) const; int32_t& offset(int map, const int i_xyz[3]); @@ -209,14 +221,14 @@ public: void to_hdf5_inner(hid_t group_id) const; private: - std::array n_cells; //!< Number of cells along each axis - Position lower_left; //!< Global lower-left corner of the lattice - Position pitch; //!< Lattice tile width along each axis + std::array n_cells_; //!< Number of cells along each axis + Position lower_left_; //!< Global lower-left corner of the lattice + Position pitch_; //!< Lattice tile width along each axis // Convenience aliases - int &nx {n_cells[0]}; - int &ny {n_cells[1]}; - int &nz {n_cells[2]}; + int &nx {n_cells_[0]}; + int &ny {n_cells_[1]}; + int &nz {n_cells_[2]}; }; //============================================================================== @@ -226,7 +238,7 @@ class HexLattice : public Lattice public: explicit HexLattice(pugi::xml_node lat_node); - int32_t& operator[](const int i_xyz[3]); + int32_t& operator[](std::array i_xyz); LatticeIter begin(); @@ -235,12 +247,12 @@ public: bool are_valid_indices(const int i_xyz[3]) const; std::pair> - distance(Position r, Direction u, const int i_xyz[3]) const; + distance(Position r, Direction u, const std::array& i_xyz) const; std::array get_indices(Position r) const; Position - get_local_position(Position r, const int i_xyz[3]) const; + get_local_position(Position r, const std::array i_xyz) const; bool is_valid_index(int indx) const; @@ -251,10 +263,10 @@ public: void to_hdf5_inner(hid_t group_id) const; private: - int n_rings; //!< Number of radial tile positions - int n_axial; //!< Number of axial tile positions - Position center; //!< Global center of lattice - std::array pitch; //!< Lattice tile width and height + int n_rings_; //!< Number of radial tile positions + int n_axial_; //!< Number of axial tile positions + Position center_; //!< Global center of lattice + std::array pitch_; //!< Lattice tile width and height }; } // namespace openmc diff --git a/include/openmc/material.h b/include/openmc/material.h index c6337f1e25..1af6f85976 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -14,7 +14,7 @@ namespace openmc { //============================================================================== class Material; -extern std::vector global_materials; +extern std::vector materials; extern std::unordered_map material_map; //============================================================================== @@ -27,6 +27,11 @@ public: int32_t id; //!< Unique ID double volume_ {-1.0}; //!< Volume in [cm^3] + //! \brief Default temperature for cells containing this material. + //! + //! A negative value indicates no default temperature was specified. + double temperature_ {-1}; + Material() {}; explicit Material(pugi::xml_node material_node); diff --git a/include/openmc/output.h b/include/openmc/output.h new file mode 100644 index 0000000000..cabd39edeb --- /dev/null +++ b/include/openmc/output.h @@ -0,0 +1,26 @@ +//! \file output.h +//! Functions for ASCII output. + +#ifndef OPENMC_OUTPUT_H +#define OPENMC_OUTPUT_H + + +namespace openmc { + +//============================================================================== +//! Display a header block. +//! +//! \param msg The main text of the header +//! \param level The lowest verbosity level at which this header is printed +//============================================================================== + +void header(const char* msg, int level); + +//============================================================================== +//! Display information regarding cell overlap checking. +//============================================================================== + +extern "C" void print_overlap_check(); + +} // namespace openmc +#endif // OPENMC_OUTPUT_H diff --git a/include/openmc/particle.h b/include/openmc/particle.h index d186cdfd49..846d2e5a6a 100644 --- a/include/openmc/particle.h +++ b/include/openmc/particle.h @@ -4,8 +4,10 @@ //! \file particle.h //! \brief Particle type -#include #include +#include +#include +#include #include "openmc/capi.h" @@ -152,6 +154,12 @@ extern "C" { //! \param message A warning message to display void mark_as_lost(const char* message); + void mark_as_lost(const std::string& message) + {mark_as_lost(message.c_str());} + + void mark_as_lost(const std::stringstream& message) + {mark_as_lost(message.str());} + //! create a particle restart HDF5 file void write_restart(); }; diff --git a/include/openmc/position.h b/include/openmc/position.h index 808e0bcf67..97ab6f0e66 100644 --- a/include/openmc/position.h +++ b/include/openmc/position.h @@ -1,6 +1,8 @@ #ifndef OPENMC_POSITION_H #define OPENMC_POSITION_H +#include + namespace openmc { //============================================================================== @@ -12,6 +14,7 @@ struct Position { Position() = default; Position(double x_, double y_, double z_) : x{x_}, y{y_}, z{z_} { }; Position(const double xyz[]) : x{xyz[0]}, y{xyz[1]}, z{xyz[2]} { }; + Position(const std::vector xyz) : x{xyz[0]}, y{xyz[1]}, z{xyz[2]} { }; // Unary operators Position& operator+=(Position); @@ -63,6 +66,12 @@ inline Position operator*(Position a, Position b) { return a *= b; } inline Position operator*(Position a, double b) { return a *= b; } inline Position operator*(double a, Position b) { return b *= a; } +inline bool operator==(Position a, Position b) +{return a.x == b.x && a.y == b.y && a.z == b.z;} + +inline bool operator!=(Position a, Position b) +{return a.x != b.x || a.y != b.y || a.z != b.z;} + //============================================================================== //! Type representing a vector direction in Cartesian coordinates //============================================================================== @@ -71,4 +80,4 @@ using Direction = Position; } // namespace openmc -#endif // OPENMC_POSITION_H \ No newline at end of file +#endif // OPENMC_POSITION_H diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index daf7393fb2..9c07790727 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -18,10 +18,11 @@ extern "C" int openmc_current_gen; extern "C" int64_t openmc_current_work; extern "C" int openmc_n_lost_particles; extern "C" int openmc_total_gen; +extern "C" bool openmc_trace; extern std::vector work_index; -#pragma omp threadprivate(openmc_current_work) +#pragma omp threadprivate(openmc_current_work, openmc_trace) //============================================================================== // Functions diff --git a/include/openmc/surface.h b/include/openmc/surface.h index 1c38118304..23d5d15438 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -32,7 +32,7 @@ extern "C" const int BC_PERIODIC; extern "C" int32_t n_surfaces; class Surface; -extern std::vector global_surfaces; +extern std::vector surfaces; extern std::map surface_map; @@ -57,11 +57,12 @@ struct BoundingBox class Surface { public: - int id; //!< Unique ID - //int neighbor_pos[], //!< List of cells on positive side - // neighbor_neg[]; //!< List of cells on negative side - int bc; //!< Boundary condition - std::string name; //!< User-defined name + int id_; //!< Unique ID + int bc_; //!< Boundary condition + std::string name_; //!< User-defined name + + std::vector neighbor_pos_; //!< List of cells on positive side + std::vector neighbor_neg_; //!< List of cells on negative side explicit Surface(pugi::xml_node surf_node); @@ -120,7 +121,7 @@ protected: class PeriodicSurface : public Surface { public: - int i_periodic{C_NONE}; //!< Index of corresponding periodic surface + int i_periodic_{C_NONE}; //!< Index of corresponding periodic surface explicit PeriodicSurface(pugi::xml_node surf_node); @@ -147,7 +148,7 @@ public: class SurfaceXPlane : public PeriodicSurface { - double x0; + double x0_; public: explicit SurfaceXPlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -167,7 +168,7 @@ public: class SurfaceYPlane : public PeriodicSurface { - double y0; + double y0_; public: explicit SurfaceYPlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -187,7 +188,7 @@ public: class SurfaceZPlane : public PeriodicSurface { - double z0; + double z0_; public: explicit SurfaceZPlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -207,7 +208,7 @@ public: class SurfacePlane : public PeriodicSurface { - double A, B, C, D; + double A_, B_, C_, D_; public: explicit SurfacePlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -228,7 +229,7 @@ public: class SurfaceXCylinder : public Surface { - double y0, z0, radius; + double y0_, z0_, radius_; public: explicit SurfaceXCylinder(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -246,7 +247,7 @@ public: class SurfaceYCylinder : public Surface { - double x0, z0, radius; + double x0_, z0_, radius_; public: explicit SurfaceYCylinder(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -264,7 +265,7 @@ public: class SurfaceZCylinder : public Surface { - double x0, y0, radius; + double x0_, y0_, radius_; public: explicit SurfaceZCylinder(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -282,7 +283,7 @@ public: class SurfaceSphere : public Surface { - double x0, y0, z0, radius; + double x0_, y0_, z0_, radius_; public: explicit SurfaceSphere(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -300,7 +301,7 @@ public: class SurfaceXCone : public Surface { - double x0, y0, z0, radius_sq; + double x0_, y0_, z0_, radius_sq_; public: explicit SurfaceXCone(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -318,7 +319,7 @@ public: class SurfaceYCone : public Surface { - double x0, y0, z0, radius_sq; + double x0_, y0_, z0_, radius_sq_; public: explicit SurfaceYCone(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -336,7 +337,7 @@ public: class SurfaceZCone : public Surface { - double x0, y0, z0, radius_sq; + double x0_, y0_, z0_, radius_sq_; public: explicit SurfaceZCone(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -354,7 +355,7 @@ public: class SurfaceQuadric : public Surface { // Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0 - double A, B, C, D, E, F, G, H, J, K; + double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_; public: explicit SurfaceQuadric(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -373,10 +374,6 @@ extern "C" { int surface_bc(Surface* surf); bool surface_sense(Surface* surf, double xyz[3], double uvw[3]); void surface_reflect(Surface* surf, double xyz[3], double uvw[3]); - double surface_distance(Surface* surf, double xyz[3], double uvw[3], - bool coincident); - void surface_normal(Surface* surf, double xyz[3], double uvw[3]); - void surface_to_hdf5(Surface* surf, hid_t group); int surface_i_periodic(PeriodicSurface* surf); bool surface_periodic(PeriodicSurface* surf, PeriodicSurface* other, double xyz[3], double uvw[3]); diff --git a/openmc/_xml.py b/openmc/_xml.py new file mode 100644 index 0000000000..9c6c219e2f --- /dev/null +++ b/openmc/_xml.py @@ -0,0 +1,51 @@ +def clean_indentation(element, level=0, spaces_per_level=2): + """ + copy and paste from http://effbot.org/zone/elementent-lib.htm#prettyprint + it basically walks your tree and adds spaces and newlines so the tree is + printed in a nice way + """ + + i = "\n" + level*spaces_per_level*" " + + if len(element): + + if not element.text or not element.text.strip(): + element.text = i + spaces_per_level*" " + + if not element.tail or not element.tail.strip(): + element.tail = i + + for sub_element in element: + clean_indentation(sub_element, level+1, spaces_per_level) + + if not sub_element.tail or not sub_element.tail.strip(): + sub_element.tail = i + + else: + if level and (not element.tail or not element.tail.strip()): + element.tail = i + + +def get_text(elem, name, default=None): + """Retrieve text of an attribute or subelement. + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + Element from which to search + name : str + Name of attribute/subelement + default : object + A defult value to return if matching attribute/subelement exists + + Returns + ------- + str + Text of attribute or subelement + + """ + if name in elem.attrib: + return elem.get(name, default) + else: + child = elem.find(name) + return child.text if child is not None else default diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 4b994ac168..1a4c8996f0 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -141,7 +141,7 @@ class Cell(_FortranObjectWithID): Which instance of the cell """ - _dll.openmc_cell_set_temperature(self._index, T, instance) + _dll.openmc_cell_set_temperature(self._index, T, c_int32(instance)) class _CellMapping(Mapping): diff --git a/openmc/cell.py b/openmc/cell.py index cbfd74b21b..8503deffa1 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -13,6 +13,7 @@ import openmc import openmc.checkvalue as cv from openmc.surface import Halfspace from openmc.region import Region, Intersection, Complement +from openmc._xml import get_text from .mixin import IDManagerMixin @@ -56,7 +57,7 @@ class Cell(IDManagerMixin): .. math:: - \left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\theta \sin\psi + \left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\phi \sin\psi + \sin\phi \sin\theta \cos\psi & \sin\phi \sin\psi + \cos\phi \sin\theta \cos\psi \\ \cos\theta \sin\psi & \cos\phi \cos\psi + \sin\phi \sin\theta \sin\psi & -\sin\phi \cos\psi + \cos\phi @@ -522,3 +523,61 @@ class Cell(IDManagerMixin): element.set("rotation", ' '.join(map(str, self.rotation))) return element + + @classmethod + def from_xml_element(cls, elem, surfaces, materials, get_universe): + """Generate cell from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + `` element + surfaces : dict + Dictionary mapping surface IDs to :class:`openmc.Surface` instances + materials : dict + Dictionary mapping material IDs to :class:`openmc.Material` + instances (defined in :math:`openmc.Geometry.from_xml`) + get_universe : function + Function returning universe (defined in + :meth:`openmc.Geometry.from_xml`) + + Returns + ------- + Cell + Cell instance + + """ + cell_id = int(get_text(elem, 'id')) + name = get_text(elem, 'name') + c = cls(cell_id, name) + + # Assign material/distributed materials or fill + mat_text = get_text(elem, 'material') + if mat_text is not None: + mat_ids = mat_text.split() + if len(mat_ids) > 1: + c.fill = [materials[i] for i in mat_ids] + else: + c.fill = materials[mat_ids[0]] + else: + fill_id = int(get_text(elem, 'fill')) + c.fill = get_universe(fill_id) + + # Assign region + region = get_text(elem, 'region') + if region is not None: + c.region = Region.from_expression(region, surfaces) + + # Check for other attributes + t = get_text(elem, 'temperature') + if t is not None: + c.temperature = float(t) + for key in ('temperature', 'rotation', 'translation'): + value = get_text(elem, key) + if value is not None: + setattr(c, key, [float(x) for x in value.split()]) + + # Add this cell to appropriate universe + univ_id = int(get_text(elem, 'universe', 0)) + get_universe(univ_id).add_cell(c) + return c diff --git a/openmc/clean_xml.py b/openmc/clean_xml.py deleted file mode 100644 index d1002070b3..0000000000 --- a/openmc/clean_xml.py +++ /dev/null @@ -1,26 +0,0 @@ -def clean_xml_indentation(element, level=0, spaces_per_level=2): - """ - copy and paste from http://effbot.org/zone/elementent-lib.htm#prettyprint - it basically walks your tree and adds spaces and newlines so the tree is - printed in a nice way - """ - - i = "\n" + level*spaces_per_level*" " - - if len(element): - - if not element.text or not element.text.strip(): - element.text = i + spaces_per_level*" " - - if not element.tail or not element.tail.strip(): - element.tail = i - - for sub_element in element: - clean_xml_indentation(sub_element, level+1, spaces_per_level) - - if not sub_element.tail or not sub_element.tail.strip(): - sub_element.tail = i - - else: - if level and (not element.tail or not element.tail.strip()): - element.tail = i diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 5444334b20..c156cd00a3 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -15,7 +15,7 @@ from numbers import Real, Integral from xml.etree import ElementTree as ET import sys -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) @@ -512,7 +512,7 @@ class CMFD(object): self._create_write_matrices_subelement() # Clean the indentation in the file to be user-readable - clean_xml_indentation(self._cmfd_file) + clean_indentation(self._cmfd_file) # Write the XML Tree to the cmfd.xml file tree = ET.ElementTree(self._cmfd_file) diff --git a/openmc/data/__init__.py b/openmc/data/__init__.py index 390a67c9b9..dec11cd3a9 100644 --- a/openmc/data/__init__.py +++ b/openmc/data/__init__.py @@ -4,7 +4,7 @@ HDF5_VERSION_MINOR = 0 HDF5_VERSION = (HDF5_VERSION_MAJOR, HDF5_VERSION_MINOR) # Version of WMP nuclear data format -WMP_VERSION = 'v0.2' +WMP_VERSION = 'v1.0' from .data import * diff --git a/openmc/data/library.py b/openmc/data/library.py index 58e5e4e16a..8d821e49fd 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -4,7 +4,7 @@ import xml.etree.ElementTree as ET import h5py from openmc.mixin import EqualityMixin -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from openmc.checkvalue import check_type @@ -95,7 +95,7 @@ class DataLibrary(EqualityMixin): lib_element.set('type', library['type']) # Clean the indentation to be user-readable - clean_xml_indentation(root) + clean_indentation(root) # Write XML file tree = ET.ElementTree(root) diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index f7a78d9532..a0d90ac019 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -10,26 +10,16 @@ import openmc.checkvalue as cv from openmc.mixin import EqualityMixin -# Formalisms -_FORM_MLBW = 2 -_FORM_RM = 3 - # Constants that determine which value to access _MP_EA = 0 # Pole -# Reich-Moore indices -_RM_RT = 1 # Residue total -_RM_RA = 2 # Residue absorption -_RM_RF = 3 # Residue fission - -# Multi-level Breit Wigner indices -_MLBW_RT = 1 # Residue total -_MLBW_RX = 2 # Residue competitive -_MLBW_RA = 3 # Residue absorption -_MLBW_RF = 4 # Residue fission +# Residue indices +_MP_RS = 1 # Residue scattering +_MP_RA = 2 # Residue absorption +_MP_RF = 3 # Residue fission # Polynomial fit indices -_FIT_T = 0 # Total +_FIT_S = 0 # Scattering _FIT_A = 1 # Absorption _FIT_F = 2 # Fission @@ -143,98 +133,61 @@ class WindowedMultipole(EqualityMixin): Parameters ---------- - formalism : {'MLBW', 'RM'} - The R-matrix formalism used to reconstruct resonances. Either 'MLBW' - for multi-level Breit Wigner or 'RM' for Reich-Moore. Attributes ---------- - num_l : Integral - Number of possible l quantum states for this nuclide. fit_order : Integral Order of the windowed curvefit. fissionable : bool Whether or not the target nuclide has fission data. - formalism : {'MLBW', 'RM'} - The R-matrix formalism used to reconstruct resonances. Either 'MLBW' - for multi-level Breit Wigner or 'RM' for Reich-Moore. spacing : Real The width of each window in sqrt(E)-space. For example, the frst window - will end at (sqrt(start_E) + spacing)**2 and the second window at - (sqrt(start_E) + 2*spacing)**2. + will end at (sqrt(E_min) + spacing)**2 and the second window at + (sqrt(E_min) + 2*spacing)**2. sqrtAWR : Real Square root of the atomic weight ratio of the target nuclide. - start_E : Real + E_min : Real Lowest energy in eV the library is valid for. - end_E : Real + E_max : Real Highest energy in eV the library is valid for. data : np.ndarray A 2D array of complex poles and residues. data[i, 0] gives the energy at which pole i is located. data[i, 1:] gives the residues associated - with the i-th pole. There are 3 residues for Reich-Moore data, one each - for the total, absorption, and fission channels. Multi-level - Breit Wigner data has an additional residue for the competitive channel. - pseudo_k0RS : np.ndarray - A 1D array of Real values. There is one value for each valid l - quantum number. The values are equal to - sqrt(2 m / hbar) * AWR / (AWR + 1) * r - where m is the neutron mass, AWR is the atomic weight ratio, and r - is the l-dependent scattering radius. - l_value : np.ndarray - A 1D array of Integral values equal to the l quantum number for each - pole + 1. - w_start : np.ndarray - A 1D array of Integral values. w_start[i] - 1 is the index of the first - pole in window i. - w_end : np.ndarray - A 1D array of Integral values. w_end[i] - 1 is the index of the last - pole in window i. + with the i-th pole. There are 3 residues, one each for the scattering, + absorption, and fission channels. + windows : np.ndarray + A 2D array of Integral values. windows[i, 0] - 1 is the index of the + first pole in window i. windows[i, 1] - 1 is the index of the last pole + in window i. broaden_poly : np.ndarray A 1D array of boolean values indicating whether or not the polynomial curvefit in that window should be Doppler broadened. curvefit : np.ndarray A 3D array of Real curvefit polynomial coefficients. curvefit[i, 0, :] - gives coefficients for the total cross section in window i. + gives coefficients for the scattering cross section in window i. curvefit[i, 1, :] gives absorption coefficients and curvefit[i, 2, :] gives fission coefficients. The polynomial terms are increasing powers of sqrt(E) starting with 1/E e.g: a/E + b/sqrt(E) + c + d sqrt(E) + ... """ - def __init__(self, formalism): - self._num_l = None - self.formalism = formalism + def __init__(self): self.spacing = None self.sqrtAWR = None - self.start_E = None - self.end_E = None + self.E_min = None + self.E_max = None self.data = None - self.pseudo_k0RS = None - self.l_value = None - self.w_start = None - self.w_end = None + self.windows = None self.broaden_poly = None self.curvefit = None - @property - def num_l(self): - return self._num_l - @property def fit_order(self): return self.curvefit.shape[1] - 1 @property def fissionable(self): - if self.formalism == 'RM': - return self.data.shape[1] == 4 - else: - # Assume self.formalism == 'MLBW' - return self.data.shape[1] == 5 - - @property - def formalism(self): - return self._formalism + return self.data.shape[1] == 4 @property def spacing(self): @@ -245,32 +198,20 @@ class WindowedMultipole(EqualityMixin): return self._sqrtAWR @property - def start_E(self): - return self._start_E + def E_min(self): + return self._E_min @property - def end_E(self): - return self._end_E + def E_max(self): + return self._E_max @property def data(self): return self._data @property - def pseudo_k0RS(self): - return self._pseudo_k0RS - - @property - def l_value(self): - return self._l_value - - @property - def w_start(self): - return self._w_start - - @property - def w_end(self): - return self._w_end + def windows(self): + return self._windows @property def broaden_poly(self): @@ -280,12 +221,6 @@ class WindowedMultipole(EqualityMixin): def curvefit(self): return self._curvefit - @formalism.setter - def formalism(self, formalism): - cv.check_type('formalism', formalism, str) - cv.check_value('formalism', formalism, ('MLBW', 'RM')) - self._formalism = formalism - @spacing.setter def spacing(self, spacing): if spacing is not None: @@ -300,19 +235,19 @@ class WindowedMultipole(EqualityMixin): cv.check_greater_than('sqrtAWR', sqrtAWR, 0.0, equality=False) self._sqrtAWR = sqrtAWR - @start_E.setter - def start_E(self, start_E): - if start_E is not None: - cv.check_type('start_E', start_E, Real) - cv.check_greater_than('start_E', start_E, 0.0, equality=True) - self._start_E = start_E + @E_min.setter + def E_min(self, E_min): + if E_min is not None: + cv.check_type('E_min', E_min, Real) + cv.check_greater_than('E_min', E_min, 0.0, equality=True) + self._E_min = E_min - @end_E.setter - def end_E(self, end_E): - if end_E is not None: - cv.check_type('end_E', end_E, Real) - cv.check_greater_than('end_E', end_E, 0.0, equality=False) - self._end_E = end_E + @E_max.setter + def E_max(self, E_max): + if E_max is not None: + cv.check_type('E_max', E_max, Real) + cv.check_greater_than('E_max', E_max, 0.0, equality=False) + self._E_max = E_max @data.setter def data(self, data): @@ -320,71 +255,25 @@ class WindowedMultipole(EqualityMixin): cv.check_type('data', data, np.ndarray) if len(data.shape) != 2: raise ValueError('Multipole data arrays must be 2D') - if self.formalism == 'RM': - if data.shape[1] not in (3, 4): - raise ValueError('For the Reich-Moore formalism, ' - 'data.shape[1] must be 3 or 4. One value for the pole.' - ' One each for the total and absorption residues. ' - 'Possibly one more for a fission residue.') - else: - # Assume self.formalism == 'MLBW' - if data.shape[1] not in (4, 5): - raise ValueError('For the Multi-level Breit-Wigner ' - 'formalism, data.shape[1] must be 4 or 5. One value ' - 'for the pole. One each for the total, competitive, ' - 'and absorption residues. Possibly one more for a ' - 'fission residue.') - if not np.issubdtype(data.dtype, complex): + if data.shape[1] not in (3, 4): + raise ValueError( + 'data.shape[1] must be 3 or 4. One value for the pole.' + ' One each for the scattering and absorption residues. ' + 'Possibly one more for a fission residue.') + if not np.issubdtype(data.dtype, np.complexfloating): raise TypeError('Multipole data arrays must be complex dtype') self._data = data - @pseudo_k0RS.setter - def pseudo_k0RS(self, pseudo_k0RS): - if pseudo_k0RS is not None: - cv.check_type('pseudo_k0RS', pseudo_k0RS, np.ndarray) - if len(pseudo_k0RS.shape) != 1: - raise ValueError('Multipole pseudo_k0RS arrays must be 1D') - if not np.issubdtype(pseudo_k0RS.dtype, float): - raise TypeError('Multipole data arrays must be float dtype') - self._pseudo_k0RS = pseudo_k0RS - - @l_value.setter - def l_value(self, l_value): - if l_value is not None: - cv.check_type('l_value', l_value, np.ndarray) - if len(l_value.shape) != 1: - raise ValueError('Multipole l_value arrays must be 1D') - if not np.issubdtype(l_value.dtype, int): - raise TypeError('Multipole l_value arrays must be integer' + @windows.setter + def windows(self, windows): + if windows is not None: + cv.check_type('windows', windows, np.ndarray) + if len(windows.shape) != 2: + raise ValueError('Multipole windows arrays must be 2D') + if not np.issubdtype(windows.dtype, np.integer): + raise TypeError('Multipole windows arrays must be integer' ' dtype') - - self._num_l = len(np.unique(l_value)) - - else: - self._num_l = None - - self._l_value = l_value - - @w_start.setter - def w_start(self, w_start): - if w_start is not None: - cv.check_type('w_start', w_start, np.ndarray) - if len(w_start.shape) != 1: - raise ValueError('Multipole w_start arrays must be 1D') - if not np.issubdtype(w_start.dtype, int): - raise TypeError('Multipole w_start arrays must be integer' - ' dtype') - self._w_start = w_start - - @w_end.setter - def w_end(self, w_end): - if w_end is not None: - cv.check_type('w_end', w_end, np.ndarray) - if len(w_end.shape) != 1: - raise ValueError('Multipole w_end arrays must be 1D') - if not np.issubdtype(w_end.dtype, int): - raise TypeError('Multipole w_end arrays must be integer dtype') - self._w_end = w_end + self._windows = windows @broaden_poly.setter def broaden_poly(self, broaden_poly): @@ -392,7 +281,7 @@ class WindowedMultipole(EqualityMixin): cv.check_type('broaden_poly', broaden_poly, np.ndarray) if len(broaden_poly.shape) != 1: raise ValueError('Multipole broaden_poly arrays must be 1D') - if not np.issubdtype(broaden_poly.dtype, bool): + if not np.issubdtype(broaden_poly.dtype, np.bool_): raise TypeError('Multipole broaden_poly arrays must be boolean' ' dtype') self._broaden_poly = broaden_poly @@ -403,10 +292,10 @@ class WindowedMultipole(EqualityMixin): cv.check_type('curvefit', curvefit, np.ndarray) if len(curvefit.shape) != 3: raise ValueError('Multipole curvefit arrays must be 3D') - if curvefit.shape[2] not in (2, 3): # sig_t, sig_a (maybe sig_f) + if curvefit.shape[2] not in (2, 3): # sig_s, sig_a (maybe sig_f) raise ValueError('The third dimension of multipole curvefit' ' arrays must have a length of 2 or 3') - if not np.issubdtype(curvefit.dtype, float): + if not np.issubdtype(curvefit.dtype, np.floating): raise TypeError('Multipole curvefit arrays must be float dtype') self._curvefit = curvefit @@ -443,19 +332,14 @@ class WindowedMultipole(EqualityMixin): 'Python API expects version ' + WMP_VERSION) group = h5file['nuclide'] - # Read scalars. + out = cls() - if group['formalism'].value == _FORM_MLBW: - out = cls('MLBW') - elif group['formalism'].value == _FORM_RM: - out = cls('RM') - else: - raise ValueError('Unrecognized/Unsupported R-matrix formalism') + # Read scalars. out.spacing = group['spacing'].value out.sqrtAWR = group['sqrtAWR'].value - out.start_E = group['start_E'].value - out.end_E = group['end_E'].value + out.E_min = group['E_min'].value + out.E_max = group['E_max'].value # Read arrays. @@ -463,27 +347,15 @@ class WindowedMultipole(EqualityMixin): out.data = group['data'].value - out.l_value = group['l_value'].value - if out.l_value.shape[0] != out.data.shape[0]: - raise ValueError(err.format('l_value', 'data')) - - out.pseudo_k0RS = group['pseudo_K0RS'].value - if out.pseudo_k0RS.shape[0] != out.num_l: - raise ValueError(err.format('pseudo_k0RS', 'l_value')) - - out.w_start = group['w_start'].value - - out.w_end = group['w_end'].value - if out.w_end.shape[0] != out.w_start.shape[0]: - raise ValueError(err.format('w_end', 'w_start')) + out.windows = group['windows'].value out.broaden_poly = group['broaden_poly'].value.astype(np.bool) - if out.broaden_poly.shape[0] != out.w_start.shape[0]: - raise ValueError(err.format('broaden_poly', 'w_start')) + if out.broaden_poly.shape[0] != out.windows.shape[0]: + raise ValueError(err.format('broaden_poly', 'windows')) out.curvefit = group['curvefit'].value - if out.curvefit.shape[0] != out.w_start.shape[0]: - raise ValueError(err.format('curvefit', 'w_start')) + if out.curvefit.shape[0] != out.windows.shape[0]: + raise ValueError(err.format('curvefit', 'windows')) # _broaden_wmp_polynomials assumes the curve fit has at least 3 terms. if out.fit_order < 2: @@ -493,7 +365,7 @@ class WindowedMultipole(EqualityMixin): return out def _evaluate(self, E, T): - """Compute total, absorption, and fission cross sections. + """Compute scattering, absorption, and fission cross sections. Parameters ---------- @@ -510,8 +382,8 @@ class WindowedMultipole(EqualityMixin): """ - if E < self.start_E: return (0, 0, 0) - if E > self.end_E: return (0, 0, 0) + if E < self.E_min: return (0, 0, 0) + if E > self.E_max: return (0, 0, 0) # ====================================================================== # Bookkeeping @@ -525,34 +397,12 @@ class WindowedMultipole(EqualityMixin): # the 1-based vs. 0-based indexing. Similarly startw needs to be # decreased by 1. endw does not need to be decreased because # range(startw, endw) does not include endw. - i_window = int(np.floor((sqrtE - sqrt(self.start_E)) / self.spacing)) - startw = self.w_start[i_window] - 1 - endw = self.w_end[i_window] - - # Fill in factors. Because of the unique interference dips in scatering - # resonances, the total cross section has a special "factor" that does - # not appear in the absorption and fission equations. - if startw <= endw: - twophi = np.zeros(self.num_l, dtype=np.float) - sig_t_factor = np.zeros(self.num_l, dtype=np.cfloat) - - for iL in range(self.num_l): - twophi[iL] = self.pseudo_k0RS[iL] * sqrtE - if iL == 1: - twophi[iL] = twophi[iL] - np.arctan(twophi[iL]) - elif iL == 2: - arg = 3.0 * twophi[iL] / (3.0 - twophi[iL]**2) - twophi[iL] = twophi[iL] - np.arctan(arg) - elif iL == 3: - arg = (twophi[iL] * (15.0 - twophi[iL]**2) - / (15.0 - 6.0 * twophi[iL]**2)) - twophi[iL] = twophi[iL] - np.arctan(arg) - - twophi = 2.0 * twophi - sig_t_factor = np.cos(twophi) - 1j*np.sin(twophi) + i_window = int(np.floor((sqrtE - sqrt(self.E_min)) / self.spacing)) + startw = self.windows[i_window, 0] - 1 + endw = self.windows[i_window, 1] # Initialize the ouptut cross sections. - sig_t = 0.0 + sig_s = 0.0 sig_a = 0.0 sig_f = 0.0 @@ -565,7 +415,7 @@ class WindowedMultipole(EqualityMixin): broadened_polynomials = _broaden_wmp_polynomials(E, dopp, self.fit_order + 1) for i_poly in range(self.fit_order+1): - sig_t += (self.curvefit[i_window, i_poly, _FIT_T] + sig_s += (self.curvefit[i_window, i_poly, _FIT_S] * broadened_polynomials[i_poly]) sig_a += (self.curvefit[i_window, i_poly, _FIT_A] * broadened_polynomials[i_poly]) @@ -575,7 +425,7 @@ class WindowedMultipole(EqualityMixin): else: temp = invE for i_poly in range(self.fit_order+1): - sig_t += self.curvefit[i_window, i_poly, _FIT_T] * temp + sig_s += self.curvefit[i_window, i_poly, _FIT_S] * temp sig_a += self.curvefit[i_window, i_poly, _FIT_A] * temp if self.fissionable: sig_f += self.curvefit[i_window, i_poly, _FIT_F] * temp @@ -589,22 +439,10 @@ class WindowedMultipole(EqualityMixin): for i_pole in range(startw, endw): psi_chi = -1j / (self.data[i_pole, _MP_EA] - sqrtE) c_temp = psi_chi / E - if self.formalism == 'MLBW': - sig_t += ((self.data[i_pole, _MLBW_RT] * c_temp * - sig_t_factor[self.l_value[i_pole]-1]).real - + (self.data[i_pole, _MLBW_RX] * c_temp).real) - sig_a += (self.data[i_pole, _MLBW_RA] * c_temp).real - if self.fissionable: - sig_f += (self.data[i_pole, _MLBW_RF] * c_temp).real - elif self.formalism == 'RM': - sig_t += (self.data[i_pole, _RM_RT] * c_temp * - sig_t_factor[self.l_value[i_pole]-1]).real - sig_a += (self.data[i_pole, _RM_RA] * c_temp).real - if self.fissionable: - sig_f += (self.data[i_pole, _RM_RF] * c_temp).real - else: - raise ValueError('Unrecognized/Unsupported R-matrix' - ' formalism') + sig_s += (self.data[i_pole, _MP_RS] * c_temp).real + sig_a += (self.data[i_pole, _MP_RA] * c_temp).real + if self.fissionable: + sig_f += (self.data[i_pole, _MP_RF] * c_temp).real else: # At temperature, use Faddeeva function-based form. @@ -612,27 +450,15 @@ class WindowedMultipole(EqualityMixin): for i_pole in range(startw, endw): Z = (sqrtE - self.data[i_pole, _MP_EA]) * dopp w_val = _faddeeva(Z) * dopp * invE * sqrt(pi) - if self.formalism == 'MLBW': - sig_t += ((self.data[i_pole, _MLBW_RT] * - sig_t_factor[self.l_value[i_pole]-1] + - self.data[i_pole, _MLBW_RX]) * w_val).real - sig_a += (self.data[i_pole, _MLBW_RA] * w_val).real - if self.fissionable: - sig_f += (self.data[i_pole, _MLBW_RF] * w_val).real - elif self.formalism == 'RM': - sig_t += (self.data[i_pole, _RM_RT] * w_val * - sig_t_factor[self.l_value[i_pole]-1]).real - sig_a += (self.data[i_pole, _RM_RA] * w_val).real - if self.fissionable: - sig_f += (self.data[i_pole, _RM_RF] * w_val).real - else: - raise ValueError('Unrecognized/Unsupported R-matrix' - ' formalism') + sig_s += (self.data[i_pole, _MP_RS] * w_val).real + sig_a += (self.data[i_pole, _MP_RA] * w_val).real + if self.fissionable: + sig_f += (self.data[i_pole, _MP_RF] * w_val).real - return sig_t, sig_a, sig_f + return sig_s, sig_a, sig_f def __call__(self, E, T): - """Compute total, absorption, and fission cross sections. + """Compute scattering, absorption, and fission cross sections. Parameters ---------- @@ -674,24 +500,14 @@ class WindowedMultipole(EqualityMixin): g = f.create_group('nuclide') # Write scalars. - if self.formalism == 'MLBW': - g.create_dataset('formalism', - data=np.array(_FORM_MLBW, dtype=np.int32)) - else: - # Assume RM. - g.create_dataset('formalism', - data=np.array(_FORM_RM, dtype=np.int32)) g.create_dataset('spacing', data=np.array(self.spacing)) g.create_dataset('sqrtAWR', data=np.array(self.sqrtAWR)) - g.create_dataset('start_E', data=np.array(self.start_E)) - g.create_dataset('end_E', data=np.array(self.end_E)) + g.create_dataset('E_min', data=np.array(self.E_min)) + g.create_dataset('E_max', data=np.array(self.E_max)) # Write arrays. g.create_dataset('data', data=self.data) - g.create_dataset('l_value', data=self.l_value) - g.create_dataset('pseudo_K0RS', data=self.pseudo_k0RS) - g.create_dataset('w_start', data=self.w_start) - g.create_dataset('w_end', data=self.w_end) + g.create_dataset('windows', data=self.windows) g.create_dataset('broaden_poly', data=self.broaden_poly.astype(np.int8)) g.create_dataset('curvefit', data=self.curvefit) diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 1826ca9ca0..4635d99d15 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -23,7 +23,7 @@ except ImportError: import scipy.sparse as sp import openmc.data -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from .nuclide import Nuclide, DecayTuple, ReactionTuple @@ -356,7 +356,7 @@ class Chain(object): if _have_lxml: tree.write(str(filename), encoding='utf-8', pretty_print=True) else: - clean_xml_indentation(root_elem) + clean_indentation(root_elem) tree.write(str(filename), encoding='utf-8') def form_matrix(self, rates): diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index 7debebd6b9..59457b6857 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -19,7 +19,7 @@ class ExpansionFilter(Filter): if type(self) is not type(other): return False else: - return self.bins == other.bins + return hash(self) == hash(other) @property def order(self): @@ -325,8 +325,8 @@ class ZernikeFilter(ExpansionFilter): This filter allows scores to be multiplied by Zernike polynomials of the particle's position normalized to a given unit circle, up to a - user-specified order. The standard Zernike polynomials follow the definition by - Born and Wolf, *Principles of Optics* and are defined as + user-specified order. The standard Zernike polynomials follow the + definition by Born and Wolf, *Principles of Optics* and are defined as .. math:: Z_n^m(\rho, \theta) = R_n^m(\rho) \cos (m\theta), \quad m > 0 @@ -342,7 +342,7 @@ class ZernikeFilter(ExpansionFilter): \frac{n+m}{2} - k)! (\frac{n-m}{2} - k)!} \rho^{n-2k}. With this definition, the integral of :math:`(Z_n^m)^2` over the unit disk - is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where + is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where :math:`\epsilon_m` is 2 if :math:`m` equals 0 and 1 otherwise. Specifying a filter with order N tallies moments for all :math:`n` from 0 @@ -390,6 +390,9 @@ class ZernikeFilter(ExpansionFilter): def __hash__(self): string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tX', self.x) + string += '{: <16}=\t{}\n'.format('\tY', self.y) + string += '{: <16}=\t{}\n'.format('\tR', self.r) return hash(string) def __repr__(self): @@ -485,7 +488,7 @@ class ZernikeRadialFilter(ZernikeFilter): is :math:`\frac{\pi}{n+1}`. If there is only radial dependency, the polynomials are integrated over - the azimuthal angles. The only terms left are :math:`Z_n^{0}(\rho, \theta) + the azimuthal angles. The only terms left are :math:`Z_n^{0}(\rho, \theta) = R_n^{0}(\rho)`. Note that :math:`n` could only be even orders. Therefore, for a radial Zernike polynomials up to order of :math:`n`, there are :math:`\frac{n}{2} + 1` terms in total. The indexing is from the diff --git a/openmc/geometry.py b/openmc/geometry.py index 1ee93dfd6d..4cbc60c249 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,10 +1,13 @@ -from collections import OrderedDict +from collections import OrderedDict, defaultdict from collections.abc import Iterable from copy import deepcopy +from pathlib import Path from xml.etree import ElementTree as ET +import numpy as np + import openmc -from openmc.clean_xml import clean_xml_indentation +import openmc._xml as xml from openmc.checkvalue import check_type @@ -92,12 +95,104 @@ class Geometry(object): x.tag, int(x.get('id')))) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + xml.clean_indentation(root_element) # Write the XML Tree to the geometry.xml file tree = ET.ElementTree(root_element) tree.write(path, xml_declaration=True, encoding='utf-8') + @classmethod + def from_xml(cls, path='geometry.xml', materials=None): + """Generate geometry from XML file + + Parameters + ---------- + path : str, optional + Path to geometry XML file + materials : openmc.Materials or None + Materials used to assign to cells. If None, an attempt is made to + generate it from the materials.xml file. + + Returns + ------- + openmc.Geometry + Geometry object + + """ + # Helper function for keeping a cache of Universe instances + universes = {} + def get_universe(univ_id): + if univ_id not in universes: + univ = openmc.Universe(univ_id) + universes[univ_id] = univ + return universes[univ_id] + + tree = ET.parse(path) + root = tree.getroot() + + # Get surfaces + surfaces = {} + periodic = {} + for surface in root.findall('surface'): + s = openmc.Surface.from_xml_element(surface) + surfaces[s.id] = s + + # Check for periodic surface + other_id = xml.get_text(surface, 'periodic_surface_id') + if other_id is not None: + periodic[s.id] = int(other_id) + + # Apply periodic surfaces + for s1, s2 in periodic.items(): + surfaces[s1].periodic_surface = surfaces[s2] + + # Dictionary that maps each universe to a list of cells/lattices that + # contain it (needed to determine which universe is the root) + child_of = defaultdict(list) + + for elem in root.findall('lattice'): + lat = openmc.RectLattice.from_xml_element(elem, get_universe) + universes[lat.id] = lat + if lat.outer is not None: + child_of[lat.outer].append(lat) + for u in lat.universes.ravel(): + child_of[u].append(lat) + + for elem in root.findall('hex_lattice'): + lat = openmc.HexLattice.from_xml_element(elem, get_universe) + universes[lat.id] = lat + if lat.outer is not None: + child_of[lat.outer].append(lat) + if lat.ndim == 2: + for ring in lat.universes: + for u in ring: + child_of[u].append(lat) + else: + for axial_slice in lat.universes: + for ring in axial_slice: + for u in ring: + child_of[u].append(lat) + + # Create dictionary to easily look up materials + if materials is None: + filename = Path(path).parent / 'materials.xml' + materials = openmc.Materials.from_xml(str(filename)) + mats = {str(m.id): m for m in materials} + mats['void'] = None + + for elem in root.findall('cell'): + c = openmc.Cell.from_xml_element(elem, surfaces, mats, get_universe) + if c.fill_type in ('universe', 'lattice'): + child_of[c.fill].append(c) + + # Determine which universe is the root by finding one which is not a + # child of any other object + for u in universes.values(): + if not child_of[u]: + return cls(u) + else: + raise ValueError('Error determining root universe.') + def find(self, point): """Find cells/universes/lattices which contain a given point diff --git a/openmc/lattice.py b/openmc/lattice.py index ef33247fca..8bf4e47571 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -10,6 +10,7 @@ import numpy as np import openmc.checkvalue as cv import openmc +from openmc._xml import get_text from openmc.mixin import IDManagerMixin @@ -768,6 +769,42 @@ class RectLattice(Lattice): # Append the XML subelement for this Lattice to the XML element xml_element.append(lattice_subelement) + @classmethod + def from_xml_element(cls, elem, get_universe): + """Generate rectangular lattice from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + `` element + get_universe : function + Function returning universe (defined in + :meth:`openmc.Geometry.from_xml`) + + Returns + ------- + RectLattice + Rectangular lattice + + """ + lat_id = int(get_text(elem, 'id')) + name = get_text(elem, 'name') + lat = cls(lat_id, name) + lat.lower_left = [float(i) for i in get_text(elem, 'lower_left').split()] + lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] + outer = get_text(elem, 'outer') + if outer is not None: + lat.outer = get_universe(int(outer)) + + # Get array of universes + dimension = get_text(elem, 'dimension').split() + shape = np.array(dimension, dtype=int)[::-1] + uarray = np.array([get_universe(int(i)) for i in + get_text(elem, 'universes').split()]) + uarray.shape = shape + lat.universes = uarray + return lat + class HexLattice(Lattice): r"""A lattice consisting of hexagonal prisms. @@ -1207,6 +1244,78 @@ class HexLattice(Lattice): # Append the XML subelement for this Lattice to the XML element xml_element.append(lattice_subelement) + @classmethod + def from_xml_element(cls, elem, get_universe): + """Generate hexagonal lattice from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + `` element + get_universe : function + Function returning universe (defined in + :meth:`openmc.Geometry.from_xml`) + + Returns + ------- + HexLattice + Hexagonal lattice + + """ + lat_id = int(get_text(elem, 'id')) + name = get_text(elem, 'name') + lat = cls(lat_id, name) + lat.center = [float(i) for i in get_text(elem, 'center').split()] + lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] + outer = get_text(elem, 'outer') + if outer is not None: + lat.outer = get_universe(int(outer)) + + # Get nested lists of universes + lat._num_rings = n_rings = int(get_text(elem, 'n_rings')) + lat._num_axial = n_axial = int(get_text(elem, 'n_axial', 1)) + + # Create empty nested lists for one axial level + univs = [[None for _ in range(max(6*(n_rings - 1 - r), 1))] + for r in range(n_rings)] + if n_axial > 1: + univs = [deepcopy(univs) for i in range(n_axial)] + + # Get flat array of universes numbers + uarray = np.array([get_universe(int(i)) for i in + get_text(elem, 'universes').split()]) + + # Fill nested lists + j = 0 + for z in range(n_axial): + # Get list for a single axial level + axial_level = univs[z] if n_axial > 1 else univs + + # Start iterating from top + x, alpha = 0, n_rings - 1 + while True: + # Set entry in list based on (x,alpha,z) coordinates + _, i_ring, i_within = lat.get_universe_index((x, alpha, z)) + axial_level[i_ring][i_within] = uarray[j] + + # Move to the right + x += 2 + alpha -= 1 + if not lat.is_valid_index((x, alpha, z)): + # Move down in y direction + alpha += x - 1 + x = 1 - x + if not lat.is_valid_index((x, alpha, z)): + # Move to the right + x += 2 + alpha -= 1 + if not lat.is_valid_index((x, alpha, z)): + # Reached the bottom + break + j += 1 + lat.universes = univs + return lat + def _repr_axial_slice(self, universes): """Return string representation for the given 2D group of universes. diff --git a/openmc/material.py b/openmc/material.py index bd31fbd23a..d11f9de497 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -9,7 +9,7 @@ import numpy as np import openmc import openmc.data import openmc.checkvalue as cv -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from .mixin import IDManagerMixin @@ -912,6 +912,56 @@ class Material(IDManagerMixin): return element + @classmethod + def from_xml_element(cls, elem): + """Generate material from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Material + Material generated from XML element + + """ + mat_id = int(elem.get('id')) + mat = cls(mat_id) + mat.name = elem.get('name') + mat.temperature = elem.get('temperature') + mat.depletable = bool(elem.get('depletable')) + + # Get each nuclide + for nuclide in elem.findall('nuclide'): + name = nuclide.attrib['name'] + if 'ao' in nuclide.attrib: + mat.add_nuclide(name, float(nuclide.attrib['ao'])) + elif 'wo' in nuclide.attrib: + mat.add_nuclide(name, float(nuclide.attrib['wo']), 'wo') + + # Get each S(a,b) table + for sab in elem.findall('sab'): + fraction = float(sab.get('fraction', 1.0)) + mat.add_s_alpha_beta(sab.get('name'), fraction) + + # Get total material density + density = elem.find('density') + units = density.get('units') + if units == 'sum': + mat.set_density(units) + else: + value = float(density.get('value')) + mat.set_density(units, value) + + # Check for isotropic scattering nuclides + isotropic = elem.find('isotropic') + if isotropic is not None: + mat.isotropic = isotropic.text.split() + + return mat + class Materials(cv.CheckedList): """Collection of Materials used for an OpenMC simulation. @@ -1031,8 +1081,41 @@ class Materials(cv.CheckedList): self._create_material_subelements(root_element) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + clean_indentation(root_element) # Write the XML Tree to the materials.xml file tree = ET.ElementTree(root_element) tree.write(path, xml_declaration=True, encoding='utf-8') + + @classmethod + def from_xml(cls, path='materials.xml'): + """Generate materials collection from XML file + + Parameters + ---------- + path : str, optional + Path to materials XML file + + Returns + ------- + openmc.Materials + Materials collection + + """ + tree = ET.parse(path) + root = tree.getroot() + + # Generate each material + materials = cls() + for material in root.findall('material'): + materials.append(Material.from_xml_element(material)) + + # Check for cross sections settings + xs = tree.find('cross_sections') + if xs is not None: + materials.cross_sections = xs.text + mpl = tree.find('multipole_library') + if mpl is not None: + materials.multipole_library = mpl.text + + return materials diff --git a/openmc/plots.py b/openmc/plots.py index 8eff78b1d9..f99392def9 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -9,7 +9,7 @@ import numpy as np import openmc import openmc.checkvalue as cv -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from openmc.mixin import IDManagerMixin @@ -816,7 +816,7 @@ class Plots(cv.CheckedList): self._create_plot_subelements() # Clean the indentation in the file to be user-readable - clean_xml_indentation(self._plots_file) + clean_indentation(self._plots_file) # Write the XML Tree to the plots.xml file tree = ET.ElementTree(self._plots_file) diff --git a/openmc/polynomial.py b/openmc/polynomial.py index b120659976..8bcb74f68a 100644 --- a/openmc/polynomial.py +++ b/openmc/polynomial.py @@ -1,9 +1,10 @@ import numpy as np import openmc import openmc.capi as capi +from collections.abc import Iterable -def legendre_from_expcoef(coef, domain= (-1,1)): +def legendre_from_expcoef(coef, domain=(-1, 1)): """Return a Legendre series object based on expansion coefficients. Given a list of coefficients from FET tally and a array of down, return @@ -73,5 +74,8 @@ class ZernikeRadial(Polynomial): return self._order def __call__(self, r): - zn_rad = capi.calc_zn_rad(self.order, r) - return np.sum(self._norm_coef * zn_rad) + if isinstance(r, Iterable): + return [np.sum(self._norm_coef * capi.calc_zn_rad(self.order, r_i)) + for r_i in r] + else: + return np.sum(self._norm_coef * capi.calc_zn_rad(self.order, r)) diff --git a/openmc/settings.py b/openmc/settings.py index 7629ea9afc..efe41b2a1b 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -6,7 +6,7 @@ import sys import numpy as np -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation import openmc.checkvalue as cv from openmc import VolumeCalculation, Source, Mesh @@ -994,7 +994,7 @@ class Settings(object): self._create_log_grid_bins_subelement(root_element) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + clean_indentation(root_element) # Write the XML Tree to the settings.xml file tree = ET.ElementTree(root_element) diff --git a/openmc/surface.py b/openmc/surface.py index ad5053e370..826ac5cc8d 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -59,7 +59,6 @@ class Surface(IDManagerMixin): def __init__(self, surface_id=None, boundary_type='transmission', name=''): self.id = surface_id self.name = name - self._type = '' self.boundary_type = boundary_type # A dictionary of the quadratic surface coefficients @@ -67,10 +66,6 @@ class Surface(IDManagerMixin): # Value - coefficient value self._coefficients = {} - # An ordered list of the coefficient names to export to XML in the - # proper order - self._coeff_keys = [] - def __neg__(self): return Halfspace(self, '-') @@ -203,6 +198,49 @@ class Surface(IDManagerMixin): return element + @staticmethod + def from_xml_element(elem): + """Generate surface from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Surface + Instance of a surface subclass + + """ + + # Determine appropriate class + surf_type = elem.get('type') + surface_classes = { + 'plane': Plane, + 'x-plane': XPlane, + 'y-plane': YPlane, + 'z-plane': ZPlane, + 'x-cylinder': XCylinder, + 'y-cylinder': YCylinder, + 'z-cylinder': ZCylinder, + 'sphere': Sphere, + 'x-cone': XCone, + 'y-cone': YCone, + 'z-cone': ZCone, + 'quadric': Quadric, + } + cls = surface_classes[surf_type] + + # Determine ID, boundary type, coefficients + kwargs = {} + kwargs['surface_id'] = int(elem.get('id')) + kwargs['boundary_type'] = elem.get('boundary', 'transmission') + coeffs = [float(x) for x in elem.get('coeffs').split()] + kwargs.update(dict(zip(cls._coeff_keys, coeffs))) + + return cls(**kwargs) + @staticmethod def from_hdf5(group): """Create surface from HDF5 group @@ -324,12 +362,12 @@ class Plane(Surface): """ + _type = 'plane' + _coeff_keys = ('A', 'B', 'C', 'D') + def __init__(self, surface_id=None, boundary_type='transmission', A=1., B=0., C=0., D=0., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'plane' - self._coeff_keys = ['A', 'B', 'C', 'D'] self._periodic_surface = None self.a = A self.b = B @@ -458,12 +496,12 @@ class XPlane(Plane): """ + _type = 'x-plane' + _coeff_keys = ('x0',) + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'x-plane' - self._coeff_keys = ['x0'] self.x0 = x0 @property @@ -563,13 +601,13 @@ class YPlane(Plane): """ + _type = 'y-plane' + _coeff_keys = ('y0',) + def __init__(self, surface_id=None, boundary_type='transmission', y0=0., name=''): # Initialize YPlane class attributes super().__init__(surface_id, boundary_type, name=name) - - self._type = 'y-plane' - self._coeff_keys = ['y0'] self.y0 = y0 @property @@ -669,13 +707,13 @@ class ZPlane(Plane): """ + _type = 'z-plane' + _coeff_keys = ('z0',) + def __init__(self, surface_id=None, boundary_type='transmission', z0=0., name=''): # Initialize ZPlane class attributes super().__init__(surface_id, boundary_type, name=name) - - self._type = 'z-plane' - self._coeff_keys = ['z0'] self.z0 = z0 @property @@ -774,8 +812,6 @@ class Cylinder(Surface, metaclass=ABCMeta): def __init__(self, surface_id=None, boundary_type='transmission', R=1., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._coeff_keys = ['R'] self.r = R @property @@ -831,12 +867,12 @@ class XCylinder(Cylinder): """ + _type = 'x-cylinder' + _coeff_keys = ('y0', 'z0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', y0=0., z0=0., R=1., name=''): super().__init__(surface_id, boundary_type, R, name=name) - - self._type = 'x-cylinder' - self._coeff_keys = ['y0', 'z0', 'R'] self.y0 = y0 self.z0 = z0 @@ -953,12 +989,12 @@ class YCylinder(Cylinder): """ + _type = 'y-cylinder' + _coeff_keys = ('x0', 'z0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., z0=0., R=1., name=''): super().__init__(surface_id, boundary_type, R, name=name) - - self._type = 'y-cylinder' - self._coeff_keys = ['x0', 'z0', 'R'] self.x0 = x0 self.z0 = z0 @@ -1075,12 +1111,12 @@ class ZCylinder(Cylinder): """ + _type = 'z-cylinder' + _coeff_keys = ('x0', 'y0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., R=1., name=''): super().__init__(surface_id, boundary_type, R, name=name) - - self._type = 'z-cylinder' - self._coeff_keys = ['x0', 'y0', 'R'] self.x0 = x0 self.y0 = y0 @@ -1201,12 +1237,12 @@ class Sphere(Surface): """ + _type = 'sphere' + _coeff_keys = ('x0', 'y0', 'z0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R=1., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'sphere' - self._coeff_keys = ['x0', 'y0', 'z0', 'R'] self.x0 = x0 self.y0 = y0 self.z0 = z0 @@ -1348,11 +1384,12 @@ class Cone(Surface, metaclass=ABCMeta): Type of the surface """ + + _coeff_keys = ('x0', 'y0', 'z0', 'R2') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R2=1., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._coeff_keys = ['x0', 'y0', 'z0', 'R2'] self.x0 = x0 self.y0 = y0 self.z0 = z0 @@ -1443,12 +1480,7 @@ class XCone(Cone): """ - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R2=1., name=''): - super().__init__(surface_id, boundary_type, x0, y0, - z0, R2, name=name) - - self._type = 'x-cone' + _type = 'x-cone' def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1519,12 +1551,7 @@ class YCone(Cone): """ - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R2=1., name=''): - super().__init__(surface_id, boundary_type, x0, y0, z0, - R2, name=name) - - self._type = 'y-cone' + _type = 'y-cone' def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1595,12 +1622,7 @@ class ZCone(Cone): """ - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R2=1., name=''): - super().__init__(surface_id, boundary_type, x0, y0, z0, - R2, name=name) - - self._type = 'z-cone' + _type = 'z-cone' def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1659,13 +1681,13 @@ class Quadric(Surface): """ + _type = 'quadric' + _coeff_keys = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k') + def __init__(self, surface_id=None, boundary_type='transmission', a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., k=0., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'quadric' - self._coeff_keys = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k'] self.a = a self.b = b self.c = c diff --git a/openmc/tallies.py b/openmc/tallies.py index 74f3429034..1e4b142fe1 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -15,7 +15,7 @@ import h5py import openmc import openmc.checkvalue as cv -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from .mixin import IDManagerMixin @@ -3187,7 +3187,7 @@ class Tallies(cv.CheckedList): self._create_derivative_subelements(root_element) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + clean_indentation(root_element) # Write the XML Tree to the tallies.xml file tree = ET.ElementTree(root_element) diff --git a/scripts/openmc-get-multipole-data b/scripts/openmc-get-multipole-data index ea25396488..c8a2d1e9b9 100755 --- a/scripts/openmc-get-multipole-data +++ b/scripts/openmc-get-multipole-data @@ -29,9 +29,9 @@ parser.add_argument('-b', '--batch', action='store_true', args = parser.parse_args() -baseUrl = 'https://github.com/smharper/windowed_multipole_library/blob/master/' -files = ['multipole_lib.tar.gz?raw=true'] -checksums = ['3985aea96f7162a9419c7ed8352e6abb'] +baseUrl = 'https://github.com/mit-crpg/WMP_Library/releases/download/v1.0/' +files = ['WMP_Library_v1.0.tar.gz'] +checksums = ['22cb675734cfccb278dffd40dcfbf26a'] block_size = 16384 # ============================================================================== @@ -101,12 +101,7 @@ for f in files: # Extract files with tarfile.open(fname, 'r') as tgz: print('Extracting {0}...'.format(fname)) - tgz.extractall(path='wmp/') - -# Move data files down one level -for filename in glob.glob('wmp/multipole_lib/*'): - shutil.move(filename, 'wmp/') -os.rmdir('wmp/multipole_lib') + tgz.extractall(path='') # ============================================================================== # PROMPT USER TO DELETE .TAR.GZ FILES diff --git a/src/api.F90 b/src/api.F90 index 33f464fa37..75876080dc 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -37,7 +37,6 @@ module openmc_api public :: openmc_cell_filter_get_bins public :: openmc_cell_get_id public :: openmc_cell_set_id - public :: openmc_cell_set_temperature public :: openmc_energy_filter_get_bins public :: openmc_energy_filter_set_bins public :: openmc_extend_filters @@ -207,8 +206,8 @@ contains err = E_UNASSIGNED if (found) then - index = p % coord(p % n_coord) % cell - instance = p % cell_instance - 1 + index = p % coord(p % n_coord) % cell + 1 + instance = p % cell_instance err = 0 else err = E_GEOMETRY diff --git a/src/cell.cpp b/src/cell.cpp index f172ddf3b4..e579b9db4f 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -7,36 +7,27 @@ #include "openmc/capi.h" #include "openmc/constants.h" #include "openmc/error.h" +#include "openmc/geometry.h" #include "openmc/hdf5_interface.h" #include "openmc/lattice.h" #include "openmc/material.h" +#include "openmc/settings.h" #include "openmc/surface.h" #include "openmc/xml_interface.h" namespace openmc { -//============================================================================== -// Constants -//============================================================================== - -// TODO: Convert to enum -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 //============================================================================== int32_t n_cells {0}; -std::vector global_cells; +std::vector cells; std::unordered_map cell_map; -std::vector global_universes; +std::vector universes; std::unordered_map universe_map; //============================================================================== @@ -192,6 +183,28 @@ generate_rpn(int32_t cell_id, std::vector infix) return rpn; } +//============================================================================== +// Universe implementation +//============================================================================== + +void +Universe::to_hdf5(hid_t universes_group) const +{ + // Create a group for this universe. + std::stringstream group_name; + group_name << "universe " << id_; + auto group = create_group(universes_group, group_name); + + // Write the contained cells. + if (cells_.size() > 0) { + std::vector cell_ids; + for (auto i_cell : cells_) cell_ids.push_back(cells[i_cell]->id_); + write_dataset(group, "cells", cell_ids); + } + + close_group(group); +} + //============================================================================== // Cell implementation //============================================================================== @@ -199,19 +212,19 @@ generate_rpn(int32_t cell_id, std::vector infix) Cell::Cell(pugi::xml_node cell_node) { if (check_for_node(cell_node, "id")) { - id = std::stoi(get_node_value(cell_node, "id")); + id_ = std::stoi(get_node_value(cell_node, "id")); } else { fatal_error("Must specify id of cell in geometry XML file."); } if (check_for_node(cell_node, "name")) { - name = get_node_value(cell_node, "name"); + name_ = get_node_value(cell_node, "name"); } if (check_for_node(cell_node, "universe")) { - universe = std::stoi(get_node_value(cell_node, "universe")); + universe_ = std::stoi(get_node_value(cell_node, "universe")); } else { - universe = 0; + universe_ = 0; } // Make sure that either material or fill was specified, but not both. @@ -219,20 +232,20 @@ Cell::Cell(pugi::xml_node cell_node) bool material_present = check_for_node(cell_node, "material"); if (!(fill_present || material_present)) { std::stringstream err_msg; - err_msg << "Neither material nor fill was specified for cell " << id; + err_msg << "Neither material nor fill was specified for cell " << id_; fatal_error(err_msg); } if (fill_present && material_present) { std::stringstream err_msg; - err_msg << "Cell " << id << " has both a material and a fill specified; " + err_msg << "Cell " << id_ << " has both a material and a fill specified; " << "only one can be specified per cell"; fatal_error(err_msg); } if (fill_present) { - fill = std::stoi(get_node_value(cell_node, "fill")); + fill_ = std::stoi(get_node_value(cell_node, "fill")); } else { - fill = C_NONE; + fill_ = C_NONE; } // Read the material element. There can be zero materials (filled with a @@ -242,21 +255,51 @@ Cell::Cell(pugi::xml_node cell_node) std::vector mats {get_node_array(cell_node, "material", true)}; if (mats.size() > 0) { - material.reserve(mats.size()); + material_.reserve(mats.size()); for (std::string mat : mats) { if (mat.compare("void") == 0) { - material.push_back(MATERIAL_VOID); + material_.push_back(MATERIAL_VOID); } else { - material.push_back(std::stoi(mat)); + material_.push_back(std::stoi(mat)); } } } else { std::stringstream err_msg; - err_msg << "An empty material element was specified for cell " << id; + err_msg << "An empty material element was specified for cell " << id_; fatal_error(err_msg); } } + // Read the temperature element which may be distributed like materials. + if (check_for_node(cell_node, "temperature")) { + sqrtkT_ = get_node_array(cell_node, "temperature"); + sqrtkT_.shrink_to_fit(); + + // Make sure this is a material-filled cell. + if (material_.size() == 0) { + std::stringstream err_msg; + err_msg << "Cell " << id_ << " was specified with a temperature but " + "no material. Temperature specification is only valid for cells " + "filled with a material."; + fatal_error(err_msg); + } + + // Make sure all temperatures are non-negative. + for (auto T : sqrtkT_) { + if (T < 0) { + std::stringstream err_msg; + err_msg << "Cell " << id_ + << " was specified with a negative temperature"; + fatal_error(err_msg); + } + } + + // Convert to sqrt(k*T). + for (auto& T : sqrtkT_) { + T = std::sqrt(K_BOLTZMANN * T); + } + } + // Read the region specification. std::string region_spec; if (check_for_node(cell_node, "region")) { @@ -264,28 +307,87 @@ Cell::Cell(pugi::xml_node cell_node) } // Get a tokenized representation of the region specification. - region = tokenize(region_spec); - region.shrink_to_fit(); + region_ = tokenize(region_spec); + region_.shrink_to_fit(); // Convert user IDs to surface indices. - for (auto &r : region) { + for (auto& r : region_) { if (r < OP_UNION) { r = copysign(surface_map[abs(r)] + 1, r); } } // Convert the infix region spec to RPN. - rpn = generate_rpn(id, region); - rpn.shrink_to_fit(); + rpn_ = generate_rpn(id_, region_); + rpn_.shrink_to_fit(); // Check if this is a simple cell. - simple = true; - for (int32_t token : rpn) { + simple_ = true; + for (int32_t token : rpn_) { if ((token == OP_COMPLEMENT) || (token == OP_UNION)) { - simple = false; + simple_ = false; break; } } + + // Read the translation vector. + if (check_for_node(cell_node, "translation")) { + if (fill_ == C_NONE) { + std::stringstream err_msg; + err_msg << "Cannot apply a translation to cell " << id_ + << " because it is not filled with another universe"; + fatal_error(err_msg); + } + + auto xyz {get_node_array(cell_node, "translation")}; + if (xyz.size() != 3) { + std::stringstream err_msg; + err_msg << "Non-3D translation vector applied to cell " << id_; + fatal_error(err_msg); + } + translation_ = xyz; + } + + // Read the rotation transform. + if (check_for_node(cell_node, "rotation")) { + if (fill_ == C_NONE) { + std::stringstream err_msg; + err_msg << "Cannot apply a rotation to cell " << id_ + << " because it is not filled with another universe"; + fatal_error(err_msg); + } + + auto rot {get_node_array(cell_node, "rotation")}; + if (rot.size() != 3) { + std::stringstream err_msg; + err_msg << "Non-3D rotation vector applied to cell " << id_; + fatal_error(err_msg); + } + + // Store the rotation angles. + rotation_.reserve(12); + rotation_.push_back(rot[0]); + rotation_.push_back(rot[1]); + rotation_.push_back(rot[2]); + + // Compute and store the rotation matrix. + auto phi = -rot[0] * PI / 180.0; + auto theta = -rot[1] * PI / 180.0; + auto psi = -rot[2] * PI / 180.0; + rotation_.push_back(std::cos(theta) * std::cos(psi)); + rotation_.push_back(-std::cos(phi) * std::sin(psi) + + std::sin(phi) * std::sin(theta) * std::cos(psi)); + rotation_.push_back(std::sin(phi) * std::sin(psi) + + std::cos(phi) * std::sin(theta) * std::cos(psi)); + rotation_.push_back(std::cos(theta) * std::sin(psi)); + rotation_.push_back(std::cos(phi) * std::cos(psi) + + std::sin(phi) * std::sin(theta) * std::sin(psi)); + rotation_.push_back(-std::sin(phi) * std::cos(psi) + + std::cos(phi) * std::sin(theta) * std::sin(psi)); + rotation_.push_back(-std::sin(theta)); + rotation_.push_back(std::sin(phi) * std::cos(theta)); + rotation_.push_back(std::cos(phi) * std::cos(theta)); + } } //============================================================================== @@ -293,7 +395,7 @@ Cell::Cell(pugi::xml_node cell_node) bool Cell::contains(Position r, Direction u, int32_t on_surface) const { - if (simple) { + if (simple_) { return contains_simple(r, u, on_surface); } else { return contains_complex(r, u, on_surface); @@ -308,14 +410,14 @@ Cell::distance(Position r, Direction u, int32_t on_surface) const double min_dist {INFTY}; int32_t i_surf {std::numeric_limits::max()}; - for (int32_t token : rpn) { + for (int32_t token : rpn_) { // Ignore this token if it corresponds to an operator rather than a region. if (token >= OP_UNION) continue; // Calculate the distance to this surface. // Note the off-by-one indexing bool coincident {token == on_surface}; - double d {global_surfaces[abs(token)-1]->distance(r, u, coincident)}; + double d {surfaces[abs(token)-1]->distance(r, u, coincident)}; // Check if this distance is the new minimum. if (d < min_dist) { @@ -332,19 +434,23 @@ Cell::distance(Position r, Direction u, int32_t on_surface) const //============================================================================== void -Cell::to_hdf5(hid_t cell_group) const +Cell::to_hdf5(hid_t cells_group) const { - if (!name.empty()) { - write_string(cell_group, "name", name, false); + // Create a group for this cell. + std::stringstream group_name; + group_name << "cell " << id_; + auto group = create_group(cells_group, group_name); + + if (!name_.empty()) { + write_string(group, "name", name_, false); } - //TODO: Fix the off-by-one indexing. - write_dataset(cell_group, "universe", global_universes[universe-1]->id); + write_dataset(group, "universe", universes[universe_]->id_); // Write the region specification. - if (!region.empty()) { + if (!region_.empty()) { std::stringstream region_spec {}; - for (int32_t token : region) { + for (int32_t token : region_) { if (token == OP_LEFT_PAREN) { region_spec << " ("; } else if (token == OP_RIGHT_PAREN) { @@ -357,11 +463,51 @@ Cell::to_hdf5(hid_t cell_group) const } else { // Note the off-by-one indexing region_spec << " " - << copysign(global_surfaces[abs(token)-1]->id, token); + << copysign(surfaces[abs(token)-1]->id_, token); } } - write_string(cell_group, "region", region_spec.str(), false); + write_string(group, "region", region_spec.str(), false); } + + // Write fill information. + if (type_ == FILL_MATERIAL) { + write_dataset(group, "fill_type", "material"); + std::vector mat_ids; + for (auto i_mat : material_) { + if (i_mat != MATERIAL_VOID) { + mat_ids.push_back(materials[i_mat]->id); + } else { + mat_ids.push_back(MATERIAL_VOID); + } + } + if (mat_ids.size() == 1) { + write_dataset(group, "material", mat_ids[0]); + } else { + write_dataset(group, "material", mat_ids); + } + + std::vector temps; + for (auto sqrtkT_val : sqrtkT_) + temps.push_back(sqrtkT_val * sqrtkT_val / K_BOLTZMANN); + write_dataset(group, "temperature", temps); + + } else if (type_ == FILL_UNIVERSE) { + write_dataset(group, "fill_type", "universe"); + write_dataset(group, "fill", universes[fill_]->id_); + if (translation_ != Position(0, 0, 0)) { + write_dataset(group, "translation", translation_); + } + if (!rotation_.empty()) { + std::array rot {rotation_[0], rotation_[1], rotation_[2]}; + write_dataset(group, "rotation", rot); + } + + } else if (type_ == FILL_LATTICE) { + write_dataset(group, "fill_type", "lattice"); + write_dataset(group, "lattice", lattices[fill_]->id_); + } + + close_group(group); } //============================================================================== @@ -369,7 +515,7 @@ Cell::to_hdf5(hid_t cell_group) const bool Cell::contains_simple(Position r, Direction u, int32_t on_surface) const { - for (int32_t token : rpn) { + for (int32_t token : rpn_) { if (token < OP_UNION) { // If the token is not an operator, evaluate the sense of particle with // respect to the surface and see if the token matches the sense. If the @@ -380,7 +526,7 @@ Cell::contains_simple(Position r, Direction u, int32_t on_surface) const return false; } else { // Note the off-by-one indexing - bool sense = global_surfaces[abs(token)-1]->sense(r, u); + bool sense = surfaces[abs(token)-1]->sense(r, u); if (sense != (token > 0)) {return false;} } } @@ -395,10 +541,10 @@ Cell::contains_complex(Position r, Direction u, int32_t on_surface) const { // Make a stack of booleans. We don't know how big it needs to be, but we do // know that rpn.size() is an upper-bound. - bool stack[rpn.size()]; + bool stack[rpn_.size()]; int i_stack = -1; - for (int32_t token : rpn) { + for (int32_t token : rpn_) { // If the token is a binary operator (intersection/union), apply it to // the last two items on the stack. If the token is a unary operator // (complement), apply it to the last item on the stack. @@ -422,7 +568,7 @@ Cell::contains_complex(Position r, Direction u, int32_t on_surface) const stack[i_stack] = false; } else { // Note the off-by-one indexing - bool sense = global_surfaces[abs(token)-1]->sense(r, u); + bool sense = surfaces[abs(token)-1]->sense(r, u); stack[i_stack] = (sense == (token > 0)); } } @@ -453,25 +599,28 @@ read_cells(pugi::xml_node* node) } // Loop over XML cell elements and populate the array. - global_cells.reserve(n_cells); + cells.reserve(n_cells); for (pugi::xml_node cell_node: node->children("cell")) { - global_cells.push_back(new Cell(cell_node)); + cells.push_back(new Cell(cell_node)); } // Populate the Universe vector and map. - for (int i = 0; i < global_cells.size(); i++) { - int32_t uid = global_cells[i]->universe; + for (int i = 0; i < cells.size(); i++) { + int32_t uid = cells[i]->universe_; auto it = universe_map.find(uid); if (it == universe_map.end()) { - global_universes.push_back(new Universe()); - global_universes.back()->id = uid; - global_universes.back()->cells.push_back(i); - universe_map[uid] = global_universes.size() - 1; + universes.push_back(new Universe()); + universes.back()->id_ = uid; + universes.back()->cells_.push_back(i); + universe_map[uid] = universes.size() - 1; } else { - global_universes[it->second]->cells.push_back(i); + universes[it->second]->cells_.push_back(i); } } - global_universes.shrink_to_fit(); + universes.shrink_to_fit(); + + // Allocate the cell overlap count if necessary. + if (settings::check_overlaps) overlap_check_count.resize(n_cells, 0); } //============================================================================== @@ -481,15 +630,15 @@ read_cells(pugi::xml_node* node) extern "C" int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n) { - if (index >= 1 && index <= global_cells.size()) { + if (index >= 1 && index <= cells.size()) { //TODO: off-by-one - Cell& c {*global_cells[index - 1]}; - *type = c.type; - if (c.type == FILL_MATERIAL) { - *indices = c.material.data(); - *n = c.material.size(); + Cell& c {*cells[index - 1]}; + *type = c.type_; + if (c.type_ == FILL_MATERIAL) { + *indices = c.material_.data(); + *n = c.material_.size(); } else { - *indices = &c.fill; + *indices = &c.fill_; *n = 1; } } else { @@ -503,29 +652,29 @@ extern "C" int openmc_cell_set_fill(int32_t index, int type, int32_t n, const int32_t* indices) { - if (index >= 1 && index <= global_cells.size()) { + if (index >= 1 && index <= cells.size()) { //TODO: off-by-one - Cell& c {*global_cells[index - 1]}; + Cell& c {*cells[index - 1]}; if (type == FILL_MATERIAL) { - c.type = FILL_MATERIAL; - c.material.clear(); + c.type_ = FILL_MATERIAL; + c.material_.clear(); for (int i = 0; i < n; i++) { int i_mat = indices[i]; if (i_mat == MATERIAL_VOID) { - c.material.push_back(MATERIAL_VOID); - } else if (i_mat >= 1 && i_mat <= global_materials.size()) { + c.material_.push_back(MATERIAL_VOID); + } else if (i_mat >= 1 && i_mat <= materials.size()) { //TODO: off-by-one - c.material.push_back(i_mat - 1); + c.material_.push_back(i_mat - 1); } else { set_errmsg("Index in materials array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } } - c.material.shrink_to_fit(); + c.material_.shrink_to_fit(); } else if (type == FILL_UNIVERSE) { - c.type = FILL_UNIVERSE; + c.type_ = FILL_UNIVERSE; } else { - c.type = FILL_LATTICE; + c.type_ = FILL_LATTICE; } } else { set_errmsg("Index in cells array is out of bounds."); @@ -534,66 +683,85 @@ openmc_cell_set_fill(int32_t index, int type, int32_t n, return 0; } +//TODO: make sure data is loaded for this temperature +extern "C" int +openmc_cell_set_temperature(int32_t index, double T, const int32_t* instance) +{ + if (index >= 1 && index <= cells.size()) { + //TODO: off-by-one + Cell& c {*cells[index - 1]}; + + if (instance) { + if (*instance >= 0 && *instance < c.sqrtkT_.size()) { + c.sqrtkT_[*instance] = std::sqrt(K_BOLTZMANN * T); + } else { + strcpy(openmc_err_msg, "Distribcell instance is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + } else { + for (auto& T_ : c.sqrtkT_) { + T_ = std::sqrt(K_BOLTZMANN * T); + } + } + + } else { + strcpy(openmc_err_msg, "Index in cells array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + return 0; +} + //============================================================================== // Fortran compatibility functions //============================================================================== extern "C" { - Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];} + Cell* cell_pointer(int32_t cell_ind) {return cells[cell_ind];} - int32_t cell_id(Cell* c) {return c->id;} + int32_t cell_id(Cell* c) {return c->id_;} - void cell_set_id(Cell* c, int32_t id) {c->id = id;} + void cell_set_id(Cell* c, int32_t id) {c->id_ = id;} - int cell_type(Cell* c) {return c->type;} + int cell_type(Cell* c) {return c->type_;} - int32_t cell_universe(Cell* c) {return c->universe;} + int32_t cell_universe(Cell* c) {return c->universe_;} - int32_t cell_fill(Cell* c) {return c->fill;} + int32_t cell_fill(Cell* c) {return c->fill_;} - int32_t cell_n_instances(Cell* c) {return c->n_instances;} + int32_t cell_n_instances(Cell* c) {return c->n_instances_;} - int cell_material_size(Cell* c) {return c->material.size();} + int cell_distribcell_index(Cell* c) {return c->distribcell_index_;} + + int cell_material_size(Cell* c) {return c->material_.size();} //TODO: off-by-one int32_t cell_material(Cell* c, int i) { - int32_t mat = c->material[i-1]; + int32_t mat = c->material_[i-1]; if (mat == MATERIAL_VOID) return MATERIAL_VOID; return mat + 1; } - bool cell_simple(Cell* c) {return c->simple;} + int cell_sqrtkT_size(Cell* c) {return c->sqrtkT_.size();} - bool cell_contains(Cell* c, double xyz[3], double uvw[3], int32_t on_surface) - { - Position r {xyz}; - Direction u {uvw}; - return c->contains(r, u, on_surface); - } + double cell_sqrtkT(Cell* c, int i) {return c->sqrtkT_[i];} - void cell_distance(Cell* c, double xyz[3], double uvw[3], int32_t on_surface, - double* min_dist, int32_t* i_surf) - { - Position r {xyz}; - Direction u {uvw}; - std::pair out = c->distance(r, u, on_surface); - *min_dist = out.first; - *i_surf = out.second; - } - - int32_t cell_offset(Cell* c, int map) {return c->offset[map];} - - void cell_to_hdf5(Cell* c, hid_t group) {c->to_hdf5(group);} + int32_t cell_offset(Cell* c, int map) {return c->offset_[map];} void extend_cells_c(int32_t n) { - global_cells.reserve(global_cells.size() + n); + cells.reserve(cells.size() + n); for (int32_t i = 0; i < n; i++) { - global_cells.push_back(new Cell()); + cells.push_back(new Cell()); } - n_cells = global_cells.size(); + n_cells = cells.size(); } + + int32_t universe_id(int i_univ) {return universes[i_univ]->id_;} + + void universes_to_hdf5(hid_t universes_group) + {for (Universe* u : universes) u->to_hdf5(universes_group);} } diff --git a/src/constants.F90 b/src/constants.F90 index ab85b277a6..a3118f84da 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -25,7 +25,7 @@ module constants integer, parameter :: VERSION_VOLUME(2) = [1, 0] integer, parameter :: VERSION_VOXEL(2) = [1, 0] integer, parameter :: VERSION_MGXS_LIBRARY(2) = [1, 0] - character(10), parameter :: VERSION_MULTIPOLE = "v0.2" + character(10), parameter :: VERSION_MULTIPOLE = "v1.0" ! ============================================================================ ! ADJUSTABLE PARAMETERS diff --git a/src/geometry.F90 b/src/geometry.F90 index 81e123d174..b5dee81fa2 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -15,529 +15,67 @@ module geometry implicit none interface - function cell_contains_c(cell_ptr, xyz, uvw, on_surface) & - bind(C, name="cell_contains") result(in_cell) - import C_PTR, C_DOUBLE, C_INT32_T, C_BOOL - type(C_PTR), intent(in), value :: cell_ptr - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - integer(C_INT32_T), intent(in), value :: on_surface - logical(C_BOOL) :: in_cell - end function cell_contains_c - function count_universe_instances(search_univ, target_univ_id) bind(C) & result(count) import C_INT32_T, C_INT integer(C_INT32_T), intent(in), value :: search_univ integer(C_INT32_T), intent(in), value :: target_univ_id integer(C_INT) :: count - end function + end function count_universe_instances + + subroutine check_cell_overlap(p) bind(C) + import Particle + type(Particle), intent(in) :: p + end subroutine check_cell_overlap + + function find_cell_c(p, search_surf) & + bind(C, name="find_cell") result(found) + import Particle, C_INT, C_BOOL + type(Particle), intent(inout) :: p + integer(C_INT), intent(in), value :: search_surf + logical(C_BOOL) :: found + end function find_cell_c + + subroutine cross_lattice(p, lattice_translation) & + bind(C, name="cross_lattice") + import Particle, C_INT + type(Particle), intent(inout) :: p + integer(C_INT), intent(in) :: lattice_translation(3) + end subroutine cross_lattice + + subroutine distance_to_boundary(p, dist, surface_crossed, & + lattice_translation, next_level) bind(C) + import Particle, C_DOUBLE, C_INT + type(Particle), intent(inout) :: p + real(C_DOUBLE), intent(out) :: dist + integer(C_INT), intent(out) :: surface_crossed + integer(C_INT), intent(out) :: lattice_translation(3) + integer(C_INT), intent(out) :: next_level + end subroutine distance_to_boundary + + subroutine neighbor_lists() bind(C) + end subroutine neighbor_lists end interface contains - function cell_contains(c, p) result(in_cell) - type(Cell), intent(in) :: c - type(Particle), intent(in) :: p - logical :: in_cell - in_cell = cell_contains_c(c%ptr, p%coord(p%n_coord)%xyz, & - p%coord(p%n_coord)%uvw, p%surface) - end function cell_contains - -!=============================================================================== -! CHECK_CELL_OVERLAP checks for overlapping cells at the current particle's -! position using cell_contains and the LocalCoord's built up by find_cell -!=============================================================================== - - subroutine check_cell_overlap(p) - - type(Particle), intent(inout) :: p - - integer :: i ! cell loop index on a level - integer :: j ! coordinate level index - integer :: n_coord ! saved number of coordinate levels - integer :: n ! number of cells to search on a level - integer :: index_cell ! index in cells array - type(Cell), pointer :: c ! pointer to cell - type(Universe), pointer :: univ ! universe to search in - - ! loop through each coordinate level - n_coord = p % n_coord - do j = 1, n_coord - p % n_coord = j - univ => universes(p % coord(j) % universe) - n = size(univ % cells) - - ! loop through each cell on this level - do i = 1, n - index_cell = univ % cells(i) - c => cells(index_cell) - - if (cell_contains(c, p)) then - ! the particle should only be contained in one cell per level - if (index_cell /= p % coord(j) % cell) then - call fatal_error("Overlapping cells detected: " & - &// trim(to_str(cells(index_cell) % id())) // ", " & - &// trim(to_str(cells(p % coord(j) % cell) % id())) & - &// " on universe " // trim(to_str(univ % id))) - end if - - overlap_check_cnt(index_cell) = overlap_check_cnt(index_cell) + 1 - - end if - - end do - end do - - end subroutine check_cell_overlap - !=============================================================================== ! FIND_CELL determines what cell a source particle is in within a particular ! universe. If the base universe is passed, the particle should be found as long ! as it's within the geometry !=============================================================================== - recursive subroutine find_cell(p, found, search_cells) + subroutine find_cell(p, found, search_surf) + type(Particle), intent(inout) :: p + logical, intent(inout) :: found + integer, optional, intent(in) :: search_surf - type(Particle), intent(inout) :: p - logical, intent(inout) :: found - integer, optional :: search_cells(:) - integer :: i ! index over cells - integer :: j, k ! coordinate level index - integer :: offset ! instance # of a distributed cell - integer :: distribcell_index - integer :: i_xyz(3) ! indices in lattice - integer :: n ! number of cells to search - integer :: i_cell ! index in cells array - integer :: i_universe ! index in universes array - logical :: use_search_cells ! use cells provided as argument - - do j = p % n_coord + 1, MAX_COORD - call reset_coord(p % coord(j)) - end do - j = p % n_coord - - ! Determine universe (if not yet set, use root universe) - i_universe = p % coord(j) % universe - if (i_universe == C_NONE) then - p % coord(j) % universe = root_universe - i_universe = root_universe - end if - - ! set size of list to search - if (present(search_cells)) then - use_search_cells = .true. - n = size(search_cells) + if (present(search_surf)) then + found = find_cell_c(p, search_surf) else - use_search_cells = .false. - n = size(universes(i_universe) % cells) - end if - - found = .false. - CELL_LOOP: do i = 1, n - ! select cells based on whether we are searching a universe or a provided - ! list of cells (this would be for lists of neighbor cells) - if (use_search_cells) then - i_cell = search_cells(i) - ! check to make sure search cell is in same universe - if (cells(i_cell) % universe() /= i_universe) cycle - else - i_cell = universes(i_universe) % cells(i) - end if - - ! Move on to the next cell if the particle is not inside this cell - if (cell_contains(cells(i_cell), p)) then - ! Set cell on this level - p % coord(j) % cell = i_cell - - ! Show cell information on trace - if (verbosity >= 10 .or. trace) then - call write_message(" Entering cell " // trim(to_str(& - cells(i_cell) % id()))) - end if - - found = .true. - exit - end if - end do CELL_LOOP - - if (found) then - associate(c => cells(i_cell)) - CELL_TYPE: if (c % type() == FILL_MATERIAL) then - ! ====================================================================== - ! AT LOWEST UNIVERSE, TERMINATE SEARCH - - ! Save previous material and temperature - p % last_material = p % material - p % last_sqrtkT = p % sqrtkT - - ! Get distributed offset - if (c % material_size() > 1 .or. size(c % sqrtkT) > 1) then - ! Distributed instances of this cell have different - ! materials/temperatures. Determine which instance this is for - ! assigning the matching material/temperature. - distribcell_index = c % distribcell_index - offset = 0 - do k = 1, p % n_coord - if (cells(p % coord(k) % cell) % type() == FILL_UNIVERSE) then - offset = offset + cells(p % coord(k) % cell) & - % offset(distribcell_index-1) - elseif (cells(p % coord(k) % cell) % type() == FILL_LATTICE) then - if (lattices(p % coord(k + 1) % lattice) % obj & - % are_valid_indices([& - p % coord(k + 1) % lattice_x, & - p % coord(k + 1) % lattice_y, & - p % coord(k + 1) % lattice_z])) then - offset = offset + lattices(p % coord(k + 1) % lattice) % obj & - % offset(distribcell_index - 1, & - [p % coord(k + 1) % lattice_x, & - p % coord(k + 1) % lattice_y, & - p % coord(k + 1) % lattice_z]) - end if - end if - end do - - ! Keep track of which instance of the cell the particle is in - p % cell_instance = offset + 1 - else - p % cell_instance = 1 - end if - - ! Save the material - if (c % material_size() > 1) then - p % material = c % material(offset + 1) - else - p % material = c % material(1) - end if - - ! Save the temperature - if (size(c % sqrtkT) > 1) then - p % sqrtkT = c % sqrtkT(offset + 1) - else - p % sqrtkT = c % sqrtkT(1) - end if - - elseif (c % type() == FILL_UNIVERSE) then CELL_TYPE - ! ====================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - - ! Store lower level coordinates - p % coord(j + 1) % xyz = p % coord(j) % xyz - p % coord(j + 1) % uvw = p % coord(j) % uvw - - ! Move particle to next level and set universe - j = j + 1 - p % n_coord = j - p % coord(j) % universe = c % fill() + 1 - - ! Apply translation - if (allocated(c % translation)) then - p % coord(j) % xyz = p % coord(j) % xyz - c % translation - end if - - ! Apply rotation - if (allocated(c % rotation_matrix)) then - p % coord(j) % xyz = matmul(c % rotation_matrix, p % coord(j) % xyz) - p % coord(j) % uvw = matmul(c % rotation_matrix, p % coord(j) % uvw) - p % coord(j) % rotated = .true. - end if - - call find_cell(p, found) - j = p % n_coord - - elseif (c % type() == FILL_LATTICE) then CELL_TYPE - ! ====================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - - associate (lat => lattices(c % fill() + 1) % obj) - ! Determine lattice indices - i_xyz = lat % get_indices(p % coord(j) % xyz + TINY_BIT * p % coord(j) % uvw) - - ! Store lower level coordinates - p % coord(j + 1) % xyz = lat % get_local_xyz(p % coord(j) % xyz, i_xyz) - p % coord(j + 1) % uvw = p % coord(j) % uvw - - ! set particle lattice indices - p % coord(j + 1) % lattice = c % fill() + 1 - p % coord(j + 1) % lattice_x = i_xyz(1) - p % coord(j + 1) % lattice_y = i_xyz(2) - p % coord(j + 1) % lattice_z = i_xyz(3) - - ! Set the next lowest coordinate level. - if (lat % are_valid_indices(i_xyz)) then - ! Particle is inside the lattice. - p % coord(j + 1) % universe = & - lat % get([i_xyz(1), i_xyz(2), i_xyz(3)]) + 1 - - else - ! Particle is outside the lattice. - if (lat % outer() == NO_OUTER_UNIVERSE) then - call warning("Particle " // trim(to_str(p %id)) & - // " is outside lattice " // trim(to_str(lat % id())) & - // " but the lattice has no defined outer universe.") - found = .false. - return - else - p % coord(j + 1) % universe = lat % outer() + 1 - end if - end if - end associate - - ! Move particle to next level and search for the lower cells. - j = j + 1 - p % n_coord = j - - call find_cell(p, found) - j = p % n_coord - - end if CELL_TYPE - end associate + found = find_cell_c(p, 0) end if end subroutine find_cell -!=============================================================================== -! CROSS_LATTICE moves a particle into a new lattice element -!=============================================================================== - - subroutine cross_lattice(p, lattice_translation) - - type(Particle), intent(inout) :: p - integer, intent(in) :: lattice_translation(3) - integer :: j - integer :: i_xyz(3) ! indices in lattice - logical :: found ! particle found in cell? - class(Lattice), pointer :: lat - - j = p % n_coord - lat => lattices(p % coord(j) % lattice) % obj - - if (verbosity >= 10 .or. trace) then - call write_message(" Crossing lattice " // trim(to_str(lat % id())) & - &// ". Current position (" // trim(to_str(p % coord(j) % lattice_x)) & - &// "," // trim(to_str(p % coord(j) % lattice_y)) // "," & - &// trim(to_str(p % coord(j) % lattice_z)) // ")") - end if - - ! Set the lattice indices. - p % coord(j) % lattice_x = p % coord(j) % lattice_x + lattice_translation(1) - p % coord(j) % lattice_y = p % coord(j) % lattice_y + lattice_translation(2) - p % coord(j) % lattice_z = p % coord(j) % lattice_z + lattice_translation(3) - i_xyz(1) = p % coord(j) % lattice_x - i_xyz(2) = p % coord(j) % lattice_y - i_xyz(3) = p % coord(j) % lattice_z - - ! Set the new coordinate position. - p % coord(j) % xyz = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz) - - OUTSIDE_LAT: if (.not. lat % are_valid_indices(i_xyz)) then - ! The particle is outside the lattice. Search for it from base coord - p % n_coord = 1 - call find_cell(p, found) - if (.not. found) then - if (p % alive) then ! Particle may have been killed in find_cell - call particle_mark_as_lost(p, "Could not locate particle " & - // trim(to_str(p % id)) // " after crossing a lattice boundary.") - return - end if - end if - - else OUTSIDE_LAT - - ! Find cell in next lattice element - p % coord(j) % universe = & - lat % get([i_xyz(1), i_xyz(2), i_xyz(3)]) + 1 - - call find_cell(p, found) - if (.not. found) then - ! In some circumstances, a particle crossing the corner of a cell may - ! not be able to be found in the next universe. In this scenario we cut - ! off all lower-level coordinates and search from universe zero - - ! Remove lower coordinates - p % n_coord = 1 - - ! Search for particle - call find_cell(p, found) - if (.not. found) then - call particle_mark_as_lost(p, "Could not locate particle " // & - trim(to_str(p % id)) // " after crossing a lattice boundary.") - return - end if - end if - end if OUTSIDE_LAT - - end subroutine cross_lattice - -!=============================================================================== -! DISTANCE_TO_BOUNDARY calculates the distance to the nearest boundary for a -! particle 'p' traveling in a certain direction. For a cell in a subuniverse -! that has a parent cell, also include the surfaces of the edge of the universe. -!=============================================================================== - - subroutine distance_to_boundary(p, dist, surface_crossed, lattice_translation, & - next_level) - type(Particle), intent(inout) :: p - real(8), intent(out) :: dist - integer, intent(out) :: surface_crossed - integer, intent(out) :: lattice_translation(3) - integer, intent(out) :: next_level - - integer :: j - integer :: i_xyz(3) ! lattice indices - integer :: level_surf_cross ! surface crossed on current level - integer :: level_lat_trans(3) ! lattice translation on current level - real(8) :: xyz_t(3) ! local particle coordinates - real(8) :: d_lat ! distance to lattice boundary - real(8) :: d_surf ! distance to surface - real(8) :: xyz_cross(3) ! coordinates at projected surface crossing - real(8) :: surf_uvw(3) ! surface normal direction - type(Cell), pointer :: c - class(Lattice), pointer :: lat - - ! inialize distance to infinity (huge) - dist = INFINITY - d_lat = INFINITY - d_surf = INFINITY - lattice_translation(:) = [0, 0, 0] - - next_level = 0 - - ! Loop over each universe level - LEVEL_LOOP: do j = 1, p % n_coord - - ! get pointer to cell on this level - c => cells(p % coord(j) % cell) - - ! ======================================================================= - ! FIND MINIMUM DISTANCE TO SURFACE IN THIS CELL - - call c % distance(p % coord(j) % xyz, p % coord(j) % uvw, p % surface, & - d_surf, level_surf_cross) - - ! ======================================================================= - ! FIND MINIMUM DISTANCE TO LATTICE SURFACES - - LAT_COORD: if (p % coord(j) % lattice /= NONE) then - lat => lattices(p % coord(j) % lattice) % obj - - i_xyz(1) = p % coord(j) % lattice_x - i_xyz(2) = p % coord(j) % lattice_y - i_xyz(3) = p % coord(j) % lattice_z - - LAT_TYPE: select type(lat) - - type is (RectLattice) - call lat % distance(p % coord(j) % xyz, p % coord(j) % uvw, & - i_xyz, d_lat, level_lat_trans) - - type is (HexLattice) LAT_TYPE - xyz_t(1) = p % coord(j-1) % xyz(1) - xyz_t(2) = p % coord(j-1) % xyz(2) - xyz_t(3) = p % coord(j) % xyz(3) - call lat % distance(xyz_t, p % coord(j) % uvw, & - i_xyz, d_lat, level_lat_trans) - end select LAT_TYPE - - if (d_lat < ZERO) then - call particle_mark_as_lost(p, "Particle " // trim(to_str(p % id)) & - //" had a negative distance to a lattice boundary. d = " & - //trim(to_str(d_lat))) - end if - end if LAT_COORD - - ! If the boundary on this lattice level is coincident with a boundary on - ! a higher level then we need to make sure that the higher level boundary - ! is selected. This logic must include consideration of floating point - ! precision. - if (d_surf < d_lat) then - if ((dist - d_surf)/dist >= FP_REL_PRECISION) then - dist = d_surf - - ! If the cell is not simple, it is possible that both the negative and - ! positive half-space were given in the region specification. Thus, we - ! have to explicitly check which half-space the particle would be - ! traveling into if the surface is crossed - if (.not. c % simple()) then - xyz_cross(:) = p % coord(j) % xyz + d_surf*p % coord(j) % uvw - call surfaces(abs(level_surf_cross)) % normal(xyz_cross, surf_uvw) - if (dot_product(p % coord(j) % uvw, surf_uvw) > ZERO) then - surface_crossed = abs(level_surf_cross) - else - surface_crossed = -abs(level_surf_cross) - end if - else - surface_crossed = level_surf_cross - end if - - lattice_translation(:) = [0, 0, 0] - next_level = j - end if - else - if ((dist - d_lat)/dist >= FP_REL_PRECISION) then - dist = d_lat - surface_crossed = NONE - lattice_translation(:) = level_lat_trans - next_level = j - end if - end if - - end do LEVEL_LOOP - - end subroutine distance_to_boundary - -!=============================================================================== -! NEIGHBOR_LISTS builds a list of neighboring cells to each surface to speed up -! searches when a cell boundary is crossed. -!=============================================================================== - - subroutine neighbor_lists() - - integer :: i ! index in cells/surfaces array - integer :: j ! index in region specification - integer :: k ! surface half-space spec - integer :: n ! size of vector - type(VectorInt), allocatable :: neighbor_pos(:) - type(VectorInt), allocatable :: neighbor_neg(:) - - call write_message("Building neighboring cells lists for each surface...", & - 6) - - allocate(neighbor_pos(n_surfaces)) - allocate(neighbor_neg(n_surfaces)) - - do i = 1, n_cells - do j = 1, size(cells(i)%region) - ! Get token from region specification and skip any tokens that - ! correspond to operators rather than regions - k = cells(i)%region(j) - if (abs(k) >= OP_UNION) cycle - - ! Add this cell ID to neighbor list for k-th surface - if (k > 0) then - call neighbor_pos(abs(k))%push_back(i) - else - call neighbor_neg(abs(k))%push_back(i) - end if - end do - end do - - do i = 1, n_surfaces - ! Copy positive neighbors to Surface instance - n = neighbor_pos(i)%size() - if (n > 0) then - allocate(surfaces(i)%neighbor_pos(n)) - surfaces(i)%neighbor_pos(:) = neighbor_pos(i)%data(1:n) - end if - - ! Copy negative neighbors to Surface instance - n = neighbor_neg(i)%size() - if (n > 0) then - allocate(surfaces(i)%neighbor_neg(n)) - surfaces(i)%neighbor_neg(:) = neighbor_neg(i)%data(1:n) - end if - end do - - end subroutine neighbor_lists - end module geometry diff --git a/src/geometry.cpp b/src/geometry.cpp new file mode 100644 index 0000000000..84121da93e --- /dev/null +++ b/src/geometry.cpp @@ -0,0 +1,407 @@ +#include "openmc/geometry.h" + +#include +#include + +#include "openmc/cell.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/lattice.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" +#include "openmc/surface.h" + + +namespace openmc { + +std::vector overlap_check_count; + +constexpr int F90_NONE {0}; //TODO: replace usage of this with C_NONE + +//============================================================================== + +extern "C" bool +check_cell_overlap(Particle* p) { + int n_coord = p->n_coord; + + // Loop through each coordinate level + for (int j = 0; j < n_coord; j++) { + Universe& univ = *universes[p->coord[j].universe]; + int n = univ.cells_.size(); + + // Loop through each cell on this level + for (auto index_cell : univ.cells_) { + Cell& c = *cells[index_cell]; + if (c.contains(p->coord[j].xyz, p->coord[j].uvw, p->surface)) { + if (index_cell != p->coord[j].cell) { + std::stringstream err_msg; + err_msg << "Overlapping cells detected: " << c.id_ << ", " + << cells[p->coord[j].cell]->id_ << " on universe " + << univ.id_; + fatal_error(err_msg); + } + ++overlap_check_count[index_cell]; + } + } + } + + return false; +} + +//============================================================================== + +extern "C" bool +find_cell(Particle* p, int search_surf) { + for (int i = p->n_coord; i < MAX_COORD; i++) { + p->coord[i].reset(); + } + + // Determine universe (if not yet set, use root universe) + int i_universe = p->coord[p->n_coord-1].universe; + if (i_universe == C_NONE) { + p->coord[p->n_coord-1].universe = openmc_root_universe; + i_universe = openmc_root_universe; + } + + // If a surface was indicated, only search cells from the neighbor list of + // that surface. The surface index is signed, and the sign signifies whether + // the positive or negative side of the surface should be searched. + const std::vector* search_cells; + if (search_surf > 0) { + search_cells = &surfaces[search_surf-1]->neighbor_pos_; + } else if (search_surf < 0) { + search_cells = &surfaces[-search_surf-1]->neighbor_neg_; + } else { + // No surface was indicated, search all cells in the universe. + search_cells = &universes[i_universe]->cells_; + } + + // Find which cell of this universe the particle is in. + bool found = false; + int32_t i_cell; + for (int i = 0; i < search_cells->size(); i++) { + i_cell = (*search_cells)[i]; + + // Make sure the search cell is in the same universe. + if (cells[i_cell]->universe_ != i_universe) continue; + + Position r {p->coord[p->n_coord-1].xyz}; + Direction u {p->coord[p->n_coord-1].uvw}; + int32_t surf = p->surface; + if (cells[i_cell]->contains(r, u, surf)) { + p->coord[p->n_coord-1].cell = i_cell; + + if (settings::verbosity >= 10 || openmc_trace) { + std::stringstream msg; + msg << " Entering cell " << cells[i_cell]->id_; + write_message(msg, 1); + } + found = true; + break; + } + } + + if (found) { + Cell& c {*cells[i_cell]}; + if (c.type_ == FILL_MATERIAL) { + //======================================================================= + //! Found a material cell which means this is the lowest coord level. + + // Find the distribcell instance number. + if (c.material_.size() > 1 || c.sqrtkT_.size() > 1) { + int offset = 0; + for (int i = 0; i < p->n_coord; i++) { + Cell& c_i {*cells[p->coord[i].cell]}; + if (c_i.type_ == FILL_UNIVERSE) { + offset += c_i.offset_[c.distribcell_index_]; + } else if (c_i.type_ == FILL_LATTICE) { + Lattice& lat {*lattices[p->coord[i+1].lattice-1]}; + int i_xyz[3] {p->coord[i+1].lattice_x, + p->coord[i+1].lattice_y, + p->coord[i+1].lattice_z}; + if (lat.are_valid_indices(i_xyz)) { + offset += lat.offset(c.distribcell_index_, i_xyz); + } + } + } + p->cell_instance = offset; + } else { + p->cell_instance = 0; + } + + // Set the material and temperature. + p->last_material = p->material; + int32_t mat; + if (c.material_.size() > 1) { + mat = c.material_[p->cell_instance]; + } else { + mat = c.material_[0]; + } + if (mat == MATERIAL_VOID) { + p->material = MATERIAL_VOID; + } else { + p->material = mat + 1; + } + p->last_sqrtkT = p->sqrtkT; + if (c.sqrtkT_.size() > 1) { + p->sqrtkT = c.sqrtkT_[p->cell_instance]; + } else { + p->sqrtkT = c.sqrtkT_[0]; + } + + return true; + + } else if (c.type_ == FILL_UNIVERSE) { + //======================================================================== + //! Found a lower universe, update this coord level then search the next. + + // Set the lower coordinate level universe. + p->coord[p->n_coord].universe = c.fill_; + + // Set the position and direction. + for (int i = 0; i < 3; i++) { + p->coord[p->n_coord].xyz[i] = p->coord[p->n_coord-1].xyz[i]; + p->coord[p->n_coord].uvw[i] = p->coord[p->n_coord-1].uvw[i]; + } + + // Apply translation. + p->coord[p->n_coord].xyz[0] -= c.translation_.x; + p->coord[p->n_coord].xyz[1] -= c.translation_.y; + p->coord[p->n_coord].xyz[2] -= c.translation_.z; + + // Apply rotation. + if (!c.rotation_.empty()) { + auto x = p->coord[p->n_coord].xyz[0]; + auto y = p->coord[p->n_coord].xyz[1]; + auto z = p->coord[p->n_coord].xyz[2]; + p->coord[p->n_coord].xyz[0] = x*c.rotation_[3] + y*c.rotation_[4] + + z*c.rotation_[5]; + p->coord[p->n_coord].xyz[1] = x*c.rotation_[6] + y*c.rotation_[7] + + z*c.rotation_[8]; + p->coord[p->n_coord].xyz[2] = x*c.rotation_[9] + y*c.rotation_[10] + + z*c.rotation_[11]; + auto u = p->coord[p->n_coord].uvw[0]; + auto v = p->coord[p->n_coord].uvw[1]; + auto w = p->coord[p->n_coord].uvw[2]; + p->coord[p->n_coord].uvw[0] = u*c.rotation_[3] + v*c.rotation_[4] + + w*c.rotation_[5]; + p->coord[p->n_coord].uvw[1] = u*c.rotation_[6] + v*c.rotation_[7] + + w*c.rotation_[8]; + p->coord[p->n_coord].uvw[2] = u*c.rotation_[9] + v*c.rotation_[10] + + w*c.rotation_[11]; + p->coord[p->n_coord].rotated = true; + } + + // Update the coordinate level and recurse. + ++p->n_coord; + return find_cell(p, 0); + + } else if (c.type_ == FILL_LATTICE) { + //======================================================================== + //! Found a lower lattice, update this coord level then search the next. + + Lattice& lat {*lattices[c.fill_]}; + + // Determine lattice indices. + Position r {p->coord[p->n_coord-1].xyz}; + Direction u {p->coord[p->n_coord-1].uvw}; + r += TINY_BIT * u; + auto i_xyz = lat.get_indices(r); + + // Store lower level coordinates. + r = lat.get_local_position(p->coord[p->n_coord-1].xyz, i_xyz); + p->coord[p->n_coord].xyz[0] = r.x; + p->coord[p->n_coord].xyz[1] = r.y; + p->coord[p->n_coord].xyz[2] = r.z; + p->coord[p->n_coord].uvw[0] = u.x; + p->coord[p->n_coord].uvw[1] = u.y; + p->coord[p->n_coord].uvw[2] = u.z; + + // Set lattice indices. + p->coord[p->n_coord].lattice = c.fill_ + 1; + p->coord[p->n_coord].lattice_x = i_xyz[0]; + p->coord[p->n_coord].lattice_y = i_xyz[1]; + p->coord[p->n_coord].lattice_z = i_xyz[2]; + + // Set the lower coordinate level universe. + if (lat.are_valid_indices(i_xyz)) { + p->coord[p->n_coord].universe = lat[i_xyz]; + } else { + if (lat.outer_ != NO_OUTER_UNIVERSE) { + p->coord[p->n_coord].universe = lat.outer_; + } else { + std::stringstream err_msg; + err_msg << "Particle " << p->id << " is outside lattice " + << lat.id_ << " but the lattice has no defined outer " + "universe."; + warning(err_msg); + return false; + } + } + + // Update the coordinate level and recurse. + ++p->n_coord; + return find_cell(p, 0); + } + } +} + +//============================================================================== + +extern "C" void +cross_lattice(Particle* p, int lattice_translation[3]) +{ + Lattice& lat {*lattices[p->coord[p->n_coord-1].lattice-1]}; + + if (settings::verbosity >= 10 || openmc_trace) { + std::stringstream msg; + msg << " Crossing lattice " << lat.id_ << ". Current position (" + << p->coord[p->n_coord-1].lattice_x << "," + << p->coord[p->n_coord-1].lattice_y << "," + << p->coord[p->n_coord-1].lattice_z << ")"; + write_message(msg, 1); + } + + // Set the lattice indices. + p->coord[p->n_coord-1].lattice_x += lattice_translation[0]; + p->coord[p->n_coord-1].lattice_y += lattice_translation[1]; + p->coord[p->n_coord-1].lattice_z += lattice_translation[2]; + std::array i_xyz {p->coord[p->n_coord-1].lattice_x, + p->coord[p->n_coord-1].lattice_y, + p->coord[p->n_coord-1].lattice_z}; + + // Set the new coordinate position. + auto r = lat.get_local_position(p->coord[p->n_coord-2].xyz, i_xyz); + p->coord[p->n_coord-1].xyz[0] = r.x; + p->coord[p->n_coord-1].xyz[1] = r.y; + p->coord[p->n_coord-1].xyz[2] = r.z; + + if (!lat.are_valid_indices(i_xyz)) { + // The particle is outside the lattice. Search for it from the base coords. + p->n_coord = 1; + bool found = find_cell(p, 0); + if (!found && p->alive) { + std::stringstream err_msg; + err_msg << "Could not locate particle " << p->id + << " after crossing a lattice boundary"; + p->mark_as_lost(err_msg); + } + + } else { + // Find cell in next lattice element. + p->coord[p->n_coord-1].universe = lat[i_xyz]; + bool found = find_cell(p, 0); + + if (!found) { + // A particle crossing the corner of a lattice tile may not be found. In + // this case, search for it from the base coords. + p->n_coord = 1; + bool found = find_cell(p, 0); + if (!found && p->alive) { + std::stringstream err_msg; + err_msg << "Could not locate particle " << p->id + << " after crossing a lattice boundary"; + p->mark_as_lost(err_msg); + } + } + } +} + +//============================================================================== + +extern "C" void +distance_to_boundary(Particle* p, double* dist, int* surface_crossed, + int lattice_translation[3], int* next_level) +{ + *dist = INFINITY; + double d_lat = INFINITY; + double d_surf = INFINITY; + lattice_translation[0] = 0; + lattice_translation[1] = 0; + lattice_translation[2] = 0; + int32_t level_surf_cross; + std::array level_lat_trans; + + // Loop over each coordinate level. + for (int i = 0; i < p->n_coord; i++) { + Position r {p->coord[i].xyz}; + Direction u {p->coord[i].uvw}; + Cell& c {*cells[p->coord[i].cell]}; + + // Find the oncoming surface in this cell and the distance to it. + auto surface_distance = c.distance(r, u, p->surface); + d_surf = surface_distance.first; + level_surf_cross = surface_distance.second; + + // Find the distance to the next lattice tile crossing. + if (p->coord[i].lattice != F90_NONE) { + Lattice& lat {*lattices[p->coord[i].lattice-1]}; + std::array i_xyz {p->coord[i].lattice_x, p->coord[i].lattice_y, + p->coord[i].lattice_z}; + //TODO: refactor so both lattice use the same position argument (which + //also means the lat.type attribute can be removed) + std::pair> lattice_distance; + switch (lat.type_) { + case LatticeType::rect: + lattice_distance = lat.distance(r, u, i_xyz); + break; + case LatticeType::hex: + Position r_hex {p->coord[i-1].xyz[0], p->coord[i-1].xyz[1], + p->coord[i].xyz[2]}; + lattice_distance = lat.distance(r_hex, u, i_xyz); + break; + } + d_lat = lattice_distance.first; + level_lat_trans = lattice_distance.second; + + if (d_lat < 0) { + std::stringstream err_msg; + err_msg << "Particle " << p->id + << " had a negative distance to a lattice boundary"; + p->mark_as_lost(err_msg); + } + } + + // If the boundary on this coordinate level is coincident with a boundary on + // a higher level then we need to make sure that the higher level boundary + // is selected. This logic must consider floating point precision. + if (d_surf < d_lat) { + if (*dist == INFINITY || ((*dist) - d_surf)/(*dist) >= FP_REL_PRECISION) { + *dist = d_surf; + + // If the cell is not simple, it is possible that both the negative and + // positive half-space were given in the region specification. Thus, we + // have to explicitly check which half-space the particle would be + // traveling into if the surface is crossed + if (c.simple_) { + *surface_crossed = level_surf_cross; + } else { + Position r_hit = r + d_surf * u; + Surface& surf {*surfaces[std::abs(level_surf_cross)-1]}; + Direction norm = surf.normal(r_hit); + if (u.dot(norm) > 0) { + *surface_crossed = std::abs(level_surf_cross); + } else { + *surface_crossed = -std::abs(level_surf_cross); + } + } + + lattice_translation[0] = 0; + lattice_translation[1] = 0; + lattice_translation[2] = 0; + *next_level = i + 1; + } + } else { + if (*dist == INFINITY || ((*dist) - d_lat)/(*dist) >= FP_REL_PRECISION) { + *dist = d_lat; + *surface_crossed = F90_NONE; + lattice_translation[0] = level_lat_trans[0]; + lattice_translation[1] = level_lat_trans[1]; + lattice_translation[2] = level_lat_trans[2]; + *next_level = i + 1; + } + } + } +} + +} // namespace openmc diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 1f2b1e6777..273aad4440 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -7,8 +7,11 @@ #include "openmc/cell.h" #include "openmc/constants.h" #include "openmc/error.h" +#include "openmc/geometry.h" #include "openmc/lattice.h" #include "openmc/material.h" +#include "openmc/settings.h" +#include "openmc/surface.h" namespace openmc { @@ -19,26 +22,26 @@ void adjust_indices() { // Adjust material/fill idices. - for (Cell* c : global_cells) { - if (c->fill != C_NONE) { - int32_t id = c->fill; + for (Cell* c : cells) { + if (c->fill_ != C_NONE) { + int32_t id = c->fill_; auto search_univ = universe_map.find(id); auto search_lat = lattice_map.find(id); if (search_univ != universe_map.end()) { - c->type = FILL_UNIVERSE; - c->fill = search_univ->second; + c->type_ = FILL_UNIVERSE; + c->fill_ = search_univ->second; } else if (search_lat != lattice_map.end()) { - c->type = FILL_LATTICE; - c->fill = search_lat->second; + c->type_ = FILL_LATTICE; + c->fill_ = search_lat->second; } else { std::stringstream err_msg; - err_msg << "Specified fill " << id << " on cell " << c->id + err_msg << "Specified fill " << id << " on cell " << c->id_ << " is neither a universe nor a lattice."; fatal_error(err_msg); } } else { - c->type = FILL_MATERIAL; - for (auto it = c->material.begin(); it != c->material.end(); it++) { + c->type_ = FILL_MATERIAL; + for (auto it = c->material_.begin(); it != c->material_.end(); it++) { int32_t mid = *it; if (mid != MATERIAL_VOID) { auto search = material_map.find(mid); @@ -47,7 +50,7 @@ adjust_indices() } else { std::stringstream err_msg; err_msg << "Could not find material " << mid - << " specified on cell " << c->id; + << " specified on cell " << c->id_; fatal_error(err_msg); } } @@ -56,51 +59,81 @@ adjust_indices() } // Change cell.universe values from IDs to indices. - for (Cell* c : global_cells) { - auto search = universe_map.find(c->universe); + for (Cell* c : cells) { + auto search = universe_map.find(c->universe_); if (search != universe_map.end()) { - //TODO: Remove this off-by-one indexing. - c->universe = search->second + 1; + c->universe_ = search->second; } else { std::stringstream err_msg; - err_msg << "Could not find universe " << c->universe - << " specified on cell " << c->id; + err_msg << "Could not find universe " << c->universe_ + << " specified on cell " << c->id_; fatal_error(err_msg); } } // Change all lattice universe values from IDs to indices. - for (Lattice* l : lattices_c) { + for (Lattice* l : lattices) { l->adjust_indices(); } } //============================================================================== +void +assign_temperatures() +{ + for (Cell* c : cells) { + // Ignore non-material cells and cells with defined temperature. + if (c->material_.size() == 0) continue; + if (c->sqrtkT_.size() > 0) continue; + + c->sqrtkT_.reserve(c->material_.size()); + for (auto i_mat : c->material_) { + if (i_mat == MATERIAL_VOID) { + // Set void region to 0K. + c->sqrtkT_.push_back(0); + + } else { + if (materials[i_mat]->temperature_ >= 0) { + // This material has a default temperature; use that value. + auto T = materials[i_mat]->temperature_; + c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * T)); + } else { + // Use the global default temperature. + c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * + settings::temperature_default)); + } + } + } + } +} + +//============================================================================== + int32_t find_root_universe() { // Find all the universes listed as a cell fill. std::unordered_set fill_univ_ids; - for (Cell* c : global_cells) { - fill_univ_ids.insert(c->fill); + for (Cell* c : cells) { + fill_univ_ids.insert(c->fill_); } // Find all the universes contained in a lattice. - for (Lattice* lat : lattices_c) { + for (Lattice* lat : lattices) { for (auto it = lat->begin(); it != lat->end(); ++it) { fill_univ_ids.insert(*it); } - if (lat->outer != NO_OUTER_UNIVERSE) { - fill_univ_ids.insert(lat->outer); + if (lat->outer_ != NO_OUTER_UNIVERSE) { + fill_univ_ids.insert(lat->outer_); } } // Figure out which universe is not in the set. This is the root universe. bool root_found {false}; int32_t root_univ; - for (int32_t i = 0; i < global_universes.size(); i++) { - auto search = fill_univ_ids.find(global_universes[i]->id); + for (int32_t i = 0; i < universes.size(); i++) { + auto search = fill_univ_ids.find(universes[i]->id_); if (search == fill_univ_ids.end()) { if (root_found) { fatal_error("Two or more universes are not used as fill universes, so " @@ -121,17 +154,116 @@ find_root_universe() //============================================================================== void -allocate_offset_tables(int n_maps) +neighbor_lists() { - for (Cell* c : global_cells) { - if (c->type != FILL_MATERIAL) { - c->offset.resize(n_maps, C_NONE); + write_message("Building neighboring cells lists for each surface...", 6); + + for (int i = 0; i < cells.size(); i++) { + for (auto token : cells[i]->region_) { + // Skip operator tokens. + if (std::abs(token) >= OP_UNION) continue; + + // This token is a surface index. Add the cell to the surface's list. + if (token > 0) { + surfaces[std::abs(token)-1]->neighbor_pos_.push_back(i); + } else { + surfaces[std::abs(token)-1]->neighbor_neg_.push_back(i); + } } } - for (Lattice* lat : lattices_c) { + for (Surface* surf : surfaces) { + surf->neighbor_pos_.shrink_to_fit(); + surf->neighbor_neg_.shrink_to_fit(); + } +} + +//============================================================================== + +void +prepare_distribcell(int32_t* filter_cell_list, int n) +{ + // Read the list of cells contained in distribcell filters from Fortran. + std::unordered_set distribcells; + for (int i = 0; i < n; i++) { + distribcells.insert(filter_cell_list[i]); + } + + // Find all cells with distributed materials or temperatures. Make sure that + // the number of materials/temperatures matches the number of cell instances. + for (int i = 0; i < cells.size(); i++) { + Cell& c {*cells[i]}; + + if (c.material_.size() > 1) { + if (c.material_.size() != c.n_instances_) { + std::stringstream err_msg; + err_msg << "Cell " << c.id_ << " was specified with " + << c.material_.size() << " materials but has " << c.n_instances_ + << " distributed instances. The number of materials must equal " + "one or the number of instances."; + fatal_error(err_msg); + } + distribcells.insert(i); + } + + if (c.sqrtkT_.size() > 1) { + if (c.sqrtkT_.size() != c.n_instances_) { + std::stringstream err_msg; + err_msg << "Cell " << c.id_ << " was specified with " + << c.sqrtkT_.size() << " temperatures but has " << c.n_instances_ + << " distributed instances. The number of temperatures must equal " + "one or the number of instances."; + fatal_error(err_msg); + } + distribcells.insert(i); + } + } + + // Search through universes for distributed cells and assign each one a + // unique distribcell array index. + int distribcell_index = 0; + std::vector target_univ_ids; + for (Universe* u : universes) { + for (auto cell_indx : u->cells_) { + if (distribcells.find(cell_indx) != distribcells.end()) { + cells[cell_indx]->distribcell_index_ = distribcell_index; + target_univ_ids.push_back(u->id_); + ++distribcell_index; + } + } + } + + // Allocate the cell and lattice offset tables. + int n_maps = target_univ_ids.size(); + for (Cell* c : cells) { + if (c->type_ != FILL_MATERIAL) { + c->offset_.resize(n_maps, C_NONE); + } + } + for (Lattice* lat : lattices) { lat->allocate_offset_table(n_maps); } + + // Fill the cell and lattice offset tables. + for (int map = 0; map < target_univ_ids.size(); map++) { + auto target_univ_id = target_univ_ids[map]; + for (Universe* univ : universes) { + int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation. + for (int32_t cell_indx : univ->cells_) { + Cell& c = *cells[cell_indx]; + + if (c.type_ == FILL_UNIVERSE) { + c.offset_[map] = offset; + int32_t search_univ = c.fill_; + offset += count_universe_instances(search_univ, target_univ_id); + + } else if (c.type_ == FILL_LATTICE) { + Lattice& lat = *lattices[c.fill_]; + offset = lat.fill_offset_table(offset, target_univ_id, map); + } + } + } + } } //============================================================================== @@ -139,17 +271,17 @@ allocate_offset_tables(int n_maps) void count_cell_instances(int32_t univ_indx) { - for (int32_t cell_indx : global_universes[univ_indx]->cells) { - Cell& c = *global_cells[cell_indx]; - ++c.n_instances; + for (int32_t cell_indx : universes[univ_indx]->cells_) { + Cell& c = *cells[cell_indx]; + ++c.n_instances_; - if (c.type == FILL_UNIVERSE) { + if (c.type_ == FILL_UNIVERSE) { // This cell contains another universe. Recurse into that universe. - count_cell_instances(c.fill); + count_cell_instances(c.fill_); - } else if (c.type == FILL_LATTICE) { + } else if (c.type_ == FILL_LATTICE) { // This cell contains a lattice. Recurse into the lattice universes. - Lattice& lat = *lattices_c[c.fill]; + Lattice& lat = *lattices[c.fill_]; for (auto it = lat.begin(); it != lat.end(); ++it) { count_cell_instances(*it); } @@ -163,20 +295,20 @@ int count_universe_instances(int32_t search_univ, int32_t target_univ_id) { // If this is the target, it can't contain itself. - if (global_universes[search_univ]->id == target_univ_id) { + if (universes[search_univ]->id_ == target_univ_id) { return 1; } int count {0}; - for (int32_t cell_indx : global_universes[search_univ]->cells) { - Cell& c = *global_cells[cell_indx]; + for (int32_t cell_indx : universes[search_univ]->cells_) { + Cell& c = *cells[cell_indx]; - if (c.type == FILL_UNIVERSE) { - int32_t next_univ = c.fill; + if (c.type_ == FILL_UNIVERSE) { + int32_t next_univ = c.fill_; count += count_universe_instances(next_univ, target_univ_id); - } else if (c.type == FILL_LATTICE) { - Lattice& lat = *lattices_c[c.fill]; + } else if (c.type_ == FILL_LATTICE) { + Lattice& lat = *lattices[c.fill_]; for (auto it = lat.begin(); it != lat.end(); ++it) { int32_t next_univ = *it; count += count_universe_instances(next_univ, target_univ_id); @@ -189,43 +321,20 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id) //============================================================================== -void -fill_offset_tables(int32_t target_univ_id, int map) -{ - for (Universe* univ : global_universes) { - int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation. - for (int32_t cell_indx : univ->cells) { - Cell& c = *global_cells[cell_indx]; - - if (c.type == FILL_UNIVERSE) { - c.offset[map] = offset; - int32_t search_univ = c.fill; - offset += count_universe_instances(search_univ, target_univ_id); - - } else if (c.type == FILL_LATTICE) { - Lattice& lat = *lattices_c[c.fill]; - offset = lat.fill_offset_table(offset, target_univ_id, map); - } - } - } -} - -//============================================================================== - std::string distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, const Universe& search_univ, int32_t offset) { std::stringstream path; - path << "u" << search_univ.id << "->"; + path << "u" << search_univ.id_ << "->"; // Check to see if this universe directly contains the target cell. If so, // write to the path and return. - for (int32_t cell_indx : search_univ.cells) { + for (int32_t cell_indx : search_univ.cells_) { if ((cell_indx == target_cell) && (offset == target_offset)) { - Cell& c = *global_cells[cell_indx]; - path << "c" << c.id; + Cell& c = *cells[cell_indx]; + path << "c" << c.id_; return path.str(); } } @@ -234,19 +343,19 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, // cell or lattice cell in this universe. Find which cell contains the // target. std::vector::const_reverse_iterator cell_it - {search_univ.cells.crbegin()}; - for (; cell_it != search_univ.cells.crend(); ++cell_it) { - Cell& c = *global_cells[*cell_it]; + {search_univ.cells_.crbegin()}; + for (; cell_it != search_univ.cells_.crend(); ++cell_it) { + Cell& c = *cells[*cell_it]; // Material cells don't contain other cells so ignore them. - if (c.type != FILL_MATERIAL) { + if (c.type_ != FILL_MATERIAL) { int32_t temp_offset; - if (c.type == FILL_UNIVERSE) { - temp_offset = offset + c.offset[map]; + if (c.type_ == FILL_UNIVERSE) { + temp_offset = offset + c.offset_[map]; } else { - Lattice& lat = *lattices_c[c.fill]; - int32_t indx = lat.universes.size()*map + lat.begin().indx; - temp_offset = offset + lat.offsets[indx]; + Lattice& lat = *lattices[c.fill_]; + int32_t indx = lat.universes_.size()*map + lat.begin().indx_; + temp_offset = offset + lat.offsets_[indx]; } // The desired cell is the first cell that gives an offset smaller or @@ -256,27 +365,27 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, } // Add the cell to the path string. - Cell& c = *global_cells[*cell_it]; - path << "c" << c.id << "->"; + Cell& c = *cells[*cell_it]; + path << "c" << c.id_ << "->"; - if (c.type == FILL_UNIVERSE) { + if (c.type_ == FILL_UNIVERSE) { // Recurse into the fill cell. - offset += c.offset[map]; + offset += c.offset_[map]; path << distribcell_path_inner(target_cell, map, target_offset, - *global_universes[c.fill], offset); + *universes[c.fill_], offset); return path.str(); } else { // Recurse into the lattice cell. - Lattice& lat = *lattices_c[c.fill]; - path << "l" << lat.id; + Lattice& lat = *lattices[c.fill_]; + path << "l" << lat.id_; for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) { - int32_t indx = lat.universes.size()*map + it.indx; - int32_t temp_offset = offset + lat.offsets[indx]; + int32_t indx = lat.universes_.size()*map + it.indx_; + int32_t temp_offset = offset + lat.offsets_[indx]; if (temp_offset <= target_offset) { offset = temp_offset; - path << "(" << lat.index_to_string(it.indx) << ")->"; + path << "(" << lat.index_to_string(it.indx_) << ")->"; path << distribcell_path_inner(target_cell, map, target_offset, - *global_universes[*it], offset); + *universes[*it], offset); return path.str(); } } @@ -289,7 +398,7 @@ int distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset, int32_t root_univ) { - Universe& root = *global_universes[root_univ]; + Universe& root = *universes[root_univ]; std::string path_ {distribcell_path_inner(target_cell, map, target_offset, root, 0)}; return path_.size() + 1; @@ -301,7 +410,7 @@ void distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset, int32_t root_univ, char* path) { - Universe& root = *global_universes[root_univ]; + Universe& root = *universes[root_univ]; std::string path_ {distribcell_path_inner(target_cell, map, target_offset, root, 0)}; path_.copy(path, path_.size()); @@ -315,13 +424,13 @@ maximum_levels(int32_t univ) { int levels_below {0}; - for (int32_t cell_indx : global_universes[univ]->cells) { - Cell& c = *global_cells[cell_indx]; - if (c.type == FILL_UNIVERSE) { - int32_t next_univ = c.fill; + for (int32_t cell_indx : universes[univ]->cells_) { + Cell& c = *cells[cell_indx]; + if (c.type_ == FILL_UNIVERSE) { + int32_t next_univ = c.fill_; levels_below = std::max(levels_below, maximum_levels(next_univ)); - } else if (c.type == FILL_LATTICE) { - Lattice& lat = *lattices_c[c.fill]; + } else if (c.type_ == FILL_LATTICE) { + Lattice& lat = *lattices[c.fill_]; for (auto it = lat.begin(); it != lat.end(); ++it) { int32_t next_univ = *it; levels_below = std::max(levels_below, maximum_levels(next_univ)); @@ -338,18 +447,20 @@ maximum_levels(int32_t univ) void free_memory_geometry_c() { - for (Cell* c : global_cells) {delete c;} - global_cells.clear(); + for (Cell* c : cells) {delete c;} + cells.clear(); cell_map.clear(); n_cells = 0; - for (Universe* u : global_universes) {delete u;} - global_universes.clear(); + for (Universe* u : universes) {delete u;} + universes.clear(); universe_map.clear(); - for (Lattice* lat : lattices_c) {delete lat;} - lattices_c.clear(); + for (Lattice* lat : lattices) {delete lat;} + lattices.clear(); lattice_map.clear(); + + overlap_check_count.clear(); } } // namespace openmc diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 7714601a42..b58b5415ba 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -16,6 +16,12 @@ module geometry_header implicit none interface + function universe_id(universe_ind) bind(C) result(id) + import C_INT, C_INT32_T + integer(C_INT), intent(in), value :: universe_ind + integer(C_INT32_T) :: id + end function universe_id + function cell_pointer(cell_ind) bind(C) result(ptr) import C_PTR, C_INT32_T integer(C_INT32_T), intent(in), value :: cell_ind @@ -60,6 +66,13 @@ module geometry_header integer(C_INT32_T) :: n_instances end function cell_n_instances_c + function cell_distribcell_index_c(cell_ptr) & + bind(C, name='cell_distribcell_index') result(distribcell_index) + import C_PTR, C_INT + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT) :: distribcell_index + end function cell_distribcell_index_c + function cell_material_size_c(cell_ptr) bind(C, name='cell_material_size') & result(n) import C_PTR, C_INT @@ -75,22 +88,20 @@ module geometry_header integer(C_INT32_T) :: mat end function cell_material_c - function cell_simple_c(cell_ptr) bind(C, name='cell_simple') result(simple) - import C_PTR, C_BOOL - type(C_PTR), intent(in), value :: cell_ptr - logical(C_BOOL) :: simple - end function cell_simple_c + function cell_sqrtkT_size_c(cell_ptr) bind(C, name='cell_sqrtkT_size') & + result(n) + import C_PTR, C_INT + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT) :: n + end function cell_sqrtkT_size_c - subroutine cell_distance_c(cell_ptr, xyz, uvw, on_surface, min_dist, & - i_surf) bind(C, name="cell_distance") - import C_PTR, C_INT32_T, C_DOUBLE - type(C_PTR), intent(in), value :: cell_ptr - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - integer(C_INT32_T), intent(in), value :: on_surface - real(C_DOUBLE), intent(out) :: min_dist - integer(C_INT32_T), intent(out) :: i_surf - end subroutine cell_distance_c + function cell_sqrtkT_c(cell_ptr, i) bind(C, name='cell_sqrtkT') & + result(sqrtkT) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT), intent(in), value :: i + real(C_DOUBLE) :: sqrtkT + end function cell_sqrtkT_c function cell_offset_c(cell_ptr, map) bind(C, name="cell_offset") & result(offset) @@ -100,12 +111,6 @@ module geometry_header integer(C_INT32_T) :: offset end function cell_offset_c - subroutine cell_to_hdf5_c(cell_ptr, group) bind(C, name='cell_to_hdf5') - import HID_T, C_PTR - type(C_PTR), intent(in), value :: cell_ptr - integer(HID_T), intent(in), value :: group - end subroutine cell_to_hdf5_c - function lattice_pointer(lat_ind) bind(C) result(ptr) import C_PTR, C_INT32_T integer(C_INT32_T), intent(in), value :: lat_ind @@ -126,40 +131,6 @@ module geometry_header logical(C_BOOL) :: is_valid end function lattice_are_valid_indices_c - subroutine lattice_distance_c(lat_ptr, xyz, uvw, i_xyz, d, lattice_trans) & - bind(C, name='lattice_distance') - import C_PTR, C_INT, C_DOUBLE - type(C_PTR), intent(in), value :: lat_ptr - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - integer(C_INT), intent(in) :: i_xyz(3) - real(C_DOUBLE), intent(out) :: d - integer(C_INT), intent(out) :: lattice_trans(3) - end subroutine lattice_distance_c - - subroutine lattice_get_indices_c(lat_ptr, xyz, i_xyz) & - bind(C, name='lattice_get_indices') - import C_PTR, C_INT, C_DOUBLE - type(C_PTR), intent(in), value :: lat_ptr - real(C_DOUBLE), intent(in) :: xyz(3) - integer(C_INT), intent(out) :: i_xyz(3) - end subroutine lattice_get_indices_c - - subroutine lattice_get_local_xyz_c(lat_ptr, global_xyz, i_xyz, local_xyz) & - bind(C, name='lattice_get_local_xyz') - import C_PTR, C_INT, C_DOUBLE - type(C_PTR), intent(in), value :: lat_ptr - real(C_DOUBLE), intent(in) :: global_xyz(3) - integer(C_INT), intent(in) :: i_xyz(3) - real(C_DOUBLE), intent(out) :: local_xyz(3) - end subroutine lattice_get_local_xyz_c - - subroutine lattice_to_hdf5_c(lat_ptr, group) bind(C, name='lattice_to_hdf5') - import HID_T, C_PTR - type(C_PTR), intent(in), value :: lat_ptr - integer(HID_T), intent(in), value :: group - end subroutine lattice_to_hdf5_c - function lattice_offset_c(lat_ptr, map, i_xyz) & bind(C, name='lattice_offset') result(offset) import C_PTR, C_INT, C_INT32_T @@ -169,79 +140,24 @@ module geometry_header integer(C_INT32_T) :: offset end function lattice_offset_c - function lattice_outer_c(lat_ptr) bind(C, name='lattice_outer') & - result(outer) - import C_PTR, C_INT32_T - type(C_PTR), intent(in), value :: lat_ptr - integer(C_INT32_T) :: outer - end function lattice_outer_c - - function lattice_universe_c(lat_ptr, i_xyz) & - bind(C, name='lattice_universe') result(univ) - import C_PTR, C_INT32_t, C_INT - type(C_PTR), intent(in), value :: lat_ptr - integer(C_INT), intent(in) :: i_xyz(3) - integer(C_INT32_T) :: univ - end function lattice_universe_c - subroutine extend_cells_c(n) bind(C) import C_INT32_t integer(C_INT32_T), intent(in), value :: n end subroutine extend_cells_c end interface -!=============================================================================== -! UNIVERSE defines a geometry that fills all phase space -!=============================================================================== - - type Universe - integer :: id ! Unique ID - integer, allocatable :: cells(:) ! List of cells within - real(8) :: x0 ! Translation in x-coordinate - real(8) :: y0 ! Translation in y-coordinate - real(8) :: z0 ! Translation in z-coordinate - end type Universe - !=============================================================================== ! LATTICE abstract type for ordered array of universes. !=============================================================================== - type, abstract :: Lattice + type :: Lattice type(C_PTR) :: ptr contains procedure :: id => lattice_id procedure :: are_valid_indices => lattice_are_valid_indices - procedure :: distance => lattice_distance - procedure :: get => lattice_get - procedure :: get_indices => lattice_get_indices - procedure :: get_local_xyz => lattice_get_local_xyz procedure :: offset => lattice_offset - procedure :: outer => lattice_outer - procedure :: to_hdf5 => lattice_to_hdf5 end type Lattice -!=============================================================================== -! RECTLATTICE extends LATTICE for rectilinear arrays. -!=============================================================================== - - type, extends(Lattice) :: RectLattice - end type RectLattice - -!=============================================================================== -! HEXLATTICE extends LATTICE for hexagonal (sometimes called triangular) arrays. -!=============================================================================== - - type, extends(Lattice) :: HexLattice - end type HexLattice - -!=============================================================================== -! LATTICECONTAINER pointer array for storing lattices -!=============================================================================== - - type LatticeContainer - class(Lattice), allocatable :: obj - end type LatticeContainer - !=============================================================================== ! CELL defines a closed volume by its bounding surfaces !=============================================================================== @@ -249,16 +165,7 @@ module geometry_header type Cell type(C_PTR) :: ptr - integer, allocatable :: region(:) ! Definition of spatial region as - ! Boolean expression of half-spaces - integer :: distribcell_index ! Index corresponding to this cell in - ! distribcell arrays - real(8), allocatable :: sqrtkT(:) ! Square root of k_Boltzmann * - ! temperature in eV. Multiple for - ! distribcell - ! Rotation matrix and translation vector - real(8), allocatable :: translation(:) real(8), allocatable :: rotation(:) real(8), allocatable :: rotation_matrix(:,:) @@ -270,12 +177,12 @@ module geometry_header procedure :: universe => cell_universe procedure :: fill => cell_fill procedure :: n_instances => cell_n_instances + procedure :: distribcell_index => cell_distribcell_index procedure :: material_size => cell_material_size procedure :: material => cell_material - procedure :: simple => cell_simple - procedure :: distance => cell_distance + procedure :: sqrtkT_size => cell_sqrtkT_size + procedure :: sqrtkT => cell_sqrtkT procedure :: offset => cell_offset - procedure :: to_hdf5 => cell_to_hdf5 end type Cell @@ -286,8 +193,7 @@ module geometry_header integer(C_INT32_T), bind(C) :: n_universes ! # of universes type(Cell), allocatable, target :: cells(:) - type(Universe), allocatable, target :: universes(:) - type(LatticeContainer), allocatable, target :: lattices(:) + type(Lattice), allocatable, target :: lattices(:) ! Dictionaries which map user IDs to indices in the global arrays type(DictIntInt) :: cell_dict @@ -309,39 +215,6 @@ contains is_valid = lattice_are_valid_indices_c(this % ptr, i_xyz) end function lattice_are_valid_indices - subroutine lattice_distance(this, xyz, uvw, i_xyz, d, lattice_trans) - class(Lattice), intent(in) :: this - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - integer(C_INT), intent(in) :: i_xyz(3) - real(C_DOUBLE), intent(out) :: d - integer(C_INT), intent(out) :: lattice_trans(3) - call lattice_distance_c(this % ptr, xyz, uvw, i_xyz, d, lattice_trans) - end subroutine lattice_distance - - function lattice_get(this, i_xyz) result(univ) - class(Lattice), intent(in) :: this - integer(C_INT), intent(in) :: i_xyz(3) - integer(C_INT32_T) :: univ - univ = lattice_universe_c(this % ptr, i_xyz) - end function lattice_get - - function lattice_get_indices(this, xyz) result(i_xyz) - class(Lattice), intent(in) :: this - real(C_DOUBLE), intent(in) :: xyz(3) - integer(C_INT) :: i_xyz(3) - call lattice_get_indices_c(this % ptr, xyz, i_xyz) - end function lattice_get_indices - - function lattice_get_local_xyz(this, global_xyz, i_xyz) & - result(local_xyz) - class(Lattice), intent(in) :: this - real(C_DOUBLE), intent(in) :: global_xyz(3) - integer(C_INT), intent(in) :: i_xyz(3) - real(C_DOUBLE) :: local_xyz(3) - call lattice_get_local_xyz_c(this % ptr, global_xyz, i_xyz, local_xyz) - end function lattice_get_local_xyz - function lattice_offset(this, map, i_xyz) result(offset) class(Lattice), intent(in) :: this integer(C_INT), intent(in) :: map @@ -350,18 +223,6 @@ contains offset = lattice_offset_c(this % ptr, map, i_xyz) end function lattice_offset - function lattice_outer(this) result(outer) - class(Lattice), intent(in) :: this - integer(C_INT32_T) :: outer - outer = lattice_outer_c(this % ptr) - end function lattice_outer - - subroutine lattice_to_hdf5(this, group) - class(Lattice), intent(in) :: this - integer(HID_T), intent(in) :: group - call lattice_to_hdf5_c(this % ptr, group) - end subroutine lattice_to_hdf5 - !=============================================================================== function cell_id(this) result(id) @@ -400,6 +261,12 @@ contains n_instances = cell_n_instances_c(this % ptr) end function cell_n_instances + function cell_distribcell_index(this) result(distribcell_index) + class(Cell), intent(in) :: this + integer(C_INT) :: distribcell_index + distribcell_index = cell_distribcell_index_c(this % ptr) + end function cell_distribcell_index + function cell_material_size(this) result(n) class(Cell), intent(in) :: this integer(C_INT) :: n @@ -413,21 +280,18 @@ contains mat = cell_material_c(this % ptr, i) end function cell_material - function cell_simple(this) result(simple) + function cell_sqrtkT_size(this) result(n) class(Cell), intent(in) :: this - logical(C_BOOL) :: simple - simple = cell_simple_c(this % ptr) - end function cell_simple + integer :: n + n = cell_sqrtkT_size_c(this % ptr) + end function cell_sqrtkT_size - subroutine cell_distance(this, xyz, uvw, on_surface, min_dist, i_surf) - class(Cell), intent(in) :: this - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - integer(C_INT32_T), intent(in) :: on_surface - real(C_DOUBLE), intent(out) :: min_dist - integer(C_INT32_T), intent(out) :: i_surf - call cell_distance_c(this % ptr, xyz, uvw, on_surface, min_dist, i_surf) - end subroutine cell_distance + function cell_sqrtkT(this, i) result(sqrtkT) + class(Cell), intent(in) :: this + integer, intent(in) :: i + real(C_DOUBLE) :: sqrtkT + sqrtkT = cell_sqrtkT_c(this % ptr, i) + end function cell_sqrtkT function cell_offset(this, map) result(offset) class(Cell), intent(in) :: this @@ -436,12 +300,6 @@ contains offset = cell_offset_c(this % ptr, map) end function cell_offset - subroutine cell_to_hdf5(this, group) - class(Cell), intent(in) :: this - integer(HID_T), intent(in) :: group - call cell_to_hdf5_c(this % ptr, group) - end subroutine cell_to_hdf5 - !=============================================================================== ! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table ! appears at in the model. Later, this list is used to determine the actual @@ -470,10 +328,10 @@ contains if (cells(i) % material(j) == MATERIAL_VOID) cycle ! Get temperature of cell (rounding to nearest integer) - if (size(cells(i) % sqrtkT) > 1) then - temperature = cells(i) % sqrtkT(j)**2 / K_BOLTZMANN + if (cells(i) % sqrtkT_size() > 1) then + temperature = cells(i) % sqrtkT(j-1)**2 / K_BOLTZMANN else - temperature = cells(i) % sqrtkT(1)**2 / K_BOLTZMANN + temperature = cells(i) % sqrtkT(0)**2 / K_BOLTZMANN end if i_material = cells(i) % material(j) @@ -522,7 +380,6 @@ contains n_universes = 0 if (allocated(cells)) deallocate(cells) - if (allocated(universes)) deallocate(universes) if (allocated(lattices)) deallocate(lattices) call cell_dict % clear() @@ -626,105 +483,4 @@ contains end if end function openmc_cell_set_id - - function openmc_cell_set_temperature(index, T, instance) result(err) bind(C) - ! Set the temperature of a cell - integer(C_INT32_T), value, intent(in) :: index ! index in cells - real(C_DOUBLE), value, intent(in) :: T ! temperature - integer(C_INT32_T), optional, intent(in) :: instance ! cell instance - - integer(C_INT) :: err ! error code - integer :: j ! looping variable - integer :: n ! number of cell instances - integer :: material_index ! material index in materials array - integer :: num_nuclides ! num nuclides in material - integer :: nuclide_index ! index of nuclide in nuclides array - real(8) :: min_temp ! min common-denominator avail temp - real(8) :: max_temp ! max common-denominator avail temp - real(8) :: temp ! actual temp we'll assign - logical :: outside_low ! lower than available data - logical :: outside_high ! higher than available data - - outside_low = .false. - outside_high = .false. - - err = E_UNASSIGNED - - if (index >= 1 .and. index <= size(cells)) then - - ! error if the cell is filled with another universe - if (cells(index) % fill() /= C_NONE) then - err = E_GEOMETRY - call set_errmsg("Cannot set temperature on a cell filled & - &with a universe.") - else - ! find which material is associated with this cell (material_index - ! is the index into the materials array) - if (present(instance)) then - material_index = cells(index) % material(instance + 1) - else - material_index = cells(index) % material(1) - end if - - ! number of nuclides associated with this material - num_nuclides = size(materials(material_index) % nuclide) - - min_temp = ZERO - max_temp = INFINITY - - do j = 1, num_nuclides - nuclide_index = materials(material_index) % nuclide(j) - min_temp = max(min_temp, minval(nuclides(nuclide_index) % kTs)) - max_temp = min(max_temp, maxval(nuclides(nuclide_index) % kTs)) - end do - - ! adjust the temperature to be within bounds if necessary - if (K_BOLTZMANN * T < min_temp) then - outside_low = .true. - temp = min_temp / K_BOLTZMANN - else if (K_BOLTZMANN * T > max_temp) then - outside_high = .true. - temp = max_temp / K_BOLTZMANN - else - temp = T - end if - - associate (c => cells(index)) - if (allocated(c % sqrtkT)) then - n = size(c % sqrtkT) - if (present(instance) .and. n > 1) then - if (instance >= 0 .and. instance < n) then - c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * temp) - err = 0 - end if - else - c % sqrtkT(:) = sqrt(K_BOLTZMANN * temp) - err = 0 - end if - end if - end associate - - ! Assign error codes for outside of temperature bounds provided the - ! temperature was changed correctly. This needs to be done after - ! changing the temperature based on the logical structure above. - if (err == 0) then - if (outside_low) then - err = E_WARNING - call set_errmsg("Nuclear data has not been loaded beyond lower & - &bound of T=" // trim(to_str(T)) // " K.") - else if (outside_high) then - err = E_WARNING - call set_errmsg("Nuclear data has not been loaded beyond upper & - &bound of T=" // trim(to_str(T)) // " K.") - end if - end if - - end if - - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in cells array is out of bounds.") - end if - end function openmc_cell_set_temperature - end module geometry_header diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index cc82307398..5056e5f78b 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -910,6 +910,8 @@ using_mpio_device(hid_t obj_id) template<> const hid_t H5TypeMap::type_id = H5T_NATIVE_INT; template<> +const hid_t H5TypeMap::type_id = H5T_NATIVE_ULONG; +template<> const hid_t H5TypeMap::type_id = H5T_NATIVE_INT64; template<> const hid_t H5TypeMap::type_id = H5T_NATIVE_DOUBLE; diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 6c1452e122..ffee754503 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -28,7 +28,7 @@ module input_xml use settings use stl_vector, only: VectorInt, VectorReal, VectorChar use string, only: to_lower, to_str, str_to_int, str_to_real, & - starts_with, ends_with, tokenize, split_string, & + starts_with, ends_with, split_string, & zero_padded, to_c_string use summary, only: write_summary use tally @@ -48,22 +48,21 @@ module input_xml subroutine adjust_indices() bind(C) end subroutine adjust_indices - subroutine allocate_offset_tables(n_maps) bind(C) - import C_INT - integer(C_INT), intent(in), value :: n_maps - end subroutine allocate_offset_tables - - subroutine fill_offset_tables(target_univ_id, map) bind(C) - import C_INT32_T, C_INT - integer(C_INT32_T), intent(in), value :: target_univ_id - integer(C_INT), intent(in), value :: map - end subroutine fill_offset_tables + subroutine assign_temperatures() bind(C) + end subroutine assign_temperatures subroutine count_cell_instances(univ_indx) bind(C) import C_INT32_T integer(C_INT32_T), intent(in), value :: univ_indx end subroutine count_cell_instances + subroutine prepare_distribcell_c(cell_list, n) & + bind(C, name="prepare_distribcell") + import C_INT32_T, C_INT + integer(C_INT), intent(in), value :: n + integer(C_INT32_T), intent(in) :: cell_list(n) + end subroutine prepare_distribcell_c + subroutine read_surfaces(node_ptr) bind(C) import C_PTR type(C_PTR) :: node_ptr @@ -173,14 +172,14 @@ contains ! Perform some final operations to set up the geometry call adjust_indices() - call count_cell_instances(root_universe-1) + call count_cell_instances(root_universe) ! After reading input and basic geometry setup is complete, build lists of ! neighboring cells for efficient tracking call neighbor_lists() ! Assign temperatures to cells that don't have temperatures already assigned - call assign_temperatures(material_temps) + call assign_temperatures() ! Determine desired txemperatures for each nuclide and S(a,b) table call get_temperatures(nuc_temps, sab_temps) @@ -188,7 +187,7 @@ contains ! Check to make sure there are not too many nested coordinate levels in the ! geometry since the coordinate list is statically allocated for performance ! reasons - if (maximum_levels(root_universe - 1) > MAX_COORD) then + if (maximum_levels(root_universe) > MAX_COORD) then call fatal_error("Too many nested coordinate levels in the geometry. & &Try increasing the maximum number of coordinate levels by & &providing the CMake -Dmaxcoord= option.") @@ -468,16 +467,14 @@ contains subroutine read_geometry_xml() - integer :: i, j, k + integer :: i integer :: n, n_rlats, n_hlats - integer :: id integer :: univ_id integer :: n_cells_in_univ real(8) :: phi, theta, psi logical :: file_exists logical :: boundary_exists character(MAX_LINE_LEN) :: filename - character(:), allocatable :: region_spec type(Cell), pointer :: c class(Lattice), pointer :: lat type(XMLDocument) :: doc @@ -487,7 +484,6 @@ contains type(XMLNode), allocatable :: node_cell_list(:) type(XMLNode), allocatable :: node_rlat_list(:) type(XMLNode), allocatable :: node_hlat_list(:) - type(VectorInt) :: tokens type(VectorInt) :: univ_ids ! List of all universe IDs type(DictIntInt) :: cells_in_univ_dict ! Used to count how many cells each ! universe contains @@ -555,20 +551,12 @@ contains ! Allocate cells array allocate(cells(n_cells)) - if (check_overlaps) then - allocate(overlap_check_cnt(n_cells)) - overlap_check_cnt = 0 - end if - n_universes = 0 do i = 1, n_cells c => cells(i) c % ptr = cell_pointer(i - 1) - ! Initialize distribcell instances and distribcell index - c % distribcell_index = NONE - ! Get pointer to i-th cell node node_cell = node_cell_list(i) @@ -578,43 +566,6 @@ contains // to_str(c % id())) end if - ! Check for region specification (also under deprecated name surfaces) - if (check_for_node(node_cell, "surfaces")) then - call warning("The use of 'surfaces' is deprecated and will be & - &disallowed in a future release. Use 'region' instead. The & - &openmc-update-inputs utility can be used to automatically & - &update geometry.xml files.") - region_spec = node_value_string(node_cell, "surfaces") - call get_node_value(node_cell, "surfaces", region_spec) - elseif (check_for_node(node_cell, "region")) then - region_spec = node_value_string(node_cell, "region") - else - region_spec = '' - end if - - if (len_trim(region_spec) > 0) then - ! Create surfaces array from string - call tokenize(region_spec, tokens) - - ! Convert user IDs to surface indices - do j = 1, tokens % size() - id = tokens % data(j) - if (id < OP_UNION) then - if (surface_dict % has(abs(id))) then - k = surface_dict % get(abs(id)) - tokens % data(j) = sign(k, id) - end if - end if - end do - - ! Copy region spec and RPN form to cell arrays - allocate(c % region(tokens%size())) - c % region(:) = tokens%data(1:tokens%size()) - - call tokens%clear() - end if - if (.not. allocated(c%region)) allocate(c%region(0)) - ! Rotation matrix if (check_for_node(node_cell, "rotation")) then ! Rotations can only be applied to cells that are being filled with @@ -650,62 +601,6 @@ contains cos(phi)*cos(theta) /), (/ 3,3 /)) end if - ! Translation vector - if (check_for_node(node_cell, "translation")) then - ! Translations can only be applied to cells that are being filled with - ! another universe - if (c % fill() == C_NONE) then - call fatal_error("Cannot apply a translation to cell " & - // trim(to_str(c % id())) // " because it is not filled with & - &another universe") - end if - - ! Read number of translation parameters - n = node_word_count(node_cell, "translation") - if (n /= 3) then - call fatal_error("Incorrect number of translation parameters on & - &cell " // to_str(c % id())) - end if - - ! Copy translation vector - allocate(c % translation(3)) - call get_node_array(node_cell, "translation", c % translation) - end if - - ! Read cell temperatures. If the temperature is not specified, set it to - ! a negative number for now. During initialization we'll replace - ! negatives with the temperature from the material data. - if (check_for_node(node_cell, "temperature")) then - n = node_word_count(node_cell, "temperature") - if (n > 0) then - ! Make sure this is a "normal" cell. - if (c % fill() /= C_NONE) call fatal_error("Cell " & - // trim(to_str(c % id())) // " was specified with a temperature & - &but no material. Temperature specification is only valid for & - &cells filled with a material.") - - ! Copy in temperatures - allocate(c % sqrtkT(n)) - call get_node_array(node_cell, "temperature", c % sqrtkT) - - ! Make sure all temperatues are positive - do j = 1, size(c % sqrtkT) - if (c % sqrtkT(j) < ZERO) call fatal_error("Cell " & - // trim(to_str(c % id())) // " was specified with a negative & - &temperature. All cell temperatures must be non-negative.") - end do - - ! Convert to sqrt(kT) - c % sqrtkT(:) = sqrt(K_BOLTZMANN * c % sqrtkT(:)) - else - allocate(c % sqrtkT(1)) - c % sqrtkT(1) = -1.0 - end if - else - allocate(c % sqrtkT(1)) - c % sqrtkT = -1.0 - end if - ! Add cell to dictionary call cell_dict % set(c % id(), i) @@ -716,7 +611,7 @@ contains if (.not. cells_in_univ_dict % has(univ_id)) then n_universes = n_universes + 1 n_cells_in_univ = 1 - call universe_dict % set(univ_id, n_universes) + call universe_dict % set(univ_id, n_universes - 1) call univ_ids % push_back(univ_id) else n_cells_in_univ = 1 + cells_in_univ_dict % get(univ_id) @@ -740,11 +635,8 @@ contains allocate(lattices(n_rlats + n_hlats)) RECT_LATTICES: do i = 1, n_rlats - allocate(RectLattice::lattices(i) % obj) - lat => lattices(i) % obj + lat => lattices(i) lat % ptr = lattice_pointer(i - 1) - select type(lat) - type is (RectLattice) ! Get pointer to i-th lattice node_lat = node_rlat_list(i) @@ -752,15 +644,11 @@ contains ! Add lattice to dictionary call lattice_dict % set(lat % id(), i) - end select end do RECT_LATTICES HEX_LATTICES: do i = 1, n_hlats - allocate(HexLattice::lattices(n_rlats + i) % obj) - lat => lattices(n_rlats + i) % obj + lat => lattices(n_rlats + i) lat % ptr = lattice_pointer(n_rlats + i - 1) - select type (lat) - type is (HexLattice) ! Get pointer to i-th lattice node_lat = node_hlat_list(i) @@ -768,36 +656,13 @@ contains ! Add lattice to dictionary call lattice_dict % set(lat % id(), n_rlats + i) - end select end do HEX_LATTICES ! ========================================================================== ! SETUP UNIVERSES ! Allocate universes, universe cell arrays, and assign base universe - allocate(universes(n_universes)) - do i = 1, n_universes - associate (u => universes(i)) - u % id = univ_ids % data(i) - - ! Allocate cell list - n_cells_in_univ = cells_in_univ_dict % get(u % id) - allocate(u % cells(n_cells_in_univ)) - u % cells(:) = 0 - end associate - end do - root_universe = find_root_universe() + 1 - - do i = 1, n_cells - ! Get index in universes array - j = universe_dict % get(cells(i) % universe()) - - ! Set the first zero entry in the universe cells array to the index in the - ! global cells array - associate (u => universes(j)) - u % cells(find(u % cells, 0)) = i - end associate - end do + root_universe = find_root_universe() ! Clear dictionary call cells_in_univ_dict%clear() @@ -3264,46 +3129,6 @@ contains end subroutine read_ce_cross_sections -!=============================================================================== -! ASSIGN_TEMPERATURES If any cells have undefined temperatures, try to find -! their temperatures from material or global default temperatures -!=============================================================================== - - subroutine assign_temperatures(material_temps) - real(8), intent(in) :: material_temps(:) - - integer :: i, j - integer :: i_material - - do i = 1, n_cells - ! Ignore non-normal cells and cells with defined temperature. - if (cells(i) % fill() /= C_NONE) cycle - if (cells(i) % sqrtkT(1) >= ZERO) cycle - - ! Set the number of temperatures equal to the number of materials. - deallocate(cells(i) % sqrtkT) - allocate(cells(i) % sqrtkT(cells(i) % material_size())) - - ! Check each of the cell materials for temperature data. - do j = 1, cells(i) % material_size() - ! Arbitrarily set void regions to 0K. - if (cells(i) % material(j) == MATERIAL_VOID) then - cells(i) % sqrtkT(j) = ZERO - cycle - end if - - ! Use material default or global default temperature - i_material = cells(i) % material(j) - if (material_temps(i_material) >= ZERO) then - cells(i) % sqrtkT(j) = sqrt(K_BOLTZMANN * & - material_temps(i_material)) - else - cells(i) % sqrtkT(j) = sqrt(K_BOLTZMANN * temperature_default) - end if - end do - end do - end subroutine assign_temperatures - !=============================================================================== ! READ_MULTIPOLE_DATA checks for the existence of a multipole library in the ! directory and loads it using multipole_read @@ -3360,9 +3185,9 @@ contains subroutine prepare_distribcell() - integer :: i, j, k + integer :: i, j type(SetInt) :: cell_list ! distribcells to track - type(ListInt) :: univ_list ! universes containing distribcells + integer(C_INT32_T), allocatable :: cell_list_c(:) ! Find all cells listed in a distribcell filter. do i = 1, n_tallies @@ -3374,56 +3199,11 @@ contains end do end do - ! Find all cells with multiple (distributed) materials or temperatures. - do i = 1, n_cells - if (cells(i) % material_size() > 1 .or. size(cells(i) % sqrtkT) > 1) then - call cell_list % add(i) - end if - end do - - ! Make sure the number of distributed materials and temperatures matches the - ! number of respective cell instances. - do i = 1, n_cells - associate (c => cells(i)) - if (c % material_size() > 1) then - if (c % material_size() /= c % n_instances()) then - call fatal_error("Cell " // trim(to_str(c % id())) // " was & - &specified with " // trim(to_str(c % material_size())) & - // " materials but has " // trim(to_str(c % n_instances())) & - // " distributed instances. The number of materials must & - &equal one or the number of instances.") - end if - end if - if (size(c % sqrtkT) > 1) then - if (size(c % sqrtkT) /= c % n_instances()) then - call fatal_error("Cell " // trim(to_str(c % id())) // " was & - &specified with " // trim(to_str(size(c % sqrtkT))) & - // " temperatures but has " // trim(to_str(c % n_instances()))& - // " distributed instances. The number of temperatures must & - &equal one or the number of instances.") - end if - end if - end associate - end do - - ! Search through universes for distributed cells and assign each one a - ! unique distribcell array index. - k = 1 - do i = 1, n_universes - do j = 1, size(universes(i) % cells) - if (cell_list % contains(universes(i) % cells(j))) then - cells(universes(i) % cells(j)) % distribcell_index = k - call univ_list % append(universes(i) % id) - k = k + 1 - end if - end do - end do - - ! Allocate and fill cell and lattice offset tables. - call allocate_offset_tables(univ_list % size()) - do i = 1, univ_list % size() - call fill_offset_tables(univ_list % get_item(i), i-1) + allocate(cell_list_c(cell_list % size())) + do i = 1, cell_list % size() + cell_list_c(i) = cell_list % get_item(i) - 1 end do + call prepare_distribcell_c(cell_list_c, cell_list % size()) end subroutine prepare_distribcell diff --git a/src/lattice.cpp b/src/lattice.cpp index a5b56eef8e..21f4768506 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -18,7 +18,7 @@ namespace openmc { // Global variables //============================================================================== -std::vector lattices_c; +std::vector lattices; std::unordered_map lattice_map; @@ -29,17 +29,17 @@ std::unordered_map lattice_map; Lattice::Lattice(pugi::xml_node lat_node) { if (check_for_node(lat_node, "id")) { - id = std::stoi(get_node_value(lat_node, "id")); + id_ = std::stoi(get_node_value(lat_node, "id")); } else { fatal_error("Must specify id of lattice in geometry XML file."); } if (check_for_node(lat_node, "name")) { - name = get_node_value(lat_node, "name"); + name_ = get_node_value(lat_node, "name"); } if (check_for_node(lat_node, "outer")) { - outer = std::stoi(get_node_value(lat_node, "outer")); + outer_ = std::stoi(get_node_value(lat_node, "outer")); } } @@ -49,10 +49,10 @@ LatticeIter Lattice::begin() {return LatticeIter(*this, 0);} LatticeIter Lattice::end() -{return LatticeIter(*this, universes.size());} +{return LatticeIter(*this, universes_.size());} ReverseLatticeIter Lattice::rbegin() -{return ReverseLatticeIter(*this, universes.size()-1);} +{return ReverseLatticeIter(*this, universes_.size()-1);} ReverseLatticeIter Lattice::rend() {return ReverseLatticeIter(*this, -1);} @@ -71,20 +71,20 @@ Lattice::adjust_indices() } else { std::stringstream err_msg; err_msg << "Invalid universe number " << uid << " specified on " - "lattice " << id; + "lattice " << id_; fatal_error(err_msg); } } // Adjust the index for the outer universe. - if (outer != NO_OUTER_UNIVERSE) { - auto search = universe_map.find(outer); + if (outer_ != NO_OUTER_UNIVERSE) { + auto search = universe_map.find(outer_); if (search != universe_map.end()) { - outer = search->second; + outer_ = search->second; } else { std::stringstream err_msg; - err_msg << "Invalid universe number " << outer << " specified on " - "lattice " << id; + err_msg << "Invalid universe number " << outer_ << " specified on " + "lattice " << id_; fatal_error(err_msg); } } @@ -96,7 +96,7 @@ int32_t Lattice::fill_offset_table(int32_t offset, int32_t target_univ_id, int map) { for (LatticeIter it = begin(); it != end(); ++it) { - offsets[map * universes.size() + it.indx] = offset; + offsets_[map * universes_.size() + it.indx_] = offset; offset += count_universe_instances(*it, target_univ_id); } return offset; @@ -109,19 +109,19 @@ Lattice::to_hdf5(hid_t lattices_group) const { // Make a group for the lattice. std::string group_name {"lattice "}; - group_name += std::to_string(id); + group_name += std::to_string(id_); hid_t lat_group = create_group(lattices_group, group_name); // Write the name and outer universe. - if (!name.empty()) { - write_string(lat_group, "name", name, false); + if (!name_.empty()) { + write_string(lat_group, "name", name_, false); } - if (outer != NO_OUTER_UNIVERSE) { - int32_t outer_id = global_universes[outer]->id; + if (outer_ != NO_OUTER_UNIVERSE) { + int32_t outer_id = universes[outer_]->id_; write_dataset(lat_group, "outer", outer_id); } else { - write_dataset(lat_group, "outer", outer); + write_dataset(lat_group, "outer", outer_); } // Call subclass-overriden function to fill in other details. @@ -137,19 +137,21 @@ Lattice::to_hdf5(hid_t lattices_group) const RectLattice::RectLattice(pugi::xml_node lat_node) : Lattice {lat_node} { + type_ = LatticeType::rect; + // Read the number of lattice cells in each dimension. std::string dimension_str {get_node_value(lat_node, "dimension")}; std::vector dimension_words {split(dimension_str)}; if (dimension_words.size() == 2) { - n_cells[0] = std::stoi(dimension_words[0]); - n_cells[1] = std::stoi(dimension_words[1]); - n_cells[2] = 1; - is_3d = false; + n_cells_[0] = std::stoi(dimension_words[0]); + n_cells_[1] = std::stoi(dimension_words[1]); + n_cells_[2] = 1; + is_3d_ = false; } else if (dimension_words.size() == 3) { - n_cells[0] = std::stoi(dimension_words[0]); - n_cells[1] = std::stoi(dimension_words[1]); - n_cells[2] = std::stoi(dimension_words[2]); - is_3d = true; + n_cells_[0] = std::stoi(dimension_words[0]); + n_cells_[1] = std::stoi(dimension_words[1]); + n_cells_[2] = std::stoi(dimension_words[2]); + is_3d_ = true; } else { fatal_error("Rectangular lattice must be two or three dimensions."); } @@ -161,9 +163,9 @@ RectLattice::RectLattice(pugi::xml_node lat_node) fatal_error("Number of entries on must be the same as the " "number of entries on ."); } - lower_left[0] = stod(ll_words[0]); - lower_left[1] = stod(ll_words[1]); - if (is_3d) {lower_left[2] = stod(ll_words[2]);} + lower_left_[0] = stod(ll_words[0]); + lower_left_[1] = stod(ll_words[1]); + if (is_3d_) {lower_left_[2] = stod(ll_words[2]);} // Read the lattice pitches. std::string pitch_str {get_node_value(lat_node, "pitch")}; @@ -172,9 +174,9 @@ RectLattice::RectLattice(pugi::xml_node lat_node) fatal_error("Number of entries on must be the same as the " "number of entries on ."); } - pitch[0] = stod(pitch_words[0]); - pitch[1] = stod(pitch_words[1]); - if (is_3d) {pitch[2] = stod(pitch_words[2]);} + pitch_[0] = stod(pitch_words[0]); + pitch_[1] = stod(pitch_words[1]); + if (is_3d_) {pitch_[2] = stod(pitch_words[2]);} // Read the universes and make sure the correct number was specified. std::string univ_str {get_node_value(lat_node, "universes")}; @@ -189,13 +191,13 @@ RectLattice::RectLattice(pugi::xml_node lat_node) } // Parse the universes. - universes.resize(nx*ny*nz, C_NONE); + universes_.resize(nx*ny*nz, C_NONE); for (int iz = 0; iz < nz; iz++) { for (int iy = ny-1; iy > -1; iy--) { for (int ix = 0; ix < nx; ix++) { int indx1 = nx*ny*iz + nx*(ny-iy-1) + ix; int indx2 = nx*ny*iz + nx*iy + ix; - universes[indx1] = std::stoi(univ_words[indx2]); + universes_[indx1] = std::stoi(univ_words[indx2]); } } } @@ -204,10 +206,10 @@ RectLattice::RectLattice(pugi::xml_node lat_node) //============================================================================== int32_t& -RectLattice::operator[](const int i_xyz[3]) +RectLattice::operator[](std::array i_xyz) { int indx = nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]; - return universes[indx]; + return universes_[indx]; } //============================================================================== @@ -215,15 +217,16 @@ RectLattice::operator[](const int i_xyz[3]) bool RectLattice::are_valid_indices(const int i_xyz[3]) const { - return ( (i_xyz[0] >= 0) && (i_xyz[0] < n_cells[0]) - && (i_xyz[1] >= 0) && (i_xyz[1] < n_cells[1]) - && (i_xyz[2] >= 0) && (i_xyz[2] < n_cells[2])); + return ( (i_xyz[0] >= 0) && (i_xyz[0] < n_cells_[0]) + && (i_xyz[1] >= 0) && (i_xyz[1] < n_cells_[1]) + && (i_xyz[2] >= 0) && (i_xyz[2] < n_cells_[2])); } //============================================================================== std::pair> -RectLattice::distance(Position r, Direction u, const int i_xyz[3]) const +RectLattice::distance(Position r, Direction u, const std::array& i_xyz) +const { // Get short aliases to the coordinates. double x = r.x; @@ -231,8 +234,8 @@ RectLattice::distance(Position r, Direction u, const int i_xyz[3]) const double z = r.z; // Determine the oncoming edge. - double x0 {copysign(0.5 * pitch[0], u.x)}; - double y0 {copysign(0.5 * pitch[1], u.y)}; + double x0 {copysign(0.5 * pitch_[0], u.x)}; + double y0 {copysign(0.5 * pitch_[1], u.y)}; // Left and right sides double d {INFTY}; @@ -260,8 +263,8 @@ RectLattice::distance(Position r, Direction u, const int i_xyz[3]) const } // Top and bottom sides - if (is_3d) { - double z0 {copysign(0.5 * pitch[2], u.z)}; + if (is_3d_) { + double z0 {copysign(0.5 * pitch_[2], u.z)}; if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) { double this_d = (z0 - z) / u.z; if (this_d < d) { @@ -283,11 +286,11 @@ RectLattice::distance(Position r, Direction u, const int i_xyz[3]) const std::array RectLattice::get_indices(Position r) const { - int ix {static_cast(std::ceil((r.x - lower_left.x) / pitch.x))-1}; - int iy {static_cast(std::ceil((r.y - lower_left.y) / pitch.y))-1}; + int ix {static_cast(std::ceil((r.x - lower_left_.x) / pitch_.x))-1}; + int iy {static_cast(std::ceil((r.y - lower_left_.y) / pitch_.y))-1}; int iz; - if (is_3d) { - iz = static_cast(std::ceil((r.z - lower_left.z) / pitch.z))-1; + if (is_3d_) { + iz = static_cast(std::ceil((r.z - lower_left_.z) / pitch_.z))-1; } else { iz = 0; } @@ -297,12 +300,13 @@ RectLattice::get_indices(Position r) const //============================================================================== Position -RectLattice::get_local_position(Position r, const int i_xyz[3]) const +RectLattice::get_local_position(Position r, const std::array i_xyz) +const { - r.x -= (lower_left.x + (i_xyz[0] + 0.5)*pitch.x); - r.y -= (lower_left.y + (i_xyz[1] + 0.5)*pitch.y); - if (is_3d) { - r.z -= (lower_left.z + (i_xyz[2] + 0.5)*pitch.z); + r.x -= (lower_left_.x + (i_xyz[0] + 0.5)*pitch_.x); + r.y -= (lower_left_.y + (i_xyz[1] + 0.5)*pitch_.y); + if (is_3d_) { + r.z -= (lower_left_.z + (i_xyz[2] + 0.5)*pitch_.z); } return r; } @@ -312,7 +316,7 @@ RectLattice::get_local_position(Position r, const int i_xyz[3]) const int32_t& RectLattice::offset(int map, const int i_xyz[3]) { - return offsets[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; + return offsets_[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; } //============================================================================== @@ -326,7 +330,7 @@ RectLattice::index_to_string(int indx) const std::string out {std::to_string(ix)}; out += ','; out += std::to_string(iy); - if (is_3d) { + if (is_3d_) { out += ','; out += std::to_string(iz); } @@ -340,25 +344,25 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const { // Write basic lattice information. write_string(lat_group, "type", "rectangular", false); - if (is_3d) { - write_dataset(lat_group, "pitch", pitch); - write_dataset(lat_group, "lower_left", lower_left); - write_dataset(lat_group, "dimension", n_cells); + if (is_3d_) { + write_dataset(lat_group, "pitch", pitch_); + write_dataset(lat_group, "lower_left", lower_left_); + write_dataset(lat_group, "dimension", n_cells_); } else { - std::array pitch_short {{pitch[0], pitch[1]}}; + std::array pitch_short {{pitch_[0], pitch_[1]}}; write_dataset(lat_group, "pitch", pitch_short); - std::array ll_short {{lower_left[0], lower_left[1]}}; + std::array ll_short {{lower_left_[0], lower_left_[1]}}; write_dataset(lat_group, "lower_left", ll_short); - std::array nc_short {{n_cells[0], n_cells[1]}}; + std::array nc_short {{n_cells_[0], n_cells_[1]}}; write_dataset(lat_group, "dimension", nc_short); } // Write the universe ids. The convention here is to switch the ordering on // the y-axis to match the way universes are input in a text file. - if (is_3d) { - hsize_t nx {static_cast(n_cells[0])}; - hsize_t ny {static_cast(n_cells[1])}; - hsize_t nz {static_cast(n_cells[2])}; + if (is_3d_) { + hsize_t nx {static_cast(n_cells_[0])}; + hsize_t ny {static_cast(n_cells_[1])}; + hsize_t nz {static_cast(n_cells_[2])}; int out[nx*ny*nz]; for (int m = 0; m < nz; m++) { @@ -366,7 +370,7 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const for (int j = 0; j < nx; j++) { int indx1 = nx*ny*m + nx*k + j; int indx2 = nx*ny*m + nx*(ny-k-1) + j; - out[indx2] = global_universes[universes[indx1]]->id; + out[indx2] = universes[universes_[indx1]]->id_; } } } @@ -375,15 +379,15 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const write_int(lat_group, 3, dims, "universes", out, false); } else { - hsize_t nx {static_cast(n_cells[0])}; - hsize_t ny {static_cast(n_cells[1])}; + hsize_t nx {static_cast(n_cells_[0])}; + hsize_t ny {static_cast(n_cells_[1])}; int out[nx*ny]; for (int k = 0; k < ny; k++) { for (int j = 0; j < nx; j++) { int indx1 = nx*k + j; int indx2 = nx*(ny-k-1) + j; - out[indx2] = global_universes[universes[indx1]]->id; + out[indx2] = universes[universes_[indx1]]->id_; } } @@ -399,52 +403,54 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const HexLattice::HexLattice(pugi::xml_node lat_node) : Lattice {lat_node} { + type_ = LatticeType::hex; + // Read the number of lattice cells in each dimension. - n_rings = std::stoi(get_node_value(lat_node, "n_rings")); + n_rings_ = std::stoi(get_node_value(lat_node, "n_rings")); if (check_for_node(lat_node, "n_axial")) { - n_axial = std::stoi(get_node_value(lat_node, "n_axial")); - is_3d = true; + n_axial_ = std::stoi(get_node_value(lat_node, "n_axial")); + is_3d_ = true; } else { - n_axial = 1; - is_3d = false; + n_axial_ = 1; + is_3d_ = false; } // Read the lattice center. std::string center_str {get_node_value(lat_node, "center")}; std::vector center_words {split(center_str)}; - if (is_3d && (center_words.size() != 3)) { + if (is_3d_ && (center_words.size() != 3)) { fatal_error("A hexagonal lattice with must have
" "specified by 3 numbers."); - } else if (!is_3d && center_words.size() != 2) { + } else if (!is_3d_ && center_words.size() != 2) { fatal_error("A hexagonal lattice without must have
" "specified by 2 numbers."); } - center[0] = stod(center_words[0]); - center[1] = stod(center_words[1]); - if (is_3d) {center[2] = stod(center_words[2]);} + center_[0] = stod(center_words[0]); + center_[1] = stod(center_words[1]); + if (is_3d_) {center_[2] = stod(center_words[2]);} // Read the lattice pitches. std::string pitch_str {get_node_value(lat_node, "pitch")}; std::vector pitch_words {split(pitch_str)}; - if (is_3d && (pitch_words.size() != 2)) { + if (is_3d_ && (pitch_words.size() != 2)) { fatal_error("A hexagonal lattice with must have " "specified by 2 numbers."); - } else if (!is_3d && (pitch_words.size() != 1)) { + } else if (!is_3d_ && (pitch_words.size() != 1)) { fatal_error("A hexagonal lattice without must have " "specified by 1 number."); } - pitch[0] = stod(pitch_words[0]); - if (is_3d) {pitch[1] = stod(pitch_words[1]);} + pitch_[0] = stod(pitch_words[0]); + if (is_3d_) {pitch_[1] = stod(pitch_words[1]);} // Read the universes and make sure the correct number was specified. - int n_univ = (3*n_rings*n_rings - 3*n_rings + 1) * n_axial; + int n_univ = (3*n_rings_*n_rings_ - 3*n_rings_ + 1) * n_axial_; std::string univ_str {get_node_value(lat_node, "universes")}; std::vector univ_words {split(univ_str)}; if (univ_words.size() != n_univ) { std::stringstream err_msg; err_msg << "Expected " << n_univ - << " universes for a hexagonal lattice with " << n_rings - << " rings and " << n_axial << " axial levels" << " but " + << " universes for a hexagonal lattice with " << n_rings_ + << " rings and " << n_axial_ << " axial levels" << " but " << univ_words.size() << " were specified."; fatal_error(err_msg); } @@ -458,25 +464,25 @@ HexLattice::HexLattice(pugi::xml_node lat_node) // in a manner that matches the input order. Note that i_x = 0, i_a = 0 // corresponds to the center of the hexagonal lattice. - universes.resize((2*n_rings-1) * (2*n_rings-1) * n_axial, C_NONE); + universes_.resize((2*n_rings_-1) * (2*n_rings_-1) * n_axial_, C_NONE); int input_index = 0; - for (int m = 0; m < n_axial; m++) { + for (int m = 0; m < n_axial_; m++) { // Initialize lattice indecies. int i_x = 1; - int i_a = n_rings - 1; + int i_a = n_rings_ - 1; // Map upper triangular region of hexagonal lattice which is found in the // first n_rings-1 rows of the input. - for (int k = 0; k < n_rings-1; k++) { + for (int k = 0; k < n_rings_-1; k++) { // Walk the index to lower-left neighbor of last row start. i_x -= 1; // Iterate over the input columns. for (int j = 0; j < k+1; j++) { - int indx = (2*n_rings-1)*(2*n_rings-1) * m - + (2*n_rings-1) * (i_a+n_rings-1) - + (i_x+n_rings-1); - universes[indx] = std::stoi(univ_words[input_index]); + int indx = (2*n_rings_-1)*(2*n_rings_-1) * m + + (2*n_rings_-1) * (i_a+n_rings_-1) + + (i_x+n_rings_-1); + universes_[indx] = std::stoi(univ_words[input_index]); input_index++; // Walk the index to the right neighbor (which is not adjacent). i_x += 2; @@ -490,7 +496,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node) // Map the middle square region of the hexagonal lattice which is found in // the next 2*n_rings-1 rows of the input. - for (int k = 0; k < 2*n_rings-1; k++) { + for (int k = 0; k < 2*n_rings_-1; k++) { if ((k % 2) == 0) { // Walk the index to the lower-left neighbor of the last row start. i_x -= 1; @@ -501,11 +507,11 @@ HexLattice::HexLattice(pugi::xml_node lat_node) } // Iterate over the input columns. - for (int j = 0; j < n_rings - (k % 2); j++) { - int indx = (2*n_rings-1)*(2*n_rings-1) * m - + (2*n_rings-1) * (i_a+n_rings-1) - + (i_x+n_rings-1); - universes[indx] = std::stoi(univ_words[input_index]); + for (int j = 0; j < n_rings_ - (k % 2); j++) { + int indx = (2*n_rings_-1)*(2*n_rings_-1) * m + + (2*n_rings_-1) * (i_a+n_rings_-1) + + (i_x+n_rings_-1); + universes_[indx] = std::stoi(univ_words[input_index]); input_index++; // Walk the index to the right neighbor (which is not adjacent). i_x += 2; @@ -513,22 +519,22 @@ HexLattice::HexLattice(pugi::xml_node lat_node) } // Return the lattice index to the start of the current row. - i_x -= 2*(n_rings - (k % 2)); - i_a += n_rings - (k % 2); + i_x -= 2*(n_rings_ - (k % 2)); + i_a += n_rings_ - (k % 2); } // Map the lower triangular region of the hexagonal lattice. - for (int k = 0; k < n_rings-1; k++) { + for (int k = 0; k < n_rings_-1; k++) { // Walk the index to the lower-right neighbor of the last row start. i_x += 1; i_a -= 1; // Iterate over the input columns. - for (int j = 0; j < n_rings-k-1; j++) { - int indx = (2*n_rings-1)*(2*n_rings-1) * m - + (2*n_rings-1) * (i_a+n_rings-1) - + (i_x+n_rings-1); - universes[indx] = std::stoi(univ_words[input_index]); + for (int j = 0; j < n_rings_-k-1; j++) { + int indx = (2*n_rings_-1)*(2*n_rings_-1) * m + + (2*n_rings_-1) * (i_a+n_rings_-1) + + (i_x+n_rings_-1); + universes_[indx] = std::stoi(univ_words[input_index]); input_index++; // Walk the index to the right neighbor (which is not adjacent). i_x += 2; @@ -536,8 +542,8 @@ HexLattice::HexLattice(pugi::xml_node lat_node) } // Return lattice index to start of current row. - i_x -= 2*(n_rings - k - 1); - i_a += n_rings - k - 1; + i_x -= 2*(n_rings_ - k - 1); + i_a += n_rings_ - k - 1; } } } @@ -545,21 +551,21 @@ HexLattice::HexLattice(pugi::xml_node lat_node) //============================================================================== int32_t& -HexLattice::operator[](const int i_xyz[3]) +HexLattice::operator[](std::array i_xyz) { - int indx = (2*n_rings-1)*(2*n_rings-1) * i_xyz[2] - + (2*n_rings-1) * i_xyz[1] + int indx = (2*n_rings_-1)*(2*n_rings_-1) * i_xyz[2] + + (2*n_rings_-1) * i_xyz[1] + i_xyz[0]; - return universes[indx]; + return universes_[indx]; } //============================================================================== LatticeIter HexLattice::begin() -{return LatticeIter(*this, n_rings-1);} +{return LatticeIter(*this, n_rings_-1);} ReverseLatticeIter HexLattice::rbegin() -{return ReverseLatticeIter(*this, universes.size()-n_rings);} +{return ReverseLatticeIter(*this, universes_.size()-n_rings_);} //============================================================================== @@ -567,16 +573,17 @@ bool HexLattice::are_valid_indices(const int i_xyz[3]) const { return ((i_xyz[0] >= 0) && (i_xyz[1] >= 0) && (i_xyz[2] >= 0) - && (i_xyz[0] < 2*n_rings-1) && (i_xyz[1] < 2*n_rings-1) - && (i_xyz[0] + i_xyz[1] > n_rings-2) - && (i_xyz[0] + i_xyz[1] < 3*n_rings-2) - && (i_xyz[2] < n_axial)); + && (i_xyz[0] < 2*n_rings_-1) && (i_xyz[1] < 2*n_rings_-1) + && (i_xyz[0] + i_xyz[1] > n_rings_-2) + && (i_xyz[0] + i_xyz[1] < 3*n_rings_-2) + && (i_xyz[2] < n_axial_)); } //============================================================================== std::pair> -HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const +HexLattice::distance(Position r, Direction u, const std::array& i_xyz) +const { // Compute the direction on the hexagonal basis. double beta_dir = u.x * std::sqrt(3.0) / 2.0 + u.y / 2.0; @@ -591,13 +598,13 @@ HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const // Upper-right and lower-left sides. double d {INFTY}; std::array lattice_trans; - double edge = -copysign(0.5*pitch[0], beta_dir); // Oncoming edge + double edge = -copysign(0.5*pitch_[0], beta_dir); // Oncoming edge Position r_t; if (beta_dir > 0) { - const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1], i_xyz[2]}; + const std::array i_xyz_t {i_xyz[0]+1, i_xyz[1], i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } else { - const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1], i_xyz[2]}; + const std::array i_xyz_t {i_xyz[0]-1, i_xyz[1], i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } double beta = r_t.x * std::sqrt(3.0) / 2.0 + r_t.y / 2.0; @@ -611,12 +618,12 @@ HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const } // Lower-right and upper-left sides. - edge = -copysign(0.5*pitch[0], gamma_dir); + edge = -copysign(0.5*pitch_[0], gamma_dir); if (gamma_dir > 0) { - const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1]-1, i_xyz[2]}; + const std::array i_xyz_t {i_xyz[0]+1, i_xyz[1]-1, i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } else { - const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1]+1, i_xyz[2]}; + const std::array i_xyz_t {i_xyz[0]-1, i_xyz[1]+1, i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } double gamma = r_t.x * std::sqrt(3.0) / 2.0 - r_t.y / 2.0; @@ -633,12 +640,12 @@ HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const } // Upper and lower sides. - edge = -copysign(0.5*pitch[0], u.y); + edge = -copysign(0.5*pitch_[0], u.y); if (u.y > 0) { - const int i_xyz_t[3] {i_xyz[0], i_xyz[1]+1, i_xyz[2]}; + const std::array i_xyz_t {i_xyz[0], i_xyz[1]+1, i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } else { - const int i_xyz_t[3] {i_xyz[0], i_xyz[1]-1, i_xyz[2]}; + const std::array i_xyz_t {i_xyz[0], i_xyz[1]-1, i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } if ((std::abs(r_t.y - edge) > FP_PRECISION) && u.y != 0) { @@ -654,9 +661,9 @@ HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const } // Top and bottom sides - if (is_3d) { + if (is_3d_) { double z = r.z; - double z0 {copysign(0.5 * pitch[1], u.z)}; + double z0 {copysign(0.5 * pitch_[1], u.z)}; if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) { double this_d = (z0 - z) / u.z; if (this_d < d) { @@ -680,13 +687,13 @@ std::array HexLattice::get_indices(Position r) const { // Offset the xyz by the lattice center. - Position r_o {r.x - center.x, r.y - center.y, r.z}; - if (is_3d) {r_o.z -= center.z;} + Position r_o {r.x - center_.x, r.y - center_.y, r.z}; + if (is_3d_) {r_o.z -= center_.z;} // Index the z direction. std::array out; - if (is_3d) { - out[2] = static_cast(std::ceil(r_o.z / pitch[1] + 0.5 * n_axial))-1; + if (is_3d_) { + out[2] = static_cast(std::ceil(r_o.z / pitch_[1] + 0.5 * n_axial_))-1; } else { out[2] = 0; } @@ -695,13 +702,13 @@ HexLattice::get_indices(Position r) const // find the index of the global coordinates to within 4 cells. double alpha = r_o.y - r_o.x / std::sqrt(3.0); out[0] = static_cast(std::floor(r_o.x - / (0.5*std::sqrt(3.0) * pitch[0]))); - out[1] = static_cast(std::floor(alpha / pitch[0])); + / (0.5*std::sqrt(3.0) * pitch_[0]))); + out[1] = static_cast(std::floor(alpha / pitch_[0])); // Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but // the array is offset so that the indices never go below 0). - out[0] += n_rings-1; - out[1] += n_rings-1; + out[0] += n_rings_-1; + out[1] += n_rings_-1; // Calculate the (squared) distance between the particle and the centers of // the four possible cells. Regular hexagonal tiles form a Voronoi @@ -715,7 +722,7 @@ HexLattice::get_indices(Position r) const double d_min {INFTY}; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { - int i_xyz[3] {out[0] + j, out[1] + i, 0}; + const std::array i_xyz {out[0] + j, out[1] + i, 0}; Position r_t = get_local_position(r, i_xyz); double d = r_t.x*r_t.x + r_t.y*r_t.y; if (d < d_min) { @@ -743,15 +750,16 @@ HexLattice::get_indices(Position r) const //============================================================================== Position -HexLattice::get_local_position(Position r, const int i_xyz[3]) const +HexLattice::get_local_position(Position r, const std::array i_xyz) +const { // x_l = x_g - (center + pitch_x*cos(30)*index_x) - r.x -= (center.x + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings + 1) * pitch[0]); + r.x -= center_.x + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings_ + 1) * pitch_[0]; // y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y) - r.y -= (center.y + (i_xyz[1] - n_rings + 1) * pitch[0] - + (i_xyz[0] - n_rings + 1) * pitch[0] / 2.0); - if (is_3d) { - r.z -= center.z - (0.5 * n_axial - i_xyz[2] - 0.5) * pitch[1]; + r.y -= (center_.y + (i_xyz[1] - n_rings_ + 1) * pitch_[0] + + (i_xyz[0] - n_rings_ + 1) * pitch_[0] / 2.0); + if (is_3d_) { + r.z -= center_.z - (0.5 * n_axial_ - i_xyz[2] - 0.5) * pitch_[1]; } return r; @@ -762,9 +770,9 @@ HexLattice::get_local_position(Position r, const int i_xyz[3]) const bool HexLattice::is_valid_index(int indx) const { - int nx {2*n_rings - 1}; - int ny {2*n_rings - 1}; - int nz {n_axial}; + int nx {2*n_rings_ - 1}; + int ny {2*n_rings_ - 1}; + int nz {n_axial_}; int iz = indx / (nx * ny); int iy = (indx - nx*ny*iz) / nx; int ix = indx - nx*ny*iz - nx*iy; @@ -777,10 +785,10 @@ HexLattice::is_valid_index(int indx) const int32_t& HexLattice::offset(int map, const int i_xyz[3]) { - int nx {2*n_rings - 1}; - int ny {2*n_rings - 1}; - int nz {n_axial}; - return offsets[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; + int nx {2*n_rings_ - 1}; + int ny {2*n_rings_ - 1}; + int nz {n_axial_}; + return offsets_[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; } //============================================================================== @@ -788,15 +796,15 @@ HexLattice::offset(int map, const int i_xyz[3]) std::string HexLattice::index_to_string(int indx) const { - int nx {2*n_rings - 1}; - int ny {2*n_rings - 1}; + int nx {2*n_rings_ - 1}; + int ny {2*n_rings_ - 1}; int iz {indx / (nx * ny)}; int iy {(indx - nx * ny * iz) / nx}; int ix {indx - nx * ny * iz - nx * iy}; - std::string out {std::to_string(ix - n_rings + 1)}; + std::string out {std::to_string(ix - n_rings_ + 1)}; out += ','; - out += std::to_string(iy - n_rings + 1); - if (is_3d) { + out += std::to_string(iy - n_rings_ + 1); + if (is_3d_) { out += ','; out += std::to_string(iz); } @@ -810,36 +818,36 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const { // Write basic lattice information. write_string(lat_group, "type", "hexagonal", false); - write_dataset(lat_group, "n_rings", n_rings); - write_dataset(lat_group, "n_axial", n_axial); - if (is_3d) { - write_dataset(lat_group, "pitch", pitch); - write_dataset(lat_group, "center", center); + write_dataset(lat_group, "n_rings", n_rings_); + write_dataset(lat_group, "n_axial", n_axial_); + if (is_3d_) { + write_dataset(lat_group, "pitch", pitch_); + write_dataset(lat_group, "center", center_); } else { - std::array pitch_short {{pitch[0]}}; + std::array pitch_short {{pitch_[0]}}; write_dataset(lat_group, "pitch", pitch_short); - std::array center_short {{center[0], center[1]}}; + std::array center_short {{center_[0], center_[1]}}; write_dataset(lat_group, "center", center_short); } // Write the universe ids. - hsize_t nx {static_cast(2*n_rings - 1)}; - hsize_t ny {static_cast(2*n_rings - 1)}; - hsize_t nz {static_cast(n_axial)}; + hsize_t nx {static_cast(2*n_rings_ - 1)}; + hsize_t ny {static_cast(2*n_rings_ - 1)}; + hsize_t nz {static_cast(n_axial_)}; int out[nx*ny*nz]; for (int m = 0; m < nz; m++) { for (int k = 0; k < ny; k++) { for (int j = 0; j < nx; j++) { int indx = nx*ny*m + nx*k + j; - if (j + k < n_rings - 1) { + if (j + k < n_rings_ - 1) { // This array position is never used; put a -1 to indicate this. out[indx] = -1; - } else if (j + k > 3*n_rings - 3) { + } else if (j + k > 3*n_rings_ - 3) { // This array position is never used; put a -1 to indicate this. out[indx] = -1; } else { - out[indx] = global_universes[universes[indx]]->id; + out[indx] = universes[universes_[indx]]->id_; } } } @@ -857,15 +865,15 @@ extern "C" void read_lattices(pugi::xml_node *node) { for (pugi::xml_node lat_node : node->children("lattice")) { - lattices_c.push_back(new RectLattice(lat_node)); + lattices.push_back(new RectLattice(lat_node)); } for (pugi::xml_node lat_node : node->children("hex_lattice")) { - lattices_c.push_back(new HexLattice(lat_node)); + lattices.push_back(new HexLattice(lat_node)); } // Fill the lattice map. - for (int i_lat = 0; i_lat < lattices_c.size(); i_lat++) { - int id = lattices_c[i_lat]->id; + for (int i_lat = 0; i_lat < lattices.size(); i_lat++) { + int id = lattices[i_lat]->id_; auto in_map = lattice_map.find(id); if (in_map == lattice_map.end()) { lattice_map[id] = i_lat; @@ -882,53 +890,15 @@ read_lattices(pugi::xml_node *node) //============================================================================== extern "C" { - Lattice* lattice_pointer(int lat_ind) {return lattices_c[lat_ind];} + Lattice* lattice_pointer(int lat_ind) {return lattices[lat_ind];} - int32_t lattice_id(Lattice *lat) {return lat->id;} + int32_t lattice_id(Lattice *lat) {return lat->id_;} bool lattice_are_valid_indices(Lattice *lat, const int i_xyz[3]) {return lat->are_valid_indices(i_xyz);} - void lattice_distance(Lattice *lat, const double xyz[3], const double uvw[3], - const int i_xyz[3], double *d, int lattice_trans[3]) - { - Position r {xyz}; - Direction u {uvw}; - std::pair> ld {lat->distance(r, u, i_xyz)}; - *d = ld.first; - lattice_trans[0] = ld.second[0]; - lattice_trans[1] = ld.second[1]; - lattice_trans[2] = ld.second[2]; - } - - void lattice_get_indices(Lattice *lat, const double xyz[3], int i_xyz[3]) - { - Position r {xyz}; - std::array inds = lat->get_indices(r); - i_xyz[0] = inds[0]; - i_xyz[1] = inds[1]; - i_xyz[2] = inds[2]; - } - - void lattice_get_local_xyz(Lattice *lat, const double global_xyz[3], - const int i_xyz[3], double local_xyz[3]) - { - Position global {global_xyz}; - Position local = lat->get_local_position(global, i_xyz); - local_xyz[0] = local.x; - local_xyz[1] = local.y; - local_xyz[2] = local.z; - } - int32_t lattice_offset(Lattice *lat, int map, const int i_xyz[3]) {return lat->offset(map, i_xyz);} - - int32_t lattice_outer(Lattice *lat) {return lat->outer;} - - void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} - - int32_t lattice_universe(Lattice *lat, const int i_xyz[3]) - {return (*lat)[i_xyz];} } } // namespace openmc diff --git a/src/material.cpp b/src/material.cpp index ec930f2082..1a0c2d2078 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -13,7 +13,7 @@ namespace openmc { // Global variables //============================================================================== -std::vector global_materials; +std::vector materials; std::unordered_map material_map; //============================================================================== @@ -28,6 +28,10 @@ Material::Material(pugi::xml_node node) fatal_error("Must specify id of material in materials XML file."); } + if (check_for_node(node, "temperature")) { + temperature_ = std::stod(get_node_value(node, "temperature")); + } + if (check_for_node(node, "volume")) { volume_ = std::stod(get_node_value(node, "volume")); } @@ -42,13 +46,13 @@ read_materials(pugi::xml_node* node) { // Loop over XML material elements and populate the array. for (pugi::xml_node material_node : node->children("material")) { - global_materials.push_back(new Material(material_node)); + materials.push_back(new Material(material_node)); } - global_materials.shrink_to_fit(); + materials.shrink_to_fit(); // Populate the material map. - for (int i = 0; i < global_materials.size(); i++) { - int32_t mid = global_materials[i]->id; + for (int i = 0; i < materials.size(); i++) { + int32_t mid = materials[i]->id; auto search = material_map.find(mid); if (search == material_map.end()) { material_map[mid] = i; @@ -67,8 +71,8 @@ read_materials(pugi::xml_node* node) extern "C" int openmc_material_get_volume(int32_t index, double* volume) { - if (index >= 1 && index <= global_materials.size()) { - Material* m = global_materials[index - 1]; + if (index >= 1 && index <= materials.size()) { + Material* m = materials[index - 1]; if (m->volume_ >= 0.0) { *volume = m->volume_; return 0; @@ -87,8 +91,8 @@ openmc_material_get_volume(int32_t index, double* volume) extern "C" int openmc_material_set_volume(int32_t index, double volume) { - if (index >= 1 && index <= global_materials.size()) { - Material* m = global_materials[index - 1]; + if (index >= 1 && index <= materials.size()) { + Material* m = materials[index - 1]; if (volume >= 0.0) { m->volume_ = volume; return 0; @@ -107,7 +111,7 @@ openmc_material_set_volume(int32_t index, double volume) //============================================================================== extern "C" { - Material* material_pointer(int32_t indx) {return global_materials[indx];} + Material* material_pointer(int32_t indx) {return materials[indx];} int32_t material_id(Material* mat) {return mat->id;} @@ -120,16 +124,16 @@ extern "C" { void extend_materials_c(int32_t n) { - global_materials.reserve(global_materials.size() + n); + materials.reserve(materials.size() + n); for (int32_t i = 0; i < n; i++) { - global_materials.push_back(new Material()); + materials.push_back(new Material()); } } void free_memory_material_c() { - for (Material *mat : global_materials) {delete mat;} - global_materials.clear(); + for (Material *mat : materials) {delete mat;} + materials.clear(); material_map.clear(); } } diff --git a/src/mgxs_data.F90 b/src/mgxs_data.F90 index b95c3a877a..03a0e9402f 100644 --- a/src/mgxs_data.F90 +++ b/src/mgxs_data.F90 @@ -160,10 +160,10 @@ contains if (cells(i) % material(j) == MATERIAL_VOID) cycle ! Get temperature of cell (rounding to nearest integer) - if (size(cells(i) % sqrtkT) > 1) then - kT = cells(i) % sqrtkT(j)**2 + if (cells(i) % sqrtkT_size() > 1) then + kT = cells(i) % sqrtkT(j-1)**2 else - kT = cells(i) % sqrtkT(1)**2 + kT = cells(i) % sqrtkT(0)**2 end if i_material = cells(i) % material(j) diff --git a/src/multipole_header.F90 b/src/multipole_header.F90 index f2d0c91062..05eb62e1aa 100644 --- a/src/multipole_header.F90 +++ b/src/multipole_header.F90 @@ -10,27 +10,16 @@ module multipole_header !======================================================================== ! Multipole related constants - ! Formalisms - integer, parameter :: FORM_MLBW = 2, & - FORM_RM = 3, & - FORM_RML = 7 - ! Constants that determine which value to access integer, parameter :: MP_EA = 1 ! Pole - ! Reich-Moore indices - integer, parameter :: RM_RT = 2, & ! Residue total - RM_RA = 3, & ! Residue absorption - RM_RF = 4 ! Residue fission - - ! Multi-level Breit Wigner indices - integer, parameter :: MLBW_RT = 2, & ! Residue total - MLBW_RX = 3, & ! Residue compettitive - MLBW_RA = 4, & ! Residue absorption - MLBW_RF = 5 ! Residue fission + ! Residue indices + integer, parameter :: MP_RS = 2, & ! Residue scattering + MP_RA = 3, & ! Residue absorption + MP_RF = 4 ! Residue fission ! Polynomial fit indices - integer, parameter :: FIT_T = 1, & ! Total + integer, parameter :: FIT_S = 1, & ! Scattering FIT_A = 2, & ! Absorption FIT_F = 3 ! Fission @@ -46,33 +35,22 @@ module multipole_header ! Isotope Properties logical :: fissionable ! Is this isotope fissionable? - integer, allocatable :: l_value(:) ! The l index of the pole - integer :: num_l ! Number of unique l values - real(8), allocatable :: pseudo_k0RS(:) ! The value (sqrt(2*mass neutron - ! /reduced planck constant) - ! * AWR/(AWR + 1) - ! * scattering radius for - ! each l complex(8), allocatable :: data(:,:) ! Poles and residues real(8) :: sqrtAWR ! Square root of the atomic ! weight ratio - integer :: formalism ! R-matrix formalism !========================================================================= ! Windows integer :: fit_order ! Order of the fit. 1 linear, ! 2 quadratic, etc. - real(8) :: start_E ! Start energy for the windows - real(8) :: end_E ! End energy for the windows + real(8) :: E_min ! Start energy for the windows + real(8) :: E_max ! End energy for the windows real(8) :: spacing ! The actual spacing in sqrt(E) ! space. - ! spacing = sqrt(multipole_w % endE - multipole_w % startE) - ! / multipole_w % windows - integer, allocatable :: w_start(:) ! Contains the index of the pole at - ! the start of the window - integer, allocatable :: w_end(:) ! Contains the index of the pole at - ! the end of the window + integer, allocatable :: windows(:, :) ! Contains the indexes of the poles + ! at the start and end of the + ! window real(8), allocatable :: curvefit(:,:,:) ! Contains the fitting function. ! (reaction type, coeff index, ! window index) @@ -96,7 +74,7 @@ contains character(len=*), intent(in) :: filename character(len=10) :: version - integer :: i, n_poles, n_residue_types, n_windows + integer :: i, n_poles, n_residues, n_windows integer(HSIZE_T) :: dims_1d(1), dims_2d(2), dims_3d(3) integer(HID_T) :: file_id integer(HID_T) :: group_id @@ -114,65 +92,49 @@ contains // trim(filename) // " uses version " // trim(version) // ".") ! Read scalar values. - call read_dataset(this % formalism, group_id, "formalism") call read_dataset(this % spacing, group_id, "spacing") call read_dataset(this % sqrtAWR, group_id, "sqrtAWR") - call read_dataset(this % start_E, group_id, "start_E") - call read_dataset(this % end_E, group_id, "end_E") + call read_dataset(this % E_min, group_id, "E_min") + call read_dataset(this % E_max, group_id, "E_max") ! Read the "data" array. Use its shape to figure out the number of poles ! and residue types in this data. dset = open_dataset(group_id, "data") call get_shape(dset, dims_2d) - n_residue_types = int(dims_2d(1), 4) - 1 + n_residues = int(dims_2d(1), 4) - 1 n_poles = int(dims_2d(2), 4) - allocate(this % data(n_residue_types+1, n_poles)) - call read_dataset(this % data, dset) + allocate(this % data(n_residues+1, n_poles)) + if (n_poles > 0) call read_dataset(this % data, dset) call close_dataset(dset) ! Check to see if this data includes fission residues. - if (this % formalism == FORM_RM) then - this % fissionable = (n_residue_types == 3) - else - ! Assume FORM_MLBW. - this % fissionable = (n_residue_types == 4) - end if + this % fissionable = (n_residues == 3) - ! Read the "l_value" array. - allocate(this % l_value(n_poles)) - call read_dataset(this % l_value, group_id, "l_value") - - ! Figure out the number of unique l values in the l_value array. - do i = 1, n_poles - if (.not. l_val_dict % has(this % l_value(i))) then - call l_val_dict % set(this % l_value(i), 0) - end if - end do - this % num_l = l_val_dict % size() - call l_val_dict % clear() - - ! Read the "pseudo_K0RS" array. - allocate(this % pseudo_k0RS(this % num_l)) - call read_dataset(this % pseudo_k0RS, group_id, "pseudo_K0RS") - - ! Read the "w_start" array and use its shape to figure out the number of + ! Read the "windows" array and use its shape to figure out the number of ! windows. - dset = open_dataset(group_id, "w_start") - call get_shape(dset, dims_1d) - n_windows = int(dims_1d(1), 4) - allocate(this % w_start(n_windows)) - call read_dataset(this % w_start, dset) + dset = open_dataset(group_id, "windows") + call get_shape(dset, dims_2d) + n_windows = int(dims_2d(2), 4) + allocate(this % windows(dims_2d(1), n_windows)) + call read_dataset(this % windows, dset) call close_dataset(dset) - ! Read the "w_end" and "broaden_poly" arrays. - allocate(this % w_end(n_windows)) - call read_dataset(this % w_end, group_id, "w_end") + ! Read the "broaden_poly" arrays. + dset = open_dataset(group_id, "broaden_poly") + call get_shape(dset, dims_1d) + if (dims_1d(1) /= n_windows) call fatal_error("broaden_poly array shape is& + ¬ consistent with the windows array shape in multipole library"& + // trim(filename) // ".") allocate(this % broaden_poly(n_windows)) - call read_dataset(this % broaden_poly, group_id, "broaden_poly") + call read_dataset(this % broaden_poly, dset) + call close_dataset(dset) ! Read the "curvefit" array. dset = open_dataset(group_id, "curvefit") call get_shape(dset, dims_3d) + if (dims_3d(3) /= n_windows) call fatal_error("curvefit array shape is not& + &consistent with the windows array shape in multipole library"& + // trim(filename) // ".") allocate(this % curvefit(dims_3d(1), dims_3d(2), dims_3d(3))) call read_dataset(this % curvefit, dset) call close_dataset(dset) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 0411ab102c..481419fdc3 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -12,11 +12,9 @@ module nuclide_header use hdf5_interface use math, only: faddeeva, w_derivative, & broaden_wmp_polynomials - use multipole_header, only: FORM_RM, FORM_MLBW, MP_EA, RM_RT, RM_RA, & - RM_RF, MLBW_RT, MLBW_RX, MLBW_RA, MLBW_RF, & - FIT_T, FIT_A, FIT_F, MultipoleArray + use multipole_header, only: MP_EA, MP_RS, MP_RA, MP_RF, & + FIT_S, FIT_A, FIT_F, MultipoleArray use message_passing - use multipole_header, only: MultipoleArray use random_lcg, only: prn, future_prn, prn_set_stream use reaction_header, only: Reaction use sab_header, only: SAlphaBeta, sab_tables @@ -849,7 +847,7 @@ contains integer :: threshold ! threshold energy index real(8) :: f ! interp factor on nuclide energy grid real(8) :: kT ! temperature in eV - real(8) :: sig_t, sig_a, sig_f ! Intermediate multipole variables + real(8) :: sig_s, sig_a, sig_f ! Intermediate multipole variables ! Initialize cached cross sections to zero micro_xs % elastic = CACHE_INVALID @@ -859,8 +857,8 @@ contains ! Check to see if there is multipole data present at this energy use_mp = .false. if (this % mp_present) then - if (E >= this % multipole % start_E .and. & - E <= this % multipole % end_E) then + if (E >= this % multipole % E_min .and. & + E <= this % multipole % E_max) then use_mp = .true. end if end if @@ -868,9 +866,10 @@ contains ! Evaluate multipole or interpolate if (use_mp) then ! Call multipole kernel - call multipole_eval(this % multipole, E, sqrtkT, sig_t, sig_a, sig_f) + call multipole_eval(this % multipole, E, sqrtkT, sig_s, sig_a, sig_f) - micro_xs % total = sig_t + micro_xs % total = sig_s + sig_a + micro_xs % elastic = sig_s micro_xs % absorption = sig_a micro_xs % fission = sig_f @@ -1076,9 +1075,6 @@ contains micro_xs % elastic = (ONE - f) * rx % xs(i_temp, i_grid) + & f * rx % xs(i_temp, i_grid + 1) end associate - else - ! For multipole, elastic is total - absorption - micro_xs % elastic = micro_xs % total - micro_xs % absorption end if end subroutine nuclide_calculate_elastic_xs @@ -1130,7 +1126,7 @@ contains ! sections in the resolved resonance regions !=============================================================================== - subroutine multipole_eval(multipole, E, sqrtkT, sig_t, sig_a, sig_f) + subroutine multipole_eval(multipole, E, sqrtkT, sig_s, sig_a, sig_f) type(MultipoleArray), intent(in) :: multipole ! The windowed multipole ! object to process. real(8), intent(in) :: E ! The energy at which to @@ -1138,7 +1134,7 @@ contains real(8), intent(in) :: sqrtkT ! The temperature in the form ! sqrt(kT), at which ! to evaluate the XS. - real(8), intent(out) :: sig_t ! Total cross section + real(8), intent(out) :: sig_s ! Scattering cross section real(8), intent(out) :: sig_a ! Absorption cross section real(8), intent(out) :: sig_f ! Fission cross section complex(8) :: psi_chi ! The value of the psi-chi function for the @@ -1146,7 +1142,6 @@ contains complex(8) :: c_temp ! complex temporary variable complex(8) :: w_val ! The faddeeva function evaluated at Z complex(8) :: Z ! sqrt(atomic weight ratio / kT) * (sqrt(E) - pole) - complex(8) :: sig_t_factor(multipole % num_l) real(8) :: broadened_polynomials(multipole % fit_order + 1) real(8) :: sqrtE ! sqrt(E), eV real(8) :: invE ! 1/E, eV @@ -1166,18 +1161,13 @@ contains invE = ONE / E ! Locate us. - i_window = floor((sqrtE - sqrt(multipole % start_E)) / multipole % spacing & + i_window = floor((sqrtE - sqrt(multipole % E_min)) / multipole % spacing & + ONE) - startw = multipole % w_start(i_window) - endw = multipole % w_end(i_window) - - ! Fill in factors. - if (startw <= endw) then - call compute_sig_t_factor(multipole, sqrtE, sig_t_factor) - end if + startw = multipole % windows(1, i_window) + endw = multipole % windows(2, i_window) ! Initialize the ouptut cross sections. - sig_t = ZERO + sig_s = ZERO sig_a = ZERO sig_f = ZERO @@ -1190,7 +1180,7 @@ contains call broaden_wmp_polynomials(E, dopp, multipole % fit_order + 1, & broadened_polynomials) do i_poly = 1, multipole % fit_order+1 - sig_t = sig_t + multipole % curvefit(FIT_T, i_poly, i_window) & + sig_s = sig_s + multipole % curvefit(FIT_S, i_poly, i_window) & * broadened_polynomials(i_poly) sig_a = sig_a + multipole % curvefit(FIT_A, i_poly, i_window) & * broadened_polynomials(i_poly) @@ -1202,7 +1192,7 @@ contains else ! Evaluate as if it were a polynomial temp = invE do i_poly = 1, multipole % fit_order+1 - sig_t = sig_t + multipole % curvefit(FIT_T, i_poly, i_window) * temp + sig_s = sig_s + multipole % curvefit(FIT_S, i_poly, i_window) * temp sig_a = sig_a + multipole % curvefit(FIT_A, i_poly, i_window) * temp if (multipole % fissionable) then sig_f = sig_f + multipole % curvefit(FIT_F, i_poly, i_window) * temp @@ -1219,21 +1209,10 @@ contains do i_pole = startw, endw psi_chi = -ONEI / (multipole % data(MP_EA, i_pole) - sqrtE) c_temp = psi_chi / E - if (multipole % formalism == FORM_MLBW) then - sig_t = sig_t + real(multipole % data(MLBW_RT, i_pole) * c_temp * & - sig_t_factor(multipole % l_value(i_pole))) & - + real(multipole % data(MLBW_RX, i_pole) * c_temp) - sig_a = sig_a + real(multipole % data(MLBW_RA, i_pole) * c_temp) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(MLBW_RF, i_pole) * c_temp) - end if - else if (multipole % formalism == FORM_RM) then - sig_t = sig_t + real(multipole % data(RM_RT, i_pole) * c_temp * & - sig_t_factor(multipole % l_value(i_pole))) - sig_a = sig_a + real(multipole % data(RM_RA, i_pole) * c_temp) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(RM_RF, i_pole) * c_temp) - end if + sig_s = sig_s + real(multipole % data(MP_RS, i_pole) * c_temp) + sig_a = sig_a + real(multipole % data(MP_RA, i_pole) * c_temp) + if (multipole % fissionable) then + sig_f = sig_f + real(multipole % data(MP_RF, i_pole) * c_temp) end if end do else @@ -1243,21 +1222,10 @@ contains do i_pole = startw, endw Z = (sqrtE - multipole % data(MP_EA, i_pole)) * dopp w_val = faddeeva(Z) * dopp * invE * SQRT_PI - if (multipole % formalism == FORM_MLBW) then - sig_t = sig_t + real((multipole % data(MLBW_RT, i_pole) * & - sig_t_factor(multipole % l_value(i_pole)) + & - multipole % data(MLBW_RX, i_pole)) * w_val) - sig_a = sig_a + real(multipole % data(MLBW_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(MLBW_RF, i_pole) * w_val) - end if - else if (multipole % formalism == FORM_RM) then - sig_t = sig_t + real(multipole % data(RM_RT, i_pole) * w_val * & - sig_t_factor(multipole % l_value(i_pole))) - sig_a = sig_a + real(multipole % data(RM_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(RM_RF, i_pole) * w_val) - end if + sig_s = sig_s + real(multipole % data(MP_RS, i_pole) * w_val) + sig_a = sig_a + real(multipole % data(MP_RA, i_pole) * w_val) + if (multipole % fissionable) then + sig_f = sig_f + real(multipole % data(MP_RF, i_pole) * w_val) end if end do end if @@ -1270,7 +1238,7 @@ contains ! temperature. !=============================================================================== - subroutine multipole_deriv_eval(multipole, E, sqrtkT, sig_t, sig_a, sig_f) + subroutine multipole_deriv_eval(multipole, E, sqrtkT, sig_s, sig_a, sig_f) type(MultipoleArray), intent(in) :: multipole ! The windowed multipole ! object to process. real(8), intent(in) :: E ! The energy at which to @@ -1278,12 +1246,11 @@ contains real(8), intent(in) :: sqrtkT ! The temperature in the form ! sqrt(kT), at which to ! evaluate the XS. - real(8), intent(out) :: sig_t ! Total cross section + real(8), intent(out) :: sig_s ! Scattering cross section real(8), intent(out) :: sig_a ! Absorption cross section real(8), intent(out) :: sig_f ! Fission cross section complex(8) :: w_val ! The faddeeva function evaluated at Z complex(8) :: Z ! sqrt(atomic weight ratio / kT) * (sqrt(E) - pole) - complex(8) :: sig_t_factor(multipole % num_l) real(8) :: sqrtE ! sqrt(E), eV real(8) :: invE ! 1/E, eV real(8) :: dopp ! sqrt(atomic weight ratio / kT) @@ -1305,18 +1272,13 @@ contains &derivatives are not implemented for 0 Kelvin cross sections.") ! Locate us - i_window = floor((sqrtE - sqrt(multipole % start_E)) / multipole % spacing & + i_window = floor((sqrtE - sqrt(multipole % E_min)) / multipole % spacing & + ONE) - startw = multipole % w_start(i_window) - endw = multipole % w_end(i_window) - - ! Fill in factors. - if (startw <= endw) then - call compute_sig_t_factor(multipole, sqrtE, sig_t_factor) - end if + startw = multipole % windows(1, i_window) + endw = multipole % windows(2, i_window) ! Initialize the ouptut cross sections. - sig_t = ZERO + sig_s = ZERO sig_a = ZERO sig_f = ZERO @@ -1333,61 +1295,18 @@ contains do i_pole = startw, endw Z = (sqrtE - multipole % data(MP_EA, i_pole)) * dopp w_val = -invE * SQRT_PI * HALF * w_derivative(Z, 2) - if (multipole % formalism == FORM_MLBW) then - sig_t = sig_t + real((multipole % data(MLBW_RT, i_pole) * & - sig_t_factor(multipole%l_value(i_pole)) + & - multipole % data(MLBW_RX, i_pole)) * w_val) - sig_a = sig_a + real(multipole % data(MLBW_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(MLBW_RF, i_pole) * w_val) - end if - else if (multipole % formalism == FORM_RM) then - sig_t = sig_t + real(multipole % data(RM_RT, i_pole) * w_val * & - sig_t_factor(multipole % l_value(i_pole))) - sig_a = sig_a + real(multipole % data(RM_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(RM_RF, i_pole) * w_val) - end if + sig_s = sig_s + real(multipole % data(MP_RS, i_pole) * w_val) + sig_a = sig_a + real(multipole % data(MP_RA, i_pole) * w_val) + if (multipole % fissionable) then + sig_f = sig_f + real(multipole % data(MP_RF, i_pole) * w_val) end if end do - sig_t = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_t + sig_s = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_s sig_a = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_a sig_f = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_f end if end subroutine multipole_deriv_eval -!=============================================================================== -! COMPUTE_SIG_T_FACTOR calculates the sig_t_factor, a factor inside of the sig_t -! equation not present in the sig_a and sig_f equations. -!=============================================================================== - - subroutine compute_sig_t_factor(multipole, sqrtE, sig_t_factor) - type(MultipoleArray), intent(in) :: multipole - real(8), intent(in) :: sqrtE - complex(8), intent(out) :: sig_t_factor(multipole % num_l) - - integer :: iL - real(8) :: twophi(multipole % num_l) - real(8) :: arg - - do iL = 1, multipole % num_l - twophi(iL) = multipole % pseudo_k0RS(iL) * sqrtE - if (iL == 2) then - twophi(iL) = twophi(iL) - atan(twophi(iL)) - else if (iL == 3) then - arg = 3.0_8 * twophi(iL) / (3.0_8 - twophi(iL)**2) - twophi(iL) = twophi(iL) - atan(arg) - else if (iL == 4) then - arg = twophi(iL) * (15.0_8 - twophi(iL)**2) & - / (15.0_8 - 6.0_8 * twophi(iL)**2) - twophi(iL) = twophi(iL) - atan(arg) - end if - end do - - twophi = 2.0_8 * twophi - sig_t_factor = cmplx(cos(twophi), -sin(twophi), KIND=8) - end subroutine compute_sig_t_factor - !=============================================================================== ! 0K_ELASTIC_XS determines the microscopic 0K elastic cross section ! for a given nuclide at the trial relative energy used in resonance scattering diff --git a/src/output.F90 b/src/output.F90 index 56d1ecfcc2..b4ecf497ea 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -211,7 +211,6 @@ contains integer :: i ! index for coordinate levels type(Cell), pointer :: c - type(Universe), pointer :: u class(Lattice), pointer :: l ! display type of particle @@ -232,20 +231,20 @@ contains write(ou,*) ' Level ' // trim(to_str(i - 1)) ! Print cell for this level - if (p % coord(i) % cell /= NONE) then - c => cells(p % coord(i) % cell) + if (p % coord(i) % cell /= C_NONE) then + c => cells(p % coord(i) % cell + 1) write(ou,*) ' Cell = ' // trim(to_str(c % id())) end if ! Print universe for this level if (p % coord(i) % universe /= NONE) then - u => universes(p % coord(i) % universe) - write(ou,*) ' Universe = ' // trim(to_str(u % id)) + write(ou,*) ' Universe = ' & + // trim(to_str(universe_id(p % coord(i) % universe))) end if ! Print information on lattice if (p % coord(i) % lattice /= NONE) then - l => lattices(p % coord(i) % lattice) % obj + l => lattices(p % coord(i) % lattice) write(ou,*) ' Lattice = ' // trim(to_str(l % id())) write(ou,*) ' Lattice position = (' // trim(to_str(& p % coord(i) % lattice_x)) // ',' // trim(to_str(& @@ -622,42 +621,6 @@ contains end subroutine print_results -!=============================================================================== -! PRINT_OVERLAP_DEBUG displays information regarding overlap checking results -!=============================================================================== - - subroutine print_overlap_check - - integer :: i, j - integer :: num_sparse = 0 - - ! display header block for geometry debugging section - call header("Cell Overlap Check Summary", 1) - - write(ou,100) 'Cell ID','No. Overlap Checks' - - do i = 1, n_cells - write(ou,101) cells(i) % id(), overlap_check_cnt(i) - if (overlap_check_cnt(i) < 10) num_sparse = num_sparse + 1 - end do - write(ou,*) - write(ou,'(1X,A)') 'There were ' // trim(to_str(num_sparse)) // & - ' cells with less than 10 overlap checks' - j = 0 - do i = 1, n_cells - if (overlap_check_cnt(i) < 10) then - j = j + 1 - write(ou,'(1X,A8)', advance='no') trim(to_str(cells(i) % id())) - if (modulo(j,8) == 0) write(ou,*) - end if - end do - write(ou,*) - -100 format (1X,A,T15,A) -101 format (1X,I8,T15,I12) - - end subroutine print_overlap_check - !=============================================================================== ! WRITE_TALLIES creates an output file and writes out the mean values of all ! tallies and their standard deviations diff --git a/src/output.cpp b/src/output.cpp new file mode 100644 index 0000000000..fba6e5af7b --- /dev/null +++ b/src/output.cpp @@ -0,0 +1,75 @@ +#include "openmc/output.h" + +#include // for std::transform +#include // for strlen +#include // for setw +#include +#include + +#include "openmc/cell.h" +#include "openmc/geometry.h" +#include "openmc/message_passing.h" +#include "openmc/capi.h" +#include "openmc/settings.h" + + +namespace openmc { + +void +header(const char* msg, int level) { + // Determine how many times to repeat the '=' character. + int n_prefix = (63 - strlen(msg)) / 2; + int n_suffix = n_prefix; + if ((strlen(msg) % 2) == 0) ++n_suffix; + + // Convert to uppercase. + std::string upper(msg); + std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper); + + // Add ===> <=== markers. + std::stringstream out; + out << ' '; + for (int i = 0; i < n_prefix; i++) out << '='; + out << "> " << upper << " <"; + for (int i = 0; i < n_suffix; i++) out << '='; + + // Print header based on verbosity level. + if (settings::verbosity >= level) { + std::cout << out.str() << std::endl << std::endl; + } +} + +//============================================================================== + +extern "C" void +print_overlap_check() { +#ifdef OPENMC_MPI + std::vector temp(overlap_check_count); + int err = MPI_Reduce(temp.data(), overlap_check_count.data(), + overlap_check_count.size(), MPI_INT64_T, MPI_SUM, 0, + mpi::intracomm); +#endif + + if (openmc_master) { + header("cell overlap check summary", 1); + std::cout << " Cell ID No. Overlap Checks\n"; + + std::vector sparse_cell_ids; + for (int i = 0; i < n_cells; i++) { + std::cout << " " << std::setw(8) << cells[i]->id_ << std::setw(17) + << overlap_check_count[i] << "\n"; + if (overlap_check_count[i] < 10) { + sparse_cell_ids.push_back(cells[i]->id_); + } + } + + std::cout << "\n There were " << sparse_cell_ids.size() + << " cells with less than 10 overlap checks\n"; + for (auto id : sparse_cell_ids) { + std::cout << " " << id; + } + std::cout << "\n"; + } +} + +} // namespace openmc diff --git a/src/particle.cpp b/src/particle.cpp index 01fea65ffe..729a860eb6 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -19,8 +19,8 @@ namespace openmc { void LocalCoord::reset() { - cell = 0; - universe = 0; + cell = C_NONE; + universe = C_NONE; lattice = 0; lattice_x = 0; lattice_y = 0; @@ -67,7 +67,7 @@ Particle::initialize() // clear attributes surface = 0; - cell_born = 0; + cell_born = C_NONE; material = 0; last_material = 0; last_sqrtkT = 0; diff --git a/src/physics.F90 b/src/physics.F90 index 70c922b420..992e487675 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -526,8 +526,8 @@ contains ! Check to see if we are in a windowed multipole range. WMP only supports ! the first fission reaction. if (nuc % mp_present) then - if (E >= nuc % multipole % start_E .and. & - E <= nuc % multipole % end_E) then + if (E >= nuc % multipole % E_min .and. & + E <= nuc % multipole % E_max) then i_reaction = nuc % index_fission(1) return end if diff --git a/src/plot.F90 b/src/plot.F90 index 2fd898206c..517c27fa17 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -84,7 +84,7 @@ contains else if (pl % color_by == PLOT_COLOR_MATS) then ! Assign color based on material - associate (c => cells(p % coord(j) % cell)) + associate (c => cells(p % coord(j) % cell + 1)) if (c % type() == FILL_UNIVERSE) then ! If we stopped on a middle universe level, treat as if not found rgb = pl % not_found % rgb @@ -100,8 +100,8 @@ contains end associate else if (pl % color_by == PLOT_COLOR_CELLS) then ! Assign color based on cell - rgb = pl % colors(p % coord(j) % cell) % rgb - id = cells(p % coord(j) % cell) % id() + rgb = pl % colors(p % coord(j) % cell + 1) % rgb + id = cells(p % coord(j) % cell + 1) % id() else rgb = 0 id = -1 diff --git a/src/simulation.F90 b/src/simulation.F90 index d5ca165b57..d334b46b8c 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -24,7 +24,7 @@ module simulation use nuclide_header, only: micro_xs, n_nuclides use output, only: header, print_columns, & print_batch_keff, print_generation, print_runtime, & - print_results, print_overlap_check, write_tallies + print_results, write_tallies use particle_header use photon_header, only: micro_photon_xs, n_elements use random_lcg, only: set_particle_seed @@ -523,7 +523,6 @@ contains integer :: n ! size of arrays integer :: mpi_err ! MPI error code integer :: count_per_filter ! number of result values for one filter bin - integer(8) :: temp real(8) :: tempr(3) ! temporary array for communication #ifdef OPENMC_MPIF08 type(MPI_Datatype) :: result_block @@ -532,6 +531,11 @@ contains #endif #endif + interface + subroutine print_overlap_check() bind(C) + end subroutine print_overlap_check + end interface + err = 0 ! Skip if simulation was never run @@ -584,12 +588,6 @@ contains k_col_abs = tempr(1) k_col_tra = tempr(2) k_abs_tra = tempr(3) - - if (check_overlaps) then - call MPI_REDUCE(overlap_check_cnt, temp, n_cells, MPI_INTEGER8, & - MPI_SUM, 0, mpi_intracomm, mpi_err) - overlap_check_cnt = temp - end if #endif ! Write tally results to tallies.out @@ -608,8 +606,8 @@ contains if (master) then if (verbosity >= 6) call print_runtime() if (verbosity >= 4) call print_results() - if (check_overlaps) call print_overlap_check() end if + if (check_overlaps) call print_overlap_check() ! Reset flags need_depletion_rx = .false. diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index d7040b8d58..0e38b76f25 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -67,10 +67,7 @@ module simulation_header integer :: restart_batch - ! Flag for enabling cell overlap checking during transport - integer(8), allocatable :: overlap_check_cnt(:) - - logical :: trace + logical(C_BOOL), bind(C, name='openmc_trace') :: trace !$omp threadprivate(trace, thread_id, current_work) @@ -90,7 +87,6 @@ contains !=============================================================================== subroutine free_memory_simulation() - if (allocated(overlap_check_cnt)) deallocate(overlap_check_cnt) if (allocated(entropy_p)) deallocate(entropy_p) if (allocated(source_frac)) deallocate(source_frac) if (allocated(work_index)) deallocate(work_index) diff --git a/src/source.cpp b/src/source.cpp index 23939d796b..666bbb7838 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -167,9 +167,9 @@ Bank SourceDistribution::sample() const if (space_box) { if (space_box->only_fissionable()) { // Determine material - auto c = global_cells[cell_index - 1]; - int32_t mat_index = c->material[instance]; - auto m = global_materials[mat_index]; + auto c = cells[cell_index - 1]; + int32_t mat_index = c->material_[instance]; + auto m = materials[mat_index]; if (mat_index == MATERIAL_VOID) { found = false; diff --git a/src/string.F90 b/src/string.F90 index 71d9d6e966..90b51d7aa0 100644 --- a/src/string.F90 +++ b/src/string.F90 @@ -64,98 +64,6 @@ contains end subroutine split_string -!=============================================================================== -! TOKENIZE takes a string that includes logical expressions for a list of -! bounding surfaces in a cell and splits it into separate tokens. The characters -! (, ), |, and ~ count as separate tokens since they represent operators. -!=============================================================================== - - subroutine tokenize(string, tokens) - character(*), intent(in) :: string - type(VectorInt), intent(inout) :: tokens - - integer :: i ! current index - integer :: i_start ! starting index of word - integer :: token - character(len=len_trim(string)) :: string_ - - ! Remove leading blanks - string_ = adjustl(string) - - i_start = 0 - i = 1 - do while (i <= len_trim(string_)) - ! Check for special characters - if (index('()|~ ', string_(i:i)) > 0) then - ! If the special character appears immediately after a non-operator, - ! create a token with the surface half-space - if (i_start > 0) then - call tokens%push_back(int(str_to_int(& - string_(i_start:i - 1)), 4)) - end if - - select case (string_(i:i)) - case ('(') - call tokens%push_back(OP_LEFT_PAREN) - case (')') - if (tokens%size() > 0) then - token = tokens%data(tokens%size()) - if (token >= OP_UNION .and. token < OP_RIGHT_PAREN) then - call fatal_error("Right parentheses cannot follow an operator in & - ®ion specification: " // trim(string)) - end if - end if - call tokens%push_back(OP_RIGHT_PAREN) - case ('|') - if (tokens%size() > 0) then - token = tokens%data(tokens%size()) - if (.not. (token < OP_UNION .or. token == OP_RIGHT_PAREN)) then - call fatal_error("Union cannot follow an operator in region & - &specification: " // trim(string)) - end if - end if - call tokens%push_back(OP_UNION) - case ('~') - call tokens%push_back(OP_COMPLEMENT) - case (' ') - ! Find next non-space character - do while (string_(i+1:i+1) == ' ') - i = i + 1 - end do - - ! If previous token is a halfspace or right parenthesis and next token - ! is not a left parenthese or union operator, that implies that the - ! whitespace is to be interpreted as an intersection operator - if (i_start > 0 .or. tokens%data(tokens%size()) == OP_RIGHT_PAREN) then - if (index(')|', string_(i+1:i+1)) == 0) then - call tokens%push_back(OP_INTERSECTION) - end if - end if - end select - - i_start = 0 - else - ! Check for invalid characters - if (index('-+0123456789', string_(i:i)) == 0) then - call fatal_error("Invalid character '" // string_(i:i) // "' in & - ®ion specification.") - end if - - ! If we haven't yet reached the start of a word, start a new word - if (i_start == 0) i_start = i - end if - - i = i + 1 - end do - - ! If we've reached the end and we're still in a word, create a token from it - ! and add it to the list - if (i_start > 0) then - call tokens%push_back(int(str_to_int(& - string_(i_start:len_trim(string_))), 4)) - end if - end subroutine tokenize - !=============================================================================== ! CONCATENATE takes an array of words and concatenates them together in one ! string with a single space between words diff --git a/src/summary.F90 b/src/summary.F90 index ddc64f707b..750a9899f4 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -29,6 +29,13 @@ contains subroutine write_summary() + interface + subroutine write_geometry(file_id) bind(C) + import HID_T + integer(HID_T), intent(in), value :: file_id + end subroutine write_geometry + end interface + integer(HID_T) :: file_id ! Display output message @@ -154,156 +161,6 @@ contains end subroutine write_nuclides -!=============================================================================== -! WRITE_GEOMETRY -!=============================================================================== - - subroutine write_geometry(file_id) - integer(HID_T), intent(in) :: file_id - - integer :: i, j, cell_fill - integer, allocatable :: cell_materials(:) - integer, allocatable :: cell_ids(:) - real(8), allocatable :: cell_temperatures(:) - integer(HID_T) :: geom_group - integer(HID_T) :: cells_group, cell_group - integer(HID_T) :: surfaces_group - integer(HID_T) :: universes_group, univ_group - integer(HID_T) :: lattices_group - type(Cell), pointer :: c - class(Lattice), pointer :: lat - - ! Use H5LT interface to write number of geometry objects - geom_group = create_group(file_id, "geometry") - call write_attribute(geom_group, "n_cells", n_cells) - call write_attribute(geom_group, "n_surfaces", n_surfaces) - call write_attribute(geom_group, "n_universes", n_universes) - call write_attribute(geom_group, "n_lattices", size(lattices)) - - ! ========================================================================== - ! WRITE INFORMATION ON CELLS - - ! Create a cell group (nothing directly written in this group) then close - cells_group = create_group(geom_group, "cells") - - ! Write information on each cell - CELL_LOOP: do i = 1, n_cells - c => cells(i) - cell_group = create_group(cells_group, "cell " // trim(to_str(c%id()))) - - call c % to_hdf5(cell_group) - - ! Write information on what fills this cell - select case (c%type()) - case (FILL_MATERIAL) - call write_dataset(cell_group, "fill_type", "material") - - if (c % material_size() == 1) then - if (c % material(1) == MATERIAL_VOID) then - call write_dataset(cell_group, "material", MATERIAL_VOID) - else - call write_dataset(cell_group, "material", & - materials(c % material(1)) % id()) - end if - else - allocate(cell_materials(c % material_size())) - do j = 1, c % material_size() - if (c % material(j) == MATERIAL_VOID) then - cell_materials(j) = MATERIAL_VOID - else - cell_materials(j) = materials(c % material(j)) % id() - end if - end do - call write_dataset(cell_group, "material", cell_materials) - deallocate(cell_materials) - end if - - allocate(cell_temperatures(size(c % sqrtkT))) - cell_temperatures(:) = c % sqrtkT(:) - cell_temperatures(:) = cell_temperatures(:)**2 / K_BOLTZMANN - call write_dataset(cell_group, "temperature", cell_temperatures) - deallocate(cell_temperatures) - - case (FILL_UNIVERSE) - call write_dataset(cell_group, "fill_type", "universe") - call write_dataset(cell_group, "fill", universes(c%fill()+1)%id) - - if (allocated(c%translation)) then - call write_dataset(cell_group, "translation", c%translation) - end if - if (allocated(c%rotation)) then - call write_dataset(cell_group, "rotation", c%rotation) - end if - - case (FILL_LATTICE) - call write_dataset(cell_group, "fill_type", "lattice") - ! Do not access the 'lattices' array with 'c % fill() + 1' directly; it - ! causes a segfault in GCC 7.3.0 - cell_fill = c % fill() + 1 - call write_dataset(cell_group, "lattice", lattices(cell_fill)%obj%id()) - end select - - call close_group(cell_group) - end do CELL_LOOP - - call close_group(cells_group) - - ! ========================================================================== - ! WRITE INFORMATION ON SURFACES - - ! Create surfaces group - surfaces_group = create_group(geom_group, "surfaces") - - ! Write information on each surface - SURFACE_LOOP: do i = 1, n_surfaces - call surfaces(i) % to_hdf5(surfaces_group) - end do SURFACE_LOOP - - call close_group(surfaces_group) - - ! ========================================================================== - ! WRITE INFORMATION ON UNIVERSES - - ! Create universes group (nothing directly written here) then close - universes_group = create_group(geom_group, "universes") - - ! Write information on each universe - UNIVERSE_LOOP: do i = 1, n_universes - associate (u => universes(i)) - univ_group = create_group(universes_group, "universe " // & - trim(to_str(u%id))) - - ! Write list of cells in this universe - if (size(u % cells) > 0) then - allocate(cell_ids(size(u % cells))) - do j = 1, size(u % cells) - cell_ids(j) = cells(u % cells(j)) % id() - end do - call write_dataset(univ_group, "cells", cell_ids) - deallocate(cell_ids) - end if - - call close_group(univ_group) - end associate - end do UNIVERSE_LOOP - - call close_group(universes_group) - - ! ========================================================================== - ! WRITE INFORMATION ON LATTICES - - lattices_group = create_group(geom_group, "lattices") - - do i = 1, size(lattices) - lat => lattices(i)%obj - call lat % to_hdf5(lattices_group) - end do - - call close_group(lattices_group) - call close_group(geom_group) - - end subroutine write_geometry - !=============================================================================== ! WRITE_MATERIALS !=============================================================================== diff --git a/src/summary.cpp b/src/summary.cpp new file mode 100644 index 0000000000..d103e78b9c --- /dev/null +++ b/src/summary.cpp @@ -0,0 +1,36 @@ +#include "openmc/cell.h" +#include "openmc/hdf5_interface.h" +#include "openmc/lattice.h" +#include "openmc/surface.h" + + +namespace openmc { + +extern "C" void +write_geometry(hid_t file_id) { + auto geom_group = create_group(file_id, "geometry"); + write_attribute(geom_group, "n_cells", cells.size()); + write_attribute(geom_group, "n_surfaces", surfaces.size()); + write_attribute(geom_group, "n_universes", universes.size()); + write_attribute(geom_group, "n_lattices", lattices.size()); + + auto cells_group = create_group(geom_group, "cells"); + for (Cell* c : cells) c->to_hdf5(cells_group); + close_group(cells_group); + + auto surfaces_group = create_group(geom_group, "surfaces"); + for (Surface* surf : surfaces) surf->to_hdf5(surfaces_group); + close_group(surfaces_group); + + auto universes_group = create_group(geom_group, "universes"); + for (Universe* u : universes) u->to_hdf5(universes_group); + close_group(universes_group); + + auto lattices_group = create_group(geom_group, "lattices"); + for (Lattice* lat : lattices) lat->to_hdf5(lattices_group); + close_group(lattices_group); + + close_group(geom_group); +} + +} // namespace openmc diff --git a/src/surface.cpp b/src/surface.cpp index 78f2302e53..d7f523ed64 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -27,7 +27,7 @@ extern "C" const int BC_PERIODIC {3}; int32_t n_surfaces; -std::vector global_surfaces; +std::vector surfaces; std::map surface_map; @@ -141,38 +141,38 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, Surface::Surface(pugi::xml_node surf_node) { if (check_for_node(surf_node, "id")) { - id = std::stoi(get_node_value(surf_node, "id")); + id_ = std::stoi(get_node_value(surf_node, "id")); } else { fatal_error("Must specify id of surface in geometry XML file."); } if (check_for_node(surf_node, "name")) { - name = get_node_value(surf_node, "name", false); + name_ = get_node_value(surf_node, "name", false); } if (check_for_node(surf_node, "boundary")) { std::string surf_bc = get_node_value(surf_node, "boundary", true, true); if (surf_bc == "transmission" || surf_bc == "transmit" ||surf_bc.empty()) { - bc = BC_TRANSMIT; + bc_ = BC_TRANSMIT; } else if (surf_bc == "vacuum") { - bc = BC_VACUUM; + bc_ = BC_VACUUM; } else if (surf_bc == "reflective" || surf_bc == "reflect" || surf_bc == "reflecting") { - bc = BC_REFLECT; + bc_ = BC_REFLECT; } else if (surf_bc == "periodic") { - bc = BC_PERIODIC; + bc_ = BC_PERIODIC; } else { std::stringstream err_msg; err_msg << "Unknown boundary condition \"" << surf_bc - << "\" specified on surface " << id; + << "\" specified on surface " << id_; fatal_error(err_msg); } } else { - bc = BC_TRANSMIT; + bc_ = BC_TRANSMIT; } } @@ -211,11 +211,11 @@ void Surface::to_hdf5(hid_t group_id) const { std::string group_name {"surface "}; - group_name += std::to_string(id); + group_name += std::to_string(id_); hid_t surf_group = create_group(group_id, group_name); - switch(bc) { + switch(bc_) { case BC_TRANSMIT : write_string(surf_group, "boundary_type", "transmission", false); break; @@ -230,8 +230,8 @@ Surface::to_hdf5(hid_t group_id) const break; } - if (!name.empty()) { - write_string(surf_group, "name", name, false); + if (!name_.empty()) { + write_string(surf_group, "name", name_, false); } to_hdf5_inner(surf_group); @@ -247,7 +247,7 @@ PeriodicSurface::PeriodicSurface(pugi::xml_node surf_node) : Surface {surf_node} { if (check_for_node(surf_node, "periodic_surface_id")) { - i_periodic = std::stoi(get_node_value(surf_node, "periodic_surface_id")); + i_periodic_ = std::stoi(get_node_value(surf_node, "periodic_surface_id")); } } @@ -273,17 +273,17 @@ axis_aligned_plane_distance(Position r, Direction u, bool coincident, double off SurfaceXPlane::SurfaceXPlane(pugi::xml_node surf_node) : PeriodicSurface(surf_node) { - read_coeffs(surf_node, id, x0); + read_coeffs(surf_node, id_, x0_); } double SurfaceXPlane::evaluate(Position r) const { - return r.x - x0; + return r.x - x0_; } double SurfaceXPlane::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_plane_distance<0>(r, u, coincident, x0); + return axis_aligned_plane_distance<0>(r, u, coincident, x0_); } Direction SurfaceXPlane::normal(Position r) const @@ -294,7 +294,7 @@ Direction SurfaceXPlane::normal(Position r) const void SurfaceXPlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "x-plane", false); - std::array coeffs {{x0}}; + std::array coeffs {{x0_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -303,15 +303,15 @@ bool SurfaceXPlane::periodic_translate(const PeriodicSurface* other, { Direction other_n = other->normal(r); if (other_n.x == 1 and other_n.y == 0 and other_n.z == 0) { - r.x = x0; + r.x = x0_; return false; } else { // Assume the partner is an YPlane (the only supported partner). Use the - // evaluate function to find y0, then adjust position/Direction for rotational - // symmetry. - double y0 = -other->evaluate({0., 0., 0.}); - r.y = r.x - x0 + y0; - r.x = x0; + // evaluate function to find y0, then adjust position/Direction for + // rotational symmetry. + double y0_ = -other->evaluate({0., 0., 0.}); + r.y = r.x - x0_ + y0_; + r.x = x0_; double ux = u.x; u.x = -u.y; @@ -324,7 +324,7 @@ bool SurfaceXPlane::periodic_translate(const PeriodicSurface* other, BoundingBox SurfaceXPlane::bounding_box() const { - return {x0, x0, -INFTY, INFTY, -INFTY, INFTY}; + return {x0_, x0_, -INFTY, INFTY, -INFTY, INFTY}; } //============================================================================== @@ -334,17 +334,17 @@ SurfaceXPlane::bounding_box() const SurfaceYPlane::SurfaceYPlane(pugi::xml_node surf_node) : PeriodicSurface(surf_node) { - read_coeffs(surf_node, id, y0); + read_coeffs(surf_node, id_, y0_); } double SurfaceYPlane::evaluate(Position r) const { - return r.y - y0; + return r.y - y0_; } double SurfaceYPlane::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_plane_distance<1>(r, u, coincident, y0); + return axis_aligned_plane_distance<1>(r, u, coincident, y0_); } Direction SurfaceYPlane::normal(Position r) const @@ -355,7 +355,7 @@ Direction SurfaceYPlane::normal(Position r) const void SurfaceYPlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "y-plane", false); - std::array coeffs {{y0}}; + std::array coeffs {{y0_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -365,15 +365,15 @@ bool SurfaceYPlane::periodic_translate(const PeriodicSurface *other, Direction other_n = other->normal(r); if (other_n.x == 0 and other_n.y == 1 and other_n.z == 0) { // The periodic partner is also aligned along y. Just change the y coord. - r.y = y0; + r.y = y0_; return false; } else { // Assume the partner is an XPlane (the only supported partner). Use the // evaluate function to find x0, then adjust position/Direction for rotational // symmetry. - double x0 = -other->evaluate({0., 0., 0.}); - r.x = r.y - y0 + x0; - r.y = y0; + double x0_ = -other->evaluate({0., 0., 0.}); + r.x = r.y - y0_ + x0_; + r.y = y0_; double ux = u.x; u.x = u.y; @@ -386,7 +386,7 @@ bool SurfaceYPlane::periodic_translate(const PeriodicSurface *other, BoundingBox SurfaceYPlane::bounding_box() const { - return {-INFTY, INFTY, y0, y0, -INFTY, INFTY}; + return {-INFTY, INFTY, y0_, y0_, -INFTY, INFTY}; } //============================================================================== @@ -396,17 +396,17 @@ SurfaceYPlane::bounding_box() const SurfaceZPlane::SurfaceZPlane(pugi::xml_node surf_node) : PeriodicSurface(surf_node) { - read_coeffs(surf_node, id, z0); + read_coeffs(surf_node, id_, z0_); } double SurfaceZPlane::evaluate(Position r) const { - return r.z - z0; + return r.z - z0_; } double SurfaceZPlane::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_plane_distance<2>(r, u, coincident, z0); + return axis_aligned_plane_distance<2>(r, u, coincident, z0_); } Direction SurfaceZPlane::normal(Position r) const @@ -417,7 +417,7 @@ Direction SurfaceZPlane::normal(Position r) const void SurfaceZPlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "z-plane", false); - std::array coeffs {{z0}}; + std::array coeffs {{z0_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -425,14 +425,14 @@ bool SurfaceZPlane::periodic_translate(const PeriodicSurface* other, Position& r, Direction& u) const { // Assume the other plane is aligned along z. Just change the z coord. - r.z = z0; + r.z = z0_; return false; } BoundingBox SurfaceZPlane::bounding_box() const { - return {-INFTY, INFTY, -INFTY, INFTY, z0, z0}; + return {-INFTY, INFTY, -INFTY, INFTY, z0_, z0_}; } //============================================================================== @@ -442,20 +442,20 @@ SurfaceZPlane::bounding_box() const SurfacePlane::SurfacePlane(pugi::xml_node surf_node) : PeriodicSurface(surf_node) { - read_coeffs(surf_node, id, A, B, C, D); + read_coeffs(surf_node, id_, A_, B_, C_, D_); } double SurfacePlane::evaluate(Position r) const { - return A*r.x + B*r.y + C*r.z - D; + return A_*r.x + B_*r.y + C_*r.z - D_; } double SurfacePlane::distance(Position r, Direction u, bool coincident) const { - const double f = A*r.x + B*r.y + C*r.z - D; - const double projection = A*u.x + B*u.y + C*u.z; + const double f = A_*r.x + B_*r.y + C_*r.z - D_; + const double projection = A_*u.x + B_*u.y + C_*u.z; if (coincident or std::abs(f) < FP_COINCIDENT or projection == 0.0) { return INFTY; } else { @@ -468,13 +468,13 @@ SurfacePlane::distance(Position r, Direction u, bool coincident) const Direction SurfacePlane::normal(Position r) const { - return {A, B, C}; + return {A_, B_, C_}; } void SurfacePlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "plane", false); - std::array coeffs {{A, B, C, D}}; + std::array coeffs {{A_, B_, C_, D_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -484,12 +484,12 @@ bool SurfacePlane::periodic_translate(const PeriodicSurface* other, Position& r, // This function assumes the other plane shares this plane's normal direction. // Determine the distance to intersection. - double d = evaluate(r) / (A*A + B*B + C*C); + double d = evaluate(r) / (A_*A_ + B_*B_ + C_*C_); // Move the particle that distance along the normal vector. - r.x -= d * A; - r.y -= d * B; - r.z -= d * C; + r.x -= d * A_; + r.y -= d * B_; + r.z -= d * C_; return false; } @@ -582,30 +582,30 @@ axis_aligned_cylinder_normal(Position r, double offset1, double offset2) SurfaceXCylinder::SurfaceXCylinder(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, y0, z0, radius); + read_coeffs(surf_node, id_, y0_, z0_, radius_); } double SurfaceXCylinder::evaluate(Position r) const { - return axis_aligned_cylinder_evaluate<1, 2>(r, y0, z0, radius); + return axis_aligned_cylinder_evaluate<1, 2>(r, y0_, z0_, radius_); } double SurfaceXCylinder::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_cylinder_distance<0, 1, 2>(r, u, coincident, y0, z0, - radius); + return axis_aligned_cylinder_distance<0, 1, 2>(r, u, coincident, y0_, z0_, + radius_); } Direction SurfaceXCylinder::normal(Position r) const { - return axis_aligned_cylinder_normal<0, 1, 2>(r, y0, z0); + return axis_aligned_cylinder_normal<0, 1, 2>(r, y0_, z0_); } void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "x-cylinder", false); - std::array coeffs {{y0, z0, radius}}; + std::array coeffs {{y0_, z0_, radius_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -616,29 +616,29 @@ void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const SurfaceYCylinder::SurfaceYCylinder(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, x0, z0, radius); + read_coeffs(surf_node, id_, x0_, z0_, radius_); } double SurfaceYCylinder::evaluate(Position r) const { - return axis_aligned_cylinder_evaluate<0, 2>(r, x0, z0, radius); + return axis_aligned_cylinder_evaluate<0, 2>(r, x0_, z0_, radius_); } double SurfaceYCylinder::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_cylinder_distance<1, 0, 2>(r, u, coincident, x0, z0, - radius); + return axis_aligned_cylinder_distance<1, 0, 2>(r, u, coincident, x0_, z0_, + radius_); } Direction SurfaceYCylinder::normal(Position r) const { - return axis_aligned_cylinder_normal<1, 0, 2>(r, x0, z0); + return axis_aligned_cylinder_normal<1, 0, 2>(r, x0_, z0_); } void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "y-cylinder", false); - std::array coeffs {{x0, z0, radius}}; + std::array coeffs {{x0_, z0_, radius_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -649,29 +649,29 @@ void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const SurfaceZCylinder::SurfaceZCylinder(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, x0, y0, radius); + read_coeffs(surf_node, id_, x0_, y0_, radius_); } double SurfaceZCylinder::evaluate(Position r) const { - return axis_aligned_cylinder_evaluate<0, 1>(r, x0, y0, radius); + return axis_aligned_cylinder_evaluate<0, 1>(r, x0_, y0_, radius_); } double SurfaceZCylinder::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_cylinder_distance<2, 0, 1>(r, u, coincident, x0, y0, - radius); + return axis_aligned_cylinder_distance<2, 0, 1>(r, u, coincident, x0_, y0_, + radius_); } Direction SurfaceZCylinder::normal(Position r) const { - return axis_aligned_cylinder_normal<2, 0, 1>(r, x0, y0); + return axis_aligned_cylinder_normal<2, 0, 1>(r, x0_, y0_); } void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "z-cylinder", false); - std::array coeffs {{x0, y0, radius}}; + std::array coeffs {{x0_, y0_, radius_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -682,24 +682,24 @@ void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const SurfaceSphere::SurfaceSphere(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, x0, y0, z0, radius); + read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_); } double SurfaceSphere::evaluate(Position r) const { - const double x = r.x - x0; - const double y = r.y - y0; - const double z = r.z - z0; - return x*x + y*y + z*z - radius*radius; + const double x = r.x - x0_; + const double y = r.y - y0_; + const double z = r.z - z0_; + return x*x + y*y + z*z - radius_*radius_; } double SurfaceSphere::distance(Position r, Direction u, bool coincident) const { - const double x = r.x - x0; - const double y = r.y - y0; - const double z = r.z - z0; + const double x = r.x - x0_; + const double y = r.y - y0_; + const double z = r.z - z0_; const double k = x*u.x + y*u.y + z*u.z; - const double c = x*x + y*y + z*z - radius*radius; + const double c = x*x + y*y + z*z - radius_*radius_; const double quad = k*k - c; if (quad < 0.0) { @@ -733,13 +733,13 @@ double SurfaceSphere::distance(Position r, Direction u, bool coincident) const Direction SurfaceSphere::normal(Position r) const { - return {2.0*(r.x - x0), 2.0*(r.y - y0), 2.0*(r.z - z0)}; + return {2.0*(r.x - x0_), 2.0*(r.y - y0_), 2.0*(r.z - z0_)}; } void SurfaceSphere::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "sphere", false); - std::array coeffs {{x0, y0, z0, radius}}; + std::array coeffs {{x0_, y0_, z0_, radius_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -835,29 +835,29 @@ axis_aligned_cone_normal(Position r, double offset1, double offset2, SurfaceXCone::SurfaceXCone(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, x0, y0, z0, radius_sq); + read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); } double SurfaceXCone::evaluate(Position r) const { - return axis_aligned_cone_evaluate<0, 1, 2>(r, x0, y0, z0, radius_sq); + return axis_aligned_cone_evaluate<0, 1, 2>(r, x0_, y0_, z0_, radius_sq_); } double SurfaceXCone::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_cone_distance<0, 1, 2>(r, u, coincident, x0, y0, z0, - radius_sq); + return axis_aligned_cone_distance<0, 1, 2>(r, u, coincident, x0_, y0_, z0_, + radius_sq_); } Direction SurfaceXCone::normal(Position r) const { - return axis_aligned_cone_normal<0, 1, 2>(r, x0, y0, z0, radius_sq); + return axis_aligned_cone_normal<0, 1, 2>(r, x0_, y0_, z0_, radius_sq_); } void SurfaceXCone::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "x-cone", false); - std::array coeffs {{x0, y0, z0, radius_sq}}; + std::array coeffs {{x0_, y0_, z0_, radius_sq_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -868,29 +868,29 @@ void SurfaceXCone::to_hdf5_inner(hid_t group_id) const SurfaceYCone::SurfaceYCone(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, x0, y0, z0, radius_sq); + read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); } double SurfaceYCone::evaluate(Position r) const { - return axis_aligned_cone_evaluate<1, 0, 2>(r, y0, x0, z0, radius_sq); + return axis_aligned_cone_evaluate<1, 0, 2>(r, y0_, x0_, z0_, radius_sq_); } double SurfaceYCone::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_cone_distance<1, 0, 2>(r, u, coincident, y0, x0, z0, - radius_sq); + return axis_aligned_cone_distance<1, 0, 2>(r, u, coincident, y0_, x0_, z0_, + radius_sq_); } Direction SurfaceYCone::normal(Position r) const { - return axis_aligned_cone_normal<1, 0, 2>(r, y0, x0, z0, radius_sq); + return axis_aligned_cone_normal<1, 0, 2>(r, y0_, x0_, z0_, radius_sq_); } void SurfaceYCone::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "y-cone", false); - std::array coeffs {{x0, y0, z0, radius_sq}}; + std::array coeffs {{x0_, y0_, z0_, radius_sq_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -901,29 +901,29 @@ void SurfaceYCone::to_hdf5_inner(hid_t group_id) const SurfaceZCone::SurfaceZCone(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, x0, y0, z0, radius_sq); + read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); } double SurfaceZCone::evaluate(Position r) const { - return axis_aligned_cone_evaluate<2, 0, 1>(r, z0, x0, y0, radius_sq); + return axis_aligned_cone_evaluate<2, 0, 1>(r, z0_, x0_, y0_, radius_sq_); } double SurfaceZCone::distance(Position r, Direction u, bool coincident) const { - return axis_aligned_cone_distance<2, 0, 1>(r, u, coincident, z0, x0, y0, - radius_sq); + return axis_aligned_cone_distance<2, 0, 1>(r, u, coincident, z0_, x0_, y0_, + radius_sq_); } Direction SurfaceZCone::normal(Position r) const { - return axis_aligned_cone_normal<2, 0, 1>(r, z0, x0, y0, radius_sq); + return axis_aligned_cone_normal<2, 0, 1>(r, z0_, x0_, y0_, radius_sq_); } void SurfaceZCone::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "z-cone", false); - std::array coeffs {{x0, y0, z0, radius_sq}}; + std::array coeffs {{x0_, y0_, z0_, radius_sq_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -934,7 +934,7 @@ void SurfaceZCone::to_hdf5_inner(hid_t group_id) const SurfaceQuadric::SurfaceQuadric(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id, A, B, C, D, E, F, G, H, J, K); + read_coeffs(surf_node, id_, A_, B_, C_, D_, E_, F_, G_, H_, J_, K_); } double @@ -943,9 +943,9 @@ SurfaceQuadric::evaluate(Position r) const const double x = r.x; const double y = r.y; const double z = r.z; - return x*(A*x + D*y + G) + - y*(B*y + E*z + H) + - z*(C*z + F*x + J) + K; + return x*(A_*x + D_*y + G_) + + y*(B_*y + E_*z + H_) + + z*(C_*z + F_*x + J_) + K_; } double @@ -958,11 +958,11 @@ SurfaceQuadric::distance(Position r, Direction ang, bool coincident) const const double &v = ang.y; const double &w = ang.z; - const double a = A*u*u + B*v*v + C*w*w + D*u*v + E*v*w + F*u*w; - const double k = (A*u*x + B*v*y + C*w*z + 0.5*(D*(u*y + v*x) + - E*(v*z + w*y) + F*(w*x + u*z) + G*u + H*v + J*w)); - const double c = A*x*x + B*y*y + C*z*z + D*x*y + E*y*z + F*x*z + G*x + H*y - + J*z + K; + const double a = A_*u*u + B_*v*v + C_*w*w + D_*u*v + E_*v*w + F_*u*w; + const double k = A_*u*x + B_*v*y + C_*w*z + 0.5*(D_*(u*y + v*x) + + E_*(v*z + w*y) + F_*(w*x + u*z) + G_*u + H_*v + J_*w); + const double c = A_*x*x + B_*y*y + C_*z*z + D_*x*y + E_*y*z + F_*x*z + G_*x + + H_*y + J_*z + K_; double quad = k*k - a*c; double d; @@ -1008,15 +1008,15 @@ SurfaceQuadric::normal(Position r) const const double &x = r.x; const double &y = r.y; const double &z = r.z; - return {2.0*A*x + D*y + F*z + G, - 2.0*B*y + D*x + E*z + H, - 2.0*C*z + E*y + F*x + J}; + return {2.0*A_*x + D_*y + F_*z + G_, + 2.0*B_*y + D_*x + E_*z + H_, + 2.0*C_*z + E_*y + F_*x + J_}; } void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "quadric", false); - std::array coeffs {{A, B, C, D, E, F, G, H, J, K}}; + std::array coeffs {{A_, B_, C_, D_, E_, F_, G_, H_, J_, K_}}; write_dataset(group_id, "coefficients", coeffs); } @@ -1032,7 +1032,7 @@ read_surfaces(pugi::xml_node* node) } // Loop over XML surface elements and populate the array. - global_surfaces.reserve(n_surfaces); + surfaces.reserve(n_surfaces); { pugi::xml_node surf_node; int i_surf; @@ -1041,40 +1041,40 @@ read_surfaces(pugi::xml_node* node) std::string surf_type = get_node_value(surf_node, "type", true, true); if (surf_type == "x-plane") { - global_surfaces.push_back(new SurfaceXPlane(surf_node)); + surfaces.push_back(new SurfaceXPlane(surf_node)); } else if (surf_type == "y-plane") { - global_surfaces.push_back(new SurfaceYPlane(surf_node)); + surfaces.push_back(new SurfaceYPlane(surf_node)); } else if (surf_type == "z-plane") { - global_surfaces.push_back(new SurfaceZPlane(surf_node)); + surfaces.push_back(new SurfaceZPlane(surf_node)); } else if (surf_type == "plane") { - global_surfaces.push_back(new SurfacePlane(surf_node)); + surfaces.push_back(new SurfacePlane(surf_node)); } else if (surf_type == "x-cylinder") { - global_surfaces.push_back(new SurfaceXCylinder(surf_node)); + surfaces.push_back(new SurfaceXCylinder(surf_node)); } else if (surf_type == "y-cylinder") { - global_surfaces.push_back(new SurfaceYCylinder(surf_node)); + surfaces.push_back(new SurfaceYCylinder(surf_node)); } else if (surf_type == "z-cylinder") { - global_surfaces.push_back(new SurfaceZCylinder(surf_node)); + surfaces.push_back(new SurfaceZCylinder(surf_node)); } else if (surf_type == "sphere") { - global_surfaces.push_back(new SurfaceSphere(surf_node)); + surfaces.push_back(new SurfaceSphere(surf_node)); } else if (surf_type == "x-cone") { - global_surfaces.push_back(new SurfaceXCone(surf_node)); + surfaces.push_back(new SurfaceXCone(surf_node)); } else if (surf_type == "y-cone") { - global_surfaces.push_back(new SurfaceYCone(surf_node)); + surfaces.push_back(new SurfaceYCone(surf_node)); } else if (surf_type == "z-cone") { - global_surfaces.push_back(new SurfaceZCone(surf_node)); + surfaces.push_back(new SurfaceZCone(surf_node)); } else if (surf_type == "quadric") { - global_surfaces.push_back(new SurfaceQuadric(surf_node)); + surfaces.push_back(new SurfaceQuadric(surf_node)); } else { std::stringstream err_msg; @@ -1086,7 +1086,7 @@ read_surfaces(pugi::xml_node* node) // Fill the surface map. for (int i_surf = 0; i_surf < n_surfaces; i_surf++) { - int id = global_surfaces[i_surf]->id; + int id = surfaces[i_surf]->id_; auto in_map = surface_map.find(id); if (in_map == surface_map.end()) { surface_map[id] = i_surf; @@ -1102,16 +1102,16 @@ read_surfaces(pugi::xml_node* node) zmin {INFTY}, zmax {-INFTY}; int i_xmin, i_xmax, i_ymin, i_ymax, i_zmin, i_zmax; for (int i_surf = 0; i_surf < n_surfaces; i_surf++) { - if (global_surfaces[i_surf]->bc == BC_PERIODIC) { + if (surfaces[i_surf]->bc_ == BC_PERIODIC) { // Downcast to the PeriodicSurface type. - Surface* surf_base = global_surfaces[i_surf]; + Surface* surf_base = surfaces[i_surf]; PeriodicSurface* surf = dynamic_cast(surf_base); // Make sure this surface inherits from PeriodicSurface. if (!surf) { std::stringstream err_msg; err_msg << "Periodic boundary condition not supported for surface " - << surf_base->id + << surf_base->id_ << ". Periodic BCs are only supported for planar surfaces."; fatal_error(err_msg); } @@ -1147,9 +1147,9 @@ read_surfaces(pugi::xml_node* node) // Set i_periodic for periodic BC surfaces. for (int i_surf = 0; i_surf < n_surfaces; i_surf++) { - if (global_surfaces[i_surf]->bc == BC_PERIODIC) { + if (surfaces[i_surf]->bc_ == BC_PERIODIC) { // Downcast to the PeriodicSurface type. - Surface* surf_base = global_surfaces[i_surf]; + Surface* surf_base = surfaces[i_surf]; PeriodicSurface* surf = dynamic_cast(surf_base); // Also try downcasting to the SurfacePlane type (which must be handled @@ -1158,48 +1158,48 @@ read_surfaces(pugi::xml_node* node) if (!surf_p) { // This is not a SurfacePlane. - if (surf->i_periodic == C_NONE) { + if (surf->i_periodic_ == C_NONE) { // The user did not specify the matching periodic surface. See if we // can find the partnered surface from the bounding box information. if (i_surf == i_xmin) { - surf->i_periodic = i_xmax; + surf->i_periodic_ = i_xmax; } else if (i_surf == i_xmax) { - surf->i_periodic = i_xmin; + surf->i_periodic_ = i_xmin; } else if (i_surf == i_ymin) { - surf->i_periodic = i_ymax; + surf->i_periodic_ = i_ymax; } else if (i_surf == i_ymax) { - surf->i_periodic = i_ymin; + surf->i_periodic_ = i_ymin; } else if (i_surf == i_zmin) { - surf->i_periodic = i_zmax; + surf->i_periodic_ = i_zmax; } else if (i_surf == i_zmax) { - surf->i_periodic = i_zmin; + surf->i_periodic_ = i_zmin; } else { fatal_error("Periodic boundary condition applied to interior " "surface"); } } else { // Convert the surface id to an index. - surf->i_periodic = surface_map[surf->i_periodic]; + surf->i_periodic_ = surface_map[surf->i_periodic_]; } } else { // This is a SurfacePlane. We won't try to find it's partner if the // user didn't specify one. - if (surf->i_periodic == C_NONE) { + if (surf->i_periodic_ == C_NONE) { std::stringstream err_msg; err_msg << "No matching periodic surface specified for periodic " - "boundary condition on surface " << surf->id; + "boundary condition on surface " << surf->id_; fatal_error(err_msg); } else { // Convert the surface id to an index. - surf->i_periodic = surface_map[surf->i_periodic]; + surf->i_periodic_ = surface_map[surf->i_periodic_]; } } // Make sure the opposite surface is also periodic. - if (global_surfaces[surf->i_periodic]->bc != BC_PERIODIC) { + if (surfaces[surf->i_periodic_]->bc_ != BC_PERIODIC) { std::stringstream err_msg; err_msg << "Could not find matching surface for periodic boundary " - "condition on surface " << surf->id; + "condition on surface " << surf->id_; fatal_error(err_msg); } } @@ -1211,11 +1211,11 @@ read_surfaces(pugi::xml_node* node) //============================================================================== extern "C" { - Surface* surface_pointer(int surf_ind) {return global_surfaces[surf_ind];} + Surface* surface_pointer(int surf_ind) {return surfaces[surf_ind];} - int surface_id(Surface* surf) {return surf->id;} + int surface_id(Surface* surf) {return surf->id_;} - int surface_bc(Surface* surf) {return surf->bc;} + int surface_bc(Surface* surf) {return surf->bc_;} void surface_reflect(Surface* surf, double xyz[3], double uvw[3]) { @@ -1228,18 +1228,7 @@ extern "C" { uvw[2] = u.z; } - void surface_normal(Surface* surf, double xyz[3], double uvw[3]) - { - Position r {xyz}; - Direction u = surf->normal(r); - uvw[0] = u.x; - uvw[1] = u.y; - uvw[2] = u.z; - } - - void surface_to_hdf5(Surface* surf, hid_t group) {surf->to_hdf5(group);} - - int surface_i_periodic(PeriodicSurface* surf) {return surf->i_periodic;} + int surface_i_periodic(PeriodicSurface* surf) {return surf->i_periodic_;} bool surface_periodic(PeriodicSurface* surf, PeriodicSurface* other, double xyz[3], @@ -1262,8 +1251,8 @@ extern "C" { void free_memory_surfaces_c() { - for (Surface* surf : global_surfaces) {delete surf;} - global_surfaces.clear(); + for (Surface* surf : surfaces) {delete surf;} + surfaces.clear(); n_surfaces = 0; surface_map.clear(); } diff --git a/src/surface_header.F90 b/src/surface_header.F90 index 4a5834ec2c..9d7e114613 100644 --- a/src/surface_header.F90 +++ b/src/surface_header.F90 @@ -38,23 +38,6 @@ module surface_header real(C_DOUBLE), intent(inout) :: uvw(3); end subroutine surface_reflect_c - pure subroutine surface_normal_c(surf_ptr, xyz, uvw) & - bind(C, name='surface_normal') - use ISO_C_BINDING - implicit none - type(C_PTR), intent(in), value :: surf_ptr - real(C_DOUBLE), intent(in) :: xyz(3); - real(C_DOUBLE), intent(out) :: uvw(3); - end subroutine surface_normal_c - - subroutine surface_to_hdf5_c(surf_ptr, group) & - bind(C, name='surface_to_hdf5') - import C_PTR, HID_T - implicit none - type(C_PTR), intent(in), value :: surf_ptr - integer(HID_T), intent(in), value :: group - end subroutine surface_to_hdf5_c - pure function surface_i_periodic_c(surf_ptr) & bind(C, name="surface_i_periodic") result(i_periodic) use ISO_C_BINDING @@ -84,9 +67,6 @@ module surface_header !=============================================================================== type :: Surface - integer, allocatable :: & - neighbor_pos(:), & ! List of cells on positive side - neighbor_neg(:) ! List of cells on negative side type(C_PTR) :: ptr contains @@ -94,8 +74,6 @@ module surface_header procedure :: id => surface_id procedure :: bc => surface_bc procedure :: reflect => surface_reflect - procedure :: normal => surface_normal - procedure :: to_hdf5 => surface_to_hdf5 procedure :: i_periodic => surface_i_periodic procedure :: periodic_translate => surface_periodic @@ -129,19 +107,6 @@ contains call surface_reflect_c(this % ptr, xyz, uvw) end subroutine surface_reflect - pure subroutine surface_normal(this, xyz, uvw) - class(Surface), intent(in) :: this - real(C_DOUBLE), intent(in) :: xyz(3); - real(C_DOUBLE), intent(out) :: uvw(3); - call surface_normal_c(this % ptr, xyz, uvw) - end subroutine surface_normal - - subroutine surface_to_hdf5(this, group) - class(Surface), intent(in) :: this - integer(HID_T), intent(in) :: group - call surface_to_hdf5_c(this % ptr, group) - end subroutine surface_to_hdf5 - pure function surface_i_periodic(this) result(i_periodic) class(Surface), intent(in) :: this integer(C_INT) :: i_periodic diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index aa750acee6..9967606d17 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -3009,7 +3009,7 @@ contains integer :: l logical :: scoring_diff_nuclide real(8) :: flux_deriv - real(8) :: dsig_t, dsig_a, dsig_f, cum_dsig + real(8) :: dsig_s, dsig_a, dsig_f, cum_dsig if (score == ZERO) return @@ -3250,17 +3250,19 @@ contains if (mat % nuclide(l) == p % event_nuclide) exit end do - dsig_t = ZERO + dsig_s = ZERO + dsig_a = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & - + dsig_t * mat % atom_density(l) / material_xs % total) + + (dsig_s + dsig_a) * mat % atom_density(l) & + / material_xs % total) end associate else score = score * flux_deriv @@ -3276,18 +3278,16 @@ contains if (mat % nuclide(l) == p % event_nuclide) exit end do - dsig_t = ZERO - dsig_a = ZERO + dsig_s = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate - score = score * (flux_deriv + (dsig_t - dsig_a) & - * mat % atom_density(l) / & + score = score * (flux_deriv + dsig_s * mat % atom_density(l) / & (material_xs % total - material_xs % absorption)) end associate else @@ -3306,10 +3306,10 @@ contains dsig_a = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv + dsig_a * mat % atom_density(l) & @@ -3331,10 +3331,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3356,10 +3356,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3392,12 +3392,13 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % total > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) - cum_dsig = cum_dsig + dsig_t * mat % atom_density(l) + p % sqrtkT, dsig_s, dsig_a, dsig_f) + cum_dsig = cum_dsig + (dsig_s + dsig_a) & + * mat % atom_density(l) end if end associate end do @@ -3406,17 +3407,18 @@ contains + cum_dsig / material_xs % total) else if (materials(p % material) % id() == deriv % diff_material & .and. material_xs % total > ZERO) then - dsig_t = ZERO + dsig_s = ZERO + dsig_a = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & - + dsig_t / micro_xs(i_nuclide) % total) + + (dsig_s + dsig_a) / micro_xs(i_nuclide) % total) else score = score * flux_deriv end if @@ -3430,14 +3432,13 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & (micro_xs(mat % nuclide(l)) % total & - micro_xs(mat % nuclide(l)) % absorption) > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) - cum_dsig = cum_dsig & - + (dsig_t - dsig_a) * mat % atom_density(l) + p % sqrtkT, dsig_s, dsig_a, dsig_f) + cum_dsig = cum_dsig + dsig_s * mat % atom_density(l) end if end associate end do @@ -3447,17 +3448,16 @@ contains else if ( materials(p % material) % id() == deriv % diff_material & .and. (material_xs % total - material_xs % absorption) > ZERO)& then - dsig_t = ZERO - dsig_a = ZERO + dsig_s = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate - score = score * (flux_deriv + (dsig_t - dsig_a) & + score = score * (flux_deriv + dsig_s & / (micro_xs(i_nuclide) % total & - micro_xs(i_nuclide) % absorption)) else @@ -3473,11 +3473,11 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % absorption > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) cum_dsig = cum_dsig + dsig_a * mat % atom_density(l) end if end associate @@ -3490,10 +3490,10 @@ contains dsig_a = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3511,11 +3511,11 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % fission > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) cum_dsig = cum_dsig + dsig_f * mat % atom_density(l) end if end associate @@ -3528,10 +3528,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3549,11 +3549,11 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % nu_fission > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) cum_dsig = cum_dsig + dsig_f * mat % atom_density(l) & * micro_xs(mat % nuclide(l)) % nu_fission & / micro_xs(mat % nuclide(l)) % fission @@ -3568,10 +3568,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3603,7 +3603,7 @@ contains real(8), intent(in) :: distance ! Neutron flight distance integer :: i, l - real(8) :: dsig_t, dsig_a, dsig_f + real(8) :: dsig_s, dsig_a, dsig_f ! A void material cannot be perturbed so it will not affect flux derivatives if (p % material == MATERIAL_VOID) return @@ -3640,15 +3640,15 @@ contains do l=1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % E >= nuc % multipole % start_E .and. & - p % E <= nuc % multipole % end_E) then + p % E >= nuc % multipole % E_min .and. & + p % E <= nuc % multipole % E_max) then ! phi is proportional to e^(-Sigma_tot * dist) ! (1 / phi) * (d_phi / d_T) = - (d_Sigma_tot / d_T) * dist ! (1 / phi) * (d_phi / d_T) = - N (d_sigma_tot / d_T) * dist call multipole_deriv_eval(nuc % multipole, p % E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) deriv % flux_deriv = deriv % flux_deriv & - - distance * dsig_t * mat % atom_density(l) + - distance * (dsig_s + dsig_a) * mat % atom_density(l) end if end associate end do @@ -3679,7 +3679,7 @@ contains type(Particle), intent(in) :: p integer :: i, j, l - real(8) :: dsig_t, dsig_a, dsig_f + real(8) :: dsig_s, dsig_a, dsig_f ! A void material cannot be perturbed so it will not affect flux derivatives if (p % material == MATERIAL_VOID) return @@ -3727,14 +3727,14 @@ contains associate (nuc => nuclides(mat % nuclide(l))) if (mat % nuclide(l) == p % event_nuclide .and. & nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then ! phi is proportional to Sigma_s ! (1 / phi) * (d_phi / d_T) = (d_Sigma_s / d_T) / Sigma_s ! (1 / phi) * (d_phi / d_T) = (d_sigma_s / d_T) / sigma_s call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) - deriv % flux_deriv = deriv % flux_deriv + (dsig_t - dsig_a)& + p % sqrtkT, dsig_s, dsig_a, dsig_f) + deriv % flux_deriv = deriv % flux_deriv + dsig_s& / (micro_xs(mat % nuclide(l)) % total & - micro_xs(mat % nuclide(l)) % absorption) ! Note that this is an approximation! The real scattering diff --git a/src/tallies/tally_filter_cell.F90 b/src/tallies/tally_filter_cell.F90 index be37467073..a394ed122b 100644 --- a/src/tallies/tally_filter_cell.F90 +++ b/src/tallies/tally_filter_cell.F90 @@ -59,7 +59,7 @@ contains ! Iterate over coordinate levels to see with cells match do i = 1, p % n_coord - val = this % map % get(p % coord(i) % cell) + val = this % map % get(p % coord(i) % cell + 1) if (val /= EMPTY) then call match % bins % push_back(val) call match % weights % push_back(ONE) diff --git a/src/tallies/tally_filter_cellborn.F90 b/src/tallies/tally_filter_cellborn.F90 index a373c2d4e1..37bf042020 100644 --- a/src/tallies/tally_filter_cellborn.F90 +++ b/src/tallies/tally_filter_cellborn.F90 @@ -55,7 +55,7 @@ contains integer :: val - val = this % map % get(p % cell_born) + val = this % map % get(p % cell_born + 1) if (val /= EMPTY) then call match % bins % push_back(val) call match % weights % push_back(ONE) diff --git a/src/tallies/tally_filter_cellfrom.F90 b/src/tallies/tally_filter_cellfrom.F90 index 83a3179d26..a99aa68832 100644 --- a/src/tallies/tally_filter_cellfrom.F90 +++ b/src/tallies/tally_filter_cellfrom.F90 @@ -42,7 +42,7 @@ contains ! Starting one coordinate level deeper, find the next bin. do i = 1, p % last_n_coord - val = this % map % get(p % last_cell(i)) + val = this % map % get(p % last_cell(i) + 1) if (val /= EMPTY) then call match % bins % push_back(val) call match % weights % push_back(ONE) diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index c9564083da..f35ffc3ec4 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -54,26 +54,25 @@ contains integer :: distribcell_index, offset, i - distribcell_index = cells(this % cell) % distribcell_index + distribcell_index = cells(this % cell) % distribcell_index() offset = 0 do i = 1, p % n_coord - if (cells(p % coord(i) % cell) % type() == FILL_UNIVERSE) then - offset = offset + cells(p % coord(i) % cell) & - % offset(distribcell_index-1) - elseif (cells(p % coord(i) % cell) % type() == FILL_LATTICE) then - if (lattices(p % coord(i + 1) % lattice) % obj & - % are_valid_indices([& + if (cells(p % coord(i) % cell + 1) % type() == FILL_UNIVERSE) then + offset = offset + cells(p % coord(i) % cell + 1) & + % offset(distribcell_index) + elseif (cells(p % coord(i) % cell + 1) % type() == FILL_LATTICE) then + if (lattices(p % coord(i + 1) % lattice) % are_valid_indices([& p % coord(i + 1) % lattice_x, & p % coord(i + 1) % lattice_y, & p % coord(i + 1) % lattice_z])) then - offset = offset + lattices(p % coord(i + 1) % lattice) % obj & - % offset(distribcell_index - 1, & + offset = offset + lattices(p % coord(i + 1) % lattice) & + % offset(distribcell_index, & [p % coord(i + 1) % lattice_x, & p % coord(i + 1) % lattice_y, & p % coord(i + 1) % lattice_z]) end if end if - if (this % cell == p % coord(i) % cell) then + if (this % cell == p % coord(i) % cell + 1) then call match % bins % push_back(offset + 1) call match % weights % push_back(ONE) return @@ -154,12 +153,12 @@ contains end interface ! Get the distribcell index for this cell - map = cells(i_cell) % distribcell_index + map = cells(i_cell) % distribcell_index() - path_len = distribcell_path_len(i_cell-1, map-1, target_offset, & - root_universe-1) + path_len = distribcell_path_len(i_cell-1, map, target_offset, & + root_universe) allocate(path_c(path_len)) - call distribcell_path(i_cell-1, map-1, target_offset, root_universe-1, & + call distribcell_path(i_cell-1, map, target_offset, root_universe, & path_c) do i = 1, min(path_len, MAX_LINE_LEN) if (path_c(i) == C_NULL_CHAR) exit diff --git a/src/tallies/tally_filter_universe.F90 b/src/tallies/tally_filter_universe.F90 index 0dc5aefe14..39c1449b3d 100644 --- a/src/tallies/tally_filter_universe.F90 +++ b/src/tallies/tally_filter_universe.F90 @@ -78,7 +78,7 @@ contains allocate(universe_ids(size(this % universes))) do i = 1, size(this % universes) - universe_ids(i) = universes(this % universes(i)) % id + universe_ids(i) = universe_id(this % universes(i)-1) end do call write_dataset(filter_group, "bins", universe_ids) end subroutine to_statepoint_universe @@ -112,7 +112,7 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - label = "Universe " // to_str(universes(this % universes(bin)) % id) + label = "Universe " // to_str(universe_id(this % universes(bin))) end function text_label_universe end module tally_filter_universe diff --git a/src/tracking.F90 b/src/tracking.F90 index dd0c26b433..d93ac9ab97 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -91,7 +91,7 @@ contains ! If the cell hasn't been determined based on the particle's location, ! initiate a search for the current cell. This generally happens at the ! beginning of the history and again for any secondary particles - if (p % coord(p % n_coord) % cell == NONE) then + if (p % coord(p % n_coord) % cell == C_NONE) then call find_cell(p, found_cell) if (.not. found_cell) then call particle_mark_as_lost(p, "Could not find the cell containing" & @@ -100,7 +100,7 @@ contains end if ! set birth cell attribute - if (p % cell_born == NONE) p % cell_born = p % coord(p % n_coord) % cell + if (p % cell_born == C_NONE) p % cell_born = p % coord(p % n_coord) % cell end if ! Write particle track. @@ -183,7 +183,7 @@ contains end do p % last_n_coord = p % n_coord - p % coord(p % n_coord) % cell = NONE + p % coord(p % n_coord) % cell = C_NONE if (any(lattice_translation /= 0)) then ! Particle crosses lattice boundary p % surface = ERROR_INT @@ -251,7 +251,7 @@ contains do j = 1, p % n_coord - 1 if (p % coord(j + 1) % rotated) then ! If next level is rotated, apply rotation matrix - p % coord(j + 1) % uvw = matmul(cells(p % coord(j) % cell) % & + p % coord(j + 1) % uvw = matmul(cells(p % coord(j) % cell + 1) % & rotation_matrix, p % coord(j) % uvw) else ! Otherwise, copy this level's direction @@ -467,21 +467,8 @@ contains ! ========================================================================== ! SEARCH NEIGHBOR LISTS FOR NEXT CELL - if (p % surface > 0 .and. allocated(surf%neighbor_pos)) then - ! If coming from negative side of surface, search all the neighboring - ! cells on the positive side - - call find_cell(p, found, surf%neighbor_pos) - if (found) return - - elseif (p % surface < 0 .and. allocated(surf%neighbor_neg)) then - ! If coming from positive side of surface, search all the neighboring - ! cells on the negative side - - call find_cell(p, found, surf%neighbor_neg) - if (found) return - - end if + call find_cell(p, found, p % surface) + if (found) return ! ========================================================================== ! COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index e0c3e88594..f7ded73611 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -9,7 +9,7 @@ module volume_calc use constants use error, only: write_message use geometry, only: find_cell - use geometry_header, only: universes, cells + use geometry_header, only: cells, universe_id use hdf5_interface, only: file_open, file_close, write_attribute, & create_group, close_group, write_dataset, HID_T use output, only: header, time_stamp @@ -205,7 +205,7 @@ contains elseif (this % domain_type == FILTER_CELL) THEN do level = 1, p % n_coord do i_domain = 1, size(this % domain_id) - if (cells(p % coord(level) % cell) % id() & + if (cells(p % coord(level) % cell + 1) % id() & == this % domain_id(i_domain)) then i_material = p % material call check_hit(i_domain, i_material, indices, hits, n_mat) @@ -216,7 +216,7 @@ contains elseif (this % domain_type == FILTER_UNIVERSE) then do level = 1, p % n_coord do i_domain = 1, size(this % domain_id) - if (universes(p % coord(level) % universe) % id == & + if (universe_id(p % coord(level) % universe) == & this % domain_id(i_domain)) then i_material = p % material call check_hit(i_domain, i_material, indices, hits, n_mat) diff --git a/tests/regression_tests/diff_tally/results_true.dat b/tests/regression_tests/diff_tally/results_true.dat index 29c906f2e8..e3cff4ca0d 100644 --- a/tests/regression_tests/diff_tally/results_true.dat +++ b/tests/regression_tests/diff_tally/results_true.dat @@ -1,27 +1,27 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. -3,,density,flux,-4.5951022e+00,4.0201889e-01 -3,,density,flux,-9.9630192e+00,1.8897688e+00 -1,,density,flux,-4.2074596e-01,6.8569557e-02 -1,,density,flux,-2.8861314e-01,1.0032157e-01 -1,O16,nuclide_density,flux,-1.7146771e+01,1.8740496e+01 -1,O16,nuclide_density,flux,-1.4411733e+01,1.5986415e+01 -1,U235,nuclide_density,flux,-3.1082590e+03,3.5930199e+02 -1,U235,nuclide_density,flux,-2.5816702e+03,2.4275334e+02 -1,,temperature,flux,-2.2847162e-06,3.5489476e-04 -1,,temperature,flux,-8.5566667e-05,4.3023777e-04 -3,,density,total,-1.5582276e+00,2.8496759e-01 -3,,density,absorption,1.5711173e-01,1.7341244e-01 -3,,density,scatter,-1.7153393e+00,1.9431809e-01 -3,,density,fission,2.0152323e-01,7.4786095e-02 -3,,density,nu-fission,4.8687131e-01,1.8265878e-01 -3,,density,total,2.2943608e-01,8.6539576e-02 -3,,density,absorption,2.4646330e-01,8.6186628e-02 -3,,density,scatter,-1.7027217e-02,3.1732345e-03 -3,,density,fission,2.1668870e-01,7.5183313e-02 -3,,density,nu-fission,5.2774399e-01,1.8320780e-01 -3,,density,total,1.3300225e+01,4.2626034e+00 -3,,density,absorption,2.3903751e-01,8.9355264e-02 -3,,density,scatter,1.3061188e+01,4.1751261e+00 +3,,density,flux,-4.7291290e+00,8.8503902e-01 +3,,density,flux,-1.0533184e+01,3.0256001e+00 +1,,density,flux,-4.9634223e-01,1.3190338e-01 +1,,density,flux,-4.7458622e-01,5.6426916e-02 +1,O16,nuclide_density,flux,-1.4897399e+01,1.3583122e+01 +1,O16,nuclide_density,flux,-2.2389753e+01,1.5574833e+01 +1,U235,nuclide_density,flux,-2.8858701e+03,4.7033287e+02 +1,U235,nuclide_density,flux,-2.4203468e+03,3.2197255e+02 +1,,temperature,flux,-1.1195282e-04,3.5426553e-04 +1,,temperature,flux,-4.4294257e-04,5.4687257e-04 +3,,density,total,-1.5555916e+00,5.3353204e-01 +3,,density,absorption,1.4925823e-01,2.3501173e-01 +3,,density,scatter,-1.7048498e+00,3.3363896e-01 +3,,density,fission,1.3210039e-01,1.6377610e-01 +3,,density,nu-fission,3.1732288e-01,3.9893191e-01 +3,,density,total,1.4744448e-01,2.0435075e-01 +3,,density,absorption,1.6665433e-01,1.9696442e-01 +3,,density,scatter,-1.9209851e-02,7.8974770e-03 +3,,density,fission,1.4878594e-01,1.6616312e-01 +3,,density,nu-fission,3.6225815e-01,4.0486140e-01 +3,,density,total,1.2662462e+01,6.2596594e+00 +3,,density,absorption,2.2864288e-01,1.3031728e-01 +3,,density,scatter,1.2433820e+01,6.1294912e+00 3,,density,fission,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,total,0.0000000e+00,0.0000000e+00 @@ -29,19 +29,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 3,,density,scatter,0.0000000e+00,0.0000000e+00 3,,density,fission,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,total,3.3373495e-01,4.6827421e-02 -1,,density,absorption,6.7487092e-04,6.5232977e-03 -1,,density,scatter,3.3306008e-01,4.0307583e-02 -1,,density,fission,-1.8402784e-03,4.2206212e-03 -1,,density,nu-fission,-3.8312038e-03,1.0337874e-02 -1,,density,total,-2.4831544e-04,5.5020059e-03 -1,,density,absorption,-4.0311629e-03,5.0266899e-03 -1,,density,scatter,3.7828475e-03,4.8043964e-04 -1,,density,fission,-3.6379968e-03,4.1821819e-03 -1,,density,nu-fission,-8.8266852e-03,1.0193373e-02 -1,,density,total,-3.6848185e-01,1.9314902e-01 -1,,density,absorption,-7.3640407e-03,4.0184508e-03 -1,,density,scatter,-3.6111780e-01,1.8936612e-01 +1,,density,total,2.9404312e-01,7.0116815e-02 +1,,density,absorption,-1.1851195e-03,1.6943078e-03 +1,,density,scatter,2.9522824e-01,6.8519873e-02 +1,,density,fission,-4.4865588e-03,2.3660449e-03 +1,,density,nu-fission,-1.0266898e-02,5.7956130e-03 +1,,density,total,-3.6990351e-03,3.5138725e-03 +1,,density,absorption,-7.0064347e-03,2.7205092e-03 +1,,density,scatter,3.3073996e-03,7.9802045e-04 +1,,density,fission,-6.3630708e-03,2.3832611e-03 +1,,density,nu-fission,-1.5466339e-02,5.8086647e-03 +1,,density,total,-5.5743331e-01,6.1625011e-02 +1,,density,absorption,-9.1430498e-03,4.0831822e-03 +1,,density,scatter,-5.4829026e-01,5.7756112e-02 1,,density,fission,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,total,0.0000000e+00,0.0000000e+00 @@ -49,19 +49,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,fission,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,O16,nuclide_density,total,3.9050701e+01,8.5131265e+00 -1,O16,nuclide_density,absorption,-7.7573286e-01,7.1182570e-01 -1,O16,nuclide_density,scatter,3.9826434e+01,7.8536529e+00 -1,O16,nuclide_density,fission,-4.2193390e-01,6.0091792e-01 -1,O16,nuclide_density,nu-fission,-1.0411912e+00,1.4662352e+00 -1,O16,nuclide_density,total,-5.8976625e-01,7.9559349e-01 -1,O16,nuclide_density,absorption,-5.0047331e-01,7.1666177e-01 -1,O16,nuclide_density,scatter,-8.9292946e-02,8.9937898e-02 -1,O16,nuclide_density,fission,-3.6634959e-01,6.1270658e-01 -1,O16,nuclide_density,nu-fission,-8.9338374e-01,1.4932014e+00 -1,O16,nuclide_density,total,-3.3511352e+00,2.2365725e+01 -1,O16,nuclide_density,absorption,1.5473875e-01,4.3692662e-01 -1,O16,nuclide_density,scatter,-3.5058740e+00,2.1928948e+01 +1,O16,nuclide_density,total,3.9360816e+01,6.1390337e+00 +1,O16,nuclide_density,absorption,-6.8169613e-01,4.5302455e-01 +1,O16,nuclide_density,scatter,4.0042512e+01,6.4069240e+00 +1,O16,nuclide_density,fission,-6.1275244e-01,2.5764187e-01 +1,O16,nuclide_density,nu-fission,-1.5090099e+00,6.3048791e-01 +1,O16,nuclide_density,total,-7.5872288e-01,3.4843447e-01 +1,O16,nuclide_density,absorption,-6.7382098e-01,3.1002267e-01 +1,O16,nuclide_density,scatter,-8.4901894e-02,6.5286818e-02 +1,O16,nuclide_density,fission,-5.5597011e-01,2.7085418e-01 +1,O16,nuclide_density,nu-fission,-1.3555055e+00,6.6014209e-01 +1,O16,nuclide_density,total,-1.5539605e+01,2.3345398e+01 +1,O16,nuclide_density,absorption,-8.5880168e-02,5.0677339e-01 +1,O16,nuclide_density,scatter,-1.5453725e+01,2.2838892e+01 1,O16,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,total,0.0000000e+00,0.0000000e+00 @@ -69,19 +69,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,U235,nuclide_density,total,-9.1643805e+02,1.8814046e+02 -1,U235,nuclide_density,absorption,1.8114000e+02,4.6345847e+01 -1,U235,nuclide_density,scatter,-1.0975781e+03,1.4310981e+02 -1,U235,nuclide_density,fission,2.8765342e+02,2.2586319e+01 -1,U235,nuclide_density,nu-fission,7.0175585e+02,5.5024716e+01 -1,U235,nuclide_density,total,4.6786282e+02,2.8271731e+01 -1,U235,nuclide_density,absorption,3.6134508e+02,2.8453765e+01 -1,U235,nuclide_density,scatter,1.0651774e+02,1.3120589e+00 -1,U235,nuclide_density,fission,2.8859249e+02,2.2522591e+01 -1,U235,nuclide_density,nu-fission,7.0430623e+02,5.4858487e+01 -1,U235,nuclide_density,total,-5.0586963e+03,6.7151365e+02 -1,U235,nuclide_density,absorption,-1.2315731e+02,1.8905184e+01 -1,U235,nuclide_density,scatter,-4.9355390e+03,6.5260939e+02 +1,U235,nuclide_density,total,-7.9766848e+02,2.3476486e+02 +1,U235,nuclide_density,absorption,2.2136536e+02,5.7727802e+01 +1,U235,nuclide_density,scatter,-1.0190338e+03,1.7827380e+02 +1,U235,nuclide_density,fission,3.0782358e+02,2.4835876e+01 +1,U235,nuclide_density,nu-fission,7.5106928e+02,6.0668638e+01 +1,U235,nuclide_density,total,4.9643664e+02,3.6425359e+01 +1,U235,nuclide_density,absorption,3.8860811e+02,3.5290173e+01 +1,U235,nuclide_density,scatter,1.0782853e+02,1.9206965e+00 +1,U235,nuclide_density,fission,3.0827647e+02,2.4266512e+01 +1,U235,nuclide_density,nu-fission,7.5228268e+02,5.9109738e+01 +1,U235,nuclide_density,total,-4.7231732e+03,7.5353357e+02 +1,U235,nuclide_density,absorption,-1.1580370e+02,1.8531790e+01 +1,U235,nuclide_density,scatter,-4.6073695e+03,7.3502263e+02 1,U235,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,total,0.0000000e+00,0.0000000e+00 @@ -89,19 +89,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,temperature,total,8.6368265e-05,1.7363899e-04 -1,,temperature,absorption,1.9220262e-05,3.4988977e-05 -1,,temperature,scatter,6.7148003e-05,1.4635162e-04 -1,,temperature,fission,-5.2927287e-06,1.2158338e-05 -1,,temperature,nu-fission,-1.2897086e-05,2.9622912e-05 -1,,temperature,total,-4.2009323e-06,1.8263855e-05 -1,,temperature,absorption,-4.3037914e-06,1.6219907e-05 -1,,temperature,scatter,1.0285902e-07,2.0676492e-06 -1,,temperature,fission,-5.2953783e-06,1.2158786e-05 -1,,temperature,nu-fission,-1.2903531e-05,2.9624000e-05 -1,,temperature,total,-1.1160261e-04,6.1705782e-04 -1,,temperature,absorption,-2.0394397e-06,8.4055062e-06 -1,,temperature,scatter,-1.0956317e-04,6.0869070e-04 +1,,temperature,total,5.7228698e-05,1.7465295e-04 +1,,temperature,absorption,2.9495471e-05,3.1637786e-05 +1,,temperature,scatter,2.7733227e-05,1.4323068e-04 +1,,temperature,fission,-5.6710689e-06,1.2800905e-05 +1,,temperature,nu-fission,-1.3819116e-05,3.1189136e-05 +1,,temperature,total,-5.8315815e-06,1.8705738e-05 +1,,temperature,absorption,-5.3204426e-06,1.6688104e-05 +1,,temperature,scatter,-5.1113883e-07,2.0213875e-06 +1,,temperature,fission,-5.6723577e-06,1.2800214e-05 +1,,temperature,nu-fission,-1.3822264e-05,3.1187458e-05 +1,,temperature,total,-5.5283703e-04,7.6408422e-04 +1,,temperature,absorption,-6.2179157e-06,1.0365194e-05 +1,,temperature,scatter,-5.4661911e-04,7.5373133e-04 1,,temperature,fission,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 1,,temperature,total,0.0000000e+00,0.0000000e+00 @@ -109,68 +109,68 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,,temperature,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,fission,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,absorption,1.3594219e-01,1.0122046e-01 -3,,density,absorption,2.8656499e-01,3.7923985e-02 -1,,density,absorption,-2.2106085e-03,9.8387075e-03 -1,,density,absorption,-3.3239716e-03,8.7768141e-03 -1,O16,nuclide_density,absorption,-1.2640173e+00,9.1596515e-01 -1,O16,nuclide_density,absorption,1.3146714e-01,9.9380272e-01 -1,U235,nuclide_density,absorption,1.5677452e+02,8.1085465e+01 -1,U235,nuclide_density,absorption,-1.4334804e+02,3.2100956e+01 -1,,temperature,absorption,-8.9633351e-06,3.5750907e-05 -1,,temperature,absorption,7.0140498e-07,1.1694324e-05 +3,,density,absorption,3.1512672e-02,2.4627803e-01 +3,,density,absorption,1.1452915e-01,1.5733776e-01 +1,,density,absorption,-1.4010827e-03,5.7414887e-03 +1,,density,absorption,-8.9116087e-03,7.6054344e-03 +1,O16,nuclide_density,absorption,-1.0238428e+00,3.5596763e-01 +1,O16,nuclide_density,absorption,-5.4386638e-01,6.3275232e-01 +1,U235,nuclide_density,absorption,1.9740455e+02,1.1770740e+02 +1,U235,nuclide_density,absorption,-1.3873202e+02,3.0030411e+01 +1,,temperature,absorption,5.0340072e-06,2.9911538e-05 +1,,temperature,absorption,-3.7625680e-05,2.4883845e-05 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,4.7213241e-01,1.6556808e-01 +3,,density,scatter,2.5895299e-01,3.3093831e-01 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,3.4067110e-02,2.1267271e-02 -3,,density,nu-fission,4.1265284e-01,1.3891654e-01 -3,,density,scatter,-2.1663022e+00,3.2444877e-01 -3,,density,nu-fission,4.4524898e-01,1.4098968e-01 -3,,density,scatter,-2.1357153e-02,1.7423446e-02 +3,,density,scatter,2.4380993e-02,2.4380993e-02 +3,,density,nu-fission,1.3982705e-01,4.0427820e-01 +3,,density,scatter,-1.8460573e+00,1.6126524e-01 +3,,density,nu-fission,1.7650022e-01,4.0560218e-01 +3,,density,scatter,3.9711501e-02,4.4405692e-02 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,8.6343785e+00,2.9292930e+00 +3,,density,scatter,7.8960553e+00,4.4763896e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,scatter,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,4.3792818e+00,1.9915652e+00 +3,,density,scatter,4.6518780e+00,1.6862698e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-5.0479210e-03,9.5688761e-03 +1,,density,scatter,-1.4155379e-02,7.8989014e-03 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,4.4418327e-04,7.8121620e-04 -1,,density,nu-fission,-1.9240114e-02,1.4935352e-02 -1,,density,scatter,3.4099348e-01,2.8182893e-02 -1,,density,nu-fission,-2.4226858e-02,1.4481188e-02 -1,,density,scatter,1.1073847e-03,2.7478584e-04 +1,,density,scatter,1.0449664e-03,5.7043392e-04 +1,,density,nu-fission,-1.3723982e-02,1.5246898e-02 +1,,density,scatter,3.0959958e-01,5.7651589e-02 +1,,density,nu-fission,-1.9529210e-02,1.4804175e-02 +1,,density,scatter,5.1063052e-04,1.7748731e-03 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-2.7695506e-01,1.3529978e-01 +1,,density,scatter,-3.4116700e-01,1.4433252e-01 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-8.8202815e-02,7.8863942e-02 +1,,density,scatter,-2.0735470e-01,8.6865144e-02 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,O16,nuclide_density,scatter,1.1279170e-01,6.9836765e-02 -1,O16,nuclide_density,nu-fission,-1.6695105e+00,1.6777445e+00 -1,O16,nuclide_density,scatter,-1.0671543e-01,1.9127943e-01 +1,O16,nuclide_density,scatter,1.4017611e-01,9.5507870e-02 +1,O16,nuclide_density,nu-fission,-8.8199870e-01,1.6570982e+00 +1,O16,nuclide_density,scatter,-1.5442745e-01,1.3136597e-01 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,U235,nuclide_density,scatter,6.3235790e+00,3.9733167e+00 -1,U235,nuclide_density,nu-fission,6.2368225e+02,1.0172850e+02 -1,U235,nuclide_density,scatter,3.2601903e+01,6.2672072e+00 +1,U235,nuclide_density,scatter,8.1567193e+00,5.7077716e+00 +1,U235,nuclide_density,nu-fission,7.1482113e+02,1.4806072e+02 +1,U235,nuclide_density,scatter,5.2848809e+01,1.3486619e+01 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 -1,,temperature,scatter,-2.0884127e-06,1.6684645e-06 -1,,temperature,nu-fission,-1.5670247e-05,4.5939944e-05 -1,,temperature,scatter,3.9749436e-06,3.9749436e-06 +1,,temperature,scatter,-2.9956385e-07,2.8883252e-07 +1,,temperature,nu-fission,-2.1023382e-05,4.9883940e-05 +1,,temperature,scatter,5.9452821e-09,8.7655524e-09 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 1,,temperature,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 diff --git a/tests/regression_tests/lattice_hex/geometry.xml b/tests/regression_tests/lattice_hex/geometry.xml index cfe39849cb..fa6a18ee16 100644 --- a/tests/regression_tests/lattice_hex/geometry.xml +++ b/tests/regression_tests/lattice_hex/geometry.xml @@ -7,42 +7,42 @@ - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - + + + - + diff --git a/tests/regression_tests/multipole/results_true.dat b/tests/regression_tests/multipole/results_true.dat index 19c34229ff..890e47efd9 100644 --- a/tests/regression_tests/multipole/results_true.dat +++ b/tests/regression_tests/multipole/results_true.dat @@ -1,36 +1,36 @@ k-combined: -1.363786E+00 1.103929E-02 +1.342579E+00 1.221176E-02 tally 1: -3.960375E+00 -3.138356E+00 -2.875799E+00 -1.655329E+00 -5.521904E-01 -6.105083E-02 -4.617974E-01 -4.274130E-02 +3.839794E+00 +2.951721E+00 +2.785273E+00 +1.554128E+00 +5.349716E-01 +5.732174E-02 +4.499834E-01 +4.055011E-02 0.000000E+00 0.000000E+00 -2.254165E+01 -1.016444E+02 +2.258507E+01 +1.020294E+02 0.000000E+00 0.000000E+00 -6.839351E-04 -9.359599E-08 -2.251829E+01 -1.014341E+02 -4.903575E-05 -1.199780E-09 -3.595002E+02 -2.586079E+04 -2.875799E+00 -1.655329E+00 -2.174747E+00 -9.465589E-01 -3.543564E+02 -2.512619E+04 -4.903575E-05 -1.199780E-09 +6.862242E-04 +9.421377E-08 +2.256396E+01 +1.018388E+02 +1.146136E-04 +3.296980E-09 +3.624627E+02 +2.628739E+04 +2.785273E+00 +1.554128E+00 +2.176478E+00 +9.480405E-01 +3.574110E+02 +2.555993E+04 +1.146136E-04 +3.296980E-09 Cell ID = 11 Name = diff --git a/tests/regression_tests/rotation/geometry.xml b/tests/regression_tests/rotation/geometry.xml index 2226178771..f324617065 100644 --- a/tests/regression_tests/rotation/geometry.xml +++ b/tests/regression_tests/rotation/geometry.xml @@ -1,11 +1,11 @@ - - - + + - - + + + diff --git a/tests/regression_tests/rotation/materials.xml b/tests/regression_tests/rotation/materials.xml index 2472a74717..160c9c6789 100644 --- a/tests/regression_tests/rotation/materials.xml +++ b/tests/regression_tests/rotation/materials.xml @@ -2,8 +2,13 @@ - + + + + + + diff --git a/tests/regression_tests/rotation/results_true.dat b/tests/regression_tests/rotation/results_true.dat index 5cb3925a62..7598a4a478 100644 --- a/tests/regression_tests/rotation/results_true.dat +++ b/tests/regression_tests/rotation/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.943619E-01 3.309646E-03 +4.240695E-01 8.013236E-03 diff --git a/tests/regression_tests/translation/geometry.xml b/tests/regression_tests/translation/geometry.xml index e2cdf23916..6c6343865a 100644 --- a/tests/regression_tests/translation/geometry.xml +++ b/tests/regression_tests/translation/geometry.xml @@ -1,11 +1,11 @@ - - - + + - - + + + diff --git a/tests/regression_tests/translation/materials.xml b/tests/regression_tests/translation/materials.xml index 2472a74717..160c9c6789 100644 --- a/tests/regression_tests/translation/materials.xml +++ b/tests/regression_tests/translation/materials.xml @@ -2,8 +2,13 @@ - + + + + + + diff --git a/tests/regression_tests/translation/results_true.dat b/tests/regression_tests/translation/results_true.dat index 5cb3925a62..a503110345 100644 --- a/tests/regression_tests/translation/results_true.dat +++ b/tests/regression_tests/translation/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.943619E-01 3.309646E-03 +4.101355E-01 1.577552E-02 diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index 1434eaf3b4..94fa6abc76 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -60,3 +60,54 @@ def cell_with_lattice(): return ([inside_cyl, outside_cyl, main_cell], [m_inside[0], m_inside[1], m_inside[3], m_outside], univ, lattice) + +@pytest.fixture +def mixed_lattice_model(uo2, water): + cyl = openmc.ZCylinder(R=0.4) + c1 = openmc.Cell(fill=uo2, region=-cyl) + c1.temperature = 600.0 + c2 = openmc.Cell(fill=water, region=+cyl) + pin = openmc.Universe(cells=[c1, c2]) + + empty = openmc.Cell() + empty_univ = openmc.Universe(cells=[empty]) + + hex_lattice = openmc.HexLattice() + hex_lattice.center = (0.0, 0.0) + hex_lattice.pitch = (1.2, 10.0) + outer_ring = [pin]*6 + inner_ring = [empty_univ] + axial_level = [outer_ring, inner_ring] + hex_lattice.universes = [axial_level]*3 + hex_lattice.outer = empty_univ + + cell_hex = openmc.Cell(fill=hex_lattice) + u = openmc.Universe(cells=[cell_hex]) + rotated_cell_hex = openmc.Cell(fill=u) + rotated_cell_hex.rotation = (0., 0., 30.) + ur = openmc.Universe(cells=[rotated_cell_hex]) + + d = 6.0 + rect_lattice = openmc.RectLattice() + rect_lattice.lower_left = (-d, -d) + rect_lattice.pitch = (d, d) + rect_lattice.outer = empty_univ + rect_lattice.universes = [ + [ur, empty_univ], + [empty_univ, u] + ] + + xmin = openmc.XPlane(x0=-d, boundary_type='periodic') + xmax = openmc.XPlane(x0=d, boundary_type='periodic') + xmin.periodic_surface = xmax + ymin = openmc.YPlane(y0=-d, boundary_type='periodic') + ymax = openmc.YPlane(y0=d, boundary_type='periodic') + main_cell = openmc.Cell(fill=rect_lattice, + region=+xmin & -xmax & +ymin & -ymax) + + # Create geometry and use unique material in each fuel cell + geometry = openmc.Geometry([main_cell]) + geometry.determine_paths() + c1.fill = [water.clone() for i in range(c1.num_instances)] + + return openmc.model.Model(geometry) diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 2c3f95b4c7..9d3bc106d2 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -49,7 +49,7 @@ def capi_init(pincell_model): @pytest.fixture(scope='module') -def capi_simulation_init(pincell_model): +def capi_simulation_init(capi_init): openmc.capi.simulation_init() yield diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index a1cc5bc02c..4683dcd3af 100644 --- a/tests/unit_tests/test_data_multipole.py +++ b/tests/unit_tests/test_data_multipole.py @@ -3,8 +3,6 @@ import os import numpy as np import pytest import openmc.data - - pytestmark = pytest.mark.skipif( 'OPENMC_MULTIPOLE_LIBRARY' not in os.environ, reason='OPENMC_MULTIPOLE_LIBRARY environment variable must be set') @@ -18,44 +16,32 @@ def u235(): @pytest.fixture(scope='module') -def u234(): +def b10(): directory = os.environ['OPENMC_MULTIPOLE_LIBRARY'] - filename = os.path.join(directory, '092234.h5') + filename = os.path.join(directory, '005010.h5') return openmc.data.WindowedMultipole.from_hdf5(filename) -@pytest.fixture(scope='module') -def fe56(): - directory = os.environ['OPENMC_MULTIPOLE_LIBRARY'] - filename = os.path.join(directory, '026056.h5') - return openmc.data.WindowedMultipole.from_hdf5(filename) - - -def test_evaluate_rm(u235): - """Make sure a Reich-Moore multipole object can be called.""" +def test_evaluate(u235): + """Test the cross section evaluation of a library.""" energies = [1e-3, 1.0, 10.0, 50.] - total, absorption, fission = u235(energies, 0.0) - assert total[1] == pytest.approx(90.64895383) - total, absorption, fission = u235(energies, 300.0) - assert total[1] == pytest.approx(91.12534964) + scattering, absorption, fission = u235(energies, 0.0) + assert (scattering[1], absorption[1], fission[1]) == \ + pytest.approx((13.09, 77.56, 67.36), rel=1e-3) + scattering, absorption, fission = u235(energies, 300.0) + assert (scattering[2], absorption[2], fission[2]) == \ + pytest.approx((11.24, 21.26, 15.50), rel=1e-3) -def test_evaluate_mlbw(u234): - """Make sure a Multi-Level Breit-Wigner multipole object can be called.""" - energies = [1e-3, 1.0, 10.0, 50.] - total, absorption, fission = u234(energies, 0.0) - assert total[3] == pytest.approx(15.02827953) - total, absorption, fission = u234(energies, 300.0) - assert total[3] == pytest.approx(15.08269143) - - -def test_high_l(fe56): - """Test a nuclide (Fe56) with a high l-value (4).""" +def test_evaluate_none_poles(b10): + """Test a library with no poles, i.e., purely polynomials.""" energies = [1e-3, 1.0, 10.0, 1e3, 1e5] - total, absorption, fission = fe56(energies, 0.0) - assert total[0] == pytest.approx(25.072619556789267) - total, absorption, fission = fe56(energies, 300.0) - assert total[0] == pytest.approx(27.85535792368082) + scattering, absorption, fission = b10(energies, 0.0) + assert (scattering[0], absorption[0], fission[0]) == \ + pytest.approx((2.201, 19330., 0.), rel=1e-3) + scattering, absorption, fission = b10(energies, 300.0) + assert (scattering[-1], absorption[-1], fission[-1]) == \ + pytest.approx((2.878, 1.982, 0.), rel=1e-3) def test_export_to_hdf5(tmpdir, u235): diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 93d2fa6341..b5d6076bb3 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -244,3 +244,14 @@ def test_determine_paths(cell_with_lattice): for i in range(4): assert geom.get_instances(cells[0].paths[i]) == i assert geom.get_instances(mats[-1].paths[i]) == i + +def test_from_xml(run_in_tmpdir, mixed_lattice_model): + # Export model + mixed_lattice_model.export_to_xml() + + # Import geometry + geom = openmc.Geometry.from_xml() + assert isinstance(geom, openmc.Geometry) + ll, ur = geom.bounding_box + assert ll == pytest.approx((-6.0, -6.0, -np.inf)) + assert ur == pytest.approx((6.0, 6.0, np.inf)) diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index b7b745408d..7cedd8deef 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -182,3 +182,37 @@ def test_borated_water(): # Test the density override m = openmc.model.borated_water(975, 566.5, 15.51, density=0.9) assert m.density == pytest.approx(0.9, 1e-3) + + +def test_from_xml(run_in_tmpdir): + # Create a materials.xml file + m1 = openmc.Material(1, 'water') + m1.add_nuclide('H1', 1.0) + m1.add_nuclide('O16', 2.0) + m1.add_s_alpha_beta('c_H_in_H2O') + m1.set_density('g/cm3', 0.9) + m1.isotropic = ['H1'] + m2 = openmc.Material(2, 'zirc') + m2.add_nuclide('Zr90', 1.0, 'wo') + m2.set_density('kg/m3', 10.0) + m3 = openmc.Material(3) + m3.add_nuclide('N14', 0.02) + + mats = openmc.Materials([m1, m2, m3]) + mats.cross_sections = 'fake_path.xml' + mats.multipole_library = 'fake_multipole/' + mats.export_to_xml() + + # Regenerate materials from XML + mats = openmc.Materials.from_xml() + assert len(mats) == 3 + m1 = mats[0] + assert m1.id == 1 + assert m1.name == 'water' + assert m1.nuclides == [('H1', 1.0, 'ao'), ('O16', 2.0, 'ao')] + assert m1.isotropic == ['H1'] + m2 = mats[1] + assert m2.nuclides == [('Zr90', 1.0, 'wo')] + assert m2.density == 10.0 + assert m2.density_units == 'kg/m3' + assert mats[2].density_units == 'sum' diff --git a/tests/unit_tests/test_polynomials.py b/tests/unit_tests/test_polynomials.py index 52c30bb000..f6ac2711fd 100644 --- a/tests/unit_tests/test_polynomials.py +++ b/tests/unit_tests/test_polynomials.py @@ -27,3 +27,18 @@ def test_zernike_radial(): test_vals = zn_rad(rho) assert ref_vals == test_vals + + rho = [0.2, 0.5] + # Reference solution from running the Fortran implementation + raw_zn1 = np.array([ + 1.00000000e+00, -9.20000000e-01, 7.69600000e-01, + -5.66720000e-01, 3.35219200e-01, -1.01747000e-01]) + raw_zn2 = np.array([ + 1.00000000e+00, -5.00000000e-01, -1.25000000e-01, + 4.37500000e-01, -2.89062500e-01, -8.98437500e-02]) + + ref_vals = [np.sum(norm_coeff * raw_zn1), np.sum(norm_coeff * raw_zn2)] + + test_vals = zn_rad(rho) + + assert np.allclose(ref_vals, test_vals) diff --git a/tools/ci/travis-before-script.sh b/tools/ci/travis-before-script.sh index bbb34358b9..6d44521a6e 100755 --- a/tools/ci/travis-before-script.sh +++ b/tools/ci/travis-before-script.sh @@ -17,5 +17,7 @@ if [[ ! -d $ENDF/neutrons || ! -d $ENDF/photoat || ! -d $ENDF/atomic_relax ]]; t fi # Download multipole library -git clone --branch=master git://github.com/smharper/windowed_multipole_library.git wmp_lib -tar -C $HOME -xzvf wmp_lib/multipole_lib.tar.gz +if [[ ! -e $HOME/WMP_Library/092235.h5 ]]; then + wget https://github.com/mit-crpg/WMP_Library/releases/download/v1.0/WMP_Library_v1.0.tar.gz + tar -C $HOME -xzvf WMP_Library_v1.0.tar.gz +fi