Merge branch 'develop' into photon-production-fix

This commit is contained in:
amandalund 2018-08-29 13:23:35 -05:00
commit d81f1b9c7b
76 changed files with 2589 additions and 3397 deletions

View file

@ -299,8 +299,6 @@ add_library(libopenmc SHARED
src/cmfd_solver.F90
src/constants.F90
src/dict_header.F90
src/distribution_multivariate.F90
src/distribution_univariate.F90
src/eigenvalue.F90
src/endf.F90
src/endf_header.F90
@ -341,8 +339,6 @@ add_library(libopenmc SHARED
src/settings.F90
src/simulation_header.F90
src/simulation.F90
src/source.F90
src/source_header.F90
src/state_point.F90
src/stl_vector.F90
src/string.F90
@ -392,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
@ -400,6 +397,8 @@ add_library(libopenmc SHARED
src/message_passing.cpp
src/mgxs.cpp
src/mgxs_interface.cpp
src/nuclide.cpp
src/output.cpp
src/particle.cpp
src/plot.cpp
src/position.cpp
@ -414,8 +413,10 @@ add_library(libopenmc SHARED
src/scattdata.cpp
src/settings.cpp
src/simulation.cpp
src/source.cpp
src/state_point.cpp
src/string_functions.cpp
src/summary.cpp
src/surface.cpp
src/thermal.cpp
src/xml_interface.cpp

View file

@ -201,7 +201,7 @@ Each ``<cell>`` 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 \\

View file

@ -30,7 +30,6 @@ extern "C" {
int openmc_extend_filters(int32_t n, int32_t* index_start, int32_t* index_end);
int openmc_extend_materials(int32_t n, int32_t* index_start, int32_t* index_end);
int openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end);
int openmc_extend_sources(int32_t n, int32_t* index_start, int32_t* index_end);
int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end);
int openmc_filter_get_id(int32_t index, int32_t* id);
int openmc_filter_get_type(int32_t index, char* type);
@ -57,6 +56,7 @@ extern "C" {
int openmc_material_add_nuclide(int32_t index, const char name[], double density);
int openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n);
int openmc_material_get_id(int32_t index, int32_t* id);
int openmc_material_get_fissionable(int32_t index, bool* fissionable);
int openmc_material_get_volume(int32_t index, double* volume);
int openmc_material_set_density(int32_t index, double density);
int openmc_material_set_densities(int32_t index, int n, const char** name, const double* density);
@ -84,7 +84,6 @@ extern "C" {
int openmc_simulation_finalize();
int openmc_simulation_init();
int openmc_source_bank(struct Bank** ptr, int64_t* n);
int openmc_source_set_strength(int32_t index, double strength);
int openmc_spatial_legendre_filter_get_order(int32_t index, int* order);
int openmc_spatial_legendre_filter_get_params(int32_t index, int* axis, double* min, double* max);
int openmc_spatial_legendre_filter_set_order(int32_t index, int order);

View file

@ -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<int32_t>::max()};
constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits<int32_t>::max() - 1};
constexpr int32_t OP_COMPLEMENT {std::numeric_limits<int32_t>::max() - 2};
constexpr int32_t OP_INTERSECTION {std::numeric_limits<int32_t>::max() - 3};
constexpr int32_t OP_UNION {std::numeric_limits<int32_t>::max() - 4};
//==============================================================================
// Global variables
//==============================================================================
@ -31,11 +39,11 @@ extern "C" int FILL_LATTICE;
extern "C" int32_t n_cells;
class Cell;
extern std::vector<Cell*> global_cells;
extern std::vector<Cell*> cells;
extern std::unordered_map<int32_t, int32_t> cell_map;
class Universe;
extern std::vector<Universe*> global_universes;
extern std::vector<Universe*> universes;
extern std::unordered_map<int32_t, int32_t> universe_map;
//==============================================================================
@ -45,9 +53,12 @@ extern std::unordered_map<int32_t, int32_t> universe_map;
class Universe
{
public:
int32_t id; //!< Unique ID
std::vector<int32_t> cells; //!< Cells within this universe
//double x0, y0, z0; //!< Translation coordinates.
int32_t id_; //!< Unique ID
std::vector<int32_t> 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<int32_t> material;
//! May be multiple materials for distribcell.
std::vector<int32_t> 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<double> sqrtkT_;
//! Definition of spatial region as Boolean expression of half-spaces
std::vector<std::int32_t> region;
std::vector<std::int32_t> region_;
//! Reverse Polish notation for region expression
std::vector<std::int32_t> rpn;
bool simple; //!< Does the region contain only intersections?
std::vector<std::int32_t> rpn_;
bool simple_; //!< Does the region contain only intersections?
std::vector<int32_t> 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<double> rotation_;
std::vector<int32_t> offset_; //!< Distribcell offset table
Cell() {};

View file

@ -36,6 +36,10 @@ public:
//! Sample a value from the distribution
//! \return Sampled value
double sample() const;
// Properties
const std::vector<double>& x() const { return x_; }
const std::vector<double>& p() const { return p_; }
private:
std::vector<double> x_; //!< Possible outcomes
std::vector<double> p_; //!< Probability of each outcome

View file

@ -3,6 +3,8 @@
#include <memory>
#include "pugixml.hpp"
#include "openmc/distribution.h"
#include "openmc/position.h"
@ -16,14 +18,15 @@ namespace openmc {
class UnitSphereDistribution {
public:
UnitSphereDistribution() { };
explicit UnitSphereDistribution(Direction u) : u_ref{u} { };
explicit UnitSphereDistribution(Direction u) : u_ref_{u} { };
explicit UnitSphereDistribution(pugi::xml_node node);
virtual ~UnitSphereDistribution() = default;
//! Sample a direction from the distribution
//! \return Direction sampled
virtual Direction sample() const = 0;
Direction u_ref {0.0, 0.0, 1.0}; //!< reference direction
Direction u_ref_ {0.0, 0.0, 1.0}; //!< reference direction
};
//==============================================================================
@ -33,6 +36,7 @@ public:
class PolarAzimuthal : public UnitSphereDistribution {
public:
PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi);
explicit PolarAzimuthal(pugi::xml_node node);
//! Sample a direction from the distribution
//! \return Direction sampled
@ -62,12 +66,15 @@ public:
class Monodirectional : public UnitSphereDistribution {
public:
Monodirectional(Direction u) : UnitSphereDistribution{u} { };
explicit Monodirectional(pugi::xml_node node) : UnitSphereDistribution{node} { };
//! Sample a direction from the distribution
//! \return Sampled direction
Direction sample() const;
};
using UPtrAngle = std::unique_ptr<UnitSphereDistribution>;
} // namespace openmc
#endif // DISTRIBUTION_MULTI_H

View file

@ -1,4 +1,4 @@
#ifndef OPENMC_DISTRIBTUION_SPATIAL_H
#ifndef OPENMC_DISTRIBUTION_SPATIAL_H
#define OPENMC_DISTRIBUTION_SPATIAL_H
#include "pugixml.hpp"
@ -43,15 +43,18 @@ private:
class SpatialBox : public SpatialDistribution {
public:
explicit SpatialBox(pugi::xml_node node);
explicit SpatialBox(pugi::xml_node node, bool fission=false);
//! Sample a position from the distribution
//! \return Sampled position
Position sample() const;
// Properties
bool only_fissionable() const { return only_fissionable_; }
private:
Position lower_left_; //!< Lower-left coordinates of box
Position upper_right_; //!< Upper-right coordinates of box
bool only_fissionable {false}; //!< Only accept sites in fissionable region?
bool only_fissionable_ {false}; //!< Only accept sites in fissionable region?
};
//==============================================================================
@ -60,6 +63,8 @@ private:
class SpatialPoint : public SpatialDistribution {
public:
SpatialPoint() : r_{} { };
SpatialPoint(Position r) : r_{r} { };
explicit SpatialPoint(pugi::xml_node node);
//! Sample a position from the distribution
@ -69,6 +74,8 @@ private:
Position r_; //!< Single position at which sites are generated
};
using UPtrSpace = std::unique_ptr<SpatialDistribution>;
} // namespace openmc
#endif // OPENMC_DISTRIBUTION_SPATIAL_H

View file

@ -0,0 +1,20 @@
#ifndef OPENMC_FILE_UTILS_H
#define OPENMC_FILE_UTILS_H
#include <fstream> // for ifstream
#include <string>
namespace openmc {
//! Determine if a file exists
//! \param[in] filename Path to file
//! \return Whether file exists
inline bool file_exists(const std::string& filename)
{
std::ifstream s {filename};
return s.good();
}
} // namespace openmc
#endif // OPENMC_FILE_UTILS_H

View file

@ -1,10 +1,60 @@
#ifndef OPENMC_GEOMETRY_H
#define OPENMC_GEOMETRY_H
#include <cstdint>
#include <vector>
#include "openmc/particle.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
extern "C" int openmc_root_universe;
extern std::vector<int64_t> 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

View file

@ -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.

View file

@ -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<T>& arr, bool indep
template<typename T> inline void
write_attribute(hid_t obj_id, const char* name, T buffer)
{
write_attr(obj_id, name, 0, nullptr, H5TypeMap<T>::type_id, &buffer);
write_attr(obj_id, 0, nullptr, name, H5TypeMap<T>::type_id, &buffer);
}
inline void
@ -334,6 +340,13 @@ write_dataset(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
write_dataset(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data(), false);
}
template<typename T> inline void
write_dataset(hid_t obj_id, const char* name, const std::vector<T>& buffer)
{
hsize_t dims[] {buffer.size()};
write_dataset(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data(), false);
}
inline void
write_dataset(hid_t obj_id, const char* name, Position r)
{

View file

@ -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<Lattice*> lattices_c;
extern std::vector<Lattice*> lattices;
extern std::unordered_map<int32_t, int32_t> 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<int32_t> universes; //!< Universes filling each lattice tile
int32_t outer {NO_OUTER_UNIVERSE}; //!< Universe tiled outside the lattice
std::vector<int32_t> offsets; //!< Distribcell offset table
int32_t id_; //!< Universe ID number
std::string name_; //!< User-defined name
LatticeType type_;
std::vector<int32_t> universes_; //!< Universes filling each lattice tile
int32_t outer_ {NO_OUTER_UNIVERSE}; //!< Universe tiled outside the lattice
std::vector<int32_t> 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<int, 3> 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<int, 3> 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<double, std::array<int, 3>>
distance(Position r, Direction u, const int i_xyz[3]) const
distance(Position r, Direction u, const std::array<int, 3>& 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<int, 3> 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 &lat;
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<int, 3> i_xyz);
bool are_valid_indices(const int i_xyz[3]) const;
std::pair<double, std::array<int, 3>>
distance(Position r, Direction u, const int i_xyz[3]) const;
distance(Position r, Direction u, const std::array<int, 3>& i_xyz) const;
std::array<int, 3> 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<int, 3> 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<int, 3> 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<int, 3> 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<int, 3> i_xyz);
LatticeIter begin();
@ -235,12 +247,12 @@ public:
bool are_valid_indices(const int i_xyz[3]) const;
std::pair<double, std::array<int, 3>>
distance(Position r, Direction u, const int i_xyz[3]) const;
distance(Position r, Direction u, const std::array<int, 3>& i_xyz) const;
std::array<int, 3> 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<int, 3> 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<double, 2> 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<double, 2> pitch_; //!< Lattice tile width and height
};
} // namespace openmc

View file

@ -14,7 +14,7 @@ namespace openmc {
//==============================================================================
class Material;
extern std::vector<Material*> global_materials;
extern std::vector<Material*> materials;
extern std::unordered_map<int32_t, int32_t> 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);

View file

@ -16,6 +16,7 @@ namespace openmc {
extern std::vector<Mgxs> nuclides_MG;
extern std::vector<Mgxs> macro_xs;
extern "C" int num_energy_groups;
//==============================================================================
// Mgxs data loading interface methods

View file

@ -1,10 +1,24 @@
//! \file nuclide.h
//! \brief Nuclide type and other associated types/data
#ifndef OPENMC_NUCLIDE_H
#define OPENMC_NUCLIDE_H
#include <array>
#include "openmc/constants.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
// Minimum/maximum transport energy for each particle type. Order corresponds to
// that of the ParticleType enum
extern std::array<double, 2> energy_min;
extern std::array<double, 2> energy_max;
//===============================================================================
//! Cached microscopic cross sections for a particular nuclide at the current
//! energy

26
include/openmc/output.h Normal file
View file

@ -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

View file

@ -4,8 +4,10 @@
//! \file particle.h
//! \brief Particle type
#include <cstdint>
#include <array>
#include <cstdint>
#include <sstream>
#include <string>
#include "openmc/capi.h"
@ -33,7 +35,7 @@ constexpr double REL_MAX_LOST_PARTICLES {1.0e-6};
//! Particle types
enum class ParticleType {
neutron, photon, electron, positron
neutron = 1, photon = 2, electron = 3, positron = 4
};
extern "C" {
@ -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();
};

View file

@ -1,6 +1,8 @@
#ifndef OPENMC_POSITION_H
#define OPENMC_POSITION_H
#include <vector>
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<double> 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
#endif // OPENMC_POSITION_H

View file

@ -18,8 +18,12 @@ namespace openmc {
// Defined on Fortran side
extern "C" bool openmc_check_overlaps;
extern "C" bool openmc_particle_restart_run;
extern "C" bool openmc_photon_transport;
extern "C" bool openmc_restart_run;
extern "C" bool openmc_run_CE;
extern "C" int openmc_verbosity;
extern "C" bool openmc_write_all_tracks;
extern "C" bool openmc_write_initial_source;
// Defined in .cpp
// TODO: Make strings instead of char* once Fortran is gone

View file

@ -1,13 +1,39 @@
//! \file simulation.h
//! \brief Variables/functions related to a running simulation
#ifndef OPENMC_SIMULATION_H
#define OPENMC_SIMULATION_H
#include <cstdint>
#include <vector>
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
extern "C" int openmc_current_batch;
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;
#pragma omp threadprivate(openmc_current_work)
extern std::vector<int64_t> work_index;
#pragma omp threadprivate(openmc_current_work, openmc_trace)
//==============================================================================
// Functions
//==============================================================================
//! Initialize simulation
extern "C" void openmc_simulation_init_c();
//! Determine number of particles to transport per process
void calculate_work();
} // namespace openmc
#endif // OPENMC_SIMULATION_H

63
include/openmc/source.h Normal file
View file

@ -0,0 +1,63 @@
//! \file source.h
//! \brief External source distributions
#ifndef OPENMC_SOURCE_H
#define OPENMC_SOURCE_H
#include <memory>
#include <vector>
#include "pugixml.hpp"
#include "openmc/distribution_multi.h"
#include "openmc/distribution_spatial.h"
#include "openmc/capi.h"
#include "openmc/particle.h"
namespace openmc {
//==============================================================================
//! External source distribution
//==============================================================================
class SourceDistribution {
public:
// Constructors
SourceDistribution(UPtrSpace space, UPtrAngle angle, UPtrDist energy);
explicit SourceDistribution(pugi::xml_node node);
//! Sample from the external source distribution
//! \return Sampled site
Bank sample() const;
// Properties
double strength() const { return strength_; }
private:
ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted
double strength_ {1.0}; //!< Source strength
UPtrSpace space_; //!< Spatial distribution
UPtrAngle angle_; //!< Angular distribution
UPtrDist energy_; //!< Energy distribution
};
//==============================================================================
// Global variables
//==============================================================================
extern std::vector<SourceDistribution> external_sources;
//==============================================================================
// Functions
//==============================================================================
//! Initialize source bank from file/distribution
extern "C" void initialize_source();
//! Sample a site from all external source distributions in proportion to their
//! source strength
//! \return Sampled source site
extern "C" Bank sample_external_source();
} // namespace openmc
#endif // OPENMC_SOURCE_H

View file

@ -32,7 +32,7 @@ extern "C" const int BC_PERIODIC;
extern "C" int32_t n_surfaces;
class Surface;
extern std::vector<Surface*> global_surfaces;
extern std::vector<Surface*> surfaces;
extern std::map<int, int> 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<int> neighbor_pos_; //!< List of cells on positive side
std::vector<int> 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]);

View file

@ -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):

View file

@ -57,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

View file

@ -20,7 +20,6 @@ module openmc_api
use random_lcg, only: openmc_get_seed, openmc_set_seed
use settings
use simulation_header
use source_header, only: openmc_extend_sources, openmc_source_set_strength
use state_point, only: openmc_statepoint_write
use tally_header
use tally_filter_header
@ -38,13 +37,11 @@ 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
public :: openmc_extend_cells
public :: openmc_extend_materials
public :: openmc_extend_sources
public :: openmc_extend_tallies
public :: openmc_filter_get_id
public :: openmc_filter_get_type
@ -83,7 +80,6 @@ module openmc_api
public :: openmc_simulation_finalize
public :: openmc_simulation_init
public :: openmc_source_bank
public :: openmc_source_set_strength
public :: openmc_tally_allocate
public :: openmc_tally_get_estimator
public :: openmc_tally_get_id
@ -210,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
@ -301,12 +297,16 @@ contains
use plot_header
use sab_header
use settings
use source_header
use surface_header
use tally_derivative_header
use trigger_header
use volume_header
interface
subroutine free_memory_source() bind(C)
end subroutine
end interface
call free_memory_geometry()
call free_memory_surfaces()
call free_memory_material()

View file

@ -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<int32_t>::max()};
constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits<int32_t>::max() - 1};
constexpr int32_t OP_COMPLEMENT {std::numeric_limits<int32_t>::max() - 2};
constexpr int32_t OP_INTERSECTION {std::numeric_limits<int32_t>::max() - 3};
constexpr int32_t OP_UNION {std::numeric_limits<int32_t>::max() - 4};
//==============================================================================
// Global variables
//==============================================================================
int32_t n_cells {0};
std::vector<Cell*> global_cells;
std::vector<Cell*> cells;
std::unordered_map<int32_t, int32_t> cell_map;
std::vector<Universe*> global_universes;
std::vector<Universe*> universes;
std::unordered_map<int32_t, int32_t> universe_map;
//==============================================================================
@ -192,6 +183,28 @@ generate_rpn(int32_t cell_id, std::vector<int32_t> 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<int32_t> 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<int32_t> 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<std::string> mats
{get_node_array<std::string>(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<double>(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<double>(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<double>(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<int32_t>::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<int32_t> 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<double> 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<double, 3> 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 (openmc_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<double, int32_t> 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);}
}

View file

@ -4,11 +4,30 @@
#include <cmath> // for sqrt, sin, cos, max
#include "openmc/constants.h"
#include "openmc/error.h"
#include "openmc/math_functions.h"
#include "openmc/random_lcg.h"
#include "openmc/xml_interface.h"
namespace openmc {
//==============================================================================
// UnitSphereDistribution implementation
//==============================================================================
UnitSphereDistribution::UnitSphereDistribution(pugi::xml_node node)
{
// Read reference directional unit vector
if (check_for_node(node, "reference_uvw")) {
auto u_ref = get_node_array<double>(node, "reference_uvw");
if (u_ref.size() != 3)
fatal_error("Angular distribution reference direction must have "
"three parameters specified.");
u_ref_ = Direction(u_ref.data());
}
}
//==============================================================================
// PolarAzimuthal implementation
//==============================================================================
@ -16,15 +35,33 @@ namespace openmc {
PolarAzimuthal::PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi) :
UnitSphereDistribution{u}, mu_{std::move(mu)}, phi_{std::move(phi)} { }
PolarAzimuthal::PolarAzimuthal(pugi::xml_node node)
: UnitSphereDistribution{node}
{
if (check_for_node(node, "mu")) {
pugi::xml_node node_dist = node.child("mu");
mu_ = distribution_from_xml(node_dist);
} else {
mu_ = UPtrDist{new Uniform(-1., 1.)};
}
if (check_for_node(node, "phi")) {
pugi::xml_node node_dist = node.child("phi");
phi_ = distribution_from_xml(node_dist);
} else {
phi_ = UPtrDist{new Uniform(0.0, 2.0*PI)};
}
}
Direction PolarAzimuthal::sample() const
{
// Sample cosine of polar angle
double mu = mu_->sample();
if (mu == 1.0) return u_ref;
if (mu == 1.0) return u_ref_;
// Sample azimuthal angle
double phi = phi_->sample();
return rotate_angle(u_ref, mu, &phi);
return rotate_angle(u_ref_, mu, &phi);
}
//==============================================================================
@ -45,7 +82,7 @@ Direction Isotropic::sample() const
Direction Monodirectional::sample() const
{
return u_ref;
return u_ref_;
}
} // namespace openmc

View file

@ -1,257 +0,0 @@
module distribution_multivariate
use constants, only: ONE, TWO, PI
use distribution_univariate
use error, only: fatal_error
use random_lcg, only: prn
use math, only: rotate_angle
use xml_interface
implicit none
!===============================================================================
! UNITSPHEREDISTRIBUTION type defines a probability density function for points
! on the unit sphere. Extensions of this type are used to sample angular
! distributions for starting sources
!===============================================================================
type, abstract :: UnitSphereDistribution
real(8) :: reference_uvw(3)
contains
procedure(unitsphere_distribution_sample_), deferred :: sample
end type UnitSphereDistribution
abstract interface
function unitsphere_distribution_sample_(this) result(uvw)
import UnitSphereDistribution
class(UnitSphereDistribution), intent(in) :: this
real(8) :: uvw(3)
end function unitsphere_distribution_sample_
end interface
!===============================================================================
! Derived classes of UnitSphereDistribution
!===============================================================================
! Explicit distribution of polar and azimuthal angles
type, extends(UnitSphereDistribution) :: PolarAzimuthal
class(Distribution), allocatable :: mu
class(Distribution), allocatable :: phi
contains
procedure :: sample => polar_azimuthal_sample
end type PolarAzimuthal
! Uniform distribution on the unit sphere
type, extends(UnitSphereDistribution) :: Isotropic
contains
procedure :: sample => isotropic_sample
end type Isotropic
! Monodirectional distribution
type, extends(UnitSphereDistribution) :: Monodirectional
contains
procedure :: sample => monodirectional_sample
end type Monodirectional
!===============================================================================
! SPATIALDISTRIBUTION type defines a probability density function for arbitrary
! points in Euclidean space.
!===============================================================================
type, abstract :: SpatialDistribution
contains
procedure(spatial_distribution_from_xml_), deferred :: from_xml
procedure(spatial_distribution_sample_), deferred :: sample
end type SpatialDistribution
abstract interface
subroutine spatial_distribution_from_xml_(this, node)
import SpatialDistribution, XMLNode
class(SpatialDistribution), intent(inout) :: this
type(XMLNode), intent(in) :: node
end subroutine spatial_distribution_from_xml_
function spatial_distribution_sample_(this) result(xyz)
import SpatialDistribution
class(SpatialDistribution), intent(in) :: this
real(8) :: xyz(3)
end function spatial_distribution_sample_
end interface
type, extends(SpatialDistribution) :: CartesianIndependent
class(Distribution), allocatable :: x
class(Distribution), allocatable :: y
class(Distribution), allocatable :: z
contains
procedure :: from_xml => cartesian_independent_from_xml
procedure :: sample => cartesian_independent_sample
end type CartesianIndependent
type, extends(SpatialDistribution) :: SpatialBox
real(8) :: lower_left(3)
real(8) :: upper_right(3)
logical :: only_fissionable = .false.
contains
procedure :: from_xml => spatial_box_from_xml
procedure :: sample => spatial_box_sample
end type SpatialBox
type, extends(SpatialDistribution) :: SpatialPoint
real(8) :: xyz(3)
contains
procedure :: from_xml => spatial_point_from_xml
procedure :: sample => spatial_point_sample
end type SpatialPoint
contains
function polar_azimuthal_sample(this) result(uvw)
class(PolarAzimuthal), intent(in) :: this
real(8) :: uvw(3)
real(8) :: mu ! cosine of polar angle
real(8) :: phi ! azimuthal angle
! Sample cosine of polar angle
mu = this % mu % sample()
if (mu == ONE) then
uvw(:) = this % reference_uvw
else
! Sample azimuthal angle
phi = this % phi % sample()
uvw = rotate_angle(this % reference_uvw, mu, phi)
end if
end function polar_azimuthal_sample
function isotropic_sample(this) result(uvw)
class(Isotropic), intent(in) :: this
real(8) :: uvw(3)
real(8) :: phi
real(8) :: mu
phi = TWO*PI*prn()
mu = TWO*prn() - ONE
uvw(1) = mu
uvw(2) = sqrt(ONE - mu*mu) * cos(phi)
uvw(3) = sqrt(ONE - mu*mu) * sin(phi)
end function isotropic_sample
function monodirectional_sample(this) result(uvw)
class(Monodirectional), intent(in) :: this
real(8) :: uvw(3)
uvw(:) = this % reference_uvw
end function monodirectional_sample
subroutine cartesian_independent_from_xml(this, node)
class(CartesianIndependent), intent(inout) :: this
type(XMLNode), intent(in) :: node
type(XMLNode) :: node_dist
! Read distribution for x coordinate
if (check_for_node(node, "x")) then
node_dist = node % child("x")
call distribution_from_xml(this % x, node_dist)
else
allocate(Discrete :: this % x)
select type (dist => this % x)
type is (Discrete)
allocate(dist % x(1), dist % p(1))
dist % x(1) = ZERO
dist % p(1) = ONE
end select
end if
! Read distribution for y coordinate
if (check_for_node(node, "y")) then
node_dist = node % child("y")
call distribution_from_xml(this % y, node_dist)
else
allocate(Discrete :: this % y)
select type (dist => this % y)
type is (Discrete)
allocate(dist % x(1), dist % p(1))
dist % x(1) = ZERO
dist % p(1) = ONE
end select
end if
if (check_for_node(node, "z")) then
node_dist = node % child("z")
call distribution_from_xml(this % z, node_dist)
else
allocate(Discrete :: this % z)
select type (dist => this % z)
type is (Discrete)
allocate(dist % x(1), dist % p(1))
dist % x(1) = ZERO
dist % p(1) = ONE
end select
end if
end subroutine cartesian_independent_from_xml
function cartesian_independent_sample(this) result(xyz)
class(CartesianIndependent), intent(in) :: this
real(8) :: xyz(3)
xyz(1) = this % x % sample()
xyz(2) = this % y % sample()
xyz(3) = this % z % sample()
end function cartesian_independent_sample
subroutine spatial_box_from_xml(this, node)
class(SpatialBox), intent(inout) :: this
type(XMLNode), intent(in) :: node
real(8), allocatable :: temp_real(:)
! Make sure correct number of parameters are given
if (node_word_count(node, "parameters") /= 6) then
call fatal_error('Box/fission spatial source must have &
&six parameters specified.')
end if
! Read lower-right/upper-left coordinates
allocate(temp_real(6))
call get_node_array(node, "parameters", temp_real)
this % lower_left(:) = temp_real(1:3)
this % upper_right(:) = temp_real(4:6)
deallocate(temp_real)
end subroutine spatial_box_from_xml
function spatial_box_sample(this) result(xyz)
class(SpatialBox), intent(in) :: this
real(8) :: xyz(3)
integer :: i
real(8) :: r(3)
r = [ (prn(), i = 1,3) ]
xyz(:) = this % lower_left + r*(this % upper_right - this % lower_left)
end function spatial_box_sample
subroutine spatial_point_from_xml(this, node)
class(SpatialPoint), intent(inout) :: this
type(XMLNode), intent(in) :: node
! Make sure correct number of parameters are given
if (node_word_count(node, "parameters") /= 3) then
call fatal_error('Point spatial source must have &
&three parameters specified.')
end if
! Read location of point source
call get_node_array(node, "parameters", this % xyz)
end subroutine spatial_point_from_xml
function spatial_point_sample(this) result(xyz)
class(SpatialPoint), intent(in) :: this
real(8) :: xyz(3)
xyz(:) = this % xyz
end function spatial_point_sample
end module distribution_multivariate

View file

@ -55,7 +55,8 @@ Position CartesianIndependent::sample() const
// SpatialBox implementation
//==============================================================================
SpatialBox::SpatialBox(pugi::xml_node node)
SpatialBox::SpatialBox(pugi::xml_node node, bool fission)
: only_fissionable_{fission}
{
// Read lower-right/upper-left coordinates
auto params = get_node_array<double>(node, "parameters");

View file

@ -1,373 +0,0 @@
module distribution_univariate
use constants, only: ZERO, ONE, HALF, HISTOGRAM, LINEAR_LINEAR, &
MAX_LINE_LEN, MAX_WORD_LEN
use error, only: fatal_error
use random_lcg, only: prn
use math, only: maxwell_spectrum, watt_spectrum
use pugixml
use string, only: to_lower
use xml_interface
implicit none
!===============================================================================
! DISTRIBUTION type defines a probability density function
!===============================================================================
type, abstract :: Distribution
contains
procedure(distribution_sample_), deferred :: sample
end type Distribution
type DistributionContainer
class(Distribution), allocatable :: obj
end type DistributionContainer
abstract interface
function distribution_sample_(this) result(x)
import Distribution
class(Distribution), intent(in) :: this
real(8) :: x
end function distribution_sample_
end interface
!===============================================================================
! Derived classes of Distribution
!===============================================================================
! Discrete distribution
type, extends(Distribution) :: Discrete
real(8), allocatable :: x(:)
real(8), allocatable :: p(:)
contains
procedure :: sample => discrete_sample
procedure :: initialize => discrete_initialize
end type Discrete
! Uniform distribution over the interval [a,b]
type, extends(Distribution) :: Uniform
real(8) :: a
real(8) :: b
contains
procedure :: sample => uniform_sample
end type Uniform
! Maxwellian distribution of form c*E*exp(-E/a)
type, extends(Distribution) :: Maxwell
real(8) :: theta
contains
procedure :: sample => maxwell_sample
end type Maxwell
! Watt fission spectrum with form c*exp(-E/a)*sinh(sqrt(b*E))
type, extends(Distribution) :: Watt
real(8) :: a
real(8) :: b
contains
procedure :: sample => watt_sample
end type Watt
! Histogram or linear-linear interpolated tabular distribution
type, extends(Distribution) :: Tabular
integer :: interpolation
real(8), allocatable :: x(:) ! tabulated independent variable
real(8), allocatable :: p(:) ! tabulated probability density
real(8), allocatable :: c(:) ! cumulative distribution at tabulated values
contains
procedure :: sample => tabular_sample
procedure :: initialize => tabular_initialize
end type Tabular
type, extends(Distribution) :: Equiprobable
real(8), allocatable :: x(:)
contains
procedure :: sample => equiprobable_sample
end type Equiprobable
contains
function discrete_sample(this) result(x)
class(Discrete), intent(in) :: this
real(8) :: x
integer :: i ! loop counter
integer :: n ! size of distribution
real(8) :: c ! cumulative frequency
real(8) :: xi ! sampled CDF value
n = size(this%x)
if (n > 1) then
xi = prn()
c = ZERO
do i = 1, size(this%x)
c = c + this%p(i)
if (xi < c) exit
end do
x = this%x(i)
else
x = this%x(1)
end if
end function discrete_sample
subroutine discrete_initialize(this, x, p)
class(Discrete), intent(inout) :: this
real(8), intent(in) :: x(:)
real(8), intent(in) :: p(:)
integer :: n
! Check length of x, p arrays
if (size(x) /= size(p)) then
call fatal_error('Tabulated probabilities not of same length as &
&independent variable.')
end if
! Copy probability density function
n = size(x)
allocate(this%x(n), this%p(n))
this%x(:) = x(:)
this%p(:) = p(:)
! Normalize density function
this%p(:) = this%p(:)/sum(this%p)
end subroutine
function uniform_sample(this) result(x)
class(Uniform), intent(in) :: this
real(8) :: x
x = this%a + prn()*(this%b - this%a)
end function uniform_sample
function maxwell_sample(this) result(x)
class(Maxwell), intent(in) :: this
real(8) :: x
x = maxwell_spectrum(this%theta)
end function maxwell_sample
function watt_sample(this) result(x)
class(Watt), intent(in) :: this
real(8) :: x
x = watt_spectrum(this%a, this%b)
end function watt_sample
function tabular_sample(this) result(x)
class(Tabular), intent(in) :: this
real(8) :: x
integer :: i
real(8) :: c ! sampled cumulative frequency
real(8) :: m ! slope of PDF
real(8) :: x_i, x_i1 ! i-th and (i+1)th x values
real(8) :: c_i, c_i1 ! i-th and (i+1)th cumulative distribution values
real(8) :: p_i, p_i1 ! i-th and (i+1)th probability density values
! Sample value of CDF
c = prn()
! Find first CDF bin which is above the sampled value
c_i = this%c(1)
do i = 1, size(this%c) - 1
c_i1 = this%c(i + 1)
if (c <= c_i1) exit
c_i = c_i1
end do
! Determine bounding PDF values
x_i = this%x(i)
p_i = this%p(i)
if (this%interpolation == HISTOGRAM) then
! Histogram interpolation
if (p_i > ZERO) then
x = x_i + (c - c_i)/p_i
else
x = x_i
end if
else
! Linear-linear interpolation
x_i1 = this%x(i + 1)
p_i1 = this%p(i + 1)
m = (p_i1 - p_i)/(x_i1 - x_i)
if (m == ZERO) then
x = x_i + (c - c_i)/p_i
else
x = x_i + (sqrt(max(ZERO, p_i*p_i + 2*m*(c - c_i))) - p_i)/m
end if
end if
end function tabular_sample
subroutine tabular_initialize(this, x, p, interp)
class(Tabular), intent(inout) :: this
real(8), intent(in) :: x(:)
real(8), intent(in) :: p(:)
integer, intent(in) :: interp
integer :: i
integer :: n
! Check interpolation parameter
if (interp /= HISTOGRAM .and. interp /= LINEAR_LINEAR) then
call fatal_error('Only histogram and linear-linear interpolation for tabular &
&distribution is supported.')
end if
! Check length of x, p arrays
if (size(x) /= size(p)) then
call fatal_error('Tabulated probabilities not of same length as &
&independent variable.')
end if
! Copy probability density function and interpolation parameter
n = size(x)
allocate(this%x(n), this%p(n), this%c(n))
this%interpolation = interp
this%x(:) = x(:)
this%p(:) = p(:)
! Calculate cumulative distribution function
this%c(1) = ZERO
do i = 2, n
if (this%interpolation == HISTOGRAM) then
this%c(i) = this%c(i-1) + this%p(i-1)*(this%x(i) - this%x(i-1))
elseif (this%interpolation == LINEAR_LINEAR) then
this%c(i) = this%c(i-1) + HALF*(this%p(i-1) + this%p(i)) * &
(this%x(i) - this%x(i-1))
end if
end do
! Normalize density and distribution functions
this%p(:) = this%p(:)/this%c(n)
this%c(:) = this%c(:)/this%c(n)
end subroutine tabular_initialize
function equiprobable_sample(this) result(x)
class(Equiprobable), intent(in) :: this
real(8) :: x
integer :: i
integer :: n
real(8) :: r
real(8) :: xl, xr
n = size(this%x)
r = prn()
i = 1 + int((n - 1)*r)
xl = this%x(i)
xr = this%x(i+1)
x = xl + ((n - 1)*r - i + ONE) * (xr - xl)
end function equiprobable_sample
subroutine distribution_from_xml(dist, node_dist)
class(Distribution), allocatable, intent(inout) :: dist
type(XMLNode), intent(in) :: node_dist
character(MAX_WORD_LEN) :: type
character(MAX_LINE_LEN) :: temp_str
integer :: n
integer :: temp_int
real(8), allocatable :: temp_real(:)
if (check_for_node(node_dist, "type")) then
! Determine type of distribution
call get_node_value(node_dist, "type", type)
! Determine number of parameters specified
if (check_for_node(node_dist, "parameters")) then
n = node_word_count(node_dist, "parameters")
else
n = 0
end if
! Allocate extension of Distribution
select case (to_lower(type))
case ('uniform')
allocate(Uniform :: dist)
if (n /= 2) then
call fatal_error('Uniform distribution must have two &
&parameters specified.')
end if
case ('maxwell')
allocate(Maxwell :: dist)
if (n /= 1) then
call fatal_error('Maxwell energy distribution must have one &
&parameter specified.')
end if
case ('watt')
allocate(Watt :: dist)
if (n /= 2) then
call fatal_error('Watt energy distribution must have two &
&parameters specified.')
end if
case ('discrete')
allocate(Discrete :: dist)
case ('tabular')
allocate(Tabular :: dist)
case default
call fatal_error('Invalid distribution type: ' // trim(type) // '.')
end select
! Read parameters and interpolation for distribution
select type (dist)
type is (Uniform)
allocate(temp_real(2))
call get_node_array(node_dist, "parameters", temp_real)
dist%a = temp_real(1)
dist%b = temp_real(2)
deallocate(temp_real)
type is (Maxwell)
call get_node_value(node_dist, "parameters", dist%theta)
type is (Watt)
allocate(temp_real(2))
call get_node_array(node_dist, "parameters", temp_real)
dist%a = temp_real(1)
dist%b = temp_real(2)
deallocate(temp_real)
type is (Discrete)
allocate(temp_real(n))
call get_node_array(node_dist, "parameters", temp_real)
call dist%initialize(temp_real(1:n/2), temp_real(n/2+1:n))
deallocate(temp_real)
type is (Tabular)
! Read interpolation
if (check_for_node(node_dist, "interpolation")) then
call get_node_value(node_dist, "interpolation", temp_str)
select case(to_lower(temp_str))
case ('histogram')
temp_int = HISTOGRAM
case ('linear-linear')
temp_int = LINEAR_LINEAR
case default
call fatal_error("Unknown interpolation type for distribution: " &
// trim(temp_str))
end select
else
temp_int = HISTOGRAM
end if
! Read and initialize tabular distribution
allocate(temp_real(n))
call get_node_array(node_dist, "parameters", temp_real)
call dist%initialize(temp_real(1:n/2), temp_real(n/2+1:n), temp_int)
deallocate(temp_real)
end select
end if
end subroutine distribution_from_xml
end module distribution_univariate

View file

@ -270,8 +270,9 @@ contains
character(kind=C_CHAR), intent(in) :: message(message_len)
integer(C_INT), intent(in), value :: level
character(message_len+1) :: message_out
! Using * in the internal write adds an extra space at the beginning
write(message_out, *) message
call write_message(message_out, level)
call write_message(message_out(2:), level)
end subroutine write_message_from_c
end module error

View file

@ -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

406
src/geometry.cpp Normal file
View file

@ -0,0 +1,406 @@
#include "openmc/geometry.h"
#include <array>
#include <sstream>
#include "openmc/cell.h"
#include "openmc/constants.h"
#include "openmc/error.h"
#include "openmc/lattice.h"
#include "openmc/simulation.h"
#include "openmc/surface.h"
namespace openmc {
std::vector<int64_t> 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<int>* 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 (openmc_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 (openmc_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<int, 3> 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<int, 3> 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<int, 3> 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<double, std::array<int, 3>> 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

View file

@ -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,80 @@ 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 * temperature_default));
}
}
}
}
}
//==============================================================================
int32_t
find_root_universe()
{
// Find all the universes listed as a cell fill.
std::unordered_set<int32_t> 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 +153,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<int32_t> 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<int32_t> 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 +270,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 +294,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 +320,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 +342,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<std::int32_t>::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 +364,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 +397,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 +409,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 +423,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 +446,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

View file

@ -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

View file

@ -910,6 +910,8 @@ using_mpio_device(hid_t obj_id)
template<>
const hid_t H5TypeMap<int>::type_id = H5T_NATIVE_INT;
template<>
const hid_t H5TypeMap<unsigned long>::type_id = H5T_NATIVE_ULONG;
template<>
const hid_t H5TypeMap<int64_t>::type_id = H5T_NATIVE_INT64;
template<>
const hid_t H5TypeMap<double>::type_id = H5T_NATIVE_DOUBLE;

View file

@ -2,7 +2,6 @@
#include <cstddef>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>

View file

@ -7,8 +7,6 @@ module input_xml
use cmfd_header, only: cmfd_mesh
use constants
use dict_header, only: DictIntInt, DictCharInt, DictEntryCI
use distribution_multivariate
use distribution_univariate
use endf, only: reaction_name
use error, only: fatal_error, warning, write_message, openmc_err_msg
use geometry, only: neighbor_lists
@ -28,10 +26,9 @@ module input_xml
use surface_header
use set_header, only: SetChar
use settings
use source_header
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
@ -51,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
@ -102,6 +98,13 @@ module input_xml
integer(C_INT32_T), intent(in), value :: univ
integer(C_INT) :: n
end function maximum_levels
subroutine set_particle_energy_bounds(particle, E_min, E_max) bind(C)
import C_INT, C_DOUBLE
integer(C_INT), value :: particle
real(C_DOUBLE), value :: E_min
real(C_DOUBLE), value :: E_max
end subroutine
end interface
contains
@ -171,14 +174,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)
@ -186,7 +189,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.")
@ -212,7 +215,6 @@ contains
integer, allocatable :: temp_int_array(:)
integer :: n_tracks
logical :: file_exists
character(MAX_WORD_LEN) :: type
character(MAX_LINE_LEN) :: filename
type(XMLDocument) :: doc
type(XMLNode) :: root
@ -227,7 +229,6 @@ contains
type(XMLNode) :: node_vol
type(XMLNode) :: node_tab_leg
type(XMLNode), allocatable :: node_mesh_list(:)
type(XMLNode), allocatable :: node_source_list(:)
type(XMLNode), allocatable :: node_vol_list(:)
! Check if settings.xml exists
@ -456,46 +457,13 @@ contains
! ==========================================================================
! EXTERNAL SOURCE
! Get point to list of <source> elements and make sure there is at least one
call get_node_list(root, "source", node_source_list)
n = size(node_source_list)
if (n == 0) then
! Default source is isotropic point source at origin with Watt spectrum
allocate(external_source(1))
external_source % particle = NEUTRON
external_source % strength = ONE
allocate(SpatialPoint :: external_source(1) % space)
select type (space => external_source(1) % space)
type is (SpatialPoint)
space % xyz(:) = [ZERO, ZERO, ZERO]
end select
allocate(Isotropic :: external_source(1) % angle)
external_source(1) % angle % reference_uvw(:) = [ZERO, ZERO, ONE]
allocate(Watt :: external_source(1) % energy)
select type(energy => external_source(1) % energy)
type is (Watt)
energy % a = 0.988e6_8
energy % b = 2.249e-6_8
end select
else
! Allocate array for sources
allocate(external_source(n))
end if
! Handled on C++ side
! Check if we want to write out source
if (check_for_node(root, "write_initial_source")) then
call get_node_value(root, "write_initial_source", write_initial_source)
end if
! Read each source
do i = 1, n
call external_source(i) % from_xml(node_source_list(i), path_source)
end do
! Survival biasing
if (check_for_node(root, "survival_biasing")) then
call get_node_value(root, "survival_biasing", survival_biasing)
@ -1003,17 +971,14 @@ contains
subroutine read_geometry_xml()
integer :: i, j, k
integer :: n, n_mats, n_rlats, n_hlats
integer :: id
integer :: i
integer :: n, n_rlats, n_hlats
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(MAX_WORD_LEN), allocatable :: sarray(:)
character(:), allocatable :: region_spec
type(Cell), pointer :: c
class(Lattice), pointer :: lat
type(XMLDocument) :: doc
@ -1023,7 +988,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
@ -1091,20 +1055,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)
@ -1114,43 +1070,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
@ -1186,62 +1105,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)
@ -1252,7 +1115,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)
@ -1276,11 +1139,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)
@ -1288,15 +1148,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)
@ -1304,36 +1160,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()
@ -1473,7 +1306,6 @@ contains
character(3) :: element ! name of element, e.g. Zr
character(MAX_WORD_LEN) :: units ! units on density
character(MAX_LINE_LEN) :: filename ! absolute path to materials.xml
character(MAX_LINE_LEN) :: temp_str ! temporary string when reading
character(MAX_WORD_LEN), allocatable :: sarray(:)
real(8) :: val ! value entered for density
real(8) :: temp_dble ! temporary double prec. real
@ -3479,6 +3311,8 @@ contains
! Get the minimum and maximum energies
energy_min(NEUTRON) = energy_bins(num_energy_groups + 1)
energy_max(NEUTRON) = energy_bins(1)
call set_particle_energy_bounds(NEUTRON, energy_min(NEUTRON), &
energy_max(NEUTRON))
! Get the datasets present in the library
call get_groups(file_id, names)
@ -3639,6 +3473,8 @@ contains
nuclides(i_nuclide) % grid(1) % energy(1))
energy_max(NEUTRON) = min(energy_max(NEUTRON), nuclides(i_nuclide) % &
grid(1) % energy(size(nuclides(i_nuclide) % grid(1) % energy)))
call set_particle_energy_bounds(NEUTRON, energy_min(NEUTRON), &
energy_max(NEUTRON))
end if
! Add name and alias to dictionary
@ -3672,6 +3508,8 @@ contains
energy_max(PHOTON) = min(energy_max(PHOTON), &
exp(elements(i_element) % energy(size(elements(i_element) &
% energy))))
call set_particle_energy_bounds(PHOTON, energy_min(PHOTON), &
energy_max(PHOTON))
end if
! Add element to set
@ -3713,6 +3551,8 @@ contains
if (size(ttb_e_grid) >= 1) then
energy_min(PHOTON) = max(energy_min(PHOTON), ttb_e_grid(1))
energy_max(PHOTON) = min(energy_max(PHOTON), ttb_e_grid(size(ttb_e_grid)))
call set_particle_energy_bounds(PHOTON, energy_min(PHOTON), &
energy_max(PHOTON))
end if
! Take logarithm of energies since they are log-log interpolated
@ -3793,46 +3633,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
@ -3889,9 +3689,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
@ -3903,56 +3703,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

View file

@ -18,7 +18,7 @@ namespace openmc {
// Global variables
//==============================================================================
std::vector<Lattice*> lattices_c;
std::vector<Lattice*> lattices;
std::unordered_map<int32_t, int32_t> lattice_map;
@ -29,17 +29,17 @@ std::unordered_map<int32_t, int32_t> 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<std::string> 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 <lower_left> must be the same as the "
"number of entries on <dimension>.");
}
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 <pitch> must be the same as the "
"number of entries on <dimension>.");
}
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<int, 3> 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<double, std::array<int, 3>>
RectLattice::distance(Position r, Direction u, const int i_xyz[3]) const
RectLattice::distance(Position r, Direction u, const std::array<int, 3>& 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<int, 3>
RectLattice::get_indices(Position r) const
{
int ix {static_cast<int>(std::ceil((r.x - lower_left.x) / pitch.x))-1};
int iy {static_cast<int>(std::ceil((r.y - lower_left.y) / pitch.y))-1};
int ix {static_cast<int>(std::ceil((r.x - lower_left_.x) / pitch_.x))-1};
int iy {static_cast<int>(std::ceil((r.y - lower_left_.y) / pitch_.y))-1};
int iz;
if (is_3d) {
iz = static_cast<int>(std::ceil((r.z - lower_left.z) / pitch.z))-1;
if (is_3d_) {
iz = static_cast<int>(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<int, 3> 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<double, 2> pitch_short {{pitch[0], pitch[1]}};
std::array<double, 2> pitch_short {{pitch_[0], pitch_[1]}};
write_dataset(lat_group, "pitch", pitch_short);
std::array<double, 2> ll_short {{lower_left[0], lower_left[1]}};
std::array<double, 2> ll_short {{lower_left_[0], lower_left_[1]}};
write_dataset(lat_group, "lower_left", ll_short);
std::array<int, 2> nc_short {{n_cells[0], n_cells[1]}};
std::array<int, 2> 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<hsize_t>(n_cells[0])};
hsize_t ny {static_cast<hsize_t>(n_cells[1])};
hsize_t nz {static_cast<hsize_t>(n_cells[2])};
if (is_3d_) {
hsize_t nx {static_cast<hsize_t>(n_cells_[0])};
hsize_t ny {static_cast<hsize_t>(n_cells_[1])};
hsize_t nz {static_cast<hsize_t>(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<hsize_t>(n_cells[0])};
hsize_t ny {static_cast<hsize_t>(n_cells[1])};
hsize_t nx {static_cast<hsize_t>(n_cells_[0])};
hsize_t ny {static_cast<hsize_t>(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<std::string> 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 <n_axial> must have <center> "
"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 <n_axial> must have <center> "
"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<std::string> 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 <n_axial> must have <pitch> "
"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 <n_axial> must have <pitch> "
"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<std::string> 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<int, 3> 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<double, std::array<int, 3>>
HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const
HexLattice::distance(Position r, Direction u, const std::array<int, 3>& 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<int, 3> 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<int, 3> 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<int, 3> 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<int, 3> 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<int, 3> 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<int, 3> 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<int, 3> 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<int, 3>
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<int, 3> out;
if (is_3d) {
out[2] = static_cast<int>(std::ceil(r_o.z / pitch[1] + 0.5 * n_axial))-1;
if (is_3d_) {
out[2] = static_cast<int>(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<int>(std::floor(r_o.x
/ (0.5*std::sqrt(3.0) * pitch[0])));
out[1] = static_cast<int>(std::floor(alpha / pitch[0]));
/ (0.5*std::sqrt(3.0) * pitch_[0])));
out[1] = static_cast<int>(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<int, 3> 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<int, 3> 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<double, 1> pitch_short {{pitch[0]}};
std::array<double, 1> pitch_short {{pitch_[0]}};
write_dataset(lat_group, "pitch", pitch_short);
std::array<double, 2> center_short {{center[0], center[1]}};
std::array<double, 2> center_short {{center_[0], center_[1]}};
write_dataset(lat_group, "center", center_short);
}
// Write the universe ids.
hsize_t nx {static_cast<hsize_t>(2*n_rings - 1)};
hsize_t ny {static_cast<hsize_t>(2*n_rings - 1)};
hsize_t nz {static_cast<hsize_t>(n_axial)};
hsize_t nx {static_cast<hsize_t>(2*n_rings_ - 1)};
hsize_t ny {static_cast<hsize_t>(2*n_rings_ - 1)};
hsize_t nz {static_cast<hsize_t>(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<double, std::array<int, 3>> 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<int, 3> 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

View file

@ -13,7 +13,7 @@ namespace openmc {
// Global variables
//==============================================================================
std::vector<Material*> global_materials;
std::vector<Material*> materials;
std::unordered_map<int32_t, int32_t> 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();
}
}

View file

@ -678,6 +678,22 @@ contains
end function openmc_material_get_id
function openmc_material_get_fissionable(index, fissionable) result(err) bind(C)
! returns whether a material is fissionable
integer(C_INT32_T), value :: index
logical(C_BOOL), intent(out) :: fissionable
integer(C_INT) :: err
if (index >= 1 .and. index <= size(materials)) then
fissionable = materials(index) % fissionable
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in materials array is out of bounds.")
end if
end function openmc_material_get_fissionable
function openmc_material_set_id(index, id) result(err) bind(C)
! Set the ID of a material
integer(C_INT32_T), value, intent(in) :: index

View file

@ -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)

View file

@ -147,7 +147,7 @@ module mgxs_interface
end interface
! Number of energy groups
integer(C_INT) :: num_energy_groups
integer(C_INT), bind(C) :: num_energy_groups
! Number of delayed groups
integer(C_INT) :: num_delayed_groups
@ -159,6 +159,13 @@ module mgxs_interface
real(8), allocatable :: energy_bin_avg(:)
! Energy group structure with increasing energy
real(8), allocatable :: rev_energy_bins(:)
real(C_DOUBLE), allocatable, target :: rev_energy_bins(:)
end module mgxs_interface
contains
function rev_energy_bins_ptr() result(ptr) bind(C)
type(C_PTR) :: ptr
ptr = C_LOC(rev_energy_bins(1))
end function
end module mgxs_interface

23
src/nuclide.cpp Normal file
View file

@ -0,0 +1,23 @@
#include "openmc/nuclide.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
std::array<double, 2> energy_min {0.0, 0.0};
std::array<double, 2> energy_max {INFTY, INFTY};
//==============================================================================
// Fortran compatibility functions
//==============================================================================
extern "C" void
set_particle_energy_bounds(int particle, double E_min, double E_max)
{
energy_min[particle - 1] = E_min;
energy_max[particle - 1] = E_max;
}
} // namespace openmc

View file

@ -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

75
src/output.cpp Normal file
View file

@ -0,0 +1,75 @@
#include "openmc/output.h"
#include <algorithm> // for std::transform
#include <cstring> // for strlen
#include <iomanip> // for setw
#include <iostream>
#include <sstream>
#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 (openmc_verbosity >= level) {
std::cout << out.str() << std::endl << std::endl;
}
}
//==============================================================================
extern "C" void
print_overlap_check() {
#ifdef OPENMC_MPI
std::vector<int64_t> 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<int32_t> 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

View file

@ -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;

View file

@ -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

View file

@ -9,7 +9,7 @@ module settings
! ============================================================================
! ENERGY TREATMENT RELATED VARIABLES
logical(C_BOOL) :: run_CE = .true. ! Run in CE mode?
logical(C_BOOL), bind(C, name='openmc_run_CE') :: run_CE = .true. ! Run in CE mode?
! ============================================================================
! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES
@ -21,12 +21,12 @@ module settings
integer(C_INT) :: temperature_method = TEMPERATURE_NEAREST
logical :: temperature_multipole = .false.
real(C_DOUBLE) :: temperature_tolerance = 10.0_8
real(8) :: temperature_default = 293.6_8
real(C_DOUBLE) :: temperature_default = 293.6_8
real(8) :: temperature_range(2) = [ZERO, ZERO]
integer :: n_log_bins ! number of bins for logarithmic grid
logical :: photon_transport = .false.
logical(C_BOOL), bind(C, name='openmc_photon_transport') :: photon_transport = .false.
integer :: electron_treatment = ELECTRON_TTB
! ============================================================================
@ -104,7 +104,8 @@ module settings
particle_restart_run = .false.
! Write out initial source
logical :: write_initial_source = .false.
logical(C_BOOL), bind(C, name='openmc_write_initial_source') :: &
write_initial_source = .false.
! Whether create fission neutrons or not. Only applied for MODE_FIXEDSOURCE
logical :: create_fission_neutrons = .true.
@ -120,7 +121,6 @@ module settings
character(MAX_FILE_LEN) :: path_input ! Path to input file
character(MAX_FILE_LEN) :: path_cross_sections = '' ! Path to cross_sections.xml
character(MAX_FILE_LEN) :: path_multipole ! Path to wmp library
character(MAX_FILE_LEN) :: path_source = '' ! Path to binary source
character(MAX_FILE_LEN) :: path_state_point ! Path to binary state point
character(MAX_FILE_LEN) :: path_source_point ! Path to binary source point
character(MAX_FILE_LEN) :: path_particle_restart ! Path to particle restart

View file

@ -2,7 +2,11 @@
#include "openmc/capi.h"
#include "openmc/constants.h"
#include "openmc/distribution.h"
#include "openmc/distribution_multi.h"
#include "openmc/distribution_spatial.h"
#include "openmc/error.h"
#include "openmc/source.h"
#include "openmc/string_utils.h"
#include "openmc/xml_interface.h"
@ -98,6 +102,24 @@ void read_settings(pugi::xml_node* root)
temperature_range[0] = range[0];
temperature_range[1] = range[1];
}
// ==========================================================================
// EXTERNAL SOURCE
// Get point to list of <source> elements and make sure there is at least one
for (pugi::xml_node node : root->children("source")) {
external_sources.emplace_back(node);
}
// If no source specified, default to isotropic point source at origin with Watt spectrum
if (external_sources.empty()) {
SourceDistribution source {
UPtrSpace{new SpatialPoint({0.0, 0.0, 0.0})},
UPtrAngle{new Isotropic()},
UPtrDist{new Watt(0.988, 2.249e-6)}
};
external_sources.push_back(std::move(source));
}
}
} // namespace openmc

View file

@ -24,13 +24,12 @@ 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
use settings
use simulation_header
use source, only: initialize_source, sample_external_source
use state_point, only: openmc_statepoint_write, write_source_point, load_state_point
use string, only: to_str
use tally, only: accumulate_tallies, setup_active_tallies, &
@ -52,6 +51,19 @@ module simulation
integer(C_INT), parameter :: STATUS_EXIT_MAX_BATCH = 1
integer(C_INT), parameter :: STATUS_EXIT_ON_TRIGGER = 2
interface
subroutine openmc_simulation_init_c() bind(C)
end subroutine
subroutine initialize_source() bind(C)
end subroutine
function sample_external_source() result(site) bind(C)
import Bank
type(Bank) :: site
end function
end interface
contains
!===============================================================================
@ -241,7 +253,10 @@ contains
subroutine finalize_generation()
integer(8) :: i
interface
subroutine fill_source_bank_fixedsource() bind(C)
end subroutine
end interface
! Update global tallies with the omp private accumulation variables
!$omp parallel
@ -294,13 +309,7 @@ contains
elseif (run_mode == MODE_FIXEDSOURCE) then
! For fixed-source mode, we need to sample the external source
if (path_source == '') then
do i = 1, work
call set_particle_seed((total_gen + overall_generation()) * &
n_particles + work_index(rank) + i)
call sample_external_source(source_bank(i))
end do
end if
call fill_source_bank_fixedsource()
end if
end subroutine finalize_generation
@ -425,6 +434,9 @@ contains
! Skip if simulation has already been initialized
if (simulation_initialized) return
! Call initialization on C++ side
call openmc_simulation_init_c()
! Set up tally procedure pointers
call init_tally_routines()
@ -511,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
@ -520,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
@ -572,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
@ -596,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.

View file

@ -1,4 +1,7 @@
#include "openmc/simulation.h"
#include "openmc/capi.h"
#include "openmc/message_passing.h"
// OPENMC_RUN encompasses all the main logic where iterations are performed
// over the batches, generations, and histories in a fixed source or k-eigenvalue
@ -16,3 +19,47 @@ int openmc_run() {
openmc_simulation_finalize();
return err;
}
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
std::vector<int64_t> work_index;
//==============================================================================
// Functions
//==============================================================================
void openmc_simulation_init_c()
{
// Determine how much work each process should do
calculate_work();
}
void calculate_work()
{
// Determine minimum amount of particles to simulate on each processor
int64_t min_work = n_particles/mpi::n_procs;
// Determine number of processors that have one extra particle
int64_t remainder = n_particles % mpi::n_procs;
int64_t i_bank = 0;
work_index.reserve(mpi::n_procs);
work_index.push_back(0);
for (int i = 0; i < mpi::n_procs; ++i) {
// Number of particles for rank i
int64_t work_i = i < remainder ? min_work + 1 : min_work;
// Set number of particles
if (mpi::rank == i) openmc_work = work_i;
// Set index into source bank for rank i
i_bank += work_i;
work_index.push_back(i_bank);
}
}
} // namespace openmc

View file

@ -22,7 +22,7 @@ module simulation_header
integer(C_INT), bind(C, name='openmc_current_batch') :: current_batch ! current batch
integer(C_INT), bind(C, name='openmc_current_gen') :: current_gen ! current generation within a batch
integer :: total_gen = 0 ! total number of generations simulated
integer(C_INT), bind(C, name='openmc_total_gen') :: total_gen = 0 ! total number of generations simulated
logical(C_BOOL), bind(C, name='openmc_simulation_initialized') :: &
simulation_initialized = .false.
logical :: need_depletion_rx ! need to calculate depletion reaction rx?
@ -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)
@ -80,8 +77,8 @@ contains
! OVERALL_GENERATION determines the overall generation number
!===============================================================================
pure function overall_generation() result(gen)
integer :: gen
pure function overall_generation() result(gen) bind(C)
integer(C_INT) :: gen
gen = gen_per_batch*(current_batch - 1) + current_gen
end function overall_generation
@ -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)

View file

@ -1,141 +0,0 @@
module source
#ifdef OPENMC_MPI
use message_passing
#endif
use algorithm, only: binary_search
use bank_header, only: Bank, source_bank
use constants
use distribution_univariate, only: Discrete
use distribution_multivariate, only: SpatialBox
use error, only: fatal_error
use geometry, only: find_cell
use hdf5_interface
use math
use message_passing, only: rank
use mgxs_interface, only: rev_energy_bins, num_energy_groups
use output, only: write_message
use particle_header, only: Particle
use random_lcg, only: prn, set_particle_seed, prn_set_stream
use settings
use simulation_header
use source_header, only: external_source
use string, only: to_str
use state_point, only: read_source_bank, write_source_bank
implicit none
contains
!===============================================================================
! INITIALIZE_SOURCE initializes particles in the source bank
!===============================================================================
subroutine initialize_source()
integer(8) :: i ! loop index over bank sites
integer(8) :: id ! particle id
integer(HID_T) :: file_id
character(MAX_WORD_LEN) :: filetype
character(MAX_FILE_LEN) :: filename
type(Bank), pointer :: src ! source bank site
call write_message("Initializing source particles...", 5)
if (path_source /= '') then
! Read the source from a binary file instead of sampling from some
! assumed source distribution
call write_message('Reading source file from ' // trim(path_source) &
&// '...', 6)
! Open the binary file
file_id = file_open(path_source, 'r', parallel=.true.)
! Read the file type
call read_attribute(filetype, file_id, "filetype")
! Check to make sure this is a source file
if (filetype /= 'source' .and. filetype /= 'statepoint') then
call fatal_error("Specified starting source file not a source file &
&type.")
end if
! Read in the source bank
call read_source_bank(file_id, work_index, source_bank)
! Close file
call file_close(file_id)
else
! Generation source sites from specified distribution in user input
do i = 1, work
! Get pointer to source bank site
src => source_bank(i)
! initialize random number seed
id = total_gen*n_particles + work_index(rank) + i
call set_particle_seed(id)
! sample external source distribution
call sample_external_source(src)
end do
end if
! Write out initial source
if (write_initial_source) then
call write_message('Writing out initial source...', 5)
filename = trim(path_output) // 'initial_source.h5'
file_id = file_open(filename, 'w', parallel=.true.)
call write_source_bank(file_id, work_index, source_bank)
call file_close(file_id)
end if
end subroutine initialize_source
!===============================================================================
! SAMPLE_EXTERNAL_SOURCE samples the user-specified external source and stores
! the position, angle, and energy in a Bank type.
!===============================================================================
subroutine sample_external_source(site)
type(Bank), intent(inout) :: site ! source site
integer :: i ! dummy loop index
integer :: n_source ! number of source distributions
real(8) :: c ! cumulative frequency
real(8) :: xi
! Set the random number generator to the source stream.
call prn_set_stream(STREAM_SOURCE)
! Sample from among multiple source distributions
n_source = size(external_source)
if (n_source > 1) then
xi = prn()*sum(external_source(:) % strength)
c = ZERO
do i = 1, n_source
c = c + external_source(i) % strength
if (xi < c) exit
end do
else
i = 1
end if
! Sample source site from i-th source distribution
site = external_source(i) % sample()
! If running in MG, convert site % E to group
if (.not. run_CE) then
site % E = real(binary_search(rev_energy_bins, num_energy_groups + 1, &
site % E), 8)
site % E = num_energy_groups + 1 - site % E
end if
! Set the random number generator back to the tracking stream.
call prn_set_stream(STREAM_TRACKING)
end subroutine sample_external_source
end module source

378
src/source.cpp Normal file
View file

@ -0,0 +1,378 @@
#include "openmc/source.h"
#include <algorithm> // for move
#include <sstream> // for stringstream
#include "xtensor/xadapt.hpp"
#include "openmc/cell.h"
#include "openmc/error.h"
#include "openmc/file_utils.h"
#include "openmc/hdf5_interface.h"
#include "openmc/material.h"
#include "openmc/message_passing.h"
#include "openmc/mgxs_interface.h"
#include "openmc/nuclide.h"
#include "openmc/capi.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
#include "openmc/state_point.h"
#include "openmc/xml_interface.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
std::vector<SourceDistribution> external_sources;
//==============================================================================
// SourceDistribution implementation
//==============================================================================
SourceDistribution::SourceDistribution(UPtrSpace space, UPtrAngle angle, UPtrDist energy)
: space_{std::move(space)}, angle_{std::move(angle)}, energy_{std::move(energy)} { }
SourceDistribution::SourceDistribution(pugi::xml_node node)
{
// Check for particle type
if (check_for_node(node, "particle")) {
auto temp_str = get_node_value(node, "particle", true, true);
if (temp_str == "neutron") {
particle_ = ParticleType::neutron;
} else if (temp_str == "photon") {
particle_ = ParticleType::photon;
openmc_photon_transport = true;
} else {
fatal_error(std::string("Unknown source particle type: ") + temp_str);
}
}
// Check for source strength
if (check_for_node(node, "strength")) {
strength_ = std::stod(get_node_value(node, "strength"));
}
// Check for external source file
if (check_for_node(node, "file")) {
// Copy path of source file
path_source = get_node_value(node, "file", false, true);
// Check if source file exists
if (!file_exists(path_source)) {
std::stringstream msg;
msg << "Source file '" << path_source << "' does not exist.";
fatal_error(msg);
}
} else {
// Spatial distribution for external source
if (check_for_node(node, "space")) {
// Get pointer to spatial distribution
pugi::xml_node node_space = node.child("space");
// Check for type of spatial distribution and read
std::string type;
if (check_for_node(node_space, "type"))
type = get_node_value(node_space, "type", true, true);
if (type == "cartesian") {
space_ = UPtrSpace{new CartesianIndependent(node_space)};
} else if (type == "box") {
space_ = UPtrSpace{new SpatialBox(node_space)};
} else if (type == "fission") {
space_ = UPtrSpace{new SpatialBox(node_space, true)};
} else if (type == "point") {
space_ = UPtrSpace{new SpatialPoint(node_space)};
} else {
std::stringstream msg;
msg << "Invalid spatial distribution for external source: " << type;
fatal_error(msg);
}
} else {
// If no spatial distribution specified, make it a point source
space_ = UPtrSpace{new SpatialPoint()};
}
// Determine external source angular distribution
if (check_for_node(node, "angle")) {
// Get pointer to angular distribution
pugi::xml_node node_angle = node.child("angle");
// Check for type of angular distribution
std::string type;
if (check_for_node(node_angle, "type"))
type = get_node_value(node_angle, "type", true, true);
if (type == "isotropic") {
angle_ = UPtrAngle{new Isotropic()};
} else if (type == "monodirectional") {
angle_ = UPtrAngle{new Monodirectional(node_angle)};
} else if (type == "mu-phi") {
angle_ = UPtrAngle{new PolarAzimuthal(node_angle)};
} else {
std::stringstream msg;
msg << "Invalid angular distribution for external source: " << type;
fatal_error(msg);
}
} else {
angle_ = UPtrAngle{new Isotropic()};
}
// Determine external source energy distribution
if (check_for_node(node, "energy")) {
pugi::xml_node node_dist = node.child("energy");
energy_ = distribution_from_xml(node_dist);
} else {
// Default to a Watt spectrum with parameters 0.988 MeV and 2.249 MeV^-1
energy_ = UPtrDist{new Watt(0.988e6, 2.249e-6)};
}
}
}
Bank SourceDistribution::sample() const
{
Bank site;
// Set weight to one by default
site.wgt = 1.0;
// Repeat sampling source location until a good site has been found
bool found = false;
int n_reject = 0;
static int n_accept = 0;
while (!found) {
// Set particle type
site.particle = static_cast<int>(particle_);
// Sample spatial distribution
Position r = space_->sample();
site.xyz[0] = r.x;
site.xyz[1] = r.y;
site.xyz[2] = r.z;
// Now search to see if location exists in geometry
int32_t cell_index, instance;
int err = openmc_find_cell(site.xyz, &cell_index, &instance);
found = (err != OPENMC_E_GEOMETRY);
// Check if spatial site is in fissionable material
if (found) {
auto space_box = dynamic_cast<SpatialBox*>(space_.get());
if (space_box) {
if (space_box->only_fissionable()) {
// Determine material
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;
} else {
bool fissionable;
openmc_material_get_fissionable(mat_index + 1, &fissionable);
if (!fissionable) found = false;
}
}
}
}
// Check for rejection
if (!found) {
++n_reject;
if (n_reject >= EXTSRC_REJECT_THRESHOLD &&
static_cast<double>(n_accept)/n_reject <= EXTSRC_REJECT_FRACTION) {
fatal_error("More than 95% of external source sites sampled were "
"rejected. Please check your external source definition.");
}
}
}
// Increment number of accepted samples
++n_accept;
// Sample angle
Direction u = angle_->sample();
site.uvw[0] = u.x;
site.uvw[1] = u.y;
site.uvw[2] = u.z;
// Check for monoenergetic source above maximum particle energy
auto p = static_cast<int>(particle_);
auto energy_ptr = dynamic_cast<Discrete*>(energy_.get());
if (energy_ptr) {
auto energies = xt::adapt(energy_ptr->x());
if (xt::any(energies > energy_max[p-1])) {
fatal_error("Source energy above range of energies of at least "
"one cross section table");
} else if (xt::any(energies < energy_min[p-1])) {
fatal_error("Source energy below range of energies of at least "
"one cross section table");
}
}
while (true) {
// Sample energy spectrum
site.E = energy_->sample();
// Resample if energy falls outside minimum or maximum particle energy
if (site.E < energy_max[p-1] && site.E > energy_min[p-1]) break;
}
// Set delayed group
site.delayed_group = 0;
return site;
}
//==============================================================================
// Non-member functions
//==============================================================================
void initialize_source()
{
write_message("Initializing source particles...", 5);
// Get pointer to source bank
Bank* source_bank;
int64_t n;
openmc_source_bank(&source_bank, &n);
if (path_source != "") {
// Read the source from a binary file instead of sampling from some
// assumed source distribution
std::stringstream msg;
msg << "Reading source file from " << path_source << "...";
write_message(msg, 6);
// Open the binary file
hid_t file_id = file_open(path_source, 'r', true);
// Read the file type
std::string filetype;
read_attribute(file_id, "filetype", filetype);
// Check to make sure this is a source file
if (filetype != "source" && filetype != "statepoint") {
fatal_error("Specified starting source file not a source file type.");
}
// Read in the source bank
read_source_bank(file_id, work_index.data(), source_bank);
// Close file
file_close(file_id);
} else {
// Generation source sites from specified distribution in user input
for (int64_t i = 0; i < openmc_work; ++i) {
// initialize random number seed
int64_t id = openmc_total_gen*n_particles + work_index[openmc::mpi::rank] + i + 1;
set_particle_seed(id);
// sample external source distribution
source_bank[i] = sample_external_source();
}
}
// Write out initial source
if (openmc_write_initial_source) {
write_message("Writing out initial source...", 5);
std::string filename = path_output + "initial_source.h5";
hid_t file_id = file_open(filename, 'w', true);
write_source_bank(file_id, work_index.data(), source_bank);
file_close(file_id);
}
}
extern "C" double* rev_energy_bins_ptr();
Bank sample_external_source()
{
// Set the random number generator to the source stream.
prn_set_stream(STREAM_SOURCE);
// Determine total source strength
double total_strength = 0.0;
for (auto& s : external_sources)
total_strength += s.strength();
// Sample from among multiple source distributions
int i = 0;
if (external_sources.size() > 1) {
double xi = prn()*total_strength;
double c = 0.0;
for (; i < external_sources.size(); ++i) {
c += external_sources[i].strength();
if (xi < c) break;
}
}
// Sample source site from i-th source distribution
Bank site {external_sources[i].sample()};
// If running in MG, convert site % E to group
if (!openmc_run_CE) {
// Get pointer to rev_energy_bins array on Fortran side
double* rev_energy_bins = rev_energy_bins_ptr();
int n = num_energy_groups + 1;
site.E = lower_bound_index(rev_energy_bins, rev_energy_bins + n, site.E);
site.E = num_energy_groups - site.E;
}
// Set the random number generator back to the tracking stream.
prn_set_stream(STREAM_TRACKING);
return site;
}
//==============================================================================
// Fortran compatibility functions
//==============================================================================
extern "C" void free_memory_source()
{
external_sources.clear();
}
extern "C" double total_source_strength()
{
double strength = 0.0;
for (const auto& s : external_sources) {
strength += s.strength();
}
return strength;
}
// Needed in fill_source_bank_fixedsource
extern "C" int overall_generation();
//! Fill source bank at end of generation for fixed source simulations
extern "C" void fill_source_bank_fixedsource()
{
if (path_source.empty()) {
// Get pointer to source bank
Bank* source_bank;
int64_t n;
openmc_source_bank(&source_bank, &n);
for (int64_t i = 0; i < openmc_work; ++i) {
// initialize random number seed
int64_t id = (openmc_total_gen + overall_generation())*n_particles +
work_index[openmc::mpi::rank] + i + 1;
set_particle_seed(id);
// sample external source distribution
source_bank[i] = sample_external_source();
}
}
}
} // namespace openmc

View file

@ -1,388 +0,0 @@
module source_header
use, intrinsic :: ISO_C_BINDING
use bank_header, only: Bank
use constants
use distribution_univariate
use distribution_multivariate
use error
use geometry, only: find_cell
use material_header, only: materials
use nuclide_header, only: energy_min, energy_max
use particle_header
use settings, only: photon_transport
use string, only: to_lower
use xml_interface
implicit none
private
public :: free_memory_source
public :: openmc_extend_sources
public :: openmc_source_set_strength
integer :: n_accept = 0 ! Number of samples accepted
integer :: n_reject = 0 ! Number of samples rejected
!===============================================================================
! SOURCEDISTRIBUTION describes an external source of particles for a
! fixed-source problem or for the starting source in a k eigenvalue problem
!===============================================================================
type, public :: SourceDistribution
integer :: particle ! particle type
real(8) :: strength = ONE ! source strength
class(SpatialDistribution), allocatable :: space ! spatial distribution
class(UnitSphereDistribution), allocatable :: angle ! angle distribution
class(Distribution), allocatable :: energy ! energy distribution
contains
procedure :: from_xml
procedure :: sample
end type SourceDistribution
! Number of external source distributions
integer(C_INT32_T), public, bind(C) :: n_sources = 0
! External source distributions
type(SourceDistribution), public, allocatable :: external_source(:)
contains
subroutine from_xml(this, node, path_source)
class(SourceDistribution), intent(inout) :: this
type(XMLNode), intent(in) :: node
character(MAX_FILE_LEN), intent(out) :: path_source
integer :: n
logical :: file_exists
character(MAX_WORD_LEN) :: type, temp_str
type(XMLNode) :: node_space
type(XMLNode) :: node_angle
type(XMLNode) :: node_dist
! Check for particle type
if (check_for_node(node, "particle")) then
call get_node_value(node, "particle", temp_str)
select case (to_lower(temp_str))
case ('neutron')
this % particle = NEUTRON
case ('photon')
this % particle = PHOTON
photon_transport = .true.
case default
call fatal_error('Unknown source particle type: ' // trim(temp_str))
end select
else
this % particle = NEUTRON
end if
! Check for source strength
if (check_for_node(node, "strength")) then
call get_node_value(node, "strength", this % strength)
end if
! Check for external source file
if (check_for_node(node, "file")) then
! Copy path of source file
call get_node_value(node, "file", path_source)
! Check if source file exists
inquire(FILE=path_source, EXIST=file_exists)
if (.not. file_exists) then
call fatal_error("Source file '" // trim(path_source) &
// "' does not exist!")
end if
else
! Spatial distribution for external source
if (check_for_node(node, "space")) then
! Get pointer to spatial distribution
node_space = node % child("space")
! Check for type of spatial distribution
type = ''
if (check_for_node(node_space, "type")) &
call get_node_value(node_space, "type", type)
select case (to_lower(type))
case ('cartesian')
allocate(CartesianIndependent :: this % space)
case ('box')
allocate(SpatialBox :: this % space)
case ('fission')
allocate(SpatialBox :: this % space)
select type(space => this % space)
type is (SpatialBox)
space % only_fissionable = .true.
end select
case ('point')
allocate(SpatialPoint :: this % space)
case default
call fatal_error("Invalid spatial distribution for external source: "&
// trim(type))
end select
! Read spatial distribution from XML
call this % space % from_xml(node_space)
else
! If no spatial distribution specified, make it a point source
allocate(SpatialPoint :: this % space)
select type (space => this % space)
type is (SpatialPoint)
space % xyz(:) = [ZERO, ZERO, ZERO]
end select
end if
! Determine external source angular distribution
if (check_for_node(node, "angle")) then
! Get pointer to angular distribution
node_angle = node % child("angle")
! Check for type of angular distribution
type = ''
if (check_for_node(node_angle, "type")) &
call get_node_value(node_angle, "type", type)
select case (to_lower(type))
case ('isotropic')
allocate(Isotropic :: this % angle)
case ('monodirectional')
allocate(Monodirectional :: this % angle)
case ('mu-phi')
allocate(PolarAzimuthal :: this % angle)
case default
call fatal_error("Invalid angular distribution for external source: "&
// trim(type))
end select
! Read reference directional unit vector
if (check_for_node(node_angle, "reference_uvw")) then
n = node_word_count(node_angle, "reference_uvw")
if (n /= 3) then
call fatal_error('Angular distribution reference direction must have &
&three parameters specified.')
end if
call get_node_array(node_angle, "reference_uvw", &
this % angle % reference_uvw)
else
! By default, set reference unit vector to be positive z-direction
this % angle % reference_uvw(:) = [ZERO, ZERO, ONE]
end if
! Read parameters for angle distribution
select type (angle => this % angle)
type is (Monodirectional)
call get_node_array(node_angle, "reference_uvw", &
this % angle % reference_uvw)
type is (PolarAzimuthal)
if (check_for_node(node_angle, "mu")) then
node_dist = node_angle % child("mu")
call distribution_from_xml(angle % mu, node_dist)
else
allocate(Uniform :: angle%mu)
select type (mu => angle%mu)
type is (Uniform)
mu % a = -ONE
mu % b = ONE
end select
end if
if (check_for_node(node_angle, "phi")) then
node_dist = node_angle % child("phi")
call distribution_from_xml(angle % phi, node_dist)
else
allocate(Uniform :: angle%phi)
select type (phi => angle%phi)
type is (Uniform)
phi % a = ZERO
phi % b = TWO*PI
end select
end if
end select
else
! Set default angular distribution isotropic
allocate(Isotropic :: this % angle)
this % angle % reference_uvw(:) = [ZERO, ZERO, ONE]
end if
! Determine external source energy distribution
if (check_for_node(node, "energy")) then
node_dist = node % child("energy")
call distribution_from_xml(this % energy, node_dist)
else
! Default to a Watt spectrum with parameters 0.988 MeV and 2.249 MeV^-1
allocate(Watt :: this % energy)
select type(energy => this % energy)
type is (Watt)
energy % a = 0.988e6_8
energy % b = 2.249e-6_8
end select
end if
end if
end subroutine from_xml
function sample(this) result(site)
class(SourceDistribution), intent(in) :: this
type(Bank) :: site
logical :: found ! Does the source particle exist within geometry?
type(Particle) :: p ! Temporary particle for using find_cell
! Set weight to one by default
site % wgt = ONE
! Repeat sampling source location until a good site has been found
found = .false.
do while (.not. found)
! Set particle defaults
call particle_initialize(p)
! Set particle type
site % particle = this % particle
! Sample spatial distribution
site % xyz(:) = this % space % sample()
! Fill p with needed data
p % coord(1) % xyz(:) = site % xyz
p % coord(1) % uvw(:) = [ ONE, ZERO, ZERO ]
! Now search to see if location exists in geometry
call find_cell(p, found)
! Check if spatial site is in fissionable material
if (found) then
select type (space => this % space)
type is (SpatialBox)
if (space % only_fissionable) then
if (p % material == MATERIAL_VOID) then
found = .false.
elseif (.not. materials(p % material) % fissionable) then
found = .false.
end if
end if
end select
end if
! Check for rejection
if (.not. found) then
n_reject = n_reject + 1
if (n_reject >= EXTSRC_REJECT_THRESHOLD .and. &
real(n_accept, 8)/n_reject <= EXTSRC_REJECT_FRACTION) then
call fatal_error("More than 95% of external source sites sampled &
&were rejected. Please check your external source definition.")
end if
end if
end do
! Increment number of accepted samples
n_accept = n_accept + 1
call particle_clear(p)
! Sample angle
site % uvw(:) = this % angle % sample()
! Check for monoenergetic source above maximum particle energy
select type (energy => this % energy)
type is (Discrete)
if (any(energy % x > energy_max(this % particle))) then
call fatal_error("Source energy above range of energies of at least &
&one cross section table")
else if (any(energy % x < energy_min(this % particle))) then
call fatal_error("Source energy below range of energies of at least &
&one cross section table")
end if
end select
do
! Sample energy spectrum
site % E = this % energy % sample()
! Resample if energy falls outside minimum or maximum particle energy
if (site % E < energy_max(this % particle) .and. &
site % E > energy_min(this % particle)) exit
end do
! Set delayed group
site % delayed_group = 0
end function sample
!===============================================================================
! FREE_MEMORY_SOURCE deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_source()
n_sources = 0
if (allocated(external_source)) deallocate(external_source)
end subroutine free_memory_source
!===============================================================================
! C API FUNCTIONS
!===============================================================================
function openmc_extend_sources(n, index_start, index_end) result(err) bind(C)
! Extend the external_source array by n elements
integer(C_INT32_T), value, intent(in) :: n
integer(C_INT32_T), optional, intent(out) :: index_start
integer(C_INT32_T), optional, intent(out) :: index_end
integer(C_INT) :: err
type(SourceDistribution), allocatable :: temp(:) ! temporary array
if (n_sources == 0) then
! Allocate external_source array
allocate(external_source(n))
else
! Allocate external_source array with increased size
allocate(temp(n_sources + n))
! Copy original source array to temporary array
temp(1:n_sources) = external_source
! Move allocation from temporary array
call move_alloc(FROM=temp, TO=external_source)
end if
! Return indices in external_source array
if (present(index_start)) index_start = n_sources + 1
if (present(index_end)) index_end = n_sources + n
n_sources = n_sources + n
err = 0
end function openmc_extend_sources
function openmc_source_set_strength(index, strength) result(err) bind(C)
integer(C_INT32_T), value, intent(in) :: index
real(C_DOUBLE), value, intent(in) :: strength
integer(C_INT) :: err
if (index >= 1 .and. index <= n_sources) then
if (strength > ZERO) then
external_source(index) % strength = strength
err = 0
else
err = E_INVALID_ARGUMENT
call set_errmsg("Source strength must be positive.")
end if
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in external source array is out of bounds.")
end if
end function openmc_source_set_strength
end module source_header

View file

@ -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 &
&region 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 &
&region 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

View file

@ -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
!===============================================================================

36
src/summary.cpp Normal file
View file

@ -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

View file

@ -27,7 +27,7 @@ extern "C" const int BC_PERIODIC {3};
int32_t n_surfaces;
std::vector<Surface*> global_surfaces;
std::vector<Surface*> surfaces;
std::map<int, int> 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<double, 1> coeffs {{x0}};
std::array<double, 1> 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<double, 1> coeffs {{y0}};
std::array<double, 1> 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<double, 1> coeffs {{z0}};
std::array<double, 1> 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<double, 4> coeffs {{A, B, C, D}};
std::array<double, 4> 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<double, 3> coeffs {{y0, z0, radius}};
std::array<double, 3> 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<double, 3> coeffs {{x0, z0, radius}};
std::array<double, 3> 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<double, 3> coeffs {{x0, y0, radius}};
std::array<double, 3> 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<double, 4> coeffs {{x0, y0, z0, radius}};
std::array<double, 4> 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<double, 4> coeffs {{x0, y0, z0, radius_sq}};
std::array<double, 4> 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<double, 4> coeffs {{x0, y0, z0, radius_sq}};
std::array<double, 4> 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<double, 4> coeffs {{x0, y0, z0, radius_sq}};
std::array<double, 4> 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<double, 10> coeffs {{A, B, C, D, E, F, G, H, J, K}};
std::array<double, 10> 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<PeriodicSurface*>(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<PeriodicSurface*>(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();
}

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -9,7 +9,6 @@ module tally_header
use message_passing, only: n_procs
use nuclide_header, only: nuclide_dict
use settings, only: reduce_tallies, run_mode
use source_header, only: external_source
use stl_vector, only: VectorInt
use string, only: to_lower, to_f_string, str_to_int, to_str
use tally_filter_header, only: TallyFilterContainer, filters, n_filters
@ -169,6 +168,13 @@ contains
real(C_DOUBLE) :: val
real(C_DOUBLE) :: total_source
interface
function total_source_strength() result(strength) bind(C)
import C_DOUBLE
real(C_DOUBLE) :: strength
end function
end interface
! Increment number of realizations
if (reduce_tallies) then
this % n_realizations = this % n_realizations + 1
@ -178,7 +184,7 @@ contains
! Calculate total source strength for normalization
if (run_mode == MODE_FIXEDSOURCE) then
total_source = sum(external_source(:) % strength)
total_source = total_source_strength()
else
total_source = ONE
end if

View file

@ -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

View file

@ -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)

View file

@ -18,6 +18,7 @@ module xml_interface
interface get_node_value
module procedure get_node_value_bool
module procedure get_node_value_cbool
module procedure get_node_value_integer
module procedure get_node_value_long
module procedure get_node_value_double
@ -118,6 +119,16 @@ contains
end if
end subroutine get_node_value_bool
subroutine get_node_value_cbool(node, name, val)
type(XMLNode), intent(in) :: node
character(*), intent(in) :: name
logical(kind=C_BOOL), intent(out) :: val
logical :: val_
call get_node_value_bool(node, name, val_)
val = val_
end subroutine get_node_value_cbool
!===============================================================================
! GET_NODE_VALUE_INTEGER returns a integer value from an attribute or node
!===============================================================================

View file

@ -1,11 +1,11 @@
<?xml version="1.0"?>
<geometry>
<surface id="1" type="sphere" coeffs="0 0 0 10" boundary="vacuum"/>
<surface id="2" type="z-plane" coeffs="-100" />
<surface id="3" type="z-plane" coeffs="100" />
<surface id="1" type="sphere" coeffs="0 0 0 10" boundary="vacuum" />
<surface id="2" type="sphere" coeffs="1 0 0 5" />
<cell id="1" fill="2" rotation="45 45 45" region="-1" />
<cell id="2" universe="2" material="1" region="2 -3" />
<cell id="1" fill="2" rotation="10 20 30" region="-1" />
<cell id="2" universe="2" material="1" region="-2" />
<cell id="3" universe="2" material="2" region="2" />
</geometry>

View file

@ -2,8 +2,13 @@
<materials>
<material id="1">
<density value="4.5" units="g/cc" />
<density value="10" units="g/cc" />
<nuclide name="U235" ao="1.0" />
</material>
<material id="2">
<density value="0.1" units="g/cc" />
<nuclide name="H1" ao="0.1" />
</material>
</materials>

View file

@ -1,2 +1,2 @@
k-combined:
2.943619E-01 3.309646E-03
4.240695E-01 8.013236E-03

View file

@ -1,11 +1,11 @@
<?xml version="1.0"?>
<geometry>
<surface id="1" type="sphere" coeffs="0 0 0 10" boundary="vacuum"/>
<surface id="2" type="z-plane" coeffs="-100" />
<surface id="3" type="z-plane" coeffs="100" />
<surface id="1" type="sphere" coeffs="0 0 0 10" boundary="vacuum" />
<surface id="2" type="sphere" coeffs="0 0 0 5" />
<cell id="1" fill="2" translation="2.3 5.0 -10.0" region="-1" />
<cell id="2" universe="2" material="1" region="2 -3" />
<cell id="1" fill="2" translation="1 2 3" region="-1" />
<cell id="2" universe="2" material="1" region="-2" />
<cell id="3" universe="2" material="2" region="2" />
</geometry>

View file

@ -2,8 +2,13 @@
<materials>
<material id="1">
<density value="4.5" units="g/cc" />
<density value="10" units="g/cc" />
<nuclide name="U235" ao="1.0" />
</material>
<material id="2">
<density value="0.1" units="g/cc" />
<nuclide name="H1" ao="0.1" />
</material>
</materials>

View file

@ -1,2 +1,2 @@
k-combined:
2.943619E-01 3.309646E-03
4.101355E-01 1.577552E-02