Merge remote-tracking branch 'upstream/develop' into cpp_tallies

This commit is contained in:
Sterling Harper 2018-09-10 16:19:25 -04:00
commit 0c8ea4f31f
149 changed files with 6802 additions and 7291 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
@ -314,7 +312,6 @@ add_library(libopenmc SHARED
src/material_header.F90
src/math.F90
src/matrix_header.F90
src/mesh.F90
src/mesh_header.F90
src/message_passing.F90
src/mgxs_data.F90
@ -341,8 +338,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
@ -384,22 +379,28 @@ add_library(libopenmc SHARED
src/tallies/trigger.F90
src/tallies/trigger_header.F90
src/cell.cpp
src/cmfd_execute.cpp
src/distribution.cpp
src/distribution_angle.cpp
src/distribution_energy.cpp
src/distribution_multi.cpp
src/distribution_spatial.cpp
src/eigenvalue.cpp
src/endf.cpp
src/initialize.cpp
src/finalize.cpp
src/geometry.cpp
src/geometry_aux.cpp
src/hdf5_interface.cpp
src/lattice.cpp
src/material.cpp
src/math_functions.cpp
src/mesh.cpp
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 +415,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/tallies/tally_filter.cpp
src/thermal.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);
@ -38,6 +37,7 @@ extern "C" {
int openmc_filter_set_type(int32_t index, const char* type);
int openmc_finalize();
int openmc_find_cell(double* xyz, int32_t* index, int32_t* instance);
int openmc_fission_bank(struct Bank** ptr, int64_t* n);
int openmc_get_cell_index(int32_t id, int32_t* index);
int openmc_get_filter_index(int32_t id, int32_t* index);
void openmc_get_filter_next_id(int32_t* id);
@ -57,6 +57,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);
@ -71,7 +72,7 @@ extern "C" {
int openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n);
int openmc_mesh_set_id(int32_t index, int32_t id);
int openmc_mesh_set_dimension(int32_t index, int n, const int* dims);
int openmc_mesh_set_params(int32_t index, const double* ll, const double* ur, const double* width, int n);
int openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, const double* width);
int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh);
int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh);
int openmc_next_batch(int* status);
@ -84,7 +85,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);
@ -136,16 +136,11 @@ extern "C" {
extern char openmc_err_msg[256];
extern double openmc_keff;
extern double openmc_keff_std;
extern int32_t gen_per_batch;
extern int32_t n_batches;
extern int32_t n_cells;
extern int32_t n_filters;
extern int32_t n_inactive;
extern int32_t n_lattices;
extern int32_t n_materials;
extern int32_t n_meshes;
extern int n_nuclides;
extern int64_t n_particles;
extern int32_t n_plots;
extern int32_t n_realizations;
extern int32_t n_sab_tables;
@ -153,9 +148,7 @@ extern "C" {
extern int32_t n_surfaces;
extern int32_t n_tallies;
extern int32_t n_universes;
extern int openmc_run_mode;
extern bool openmc_simulation_initialized;
extern int openmc_verbosity;
// Variables that are shared by necessity (can be removed from public header
// later)
@ -165,13 +158,6 @@ extern "C" {
extern int openmc_rank;
extern int64_t openmc_work;
// Run modes
const int RUN_MODE_FIXEDSOURCE = 1;
const int RUN_MODE_EIGENVALUE = 2;
const int RUN_MODE_PLOTTING = 3;
const int RUN_MODE_PARTICLE = 4;
const int RUN_MODE_VOLUME = 5;
#ifdef __cplusplus
}
#endif

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

@ -11,16 +11,9 @@
namespace openmc {
// TODO: Replace with xtensor/other library?
typedef std::vector<double> double_1dvec;
typedef std::vector<std::vector<double> > double_2dvec;
typedef std::vector<std::vector<std::vector<double> > > double_3dvec;
typedef std::vector<std::vector<std::vector<std::vector<double> > > > double_4dvec;
typedef std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > > double_5dvec;
typedef std::vector<std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > > > double_6dvec;
typedef std::vector<int> int_1dvec;
typedef std::vector<std::vector<int> > int_2dvec;
typedef std::vector<std::vector<std::vector<int> > > int_3dvec;
using double_2dvec = std::vector<std::vector<double>>;
using double_3dvec = std::vector<std::vector<std::vector<double>>>;
using double_4dvec = std::vector<std::vector<std::vector<std::vector<double>>>>;
// ============================================================================
// VERSIONING NUMBERS
@ -431,6 +424,13 @@ enum class Interpolation {
histogram, lin_lin, lin_log, log_lin, log_log
};
// Run modes
constexpr int RUN_MODE_FIXEDSOURCE {1};
constexpr int RUN_MODE_EIGENVALUE {2};
constexpr int RUN_MODE_PLOTTING {3};
constexpr int RUN_MODE_PARTICLE {4};
constexpr int RUN_MODE_VOLUME {5};
} // namespace openmc
#endif // OPENMC_CONSTANTS_H

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,41 @@
#ifndef OPENMC_EIGENVALUE_H
#define OPENMC_EIGENVALUE_H
#include <cstdint> // for int64_t
#include <vector>
#include "xtensor/xtensor.hpp"
#include "openmc/particle.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
extern std::vector<double> entropy; //!< Shannon entropy at each generation
extern xt::xtensor<double, 1> source_frac; //!< Source fraction for UFS
extern "C" int64_t n_bank;
#pragma omp threadprivate(n_bank)
//==============================================================================
// Non-member functions
//==============================================================================
//! Calculates the Shannon entropy of the fission source distribution to assess
//! source convergence
extern "C" void shannon_entropy();
//! Determines the source fraction in each UFS mesh cell and reweights the
//! source bank so that the sum of the weights is equal to n_particles. The
//! 'source_frac' variable is used later to bias the production of fission sites
extern "C" void ufs_count_sites();
//! Get UFS weight corresponding to particle's location
extern "C" double ufs_get_weight(const Particle* p);
} // namespace openmc
#endif // OPENMC_EIGENVALUE_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

@ -14,6 +14,7 @@
#include "xtensor/xarray.hpp"
#include "openmc/position.h"
#include "openmc/error.h"
namespace openmc {
@ -36,43 +37,16 @@ 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);
void
read_nd_vector(hid_t obj_id, const char* name, std::vector<double>& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<double> >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<int> >& result, bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<double> > >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<int> > >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<double> > > >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > >& result,
bool must_have = false);
std::vector<hsize_t> attribute_shape(hid_t obj_id, const char* name);
std::vector<std::string> dataset_names(hid_t group_id);
void ensure_exists(hid_t group_id, const char* name);
@ -230,7 +204,7 @@ read_attribute(hid_t obj_id, const char* name, std::vector<std::string>& vec)
}
//==============================================================================
// Templates/overloads for read_dataset
// Templates/overloads for read_dataset and related methods
//==============================================================================
template<typename T>
@ -288,6 +262,40 @@ void read_dataset(hid_t obj_id, const char* name, xt::xarray<T>& arr, bool indep
close_dataset(dset);
}
template <typename T, std::size_t N>
void read_dataset_as_shape(hid_t obj_id, const char* name,
xt::xtensor<T, N>& arr, bool indep=false)
{
hid_t dset = open_dataset(obj_id, name);
// Allocate new array to read data into
std::size_t size = 1;
for (const auto x : arr.shape())
size *= x;
T* buffer = new T[size];
// Read data from attribute
read_dataset(dset, nullptr, H5TypeMap<T>::type_id, buffer, indep);
// Adapt into xarray
arr = xt::adapt(buffer, size, xt::acquire_ownership(), arr.shape());
close_dataset(dset);
}
template <typename T, std::size_t N>
void read_nd_vector(hid_t obj_id, const char* name, xt::xtensor<T, N>& result,
bool must_have=false)
{
if (object_exists(obj_id, name)) {
read_dataset_as_shape(obj_id, name, result, true);
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
//==============================================================================
// Templates/overloads for write_attribute
//==============================================================================
@ -295,7 +303,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
@ -311,6 +319,14 @@ write_attribute(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
write_attr(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data());
}
template<typename T> inline void
write_attribute(hid_t obj_id, const char* name, const std::vector<T>& buffer)
{
hsize_t dims[] {buffer.size()};
write_attr(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data());
}
//==============================================================================
// Templates/overloads for write_dataset
//==============================================================================
@ -334,6 +350,22 @@ 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);
}
template<typename T> inline void
write_dataset(hid_t obj_id, const char* name, const xt::xarray<T>& arr)
{
auto s = arr.shape();
std::vector<hsize_t> dims {s.cbegin(), s.cend()};
write_dataset(obj_id, dims.size(), dims.data(), name, H5TypeMap<T>::type_id,
arr.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);

131
include/openmc/mesh.h Normal file
View file

@ -0,0 +1,131 @@
//! \file mesh.h
//! \brief Mesh types used for tallies, Shannon entropy, CMFD, etc.
#ifndef OPENMC_MESH_H
#define OPENMC_MESH_H
#include <memory> // for unique_ptr
#include <vector>
#include <unordered_map>
#include "hdf5.h"
#include "pugixml.hpp"
#include "xtensor/xarray.hpp"
#include "openmc/particle.h"
#include "openmc/position.h"
namespace openmc {
//==============================================================================
//! Tessellation of n-dimensional Euclidean space by congruent squares or cubes
//==============================================================================
class RegularMesh {
public:
// Constructors
RegularMesh() = default;
RegularMesh(pugi::xml_node node);
// Methods
//! Determine which bins were crossed by a particle
//!
//! \param[in] p Particle to check
//! \param[out] bins Bins that were crossed
//! \param[out] lengths Fraction of tracklength in each bin
void bins_crossed(const Particle* p, std::vector<int>& bins,
std::vector<double>& lengths) const;
//! Determine which surface bins were crossed by a particle
//!
//! \param[in] p Particle to check
//! \param[out] bins Surface bins that were crossed
void surface_bins_crossed(const Particle* p, std::vector<int>& bins) const;
//! Get bin at a given position in space
//!
//! \param[in] r Position to get bin for
//! \return Mesh bin
int get_bin(Position r) const;
//! Get bin given mesh indices
//!
//! \param[in] Array of mesh indices
//! \return Mesh bin
int get_bin_from_indices(const int* ijk) const;
//! Get mesh indices given a position
//!
//! \param[in] r Position to get indices for
//! \param[out] ijk Array of mesh indices
//! \param[out] in_mesh Whether position is in mesh
void get_indices(Position r, int* ijk, bool* in_mesh) const;
//! Get mesh indices corresponding to a mesh bin
//!
//! \param[in] bin Mesh bin
//! \param[out] ijk Mesh indices
void get_indices_from_bin(int bin, int* ijk) const;
//! Check if a line connected by two points intersects the mesh
//!
//! \param[in] r0 Starting position
//! \param[in] r1 Ending position
//! \return Whether line connecting r0 and r1 intersects mesh
bool intersects(Position r0, Position r1) const;
//! Write mesh data to an HDF5 group
//!
//! \param[in] group HDF5 group
void to_hdf5(hid_t group) const;
//! Count number of bank sites in each mesh bin / energy bin
//!
//! \param[in] n Number of bank sites
//! \param[in] bank Array of bank sites
//! \param[in] n_energy Number of energies
//! \param[in] energies Array of energies
//! \param[out] Whether any bank sites are outside the mesh
//! \return Array indicating number of sites in each mesh/energy bin
xt::xarray<double> count_sites(int64_t n, const Bank* bank,
int n_energy, const double* energies, bool* outside) const;
int id_ {-1}; //!< User-specified ID
int n_dimension_; //!< Number of dimensions
double volume_frac_; //!< Volume fraction of each mesh element
xt::xarray<int> shape_; //!< Number of mesh elements in each dimension
xt::xarray<double> lower_left_; //!< Lower-left coordinates of mesh
xt::xarray<double> upper_right_; //!< Upper-right coordinates of mesh
xt::xarray<double> width_; //!< Width of each mesh element
private:
bool intersects_1d(Position r0, Position r1) const;
bool intersects_2d(Position r0, Position r1) const;
bool intersects_3d(Position r0, Position r1) const;
};
//==============================================================================
// Non-member functions
//==============================================================================
//! Read meshes from either settings/tallies
//! \param[in] root XML node
extern "C" void read_meshes(pugi::xml_node* root);
//! Write mesh data to an HDF5 group
//! \param[in] group HDF5 group
extern "C" void meshes_to_hdf5(hid_t group);
//==============================================================================
// Global variables
//==============================================================================
extern std::vector<std::unique_ptr<RegularMesh>> meshes;
extern std::unordered_map<int32_t, int32_t> mesh_map;
} // namespace openmc
#endif // OPENMC_MESH_H

View file

@ -10,6 +10,7 @@ namespace mpi {
extern int rank;
extern int n_procs;
extern bool master;
#ifdef OPENMC_MPI
extern MPI_Datatype bank;

View file

@ -7,6 +7,8 @@
#include <string>
#include <vector>
#include "xtensor/xtensor.hpp"
#include "openmc/constants.h"
#include "openmc/hdf5_interface.h"
#include "openmc/xsdata.h"
@ -35,7 +37,7 @@ struct CacheData {
class Mgxs {
private:
double_1dvec kTs; // temperature in eV (k * T)
xt::xtensor<double, 1> kTs; // temperature in eV (k * T)
int scatter_format; // flag for if this is legendre, histogram, or tabular
int num_delayed_groups; // number of delayed neutron groups
int num_groups; // number of energy groups
@ -44,8 +46,8 @@ class Mgxs {
bool is_isotropic; // used to skip search for angle indices if isotropic
int n_pol;
int n_azi;
double_1dvec polar;
double_1dvec azimuthal;
std::vector<double> polar;
std::vector<double> azimuthal;
//! \brief Initializes the Mgxs object metadata
//!
@ -62,10 +64,10 @@ class Mgxs {
//! @param in_polar Polar angle grid.
//! @param in_azimuthal Azimuthal angle grid.
void
init(const std::string& in_name, double in_awr, const double_1dvec& in_kTs,
init(const std::string& in_name, double in_awr, const std::vector<double>& in_kTs,
bool in_fissionable, int in_scatter_format, int in_num_groups,
int in_num_delayed_groups, bool in_is_isotropic,
const double_1dvec& in_polar, const double_1dvec& in_azimuthal);
const std::vector<double>& in_polar, const std::vector<double>& in_azimuthal);
//! \brief Initializes the Mgxs object metadata from the HDF5 file
//!
@ -80,8 +82,8 @@ class Mgxs {
//! @param method Method of choosing nearest temperatures.
void
metadata_from_hdf5(hid_t xs_id, int in_num_groups,
int in_num_delayed_groups, const double_1dvec& temperature,
double tolerance, int_1dvec& temps_to_read, int& order_dim,
int in_num_delayed_groups, const std::vector<double>& temperature,
double tolerance, std::vector<int>& temps_to_read, int& order_dim,
int& method);
//! \brief Performs the actual act of combining the microscopic data for a
@ -93,8 +95,8 @@ class Mgxs {
//! corresponds to the temperature of interest.
//! @param this_t The temperature index of the macroscopic object.
void
combine(const std::vector<Mgxs*>& micros, const double_1dvec& scalars,
const int_1dvec& micro_ts, int this_t);
combine(const std::vector<Mgxs*>& micros, const std::vector<double>& scalars,
const std::vector<int>& micro_ts, int this_t);
//! \brief Checks to see if this and that are able to be combined
//!
@ -128,7 +130,7 @@ class Mgxs {
//! provides the number of points to use in the tabular representation.
//! @param method Method of choosing nearest temperatures.
Mgxs(hid_t xs_id, int energy_groups,
int delayed_groups, const double_1dvec& temperature, double tolerance,
int delayed_groups, const std::vector<double>& temperature, double tolerance,
int max_order, bool legendre_to_tabular,
int legendre_to_tabular_points, int& method);
@ -141,8 +143,8 @@ class Mgxs {
//! @param atom_densities Atom densities of those microscopic quantities.
//! @param tolerance Tolerance of temperature selection method.
//! @param method Method of choosing nearest temperatures.
Mgxs(const std::string& in_name, const double_1dvec& mat_kTs,
const std::vector<Mgxs*>& micros, const double_1dvec& atom_densities,
Mgxs(const std::string& in_name, const std::vector<double>& mat_kTs,
const std::vector<Mgxs*>& micros, const std::vector<double>& atom_densities,
double tolerance, int& method);
//! \brief Provides a cross section value given certain parameters

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

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

@ -0,0 +1,28 @@
//! \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();
extern "C" void title();
} // 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,8 +154,14 @@ 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();
void write_restart() const;
};

View file

@ -1,6 +1,9 @@
#ifndef OPENMC_POSITION_H
#define OPENMC_POSITION_H
#include <cmath>
#include <vector>
namespace openmc {
//==============================================================================
@ -12,6 +15,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);
@ -43,6 +47,9 @@ struct Position {
inline double dot(Position other) {
return x*other.x + y*other.y + z*other.z;
}
inline double norm() {
return std::sqrt(x*x + y*y + z*z);
}
// Data members
double x = 0.;
@ -63,6 +70,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 +84,4 @@ using Direction = Position;
} // namespace openmc
#endif // OPENMC_POSITION_H
#endif // OPENMC_POSITION_H

View file

@ -6,6 +6,8 @@
#include <vector>
#include "xtensor/xtensor.hpp"
#include "openmc/constants.h"
namespace openmc {
@ -25,23 +27,25 @@ class ScattData {
protected:
//! \brief Initializes the attributes of the base class.
void
base_init(int order, const int_1dvec& in_gmin, const int_1dvec& in_gmax,
const double_2dvec& in_energy, const double_2dvec& in_mult);
base_init(int order, const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_energy,
const double_2dvec& in_mult);
//! \brief Combines microscopic ScattDatas into a macroscopic one.
void
base_combine(int max_order, const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars, int_1dvec& in_gmin, int_1dvec& in_gmax,
double_2dvec& sparse_mult, double_3dvec& sparse_scatter);
base_combine(size_t max_order, const std::vector<ScattData*>& those_scatts,
const std::vector<double>& scalars, xt::xtensor<int, 1>& in_gmin,
xt::xtensor<int, 1>& in_gmax, double_2dvec& sparse_mult,
double_3dvec& sparse_scatter);
public:
double_2dvec energy; // Normalized p0 matrix for sampling Eout
double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt)
double_3dvec dist; // Angular distribution
int_1dvec gmin; // minimum outgoing group
int_1dvec gmax; // maximum outgoing group
double_1dvec scattxs; // Isotropic Sigma_{s,g_{in}}
double_2dvec energy; // Normalized p0 matrix for sampling Eout
double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt)
double_3dvec dist; // Angular distribution
xt::xtensor<double, 1> gmin; // minimum outgoing group
xt::xtensor<double, 1> gmax; // maximum outgoing group
xt::xtensor<double, 1> scattxs; // Isotropic Sigma_{s,g_{in}}
//! \brief Calculates the value of normalized f(mu).
//!
@ -72,7 +76,7 @@ class ScattData {
//! @param in_mult Input sparse multiplicity matrix
//! @param coeffs Input sparse scattering matrix
virtual void
init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
init(const xt::xtensor<int, 1>& in_gmin, const xt::xtensor<int, 1>& in_gmax,
const double_2dvec& in_mult, const double_3dvec& coeffs) = 0;
//! \brief Combines the microscopic data.
@ -81,7 +85,7 @@ class ScattData {
//! @param scalars Scalars to multiply the microscopic data by.
virtual void
combine(const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars) = 0;
const std::vector<double>& scalars) = 0;
//! \brief Getter for the dimensionality of the scattering order.
//!
@ -89,7 +93,7 @@ class ScattData {
//! of points, and for Histogram this is the number of bins.
//!
//! @return The order.
virtual int
virtual size_t
get_order() = 0;
//! \brief Builds a dense scattering matrix from the constituent parts
@ -97,8 +101,8 @@ class ScattData {
//! @param max_order If Legendre this is the maximum value of "n" in "Pn"
//! requested; ignored otherwise.
//! @return The dense scattering matrix.
virtual double_3dvec
get_matrix(int max_order) = 0;
virtual xt::xtensor<double, 3>
get_matrix(size_t max_order) = 0;
//! \brief Samples the outgoing energy from the ScattData info.
//!
@ -142,12 +146,12 @@ class ScattDataLegendre: public ScattData {
public:
void
init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
init(const xt::xtensor<int, 1>& in_gmin, const xt::xtensor<int, 1>& in_gmax,
const double_2dvec& in_mult, const double_3dvec& coeffs);
void
combine(const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars);
const std::vector<double>& scalars);
//! \brief Find the maximal value of the angular distribution to use as a
// bounding box with rejection sampling.
@ -160,11 +164,11 @@ class ScattDataLegendre: public ScattData {
void
sample(int gin, int& gout, double& mu, double& wgt);
int
size_t
get_order() {return dist[0][0].size() - 1;};
double_3dvec
get_matrix(int max_order);
xt::xtensor<double, 3>
get_matrix(size_t max_order);
};
//==============================================================================
@ -176,19 +180,19 @@ class ScattDataHistogram: public ScattData {
protected:
double_1dvec mu; // Angle distribution mu bin boundaries
double dmu; // Quick storage of the spacing between the mu bin points
double_3dvec fmu; // The angular distribution histogram
xt::xtensor<double, 1> mu; // Angle distribution mu bin boundaries
double dmu; // Quick storage of the mu spacing
double_3dvec fmu; // The angular distribution histogram
public:
void
init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
init(const xt::xtensor<int, 1>& in_gmin, const xt::xtensor<int, 1>& in_gmax,
const double_2dvec& in_mult, const double_3dvec& coeffs);
void
combine(const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars);
const std::vector<double>& scalars);
double
calc_f(int gin, int gout, double mu);
@ -196,11 +200,11 @@ class ScattDataHistogram: public ScattData {
void
sample(int gin, int& gout, double& mu, double& wgt);
int
size_t
get_order() {return dist[0][0].size();};
double_3dvec
get_matrix(int max_order);
xt::xtensor<double, 3>
get_matrix(size_t max_order);
};
//==============================================================================
@ -212,9 +216,9 @@ class ScattDataTabular: public ScattData {
protected:
double_1dvec mu; // Angle distribution mu grid points
double dmu; // Quick storage of the spacing between the mu points
double_3dvec fmu; // The angular distribution function
xt::xtensor<double, 1> mu; // Angle distribution mu grid points
double dmu; // Quick storage of the mu spacing
double_3dvec fmu; // The angular distribution function
// Friend convert_legendre_to_tabular so it has access to protected
// parameters
@ -225,12 +229,12 @@ class ScattDataTabular: public ScattData {
public:
void
init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
init(const xt::xtensor<int, 1>& in_gmin, const xt::xtensor<int, 1>& in_gmax,
const double_2dvec& in_mult, const double_3dvec& coeffs);
void
combine(const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars);
const std::vector<double>& scalars);
double
calc_f(int gin, int gout, double mu);
@ -238,10 +242,11 @@ class ScattDataTabular: public ScattData {
void
sample(int gin, int& gout, double& mu, double& wgt);
int
size_t
get_order() {return dist[0][0].size();};
double_3dvec get_matrix(int max_order);
xt::xtensor<double, 3>
get_matrix(size_t max_order);
};
//==============================================================================

View file

@ -5,6 +5,7 @@
//! \brief Settings for OpenMC
#include <array>
#include <cstdint>
#include <string>
#include "pugixml.hpp"
@ -15,35 +16,84 @@ namespace openmc {
// Global variable declarations
//==============================================================================
// Defined on Fortran side
extern "C" bool openmc_check_overlaps;
extern "C" bool openmc_particle_restart_run;
extern "C" bool openmc_restart_run;
extern "C" bool openmc_write_all_tracks;
namespace settings {
// Defined in .cpp
// TODO: Make strings instead of char* once Fortran is gone
extern "C" char* openmc_path_input;
extern "C" char* openmc_path_statepoint;
extern "C" char* openmc_path_sourcepoint;
extern "C" char* openmc_path_particle_restart;
extern std::string path_cross_sections;
extern std::string path_multipole;
extern std::string path_output;
// Boolean flags
extern "C" bool assume_separate; //!< assume tallies are spatially separate?
extern "C" bool check_overlaps; //!< check overlaps in geometry?
extern "C" bool cmfd_run; //!< use CMFD?
extern "C" bool confidence_intervals; //!< use confidence intervals for results?
extern "C" bool create_fission_neutrons; //!< create fission neutrons (fixed source)?
extern "C" bool entropy_on; //!< calculate Shannon entropy?
extern "C" bool legendre_to_tabular; //!< convert Legendre distributions to tabular?
extern "C" bool output_summary; //!< write summary.h5?
extern "C" bool output_tallies; //!< write tallies.out?
extern "C" bool particle_restart_run; //!< particle restart run?
extern "C" bool photon_transport; //!< photon transport turned on?
extern "C" bool reduce_tallies; //!< reduce tallies at end of batch?
extern "C" bool res_scat_on; //!< use resonance upscattering method?
extern "C" bool restart_run; //!< restart run?
extern "C" bool run_CE; //!< run with continuous-energy data?
extern "C" bool source_latest; //!< write latest source at each batch?
extern "C" bool source_separate; //!< write source to separate file?
extern "C" bool source_write; //!< write source in HDF5 files?
extern "C" bool survival_biasing; //!< use survival biasing?
extern "C" bool temperature_multipole; //!< use multipole data?
extern "C" bool trigger_on; //!< tally triggers enabled?
extern "C" bool trigger_predict; //!< predict batches for triggers?
extern "C" bool ufs_on; //!< uniform fission site method on?
extern "C" bool urr_ptables_on; //!< use unresolved resonance prob. tables?
extern "C" bool write_all_tracks; //!< write track files for every particle?
extern "C" bool write_initial_source; //!< write out initial source file?
// Paths to various files
extern std::string path_cross_sections; //!< path to cross_sections.xml
extern std::string path_input; //!< directory where main .xml files resides
extern std::string path_multipole; //!< directory containing multipole files
extern std::string path_output; //!< directory where output files are written
extern std::string path_particle_restart; //!< path to a particle restart file
extern std::string path_source;
extern std::string path_sourcepoint; //!< path to a source file
extern std::string path_statepoint; //!< path to a statepoint file
extern int temperature_method;
extern bool temperature_multipole;
extern double temperature_tolerance;
extern double temperature_default;
extern std::array<double, 2> temperature_range;
extern "C" int32_t index_entropy_mesh; //!< Index of entropy mesh in global mesh array
extern "C" int32_t index_ufs_mesh; //!< Index of UFS mesh in global mesh array
extern "C" int32_t n_batches; //!< number of (inactive+active) batches
extern "C" int32_t n_inactive; //!< number of inactive batches
extern "C" int32_t gen_per_batch; //!< number of generations per batch
extern "C" int64_t n_particles; //!< number of particles per generation
extern "C" int electron_treatment; //!< how to treat secondary electrons
extern "C" double energy_cutoff[4]; //!< Energy cutoff in [eV] for each particle type
extern "C" int legendre_to_tabular_points; //!< number of points to convert Legendres
extern "C" int max_order; //!< Maximum Legendre order for multigroup data
extern "C" int n_log_bins; //!< number of bins for logarithmic energy grid
extern "C" int n_max_batches; //!< Maximum number of batches
extern "C" int res_scat_method; //!< resonance upscattering method
extern "C" double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering
extern "C" double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering
extern "C" int run_mode; //!< Run mode (eigenvalue, fixed src, etc.)
extern "C" int temperature_method; //!< method for choosing temperatures
extern "C" double temperature_tolerance; //!< Tolerance in [K] on choosing temperatures
extern "C" double temperature_default; //!< Default T in [K]
extern "C" double temperature_range[2]; //!< Min/max T in [K] over which to load xs
extern "C" int trace_batch; //!< Batch to trace particle on
extern "C" int trace_gen; //!< Generation to trace particle on
extern "C" int64_t trace_particle; //!< Particle ID to enable trace on
extern "C" int trigger_batch_interval; //!< Batch interval for triggers
extern "C" int verbosity; //!< How verbose to make output
extern "C" double weight_cutoff; //!< Weight cutoff for Russian roulette
extern "C" double weight_survive; //!< Survival weight after Russian roulette
} // namespace settings
//==============================================================================
//! Read settings from XML file
//! \param[in] root XML node for <settings>
//==============================================================================
extern "C" void read_settings_xml();
extern "C" void read_settings(pugi::xml_node* root);
extern "C" void read_settings_xml_f(pugi::xml_node_struct* root_ptr);
} // namespace openmc

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

@ -1,11 +1,14 @@
#ifndef OPENMC_XML_INTERFACE_H
#define OPENMC_XML_INTERFACE_H
#include <cstddef> // for size_t
#include <sstream> // for stringstream
#include <string>
#include <vector>
#include "pugixml.hpp"
#include "xtensor/xarray.hpp"
#include "xtensor/xadapt.hpp"
namespace openmc {
@ -38,5 +41,14 @@ std::vector<T> get_node_array(pugi::xml_node node, const char* name,
return values;
}
template <typename T>
xt::xarray<T> get_node_xarray(pugi::xml_node node, const char* name,
bool lowercase=false)
{
std::vector<T> v = get_node_array<T>(node, name, lowercase);
std::vector<std::size_t> shape = {v.size()};
return xt::adapt(v, shape);
}
} // namespace openmc
#endif // OPENMC_XML_INTERFACE_H

View file

@ -7,6 +7,8 @@
#include <memory>
#include <vector>
#include "xtensor/xtensor.hpp"
#include "openmc/hdf5_interface.h"
#include "openmc/scattdata.h"
@ -22,41 +24,77 @@ class XsData {
private:
//! \brief Reads scattering data from the HDF5 file
void
scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, int energy_groups,
scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
int scatter_format, int final_scatter_format, int order_data,
int max_order, int legendre_to_tabular_points);
//! \brief Reads fission data from the HDF5 file
void
fission_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, int energy_groups,
int delayed_groups, bool is_isotropic);
fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
size_t delayed_groups, bool is_isotropic);
//! \brief Reads fission data formatted as chi and nu-fission vectors from
// the HDF5 file when beta is provided.
void
fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
size_t energy_groups, size_t delayed_groups, bool is_isotropic);
//! \brief Reads fission data formatted as chi and nu-fission vectors from
// the HDF5 file when beta is not provided.
void
fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
size_t energy_groups, size_t delayed_groups);
//! \brief Reads fission data formatted as chi and nu-fission vectors from
// the HDF5 file when no delayed data is provided.
void
fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
size_t energy_groups);
//! \brief Reads fission data formatted as a nu-fission matrix from
// the HDF5 file when beta is provided.
void
fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
size_t energy_groups, size_t delayed_groups, bool is_isotropic);
//! \brief Reads fission data formatted as a nu-fission matrix from
// the HDF5 file when beta is not provided.
void
fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
size_t energy_groups, size_t delayed_groups);
//! \brief Reads fission data formatted as a nu-fission matrix from
// the HDF5 file when no delayed data is provided.
void
fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
size_t energy_groups);
public:
// The following quantities have the following dimensions:
// [angle][incoming group]
double_2dvec total;
double_2dvec absorption;
double_2dvec nu_fission;
double_2dvec prompt_nu_fission;
double_2dvec kappa_fission;
double_2dvec fission;
double_2dvec inverse_velocity;
xt::xtensor<double, 2> total;
xt::xtensor<double, 2> absorption;
xt::xtensor<double, 2> nu_fission;
xt::xtensor<double, 2> prompt_nu_fission;
xt::xtensor<double, 2> kappa_fission;
xt::xtensor<double, 2> fission;
xt::xtensor<double, 2> inverse_velocity;
// decay_rate has the following dimensions:
// [angle][delayed group]
double_2dvec decay_rate;
xt::xtensor<double, 2> decay_rate;
// delayed_nu_fission has the following dimensions:
// [angle][incoming group][delayed group]
double_3dvec delayed_nu_fission;
xt::xtensor<double, 3> delayed_nu_fission;
// chi_prompt has the following dimensions:
// [angle][incoming group][outgoing group]
double_3dvec chi_prompt;
xt::xtensor<double, 3> chi_prompt;
// chi_delayed has the following dimensions:
// [angle][incoming group][outgoing group][delayed group]
double_4dvec chi_delayed;
xt::xtensor<double, 4> chi_delayed;
// scatter has the following dimensions: [angle]
std::vector<std::shared_ptr<ScattData> > scatter;
std::vector<std::shared_ptr<ScattData>> scatter;
XsData() = default;
@ -68,7 +106,7 @@ class XsData {
//! @param scatter_format The scattering representation of the file.
//! @param n_pol Number of polar angles.
//! @param n_azi Number of azimuthal angles.
XsData(int num_groups, int num_delayed_groups, bool fissionable,
XsData(size_t num_groups, size_t num_delayed_groups, bool fissionable,
int scatter_format, int n_pol, int n_azi);
//! \brief Loads the XsData object from the HDF5 file
@ -101,7 +139,7 @@ class XsData {
//! @param micros Microscopic objects to combine.
//! @param scalars Scalars to multiply the microscopic data by.
void
combine(const std::vector<XsData*>& those_xs, const double_1dvec& scalars);
combine(const std::vector<XsData*>& those_xs, const std::vector<double>& scalars);
//! \brief Checks to see if this and that are able to be combined
//!

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

@ -41,6 +41,8 @@ _dll.openmc_mesh_set_params.errcheck = _error_handler
_dll.openmc_get_mesh_index.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_get_mesh_index.restype = c_int
_dll.openmc_get_mesh_index.errcheck = _error_handler
_dll.n_meshes.argtypes = []
_dll.n_meshes.restype = c_int
class Mesh(_FortranObjectWithID):
@ -172,10 +174,10 @@ class _MeshMapping(Mapping):
def __iter__(self):
for i in range(len(self)):
yield Mesh(index=i + 1).id
yield Mesh(index=i).id
def __len__(self):
return c_int32.in_dll(_dll, 'n_meshes').value
return _dll.n_meshes()
def __repr__(self):
return repr(dict(self))

View file

@ -20,11 +20,11 @@ class _Settings(object):
generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch')
inactive = _DLLGlobal(c_int32, 'n_inactive')
particles = _DLLGlobal(c_int64, 'n_particles')
verbosity = _DLLGlobal(c_int, 'openmc_verbosity')
verbosity = _DLLGlobal(c_int, 'verbosity')
@property
def run_mode(self):
i = c_int.in_dll(_dll, 'openmc_run_mode').value
i = c_int.in_dll(_dll, 'run_mode').value
try:
return _RUN_MODES[i]
except KeyError:
@ -32,7 +32,7 @@ class _Settings(object):
@run_mode.setter
def run_mode(self, mode):
current_idx = c_int.in_dll(_dll, 'openmc_run_mode')
current_idx = c_int.in_dll(_dll, 'run_mode')
for idx, mode_value in _RUN_MODES.items():
if mode_value == mode:
current_idx.value = idx

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

@ -1,3 +1,5 @@
from numbers import Integral
import numpy as np
import openmc
@ -538,20 +540,20 @@ def pwr_assembly():
return model
def slab_mg(reps=None, as_macro=True):
"""Create a one-group, 1D slab model.
def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5'):
"""Create a 1D slab model.
Parameters
----------
reps : list, optional
List of angular representations. Each item corresponds to materials and
dictates the angular representation of the multi-group cross
sections---isotropic ('iso') or angle-dependent ('ang'), and if Legendre
scattering or tabular scattering ('mu') is used. Thus, items can be
'ang', 'ang_mu', 'iso', or 'iso_mu'.
num_regions : int, optional
Number of regions in the problem, each with a unique MGXS dataset.
Defaults to 1.
as_macro : bool, optional
Whether :class:`openmc.Macroscopic` is used
mat_names : Iterable of str, optional
List of the material names to use; defaults to ['mat_1', 'mat_2',...].
mgxslib_name : str, optional
MGXS Library file to use; defaults to '2g.h5'.
Returns
-------
@ -559,71 +561,82 @@ def slab_mg(reps=None, as_macro=True):
One-group, 1D slab model
"""
openmc.check_type('num_regions', num_regions, Integral)
openmc.check_greater_than('num_regions', num_regions, 0)
if mat_names is not None:
openmc.check_length('mat_names', mat_names, num_regions)
openmc.check_iterable_type('mat_names', mat_names, str)
else:
mat_names = []
for i in range(num_regions):
mat_names.append('mat_' + str(i + 1))
# # Make Materials
materials_file = openmc.Materials()
macros = []
mats = []
for i in range(len(mat_names)):
macros.append(openmc.Macroscopic('mat_' + str(i + 1)))
mats.append(openmc.Material(name=mat_names[i]))
mats[-1].set_density('macro', 1.0)
mats[-1].add_macroscopic(macros[-1])
materials_file += mats
materials_file.cross_sections = mgxslib_name
# # Make Geometry
rad_outer = 929.45
# Set a cell boundary to exist for every material above (exclude the 0)
rads = np.linspace(0., rad_outer, len(mats) + 1, endpoint=True)[1:]
# Instantiate Universe
root = openmc.Universe(universe_id=0, name='root universe')
cells = []
surfs = []
surfs.append(openmc.XPlane(x0=0., boundary_type='reflective'))
for r, rad in enumerate(rads):
if r == len(rads) - 1:
surfs.append(openmc.XPlane(x0=rad, boundary_type='vacuum'))
else:
surfs.append(openmc.XPlane(x0=rad))
# Instantiate Cells
cells = []
for c in range(len(surfs) - 1):
cells.append(openmc.Cell())
cells[-1].region = (+surfs[c] & -surfs[c + 1])
cells[-1].fill = mats[c]
# Register Cells with Universe
root.add_cells(cells)
# Instantiate a Geometry, register the root Universe, and export to XML
geometry_file = openmc.Geometry(root)
# # Make Settings
# Instantiate a Settings object, set all runtime parameters
settings_file = openmc.Settings()
settings_file.energy_mode = "multi-group"
settings_file.tabular_legendre = {'enable': False}
settings_file.batches = 10
settings_file.inactive = 5
settings_file.particles = 1000
# Build source distribution
INF = 1000.
bounds = [0., -INF, -INF, rads[0], INF, INF]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:])
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.output = {'summary': False}
model = openmc.model.Model()
# Define materials needed for 1D/1G slab problem
mat_names = ['uo2', 'clad', 'lwtr']
mgxs_reps = ['ang', 'ang_mu', 'iso', 'iso_mu']
if reps is None:
reps = mgxs_reps
xs = []
i = 0
for mat in mat_names:
for rep in reps:
i += 1
name = mat + '_' + rep
xs.append(name)
if as_macro:
m = openmc.Material(name=str(i))
m.set_density('macro', 1.)
m.add_macroscopic(name)
else:
m = openmc.Material(name=str(i))
m.set_density('atom/b-cm', 1.)
m.add_nuclide(name, 1.0, 'ao')
model.materials.append(m)
# Define the materials file
model.xs_data = xs
model.materials.cross_sections = "../../1d_mgxs.h5"
# Define surfaces.
# Assembly/Problem Boundary
left = openmc.XPlane(x0=0.0, boundary_type='reflective')
right = openmc.XPlane(x0=10.0, boundary_type='reflective')
bottom = openmc.YPlane(y0=0.0, boundary_type='reflective')
top = openmc.YPlane(y0=10.0, boundary_type='reflective')
# for each material add a plane
planes = [openmc.ZPlane(z0=0.0, boundary_type='reflective')]
dz = round(5. / float(len(model.materials)), 4)
for i in range(len(model.materials) - 1):
planes.append(openmc.ZPlane(z0=dz * float(i + 1)))
planes.append(openmc.ZPlane(z0=5.0, boundary_type='reflective'))
# Define cells for each material
model.geometry.root_universe = openmc.Universe(name='root universe')
xy = +left & -right & +bottom & -top
for i, mat in enumerate(model.materials):
c = openmc.Cell(fill=mat, region=xy & +planes[i] & -planes[i + 1])
model.geometry.root_universe.add_cell(c)
model.settings.batches = 10
model.settings.inactive = 5
model.settings.particles = 100
model.settings.source = openmc.Source(space=openmc.stats.Box(
[0.0, 0.0, 0.0], [10.0, 10.0, 5.]))
model.settings.energy_mode = "multi-group"
plot = openmc.Plot()
plot.filename = 'mat'
plot.origin = (5.0, 5.0, 2.5)
plot.width = (2.5, 2.5)
plot.basis = 'xz'
plot.pixels = (3000, 3000)
plot.color_by = 'material'
model.plots.append(plot)
model.geometry = geometry_file
model.materials = materials_file
model.settings = settings_file
model.xs_data = macros
return model

View file

@ -19,7 +19,7 @@ class ExpansionFilter(Filter):
if type(self) is not type(other):
return False
else:
return self.bins == other.bins
return hash(self) == hash(other)
@property
def order(self):
@ -325,8 +325,8 @@ class ZernikeFilter(ExpansionFilter):
This filter allows scores to be multiplied by Zernike polynomials of the
particle's position normalized to a given unit circle, up to a
user-specified order. The standard Zernike polynomials follow the definition by
Born and Wolf, *Principles of Optics* and are defined as
user-specified order. The standard Zernike polynomials follow the
definition by Born and Wolf, *Principles of Optics* and are defined as
.. math::
Z_n^m(\rho, \theta) = R_n^m(\rho) \cos (m\theta), \quad m > 0
@ -342,7 +342,7 @@ class ZernikeFilter(ExpansionFilter):
\frac{n+m}{2} - k)! (\frac{n-m}{2} - k)!} \rho^{n-2k}.
With this definition, the integral of :math:`(Z_n^m)^2` over the unit disk
is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where
is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where
:math:`\epsilon_m` is 2 if :math:`m` equals 0 and 1 otherwise.
Specifying a filter with order N tallies moments for all :math:`n` from 0
@ -390,6 +390,9 @@ class ZernikeFilter(ExpansionFilter):
def __hash__(self):
string = type(self).__name__ + '\n'
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
string += '{: <16}=\t{}\n'.format('\tX', self.x)
string += '{: <16}=\t{}\n'.format('\tY', self.y)
string += '{: <16}=\t{}\n'.format('\tR', self.r)
return hash(string)
def __repr__(self):
@ -485,7 +488,7 @@ class ZernikeRadialFilter(ZernikeFilter):
is :math:`\frac{\pi}{n+1}`.
If there is only radial dependency, the polynomials are integrated over
the azimuthal angles. The only terms left are :math:`Z_n^{0}(\rho, \theta)
the azimuthal angles. The only terms left are :math:`Z_n^{0}(\rho, \theta)
= R_n^{0}(\rho)`. Note that :math:`n` could only be even orders.
Therefore, for a radial Zernike polynomials up to order of :math:`n`,
there are :math:`\frac{n}{2} + 1` terms in total. The indexing is from the

View file

@ -1,9 +1,10 @@
import numpy as np
import openmc
import openmc.capi as capi
from collections.abc import Iterable
def legendre_from_expcoef(coef, domain= (-1,1)):
def legendre_from_expcoef(coef, domain=(-1, 1)):
"""Return a Legendre series object based on expansion coefficients.
Given a list of coefficients from FET tally and a array of down, return
@ -73,5 +74,8 @@ class ZernikeRadial(Polynomial):
return self._order
def __call__(self, r):
zn_rad = capi.calc_zn_rad(self.order, r)
return np.sum(self._norm_coef * zn_rad)
if isinstance(r, Iterable):
return [np.sum(self._norm_coef * capi.calc_zn_rad(self.order, r_i))
for r_i in r]
else:
return np.sum(self._norm_coef * capi.calc_zn_rad(self.order, r))

View file

@ -148,16 +148,11 @@ if not response or response.lower().startswith('y'):
# get a list of all ACE files
ace_files = sorted(glob.glob(os.path.join('nndc', '**', '*.ace*')))
# Get path to fission energy release data
data_dir = os.path.dirname(sys.modules['openmc.data'].__file__)
fer_file = os.path.join(data_dir, 'fission_Q_data_endfb71.h5')
# Call the ace-to-hdf5 conversion script
pwd = os.path.dirname(os.path.realpath(__file__))
ace2hdf5 = os.path.join(pwd, 'openmc-ace-to-hdf5')
subprocess.call([ace2hdf5,
'-d', 'nndc_hdf5',
'--fission_energy_release', fer_file,
'--libver', args.libver] + ace_files)
# Generate photo interaction library files

View file

@ -32,7 +32,7 @@ kwargs = {
# Data files and librarries
'package_data': {
'openmc.capi': ['libopenmc.{}'.format(suffix)],
'openmc.data': ['mass.mas12', '*.h5']
'openmc.data': ['mass.mas12', 'BREMX.DAT', '*.h5']
},
# Metadata
@ -52,6 +52,7 @@ kwargs = {
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
# Required dependencies

View file

@ -11,7 +11,6 @@ module openmc_api
use hdf5_interface
use material_header
use math
use mesh_header
use message_passing
use nuclide_header
use initialize, only: openmc_init_f
@ -20,7 +19,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 +36,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 +79,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
@ -136,7 +131,7 @@ contains
legendre_to_tabular_points = C_NONE
n_batch_interval = 1
n_lost_particles = 0
n_particles = 0
n_particles = -1
n_source_points = 0
n_state_points = 0
n_tallies = 0
@ -153,7 +148,7 @@ contains
restart_run = .false.
root_universe = -1
run_CE = .true.
run_mode = NONE
run_mode = -1
satisfy_triggers = .false.
call openmc_set_seed(DEFAULT_SEED)
source_latest = .false.
@ -210,8 +205,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 +296,19 @@ 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
subroutine free_memory_mesh() bind(C)
end subroutine free_memory_mesh
end interface
call free_memory_geometry()
call free_memory_surfaces()
call free_memory_material()

View file

@ -28,7 +28,7 @@ module bank_header
type(Bank), allocatable, target :: master_fission_bank(:)
#endif
integer(8) :: n_bank ! # of sites in fission bank
integer(C_INT64_T), bind(C) :: n_bank ! # of sites in fission bank
!$omp threadprivate(fission_bank, n_bank)
@ -71,4 +71,20 @@ contains
end if
end function openmc_source_bank
function openmc_fission_bank(ptr, n) result(err) bind(C)
! Return a pointer to the source bank
type(C_PTR), intent(out) :: ptr
integer(C_INT64_T), intent(out) :: n
integer(C_INT) :: err
if (.not. allocated(fission_bank)) then
err = E_ALLOCATE
call set_errmsg("Fission bank has not been allocated.")
else
err = 0
ptr = C_LOC(fission_bank)
n = size(fission_bank)
end if
end function openmc_fission_bank
end module bank_header

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 (settings::check_overlaps) overlap_check_count.resize(n_cells, 0);
}
//==============================================================================
@ -481,15 +630,15 @@ read_cells(pugi::xml_node* node)
extern "C" int
openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n)
{
if (index >= 1 && index <= global_cells.size()) {
if (index >= 1 && index <= cells.size()) {
//TODO: off-by-one
Cell& c {*global_cells[index - 1]};
*type = c.type;
if (c.type == FILL_MATERIAL) {
*indices = c.material.data();
*n = c.material.size();
Cell& c {*cells[index - 1]};
*type = c.type_;
if (c.type_ == FILL_MATERIAL) {
*indices = c.material_.data();
*n = c.material_.size();
} else {
*indices = &c.fill;
*indices = &c.fill_;
*n = 1;
}
} else {
@ -503,29 +652,29 @@ extern "C" int
openmc_cell_set_fill(int32_t index, int type, int32_t n,
const int32_t* indices)
{
if (index >= 1 && index <= global_cells.size()) {
if (index >= 1 && index <= cells.size()) {
//TODO: off-by-one
Cell& c {*global_cells[index - 1]};
Cell& c {*cells[index - 1]};
if (type == FILL_MATERIAL) {
c.type = FILL_MATERIAL;
c.material.clear();
c.type_ = FILL_MATERIAL;
c.material_.clear();
for (int i = 0; i < n; i++) {
int i_mat = indices[i];
if (i_mat == MATERIAL_VOID) {
c.material.push_back(MATERIAL_VOID);
} else if (i_mat >= 1 && i_mat <= global_materials.size()) {
c.material_.push_back(MATERIAL_VOID);
} else if (i_mat >= 1 && i_mat <= materials.size()) {
//TODO: off-by-one
c.material.push_back(i_mat - 1);
c.material_.push_back(i_mat - 1);
} else {
set_errmsg("Index in materials array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
}
c.material.shrink_to_fit();
c.material_.shrink_to_fit();
} else if (type == FILL_UNIVERSE) {
c.type = FILL_UNIVERSE;
c.type_ = FILL_UNIVERSE;
} else {
c.type = FILL_LATTICE;
c.type_ = FILL_LATTICE;
}
} else {
set_errmsg("Index in cells array is out of bounds.");
@ -534,66 +683,85 @@ openmc_cell_set_fill(int32_t index, int type, int32_t n,
return 0;
}
//TODO: make sure data is loaded for this temperature
extern "C" int
openmc_cell_set_temperature(int32_t index, double T, const int32_t* instance)
{
if (index >= 1 && index <= cells.size()) {
//TODO: off-by-one
Cell& c {*cells[index - 1]};
if (instance) {
if (*instance >= 0 && *instance < c.sqrtkT_.size()) {
c.sqrtkT_[*instance] = std::sqrt(K_BOLTZMANN * T);
} else {
strcpy(openmc_err_msg, "Distribcell instance is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
} else {
for (auto& T_ : c.sqrtkT_) {
T_ = std::sqrt(K_BOLTZMANN * T);
}
}
} else {
strcpy(openmc_err_msg, "Index in cells array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
return 0;
}
//==============================================================================
// Fortran compatibility functions
//==============================================================================
extern "C" {
Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];}
Cell* cell_pointer(int32_t cell_ind) {return cells[cell_ind];}
int32_t cell_id(Cell* c) {return c->id;}
int32_t cell_id(Cell* c) {return c->id_;}
void cell_set_id(Cell* c, int32_t id) {c->id = id;}
void cell_set_id(Cell* c, int32_t id) {c->id_ = id;}
int cell_type(Cell* c) {return c->type;}
int cell_type(Cell* c) {return c->type_;}
int32_t cell_universe(Cell* c) {return c->universe;}
int32_t cell_universe(Cell* c) {return c->universe_;}
int32_t cell_fill(Cell* c) {return c->fill;}
int32_t cell_fill(Cell* c) {return c->fill_;}
int32_t cell_n_instances(Cell* c) {return c->n_instances;}
int32_t cell_n_instances(Cell* c) {return c->n_instances_;}
int cell_material_size(Cell* c) {return c->material.size();}
int cell_distribcell_index(Cell* c) {return c->distribcell_index_;}
int cell_material_size(Cell* c) {return c->material_.size();}
//TODO: off-by-one
int32_t cell_material(Cell* c, int i)
{
int32_t mat = c->material[i-1];
int32_t mat = c->material_[i-1];
if (mat == MATERIAL_VOID) return MATERIAL_VOID;
return mat + 1;
}
bool cell_simple(Cell* c) {return c->simple;}
int cell_sqrtkT_size(Cell* c) {return c->sqrtkT_.size();}
bool cell_contains(Cell* c, double xyz[3], double uvw[3], int32_t on_surface)
{
Position r {xyz};
Direction u {uvw};
return c->contains(r, u, on_surface);
}
double cell_sqrtkT(Cell* c, int i) {return c->sqrtkT_[i];}
void cell_distance(Cell* c, double xyz[3], double uvw[3], int32_t on_surface,
double* min_dist, int32_t* i_surf)
{
Position r {xyz};
Direction u {uvw};
std::pair<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

@ -80,7 +80,7 @@ contains
integer :: i_mesh ! flattend index for mesh
logical :: energy_filters! energy filters present
real(8) :: flux ! temp variable for flux
type(RegularMesh), pointer :: m ! pointer for mesh object
type(RegularMesh) :: m ! pointer for mesh object
! Extract spatial and energy indices from object
nx = cmfd % indices(1)
@ -99,7 +99,7 @@ contains
select type(filt => filters(i_filter_mesh) % obj)
type is (MeshFilter)
m => meshes(filt % mesh)
m = meshes(filt % mesh)
end select
! Set mesh widths
@ -354,9 +354,6 @@ contains
! Normalize openmc source distribution
cmfd % openmc_src = cmfd % openmc_src/sum(cmfd % openmc_src)*cmfd%norm
! Nullify all pointers
if (associated(m)) nullify(m)
end subroutine compute_xs
!===============================================================================

View file

@ -214,8 +214,6 @@ contains
use bank_header, only: source_bank
use constants, only: ZERO, ONE
use error, only: warning, fatal_error
use mesh_header, only: RegularMesh
use mesh, only: count_bank_sites
use message_passing
use string, only: to_str
@ -224,7 +222,7 @@ contains
integer :: nx ! maximum number of cells in x direction
integer :: ny ! maximum number of cells in y direction
integer :: nz ! maximum number of cells in z direction
integer :: ng ! maximum number of energy groups
integer(C_INT) :: ng ! maximum number of energy groups
integer :: i ! iteration counter
integer :: g ! index for group
integer :: ijk(3) ! spatial bin location
@ -232,12 +230,22 @@ contains
integer :: mesh_bin ! mesh bin of soruce particle
integer :: n_groups ! number of energy groups
real(8) :: norm ! normalization factor
logical :: outside ! any source sites outside mesh
logical(C_BOOL) :: outside ! any source sites outside mesh
logical :: in_mesh ! source site is inside mesh
#ifdef OPENMC_MPI
integer :: mpi_err
#endif
interface
subroutine cmfd_populate_sourcecounts(ng, energies, source_counts, outside) bind(C)
import C_INT, C_DOUBLE, C_BOOL
integer(C_INT), value :: ng
real(C_DOUBLE), intent(in) :: energies
real(C_DOUBLE), intent(out) :: source_counts
logical(C_BOOL), intent(out) :: outside
end subroutine
end interface
! Get maximum of spatial and group indices
nx = cmfd % indices(1)
ny = cmfd % indices(2)
@ -261,8 +269,8 @@ contains
cmfd%weightfactors = ONE
! Count bank sites in mesh and reverse due to egrid structure
call count_bank_sites(cmfd_mesh, source_bank, cmfd%sourcecounts, &
cmfd % egrid, sites_outside=outside, size_bank=work)
call cmfd_populate_sourcecounts(ng + 1, cmfd % egrid(1), &
cmfd % sourcecounts(1,1), outside)
! Check for sites outside of the mesh
if (master .and. outside) then

34
src/cmfd_execute.cpp Normal file
View file

@ -0,0 +1,34 @@
#include <algorithm> // for copy
#include <cstdint>
#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "openmc/capi.h"
#include "openmc/mesh.h"
namespace openmc {
extern "C" int index_cmfd_mesh;
extern "C" void
cmfd_populate_sourcecounts(int n_energy, const double* energies,
double* source_counts, bool* outside)
{
// Get pointer to source bank
Bank* source_bank;
int64_t n;
openmc_source_bank(&source_bank, &n);
// Get source counts in each mesh bin / energy bin
auto& m = meshes.at(index_cmfd_mesh);
xt::xarray<double> counts = m->count_sites(openmc_work, source_bank, n_energy, energies, outside);
std::cout << counts << "\n";
// Copy data from the xarray into the source counts array
std::copy(counts.begin(), counts.end(), source_counts);
}
} // namespace openmc

View file

@ -1,5 +1,7 @@
module cmfd_header
use, intrinsic :: ISO_C_BINDING
use constants, only: CMFD_NOACCEL, ZERO, ONE
use mesh_header, only: RegularMesh
use set_header, only: SetInt
@ -24,7 +26,7 @@ module cmfd_header
integer :: mat_dim = CMFD_NOACCEL
! Energy grid
real(8), allocatable :: egrid(:)
real(C_DOUBLE), allocatable :: egrid(:)
! Cross sections
real(8), allocatable :: totalxs(:,:,:,:)
@ -53,7 +55,7 @@ module cmfd_header
real(8), allocatable :: openmc_src(:,:,:,:)
! Source sites in each mesh box
real(8), allocatable :: sourcecounts(:,:)
real(C_DOUBLE), allocatable :: sourcecounts(:,:)
! Weight adjustment factors
real(8), allocatable :: weightfactors(:,:,:,:)
@ -95,7 +97,8 @@ module cmfd_header
! Main object
type(cmfd_type), public :: cmfd
type(RegularMesh), public, pointer :: cmfd_mesh => null()
integer(C_INT), public, bind(C) :: index_cmfd_mesh
type(RegularMesh), public :: cmfd_mesh
! Pointers for different tallies
type(TallyContainer), public, pointer :: cmfd_tallies(:) => null()

View file

@ -3,7 +3,7 @@ module cmfd_input
use, intrinsic :: ISO_C_BINDING
use cmfd_header
use mesh_header, only: mesh_dict
use mesh_header
use mgxs_interface, only: energy_bins, num_energy_groups
use tally
use tally_header
@ -241,7 +241,7 @@ contains
use constants, only: MAX_LINE_LEN
use error, only: fatal_error, warning
use mesh_header, only: RegularMesh, openmc_extend_meshes
use mesh_header
use string
use tally, only: openmc_tally_allocate
use tally_header, only: openmc_extend_tallies
@ -263,115 +263,23 @@ contains
integer :: i_filt ! index in filters array
integer :: filt_id
integer :: tally_id
integer :: iarray3(3) ! temp integer array
real(8) :: rarray3(3) ! temp double array
real(C_DOUBLE), allocatable :: energies(:)
type(RegularMesh), pointer :: m
type(XMLNode) :: node_mesh
err = openmc_extend_meshes(1, i_start)
! Read CMFD mesh
call read_meshes(root % ptr)
! Allocate mesh
cmfd_mesh => meshes(i_start)
m => meshes(i_start)
! Get index of cmfd mesh and set ID
i_start = n_meshes() - 1
err = openmc_mesh_set_id(i_start, i_start)
! Set mesh id
m % id = i_start
! Set mesh type to rectangular
m % type = MESH_REGULAR
! Save reference to CMFD mesh
index_cmfd_mesh = i_start
cmfd_mesh = meshes(i_start)
! Get pointer to mesh XML node
node_mesh = root % child("mesh")
! Determine number of dimensions for mesh
n = node_word_count(node_mesh, "dimension")
if (n /= 2 .and. n /= 3) then
call fatal_error("Mesh must be two or three dimensions.")
end if
m % n_dimension = n
! Allocate attribute arrays
allocate(m % dimension(n))
allocate(m % lower_left(n))
allocate(m % width(n))
allocate(m % upper_right(n))
! Check that dimensions are all greater than zero
call get_node_array(node_mesh, "dimension", iarray3(1:n))
if (any(iarray3(1:n) <= 0)) then
call fatal_error("All entries on the <dimension> element for a tally mesh&
& must be positive.")
end if
! Read dimensions in each direction
m % dimension = iarray3(1:n)
! Read mesh lower-left corner location
if (m % n_dimension /= node_word_count(node_mesh, "lower_left")) then
call fatal_error("Number of entries on <lower_left> must be the same as &
&the number of entries on <dimension>.")
end if
call get_node_array(node_mesh, "lower_left", m % lower_left)
! Make sure both upper-right or width were specified
if (check_for_node(node_mesh, "upper_right") .and. &
check_for_node(node_mesh, "width")) then
call fatal_error("Cannot specify both <upper_right> and <width> on a &
&tally mesh.")
end if
! Make sure either upper-right or width was specified
if (.not.check_for_node(node_mesh, "upper_right") .and. &
.not.check_for_node(node_mesh, "width")) then
call fatal_error("Must specify either <upper_right> and <width> on a &
&tally mesh.")
end if
if (check_for_node(node_mesh, "width")) then
! Check to ensure width has same dimensions
if (node_word_count(node_mesh, "width") /= &
node_word_count(node_mesh, "lower_left")) then
call fatal_error("Number of entries on <width> must be the same as the &
&number of entries on <lower_left>.")
end if
! Check for negative widths
call get_node_array(node_mesh, "width", rarray3(1:n))
if (any(rarray3(1:n) < ZERO)) then
call fatal_error("Cannot have a negative <width> on a tally mesh.")
end if
! Set width and upper right coordinate
m % width = rarray3(1:n)
m % upper_right = m % lower_left + m % dimension * m % width
elseif (check_for_node(node_mesh, "upper_right")) then
! Check to ensure width has same dimensions
if (node_word_count(node_mesh, "upper_right") /= &
node_word_count(node_mesh, "lower_left")) then
call fatal_error("Number of entries on <upper_right> must be the same &
&as the number of entries on <lower_left>.")
end if
! Check that upper-right is above lower-left
call get_node_array(node_mesh, "upper_right", rarray3(1:n))
if (any(rarray3(1:n) < m % lower_left)) then
call fatal_error("The <upper_right> coordinates must be greater than &
&the <lower_left> coordinates on a tally mesh.")
end if
! Set upper right coordinate and width
m % upper_right = rarray3(1:n)
m % width = (m % upper_right - m % lower_left) / real(m % dimension, 8)
end if
! Set volume fraction
m % volume_frac = ONE/real(product(m % dimension),8)
! Add mesh to dictionary
call mesh_dict % set(m % id, i_start)
! Determine number of filters
energy_filters = check_for_node(node_mesh, "energy")
n = merge(5, 3, energy_filters)

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

@ -6,8 +6,6 @@ module eigenvalue
use constants, only: ZERO
use error, only: fatal_error, warning
use math, only: t_percentile
use mesh, only: count_bank_sites
use mesh_header, only: RegularMesh, meshes
use message_passing
use random_lcg, only: prn, set_particle_seed, advance_prn_seed
use settings
@ -294,46 +292,6 @@ contains
end subroutine synchronize_bank
!===============================================================================
! SHANNON_ENTROPY calculates the Shannon entropy of the fission source
! distribution to assess source convergence
!===============================================================================
subroutine shannon_entropy()
integer :: i ! index for mesh elements
real(8) :: entropy_gen ! entropy at this generation
logical :: sites_outside ! were there sites outside entropy box?
associate (m => meshes(index_entropy_mesh))
! count number of fission sites over mesh
call count_bank_sites(m, fission_bank, entropy_p, &
size_bank=n_bank, sites_outside=sites_outside)
! display warning message if there were sites outside entropy box
if (sites_outside) then
if (master) call warning("Fission source site(s) outside of entropy box.")
end if
! sum values to obtain shannon entropy
if (master) then
! Normalize to total weight of bank sites
entropy_p = entropy_p / sum(entropy_p)
entropy_gen = ZERO
do i = 1, size(entropy_p, 2)
if (entropy_p(1,i) > ZERO) then
entropy_gen = entropy_gen - &
entropy_p(1,i) * log(entropy_p(1,i))/log(TWO)
end if
end do
! Add value to vector
call entropy % push_back(entropy_gen)
end if
end associate
end subroutine shannon_entropy
!===============================================================================
! CALCULATE_GENERATION_KEFF collects the single-processor tracklength k's onto
! the master processor and normalizes them. This should work whether or not the
@ -577,61 +535,6 @@ contains
end function openmc_get_keff
!===============================================================================
! COUNT_SOURCE_FOR_UFS determines the source fraction in each UFS mesh cell and
! reweights the source bank so that the sum of the weights is equal to
! n_particles. The 'source_frac' variable is used later to bias the production
! of fission sites
!===============================================================================
subroutine count_source_for_ufs()
real(8) :: total ! total weight in source bank
logical :: sites_outside ! were there sites outside the ufs mesh?
#ifdef OPENMC_MPI
integer :: n ! total number of ufs mesh cells
integer :: mpi_err ! MPI error code
#endif
associate (m => meshes(index_ufs_mesh))
if (current_batch == 1 .and. current_gen == 1) then
! On the first generation, just assume that the source is already evenly
! distributed so that effectively the production of fission sites is not
! biased
source_frac = m % volume_frac
else
! count number of source sites in each ufs mesh cell
call count_bank_sites(m, source_bank, source_frac, &
sites_outside=sites_outside, size_bank=work)
! Check for sites outside of the mesh
if (master .and. sites_outside) then
call fatal_error("Source sites outside of the UFS mesh!")
end if
#ifdef OPENMC_MPI
! Send source fraction to all processors
n = product(m % dimension)
call MPI_BCAST(source_frac, n, MPI_REAL8, 0, mpi_intracomm, mpi_err)
#endif
! Normalize to total weight to get fraction of source in each cell
total = sum(source_frac)
source_frac = source_frac / total
! Since the total starting weight is not equal to n_particles, we need to
! renormalize the weight of the source sites
source_bank % wgt = source_bank % wgt * n_particles / total
end if
end associate
end subroutine count_source_for_ufs
#ifdef _OPENMP
!===============================================================================
! JOIN_BANK_FROM_THREADS joins threadprivate fission banks into a single fission

155
src/eigenvalue.cpp Normal file
View file

@ -0,0 +1,155 @@
#include "openmc/eigenvalue.h"
#include "xtensor/xmath.hpp"
#include "xtensor/xtensor.hpp"
#include "xtensor/xview.hpp"
#include "openmc/capi.h"
#include "openmc/error.h"
#include "openmc/hdf5_interface.h"
#include "openmc/mesh.h"
#include "openmc/message_passing.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
std::vector<double> entropy;
xt::xtensor<double, 1> source_frac;
//==============================================================================
// Non-member functions
//==============================================================================
void shannon_entropy()
{
// Get pointer to entropy mesh
auto& m = meshes[settings::index_entropy_mesh];
// Get pointer to fission bank
Bank* fission_bank;
int64_t n;
openmc_fission_bank(&fission_bank, &n);
// Get source weight in each mesh bin
bool sites_outside;
xt::xtensor<double, 1> p = m->count_sites(
n_bank, fission_bank, 0, nullptr, &sites_outside);
// display warning message if there were sites outside entropy box
if (sites_outside) {
if (mpi::master) warning("Fission source site(s) outside of entropy box.");
}
// sum values to obtain shannon entropy
if (mpi::master) {
// Normalize to total weight of bank sites
p /= xt::sum(p);
double H = 0.0;
for (auto p_i : p) {
if (p_i > 0.0) {
H -= p_i * std::log(p_i)/std::log(2.0);
}
}
// Add value to vector
entropy.push_back(H);
}
}
void ufs_count_sites()
{
auto &m = meshes[settings::index_ufs_mesh];
if (openmc_current_batch == 1 && openmc_current_gen == 1) {
// On the first generation, just assume that the source is already evenly
// distributed so that effectively the production of fission sites is not
// biased
auto s = xt::view(source_frac, xt::all());
s = m->volume_frac_;
} else {
// Get pointer to source bank
Bank* source_bank;
int64_t n;
openmc_source_bank(&source_bank, &n);
// count number of source sites in each ufs mesh cell
bool sites_outside;
source_frac = m->count_sites(openmc_work, source_bank, 0, nullptr,
&sites_outside);
// Check for sites outside of the mesh
if (mpi::master && sites_outside) {
fatal_error("Source sites outside of the UFS mesh!");
}
#ifdef OPENMC_MPI
// Send source fraction to all processors
int n_bins = xt::prod(m->shape_)();
MPI_Bcast(source_frac.data(), n_bins, MPI_DOUBLE, 0, mpi::intracomm);
#endif
// Normalize to total weight to get fraction of source in each cell
double total = xt::sum(source_frac)();
source_frac /= total;
// Since the total starting weight is not equal to n_particles, we need to
// renormalize the weight of the source sites
for (int i = 0; i < openmc_work; ++i) {
source_bank[i].wgt *= settings::n_particles / total;
}
}
}
double ufs_get_weight(const Particle* p)
{
auto& m = meshes[settings::index_ufs_mesh];
// Determine indices on ufs mesh for current location
// TODO: off by one
int mesh_bin = m->get_bin({p->coord[0].xyz}) - 1;
if (mesh_bin < 0) {
p->write_restart();
fatal_error("Source site outside UFS mesh!");
}
if (source_frac(mesh_bin) != 0.0) {
return m->volume_frac_ / source_frac(mesh_bin);
} else {
return 1.0;
}
}
extern "C" void entropy_to_hdf5(hid_t group)
{
if (settings::entropy_on) {
write_dataset(group, "entropy", entropy);
}
}
extern "C" void entropy_from_hdf5(hid_t group)
{
if (settings::entropy_on) {
read_dataset(group, "entropy", entropy);
}
}
extern "C" double entropy_c(int i)
{
return entropy.at(i - 1);
}
extern "C" double entropy_clear()
{
entropy.clear();
}
} // namespace openmc

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

409
src/geometry.cpp Normal file
View file

@ -0,0 +1,409 @@
#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/settings.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 (settings::verbosity >= 10 || openmc_trace) {
std::stringstream msg;
msg << " Entering cell " << cells[i_cell]->id_;
write_message(msg, 1);
}
found = true;
break;
}
}
if (found) {
Cell& c {*cells[i_cell]};
if (c.type_ == FILL_MATERIAL) {
//=======================================================================
//! Found a material cell which means this is the lowest coord level.
// Find the distribcell instance number.
if (c.material_.size() > 1 || c.sqrtkT_.size() > 1) {
int offset = 0;
for (int i = 0; i < p->n_coord; i++) {
Cell& c_i {*cells[p->coord[i].cell]};
if (c_i.type_ == FILL_UNIVERSE) {
offset += c_i.offset_[c.distribcell_index_];
} else if (c_i.type_ == FILL_LATTICE) {
Lattice& lat {*lattices[p->coord[i+1].lattice-1]};
int i_xyz[3] {p->coord[i+1].lattice_x,
p->coord[i+1].lattice_y,
p->coord[i+1].lattice_z};
if (lat.are_valid_indices(i_xyz)) {
offset += lat.offset(c.distribcell_index_, i_xyz);
}
}
}
p->cell_instance = offset;
} else {
p->cell_instance = 0;
}
// Set the material and temperature.
p->last_material = p->material;
int32_t mat;
if (c.material_.size() > 1) {
mat = c.material_[p->cell_instance];
} else {
mat = c.material_[0];
}
if (mat == MATERIAL_VOID) {
p->material = MATERIAL_VOID;
} else {
p->material = mat + 1;
}
p->last_sqrtkT = p->sqrtkT;
if (c.sqrtkT_.size() > 1) {
p->sqrtkT = c.sqrtkT_[p->cell_instance];
} else {
p->sqrtkT = c.sqrtkT_[0];
}
return true;
} else if (c.type_ == FILL_UNIVERSE) {
//========================================================================
//! Found a lower universe, update this coord level then search the next.
// Set the lower coordinate level universe.
p->coord[p->n_coord].universe = c.fill_;
// Set the position and direction.
for (int i = 0; i < 3; i++) {
p->coord[p->n_coord].xyz[i] = p->coord[p->n_coord-1].xyz[i];
p->coord[p->n_coord].uvw[i] = p->coord[p->n_coord-1].uvw[i];
}
// Apply translation.
p->coord[p->n_coord].xyz[0] -= c.translation_.x;
p->coord[p->n_coord].xyz[1] -= c.translation_.y;
p->coord[p->n_coord].xyz[2] -= c.translation_.z;
// Apply rotation.
if (!c.rotation_.empty()) {
auto x = p->coord[p->n_coord].xyz[0];
auto y = p->coord[p->n_coord].xyz[1];
auto z = p->coord[p->n_coord].xyz[2];
p->coord[p->n_coord].xyz[0] = x*c.rotation_[3] + y*c.rotation_[4]
+ z*c.rotation_[5];
p->coord[p->n_coord].xyz[1] = x*c.rotation_[6] + y*c.rotation_[7]
+ z*c.rotation_[8];
p->coord[p->n_coord].xyz[2] = x*c.rotation_[9] + y*c.rotation_[10]
+ z*c.rotation_[11];
auto u = p->coord[p->n_coord].uvw[0];
auto v = p->coord[p->n_coord].uvw[1];
auto w = p->coord[p->n_coord].uvw[2];
p->coord[p->n_coord].uvw[0] = u*c.rotation_[3] + v*c.rotation_[4]
+ w*c.rotation_[5];
p->coord[p->n_coord].uvw[1] = u*c.rotation_[6] + v*c.rotation_[7]
+ w*c.rotation_[8];
p->coord[p->n_coord].uvw[2] = u*c.rotation_[9] + v*c.rotation_[10]
+ w*c.rotation_[11];
p->coord[p->n_coord].rotated = true;
}
// Update the coordinate level and recurse.
++p->n_coord;
return find_cell(p, 0);
} else if (c.type_ == FILL_LATTICE) {
//========================================================================
//! Found a lower lattice, update this coord level then search the next.
Lattice& lat {*lattices[c.fill_]};
// Determine lattice indices.
Position r {p->coord[p->n_coord-1].xyz};
Direction u {p->coord[p->n_coord-1].uvw};
r += TINY_BIT * u;
auto i_xyz = lat.get_indices(r);
// Store lower level coordinates.
r = lat.get_local_position(p->coord[p->n_coord-1].xyz, i_xyz);
p->coord[p->n_coord].xyz[0] = r.x;
p->coord[p->n_coord].xyz[1] = r.y;
p->coord[p->n_coord].xyz[2] = r.z;
p->coord[p->n_coord].uvw[0] = u.x;
p->coord[p->n_coord].uvw[1] = u.y;
p->coord[p->n_coord].uvw[2] = u.z;
// Set lattice indices.
p->coord[p->n_coord].lattice = c.fill_ + 1;
p->coord[p->n_coord].lattice_x = i_xyz[0];
p->coord[p->n_coord].lattice_y = i_xyz[1];
p->coord[p->n_coord].lattice_z = i_xyz[2];
// Set the lower coordinate level universe.
if (lat.are_valid_indices(i_xyz)) {
p->coord[p->n_coord].universe = lat[i_xyz];
} else {
if (lat.outer_ != NO_OUTER_UNIVERSE) {
p->coord[p->n_coord].universe = lat.outer_;
} else {
std::stringstream err_msg;
err_msg << "Particle " << p->id << " is outside lattice "
<< lat.id_ << " but the lattice has no defined outer "
"universe.";
warning(err_msg);
return false;
}
}
// Update the coordinate level and recurse.
++p->n_coord;
return find_cell(p, 0);
}
}
return found;
}
//==============================================================================
extern "C" void
cross_lattice(Particle* p, int lattice_translation[3])
{
Lattice& lat {*lattices[p->coord[p->n_coord-1].lattice-1]};
if (settings::verbosity >= 10 || openmc_trace) {
std::stringstream msg;
msg << " Crossing lattice " << lat.id_ << ". Current position ("
<< p->coord[p->n_coord-1].lattice_x << ","
<< p->coord[p->n_coord-1].lattice_y << ","
<< p->coord[p->n_coord-1].lattice_z << ")";
write_message(msg, 1);
}
// Set the lattice indices.
p->coord[p->n_coord-1].lattice_x += lattice_translation[0];
p->coord[p->n_coord-1].lattice_y += lattice_translation[1];
p->coord[p->n_coord-1].lattice_z += lattice_translation[2];
std::array<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,81 @@ adjust_indices()
}
// Change cell.universe values from IDs to indices.
for (Cell* c : global_cells) {
auto search = universe_map.find(c->universe);
for (Cell* c : cells) {
auto search = universe_map.find(c->universe_);
if (search != universe_map.end()) {
//TODO: Remove this off-by-one indexing.
c->universe = search->second + 1;
c->universe_ = search->second;
} else {
std::stringstream err_msg;
err_msg << "Could not find universe " << c->universe
<< " specified on cell " << c->id;
err_msg << "Could not find universe " << c->universe_
<< " specified on cell " << c->id_;
fatal_error(err_msg);
}
}
// Change all lattice universe values from IDs to indices.
for (Lattice* l : lattices_c) {
for (Lattice* l : lattices) {
l->adjust_indices();
}
}
//==============================================================================
void
assign_temperatures()
{
for (Cell* c : cells) {
// Ignore non-material cells and cells with defined temperature.
if (c->material_.size() == 0) continue;
if (c->sqrtkT_.size() > 0) continue;
c->sqrtkT_.reserve(c->material_.size());
for (auto i_mat : c->material_) {
if (i_mat == MATERIAL_VOID) {
// Set void region to 0K.
c->sqrtkT_.push_back(0);
} else {
if (materials[i_mat]->temperature_ >= 0) {
// This material has a default temperature; use that value.
auto T = materials[i_mat]->temperature_;
c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * T));
} else {
// Use the global default temperature.
c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN *
settings::temperature_default));
}
}
}
}
}
//==============================================================================
int32_t
find_root_universe()
{
// Find all the universes listed as a cell fill.
std::unordered_set<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 +154,116 @@ find_root_universe()
//==============================================================================
void
allocate_offset_tables(int n_maps)
neighbor_lists()
{
for (Cell* c : global_cells) {
if (c->type != FILL_MATERIAL) {
c->offset.resize(n_maps, C_NONE);
write_message("Building neighboring cells lists for each surface...", 6);
for (int i = 0; i < cells.size(); i++) {
for (auto token : cells[i]->region_) {
// Skip operator tokens.
if (std::abs(token) >= OP_UNION) continue;
// This token is a surface index. Add the cell to the surface's list.
if (token > 0) {
surfaces[std::abs(token)-1]->neighbor_pos_.push_back(i);
} else {
surfaces[std::abs(token)-1]->neighbor_neg_.push_back(i);
}
}
}
for (Lattice* lat : lattices_c) {
for (Surface* surf : surfaces) {
surf->neighbor_pos_.shrink_to_fit();
surf->neighbor_neg_.shrink_to_fit();
}
}
//==============================================================================
void
prepare_distribcell(int32_t* filter_cell_list, int n)
{
// Read the list of cells contained in distribcell filters from Fortran.
std::unordered_set<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 +271,17 @@ allocate_offset_tables(int n_maps)
void
count_cell_instances(int32_t univ_indx)
{
for (int32_t cell_indx : global_universes[univ_indx]->cells) {
Cell& c = *global_cells[cell_indx];
++c.n_instances;
for (int32_t cell_indx : universes[univ_indx]->cells_) {
Cell& c = *cells[cell_indx];
++c.n_instances_;
if (c.type == FILL_UNIVERSE) {
if (c.type_ == FILL_UNIVERSE) {
// This cell contains another universe. Recurse into that universe.
count_cell_instances(c.fill);
count_cell_instances(c.fill_);
} else if (c.type == FILL_LATTICE) {
} else if (c.type_ == FILL_LATTICE) {
// This cell contains a lattice. Recurse into the lattice universes.
Lattice& lat = *lattices_c[c.fill];
Lattice& lat = *lattices[c.fill_];
for (auto it = lat.begin(); it != lat.end(); ++it) {
count_cell_instances(*it);
}
@ -163,20 +295,20 @@ int
count_universe_instances(int32_t search_univ, int32_t target_univ_id)
{
// If this is the target, it can't contain itself.
if (global_universes[search_univ]->id == target_univ_id) {
if (universes[search_univ]->id_ == target_univ_id) {
return 1;
}
int count {0};
for (int32_t cell_indx : global_universes[search_univ]->cells) {
Cell& c = *global_cells[cell_indx];
for (int32_t cell_indx : universes[search_univ]->cells_) {
Cell& c = *cells[cell_indx];
if (c.type == FILL_UNIVERSE) {
int32_t next_univ = c.fill;
if (c.type_ == FILL_UNIVERSE) {
int32_t next_univ = c.fill_;
count += count_universe_instances(next_univ, target_univ_id);
} else if (c.type == FILL_LATTICE) {
Lattice& lat = *lattices_c[c.fill];
} else if (c.type_ == FILL_LATTICE) {
Lattice& lat = *lattices[c.fill_];
for (auto it = lat.begin(); it != lat.end(); ++it) {
int32_t next_univ = *it;
count += count_universe_instances(next_univ, target_univ_id);
@ -189,43 +321,20 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id)
//==============================================================================
void
fill_offset_tables(int32_t target_univ_id, int map)
{
for (Universe* univ : global_universes) {
int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation.
for (int32_t cell_indx : univ->cells) {
Cell& c = *global_cells[cell_indx];
if (c.type == FILL_UNIVERSE) {
c.offset[map] = offset;
int32_t search_univ = c.fill;
offset += count_universe_instances(search_univ, target_univ_id);
} else if (c.type == FILL_LATTICE) {
Lattice& lat = *lattices_c[c.fill];
offset = lat.fill_offset_table(offset, target_univ_id, map);
}
}
}
}
//==============================================================================
std::string
distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
const Universe& search_univ, int32_t offset)
{
std::stringstream path;
path << "u" << search_univ.id << "->";
path << "u" << search_univ.id_ << "->";
// Check to see if this universe directly contains the target cell. If so,
// write to the path and return.
for (int32_t cell_indx : search_univ.cells) {
for (int32_t cell_indx : search_univ.cells_) {
if ((cell_indx == target_cell) && (offset == target_offset)) {
Cell& c = *global_cells[cell_indx];
path << "c" << c.id;
Cell& c = *cells[cell_indx];
path << "c" << c.id_;
return path.str();
}
}
@ -234,19 +343,19 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
// cell or lattice cell in this universe. Find which cell contains the
// target.
std::vector<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 +365,27 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
}
// Add the cell to the path string.
Cell& c = *global_cells[*cell_it];
path << "c" << c.id << "->";
Cell& c = *cells[*cell_it];
path << "c" << c.id_ << "->";
if (c.type == FILL_UNIVERSE) {
if (c.type_ == FILL_UNIVERSE) {
// Recurse into the fill cell.
offset += c.offset[map];
offset += c.offset_[map];
path << distribcell_path_inner(target_cell, map, target_offset,
*global_universes[c.fill], offset);
*universes[c.fill_], offset);
return path.str();
} else {
// Recurse into the lattice cell.
Lattice& lat = *lattices_c[c.fill];
path << "l" << lat.id;
Lattice& lat = *lattices[c.fill_];
path << "l" << lat.id_;
for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) {
int32_t indx = lat.universes.size()*map + it.indx;
int32_t temp_offset = offset + lat.offsets[indx];
int32_t indx = lat.universes_.size()*map + it.indx_;
int32_t temp_offset = offset + lat.offsets_[indx];
if (temp_offset <= target_offset) {
offset = temp_offset;
path << "(" << lat.index_to_string(it.indx) << ")->";
path << "(" << lat.index_to_string(it.indx_) << ")->";
path << distribcell_path_inner(target_cell, map, target_offset,
*global_universes[*it], offset);
*universes[*it], offset);
return path.str();
}
}
@ -289,7 +398,7 @@ int
distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
int32_t root_univ)
{
Universe& root = *global_universes[root_univ];
Universe& root = *universes[root_univ];
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
root, 0)};
return path_.size() + 1;
@ -301,7 +410,7 @@ void
distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset,
int32_t root_univ, char* path)
{
Universe& root = *global_universes[root_univ];
Universe& root = *universes[root_univ];
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
root, 0)};
path_.copy(path, path_.size());
@ -315,13 +424,13 @@ maximum_levels(int32_t univ)
{
int levels_below {0};
for (int32_t cell_indx : global_universes[univ]->cells) {
Cell& c = *global_cells[cell_indx];
if (c.type == FILL_UNIVERSE) {
int32_t next_univ = c.fill;
for (int32_t cell_indx : universes[univ]->cells_) {
Cell& c = *cells[cell_indx];
if (c.type_ == FILL_UNIVERSE) {
int32_t next_univ = c.fill_;
levels_below = std::max(levels_below, maximum_levels(next_univ));
} else if (c.type == FILL_LATTICE) {
Lattice& lat = *lattices_c[c.fill];
} else if (c.type_ == FILL_LATTICE) {
Lattice& lat = *lattices[c.fill_];
for (auto it = lat.begin(); it != lat.end(); ++it) {
int32_t next_univ = *it;
levels_below = std::max(levels_below, maximum_levels(next_univ));
@ -338,18 +447,20 @@ maximum_levels(int32_t univ)
void
free_memory_geometry_c()
{
for (Cell* c : global_cells) {delete c;}
global_cells.clear();
for (Cell* c : cells) {delete c;}
cells.clear();
cell_map.clear();
n_cells = 0;
for (Universe* u : global_universes) {delete u;}
global_universes.clear();
for (Universe* u : universes) {delete u;}
universes.clear();
universe_map.clear();
for (Lattice* lat : lattices_c) {delete lat;}
lattices_c.clear();
for (Lattice* lat : lattices) {delete lat;}
lattices.clear();
lattice_map.clear();
overlap_check_count.clear();
}
} // namespace openmc

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

@ -5,13 +5,15 @@
#include <sstream>
#include <string>
#include "xtensor/xtensor.hpp"
#include "xtensor/xarray.hpp"
#include "hdf5.h"
#include "hdf5_hl.h"
#ifdef OPENMC_MPI
#include "mpi.h"
#include "openmc/message_passing.h"
#endif
#include "openmc/error.h"
namespace openmc {
@ -532,172 +534,6 @@ read_complex(hid_t obj_id, const char* name, std::complex<double>* buffer, bool
}
void
read_nd_vector(hid_t obj_id, const char* name, std::vector<double>& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
read_double(obj_id, name, result.data(), true);
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<double> >& result, bool must_have)
{
if (object_exists(obj_id, name)) {
int dim1 = result.size();
int dim2 = result[0].size();
double temp_arr[dim1 * dim2];
read_double(obj_id, name, temp_arr, true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
result[i][j] = temp_arr[temp_idx++];
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<int> >& result, bool must_have)
{
if (object_exists(obj_id, name)) {
int dim1 = result.size();
int dim2 = result[0].size();
int temp_arr[dim1 * dim2];
read_int(obj_id, name, temp_arr, true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
result[i][j] = temp_arr[temp_idx++];
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<double> > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
int dim1 = result.size();
int dim2 = result[0].size();
int dim3 = result[0][0].size();
double temp_arr[dim1 * dim2 * dim3];
read_double(obj_id, name, temp_arr, true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
result[i][j][k] = temp_arr[temp_idx++];
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<int> > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
int dim1 = result.size();
int dim2 = result[0].size();
int dim3 = result[0][0].size();
int temp_arr[dim1 * dim2 * dim3];
read_int(obj_id, name, temp_arr, true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
result[i][j][k] = temp_arr[temp_idx++];
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<double> > > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
int dim1 = result.size();
int dim2 = result[0].size();
int dim3 = result[0][0].size();
int dim4 = result[0][0][0].size();
double temp_arr[dim1 * dim2 * dim3 * dim4];
read_double(obj_id, name, temp_arr, true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
for (int l = 0; l < dim4; l++) {
result[i][j][k][l] = temp_arr[temp_idx++];
}
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
int dim1 = result.size();
int dim2 = result[0].size();
int dim3 = result[0][0].size();
int dim4 = result[0][0][0].size();
int dim5 = result[0][0][0][0].size();
double temp_arr[dim1 * dim2 * dim3 * dim4 * dim5];
read_double(obj_id, name, temp_arr, true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
for (int l = 0; l < dim4; l++) {
for (int m = 0; m < dim5; m++) {
result[i][j][k][l][m] = temp_arr[temp_idx++];
}
}
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, double* results)
{
@ -910,6 +746,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

@ -17,10 +17,28 @@ module initialize
implicit none
type(C_PTR), bind(C) :: openmc_path_input
type(C_PTR), bind(C) :: openmc_path_statepoint
type(C_PTR), bind(C) :: openmc_path_sourcepoint
type(C_PTR), bind(C) :: openmc_path_particle_restart
interface
function openmc_path_input() result(ptr) bind(C)
import C_PTR
type(C_PTR) :: ptr
end function
function openmc_path_output() result(ptr) bind(C)
import C_PTR
type(C_PTR) :: ptr
end function
function openmc_path_particle_restart() result(ptr) bind(C)
import C_PTR
type(C_PTR) :: ptr
end function
function openmc_path_statepoint() result(ptr) bind(C)
import C_PTR
type(C_PTR) :: ptr
end function
function openmc_path_sourcepoint() result(ptr) bind(C)
import C_PTR
type(C_PTR) :: ptr
end function
end interface
contains
@ -164,29 +182,24 @@ contains
end function is_null
end interface
if (.not. is_null(openmc_path_input)) then
call c_f_pointer(openmc_path_input, string, [255])
if (.not. is_null(openmc_path_input())) then
call c_f_pointer(openmc_path_input(), string, [255])
path_input = to_f_string(string)
else
path_input = ''
end if
if (.not. is_null(openmc_path_statepoint)) then
call c_f_pointer(openmc_path_statepoint, string, [255])
if (.not. is_null(openmc_path_statepoint())) then
call c_f_pointer(openmc_path_statepoint(), string, [255])
path_state_point = to_f_string(string)
end if
if (.not. is_null(openmc_path_sourcepoint)) then
call c_f_pointer(openmc_path_sourcepoint, string, [255])
if (.not. is_null(openmc_path_sourcepoint())) then
call c_f_pointer(openmc_path_sourcepoint(), string, [255])
path_source_point = to_f_string(string)
end if
if (.not. is_null(openmc_path_particle_restart)) then
call c_f_pointer(openmc_path_particle_restart, string, [255])
if (.not. is_null(openmc_path_particle_restart())) then
call c_f_pointer(openmc_path_particle_restart(), string, [255])
path_particle_restart = to_f_string(string)
end if
! Add slash at end of directory if it isn't there
if (len_trim(path_input) > 0 .and. .not. ends_with(path_input, "/")) then
path_input = trim(path_input) // "/"
end if
end subroutine read_command_line
end module initialize

View file

@ -2,7 +2,6 @@
#include <cstddef>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
@ -11,10 +10,12 @@
#endif
#include "openmc/capi.h"
#include "openmc/constants.h"
#include "openmc/error.h"
#include "openmc/hdf5_interface.h"
#include "openmc/message_passing.h"
#include "openmc/settings.h"
#include "openmc/string_utils.h"
// data/functions from Fortran side
extern "C" void print_usage();
@ -60,7 +61,7 @@ namespace openmc {
#ifdef OPENMC_MPI
void initialize_mpi(MPI_Comm intracomm)
{
openmc::mpi::intracomm = intracomm;
mpi::intracomm = intracomm;
// Initialize MPI
int flag;
@ -68,13 +69,13 @@ void initialize_mpi(MPI_Comm intracomm)
if (!flag) MPI_Init(nullptr, nullptr);
// Determine number of processes and rank for each
MPI_Comm_size(intracomm, &openmc::mpi::n_procs);
MPI_Comm_rank(intracomm, &openmc::mpi::rank);
MPI_Comm_size(intracomm, &mpi::n_procs);
MPI_Comm_rank(intracomm, &mpi::rank);
// Set variable for Fortran side
openmc_n_procs = openmc::mpi::n_procs;
openmc_rank = openmc::mpi::rank;
openmc_master = (openmc::mpi::rank == 0);
openmc_n_procs = mpi::n_procs;
openmc_rank = mpi::rank;
openmc_master = mpi::master = (mpi::rank == 0);
// Create bank datatype
Bank b;
@ -87,19 +88,12 @@ void initialize_mpi(MPI_Comm intracomm)
};
int blocks[] {1, 3, 3, 1, 1};
MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_INT};
MPI_Type_create_struct(5, blocks, disp, types, &openmc::mpi::bank);
MPI_Type_commit(&openmc::mpi::bank);
MPI_Type_create_struct(5, blocks, disp, types, &mpi::bank);
MPI_Type_commit(&mpi::bank);
}
#endif // OPENMC_MPI
inline bool ends_with(std::string const& value, std::string const& ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
int
parse_command_line(int argc, char* argv[])
{
@ -109,12 +103,12 @@ parse_command_line(int argc, char* argv[])
std::string arg {argv[i]};
if (arg[0] == '-') {
if (arg == "-p" || arg == "--plot") {
openmc_run_mode = RUN_MODE_PLOTTING;
openmc_check_overlaps = true;
settings::run_mode = RUN_MODE_PLOTTING;
settings::check_overlaps = true;
} else if (arg == "-n" || arg == "--particles") {
i += 1;
n_particles = std::stoll(argv[i]);
settings::n_particles = std::stoll(argv[i]);
} else if (arg == "-r" || arg == "--restart") {
i += 1;
@ -128,11 +122,11 @@ parse_command_line(int argc, char* argv[])
// Set path and flag for type of run
if (filetype == "statepoint") {
openmc_path_statepoint = argv[i];
openmc_restart_run = true;
settings::path_statepoint = argv[i];
settings::restart_run = true;
} else if (filetype == "particle restart") {
openmc_path_particle_restart = argv[i];
openmc_particle_restart_run = true;
settings::path_particle_restart = argv[i];
settings::particle_restart_run = true;
} else {
std::stringstream msg;
msg << "Unrecognized file after restart flag: " << filetype << ".";
@ -141,7 +135,7 @@ parse_command_line(int argc, char* argv[])
}
// If its a restart run check for additional source file
if (openmc_restart_run && i + 1 < argc) {
if (settings::restart_run && i + 1 < argc) {
// Check if it has extension we can read
if (ends_with(argv[i+1], ".h5")) {
@ -157,23 +151,23 @@ parse_command_line(int argc, char* argv[])
}
// It is a source file
openmc_path_sourcepoint = argv[i+1];
settings::path_sourcepoint = argv[i+1];
i += 1;
} else {
// Source is in statepoint file
openmc_path_sourcepoint = openmc_path_statepoint;
settings::path_sourcepoint = settings::path_statepoint;
}
} else {
// Source is assumed to be in statepoint file
openmc_path_sourcepoint = openmc_path_statepoint;
settings::path_sourcepoint = settings::path_statepoint;
}
} else if (arg == "-g" || arg == "--geometry-debug") {
openmc_check_overlaps = true;
settings::check_overlaps = true;
} else if (arg == "-c" || arg == "--volume") {
openmc_run_mode = RUN_MODE_VOLUME;
settings::run_mode = RUN_MODE_VOLUME;
} else if (arg == "-s" || arg == "--threads") {
// Read number of threads
i += 1;
@ -201,7 +195,7 @@ parse_command_line(int argc, char* argv[])
return OPENMC_E_UNASSIGNED;
} else if (arg == "-t" || arg == "--track") {
openmc_write_all_tracks = true;
settings::write_all_tracks = true;
} else {
std::cerr << "Unknown option: " << argv[i] << '\n';
@ -214,7 +208,14 @@ parse_command_line(int argc, char* argv[])
}
// Determine directory where XML input files are
if (argc > 1 && last_flag < argc) openmc_path_input = argv[last_flag + 1];
if (argc > 1 && last_flag < argc - 1) {
settings::path_input = std::string(argv[last_flag + 1]);
// Add slash at end of directory if it isn't there
if (!ends_with(settings::path_input, "/")) {
settings::path_input += "/";
}
}
return 0;
}

File diff suppressed because it is too large Load diff

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

@ -2,7 +2,9 @@
#include "mpi.h"
#endif
#include "openmc/capi.h"
#include "openmc/constants.h"
#include "openmc/error.h"
#include "openmc/settings.h"
int main(int argc, char* argv[]) {
@ -23,18 +25,18 @@ int main(int argc, char* argv[]) {
}
// start problem based on mode
switch (openmc_run_mode) {
case RUN_MODE_FIXEDSOURCE:
case RUN_MODE_EIGENVALUE:
switch (openmc::settings::run_mode) {
case openmc::RUN_MODE_FIXEDSOURCE:
case openmc::RUN_MODE_EIGENVALUE:
err = openmc_run();
break;
case RUN_MODE_PLOTTING:
case openmc::RUN_MODE_PLOTTING:
err = openmc_plot_geometry();
break;
case RUN_MODE_PARTICLE:
case openmc::RUN_MODE_PARTICLE:
if (openmc_master) err = openmc_particle_restart();
break;
case RUN_MODE_VOLUME:
case openmc::RUN_MODE_VOLUME:
err = openmc_calculate_volumes();
break;
}

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

@ -1,106 +0,0 @@
module mesh
use algorithm, only: binary_search
use bank_header, only: bank
use constants
use mesh_header
use message_passing
implicit none
contains
!===============================================================================
! COUNT_BANK_SITES determines the number of fission bank sites in each cell of a
! given mesh as well as an optional energy group structure. This can be used for
! a variety of purposes (Shannon entropy, CMFD, uniform fission source
! weighting)
!===============================================================================
subroutine count_bank_sites(m, bank_array, cnt, energies, size_bank, &
sites_outside)
type(RegularMesh), intent(in) :: m ! mesh to count sites
type(Bank), intent(in) :: bank_array(:) ! fission or source bank
real(8), intent(out) :: cnt(:,:) ! weight of sites in each
! cell and energy group
real(8), intent(in), optional :: energies(:) ! energy grid to search
integer(8), intent(in), optional :: size_bank ! # of bank sites (on each proc)
logical, intent(inout), optional :: sites_outside ! were there sites outside mesh?
real(8), allocatable :: cnt_(:,:)
integer :: i ! loop index for local fission sites
integer :: n_sites ! size of bank array
integer :: n ! number of energy groups / size
integer :: mesh_bin ! mesh bin
integer :: e_bin ! energy bin
#ifdef OPENMC_MPI
integer :: mpi_err ! MPI error code
#endif
logical :: outside ! was any site outside mesh?
! initialize variables
allocate(cnt_(size(cnt,1), size(cnt,2)))
cnt_ = ZERO
outside = .false.
! Set size of bank
if (present(size_bank)) then
n_sites = int(size_bank,4)
else
n_sites = size(bank_array)
end if
! Determine number of energies in group structure
if (present(energies)) then
n = size(energies) - 1
else
n = 1
end if
! loop over fission sites and count how many are in each mesh box
FISSION_SITES: do i = 1, n_sites
! determine scoring bin for entropy mesh
call m % get_bin(bank_array(i) % xyz, mesh_bin)
! if outside mesh, skip particle
if (mesh_bin == NO_BIN_FOUND) then
outside = .true.
cycle
end if
! determine energy bin
if (present(energies)) then
if (bank_array(i) % E < energies(1)) then
e_bin = 1
elseif (bank_array(i) % E > energies(n + 1)) then
e_bin = n
else
e_bin = binary_search(energies, n + 1, bank_array(i) % E)
end if
else
e_bin = 1
end if
! add to appropriate mesh box
cnt_(e_bin, mesh_bin) = cnt_(e_bin, mesh_bin) + bank_array(i) % wgt
end do FISSION_SITES
#ifdef OPENMC_MPI
! collect values from all processors
n = size(cnt_)
call MPI_REDUCE(cnt_, cnt, n, MPI_REAL8, MPI_SUM, 0, mpi_intracomm, mpi_err)
! Check if there were sites outside the mesh for any processor
if (present(sites_outside)) then
call MPI_REDUCE(outside, sites_outside, 1, MPI_LOGICAL, MPI_LOR, 0, &
mpi_intracomm, mpi_err)
end if
#else
sites_outside = outside
cnt = cnt_
#endif
end subroutine count_bank_sites
end module mesh

963
src/mesh.cpp Normal file
View file

@ -0,0 +1,963 @@
#include "openmc/mesh.h"
#include <algorithm> // for copy, min
#include <cstddef> // for size_t
#include <cmath> // for ceil
#include <string>
#ifdef OPENMC_MPI
#include "mpi.h"
#endif
#include "xtensor/xbuilder.hpp"
#include "xtensor/xeval.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xsort.hpp"
#include "xtensor/xtensor.hpp"
#include "openmc/capi.h"
#include "openmc/constants.h"
#include "openmc/error.h"
#include "openmc/hdf5_interface.h"
#include "openmc/message_passing.h"
#include "openmc/search.h"
#include "openmc/tallies/tally_filter.h"
#include "openmc/xml_interface.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
std::vector<std::unique_ptr<RegularMesh>> meshes;
std::unordered_map<int32_t, int32_t> mesh_map;
//==============================================================================
// RegularMesh implementation
//==============================================================================
RegularMesh::RegularMesh(pugi::xml_node node)
{
// Copy mesh id
if (check_for_node(node, "id")) {
id_ = std::stoi(get_node_value(node, "id"));
// Check to make sure 'id' hasn't been used
if (mesh_map.find(id_) != mesh_map.end()) {
fatal_error("Two or more meshes use the same unique ID: " +
std::to_string(id_));
}
}
// Read mesh type
if (check_for_node(node, "type")) {
auto temp = get_node_value(node, "type", true, true);
if (temp == "regular") {
// TODO: move elsewhere
} else {
fatal_error("Invalid mesh type: " + temp);
}
}
// Determine number of dimensions for mesh
if (check_for_node(node, "dimension")) {
shape_ = get_node_xarray<int>(node, "dimension");
int n = n_dimension_ = shape_.size();
if (n != 1 && n != 2 && n != 3) {
fatal_error("Mesh must be one, two, or three dimensions.");
}
// Check that dimensions are all greater than zero
if (xt::any(shape_ <= 0)) {
fatal_error("All entries on the <dimension> element for a tally "
"mesh must be positive.");
}
}
// Check for lower-left coordinates
if (check_for_node(node, "lower_left")) {
// Read mesh lower-left corner location
lower_left_ = get_node_xarray<double>(node, "lower_left");
} else {
fatal_error("Must specify <lower_left> on a mesh.");
}
if (check_for_node(node, "width")) {
// Make sure both upper-right or width were specified
if (check_for_node(node, "upper_right")) {
fatal_error("Cannot specify both <upper_right> and <width> on a mesh.");
}
width_ = get_node_xarray<double>(node, "width");
// Check to ensure width has same dimensions
auto n = width_.size();
if (n != lower_left_.size()) {
fatal_error("Number of entries on <width> must be the same as "
"the number of entries on <lower_left>.");
}
// Check for negative widths
if (xt::any(width_ < 0.0)) {
fatal_error("Cannot have a negative <width> on a tally mesh.");
}
// Set width and upper right coordinate
upper_right_ = xt::eval(lower_left_ + shape_ * width_);
} else if (check_for_node(node, "upper_right")) {
upper_right_ = get_node_xarray<double>(node, "upper_right");
// Check to ensure width has same dimensions
auto n = upper_right_.size();
if (n != lower_left_.size()) {
fatal_error("Number of entries on <upper_right> must be the "
"same as the number of entries on <lower_left>.");
}
// Check that upper-right is above lower-left
if (xt::any(upper_right_ < lower_left_)) {
fatal_error("The <upper_right> coordinates must be greater than "
"the <lower_left> coordinates on a tally mesh.");
}
// Set width and upper right coordinate
width_ = xt::eval((upper_right_ - lower_left_) / shape_);
} else {
fatal_error("Must specify either <upper_right> and <width> on a mesh.");
}
if (shape_.dimension() > 0) {
if (shape_.size() != lower_left_.size()) {
fatal_error("Number of entries on <lower_left> must be the same "
"as the number of entries on <dimension>.");
}
// Set volume fraction
volume_frac_ = 1.0/xt::prod(shape_)();
}
}
int RegularMesh::get_bin(Position r) const
{
// Loop over the dimensions of the mesh
for (int i = 0; i < n_dimension_; ++i) {
// Check for cases where particle is outside of mesh
if (r[i] < lower_left_[i]) {
return -1;
} else if (r[i] > upper_right_[i]) {
return -1;
}
}
// Determine indices
int ijk[n_dimension_];
bool in_mesh;
get_indices(r, ijk, &in_mesh);
if (!in_mesh) return -1;
// Convert indices to bin
return get_bin_from_indices(ijk);
}
int RegularMesh::get_bin_from_indices(const int* ijk) const
{
if (n_dimension_ == 1) {
return ijk[0];
} else if (n_dimension_ == 2) {
return (ijk[1] - 1)*shape_[0] + ijk[0];
} else if (n_dimension_ == 3) {
return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0];
}
}
void RegularMesh::get_indices(Position r, int* ijk, bool* in_mesh) const
{
// Find particle in mesh
*in_mesh = true;
for (int i = 0; i < n_dimension_; ++i) {
ijk[i] = std::ceil((r[i] - lower_left_[i]) / width_[i]);
// Check if indices are within bounds
if (ijk[i] < 1 || ijk[i] > shape_[i]) *in_mesh = false;
}
}
void RegularMesh::get_indices_from_bin(int bin, int* ijk) const
{
if (n_dimension_ == 1) {
ijk[0] = bin;
} else if (n_dimension_ == 2) {
ijk[0] = (bin - 1) % shape_[0] + 1;
ijk[1] = (bin - 1) / shape_[0] + 1;
} else if (n_dimension_ == 3) {
ijk[0] = (bin - 1) % shape_[0] + 1;
ijk[1] = ((bin - 1) % (shape_[0] * shape_[1])) / shape_[0] + 1;
ijk[2] = (bin - 1) / (shape_[0] * shape_[1]) + 1;
}
}
bool RegularMesh::intersects(Position r0, Position r1) const
{
switch(n_dimension_) {
case 1:
return intersects_1d(r0, r1);
case 2:
return intersects_2d(r0, r1);
case 3:
return intersects_3d(r0, r1);
}
}
bool RegularMesh::intersects_1d(Position r0, Position r1) const
{
// Copy coordinates of mesh lower_left and upper_right
double left = lower_left_[0];
double right = upper_right_[0];
// Check if line intersects either left or right surface
if (r0.x < left) {
return r1.x > left;
} else if (r0.x < right) {
return r1.x < left || r1.x > right;
} else {
return r1.x < right;
}
}
bool RegularMesh::intersects_2d(Position r0, Position r1) const
{
// Copy coordinates of starting point
double x0 = r0.x;
double y0 = r0.y;
// Copy coordinates of ending point
double x1 = r1.x;
double y1 = r1.y;
// Copy coordinates of mesh lower_left
double xm0 = lower_left_[0];
double ym0 = lower_left_[1];
// Copy coordinates of mesh upper_right
double xm1 = upper_right_[0];
double ym1 = upper_right_[1];
// Check if line intersects left surface -- calculate the intersection point y
if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) {
double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0);
if (yi >= ym0 && yi < ym1) {
return true;
}
}
// Check if line intersects back surface -- calculate the intersection point
// x
if ((y0 < ym0 && y1 > ym0) || (y0 > ym0 && y1 < ym0)) {
double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0);
if (xi >= xm0 && xi < xm1) {
return true;
}
}
// Check if line intersects right surface -- calculate the intersection
// point y
if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) {
double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0);
if (yi >= ym0 && yi < ym1) {
return true;
}
}
// Check if line intersects front surface -- calculate the intersection point
// x
if ((y0 < ym1 && y1 > ym1) || (y0 > ym1 && y1 < ym1)) {
double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0);
if (xi >= xm0 && xi < xm1) {
return true;
}
}
return false;
}
bool RegularMesh::intersects_3d(Position r0, Position r1) const
{
// Copy coordinates of starting point
double x0 = r0.x;
double y0 = r0.y;
double z0 = r0.z;
// Copy coordinates of ending point
double x1 = r1.x;
double y1 = r1.y;
double z1 = r1.z;
// Copy coordinates of mesh lower_left
double xm0 = lower_left_[0];
double ym0 = lower_left_[1];
double zm0 = lower_left_[2];
// Copy coordinates of mesh upper_right
double xm1 = upper_right_[0];
double ym1 = upper_right_[1];
double zm1 = upper_right_[2];
// Check if line intersects left surface -- calculate the intersection point
// (y,z)
if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) {
double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0);
double zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0);
if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) {
return true;
}
}
// Check if line intersects back surface -- calculate the intersection point
// (x,z)
if ((y0 < ym0 && y1 > ym0) || (y0 > ym0 && y1 < ym0)) {
double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0);
double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) {
return true;
}
}
// Check if line intersects bottom surface -- calculate the intersection
// point (x,y)
if ((z0 < zm0 && z1 > zm0) || (z0 > zm0 && z1 < zm0)) {
double xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0);
double yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0);
if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) {
return true;
}
}
// Check if line intersects right surface -- calculate the intersection point
// (y,z)
if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) {
double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0);
double zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0);
if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) {
return true;
}
}
// Check if line intersects front surface -- calculate the intersection point
// (x,z)
if ((y0 < ym1 && y1 > ym1) || (y0 > ym1 && y1 < ym1)) {
double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0);
double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) {
return true;
}
}
// Check if line intersects top surface -- calculate the intersection point
// (x,y)
if ((z0 < zm1 && z1 > zm1) || (z0 > zm1 && z1 < zm1)) {
double xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0);
double yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0);
if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) {
return true;
}
}
return false;
}
void RegularMesh::bins_crossed(const Particle* p, std::vector<int>& bins,
std::vector<double>& lengths) const
{
constexpr int MAX_SEARCH_ITER = 100;
// ========================================================================
// Determine if the track intersects the tally mesh.
// Copy the starting and ending coordinates of the particle. Offset these
// just a bit for the purposes of determining if there was an intersection
// in case the mesh surfaces coincide with lattice/geometric surfaces which
// might produce finite-precision errors.
Position last_r {p->last_xyz};
Position r {p->coord[0].xyz};
Direction u {p->coord[0].uvw};
Position r0 = last_r + TINY_BIT*u;
Position r1 = r - TINY_BIT*u;
// Determine indices for starting and ending location.
int n = n_dimension_;
xt::xtensor<int, 1> ijk0 = xt::empty<int>({n});
bool start_in_mesh;
get_indices(r0, ijk0.data(), &start_in_mesh);
xt::xtensor<int, 1> ijk1 = xt::empty<int>({n});
bool end_in_mesh;
get_indices(r1, ijk1.data(), &end_in_mesh);
// Check if the track intersects any part of the mesh.
if (!start_in_mesh && !end_in_mesh) {
if (!intersects(r0, r1)) return;
}
// ========================================================================
// Figure out which mesh cell to tally.
// Copy the un-modified coordinates the particle direction.
r0 = last_r;
r1 = r;
// Compute the length of the entire track.
double total_distance = (r1 - r0).norm();
// We are looking for the first valid mesh bin. Check to see if the
// particle starts inside the mesh.
if (!start_in_mesh) {
xt::xtensor<double, 1> d = xt::zeros<double>({n});
// The particle does not start in the mesh. Note that we nudged the
// start and end coordinates by a TINY_BIT each so we will have
// difficulty resolving tracks that are less than 2*TINY_BIT in length.
// If the track is that short, it is also insignificant so we can
// safely ignore it in the tallies.
if (total_distance < 2*TINY_BIT) return;
// The particle does not start in the mesh so keep iterating the ijk0
// indices to cross the nearest mesh surface until we've found a valid
// bin. MAX_SEARCH_ITER prevents an infinite loop.
int search_iter = 0;
int j;
while (xt::any(ijk0 < 1) || xt::any(ijk0 > shape_)) {
if (search_iter == MAX_SEARCH_ITER) {
warning("Failed to find a mesh intersection on a tally mesh filter.");
return;
}
for (j = 0; j < n; ++j) {
if (std::fabs(u[j]) < FP_PRECISION) {
d(j) = INFTY;
} else if (u[j] > 0.0) {
double xyz_cross = lower_left_[j] + ijk0(j) * width_[j];
d(j) = (xyz_cross - r0[j]) / u[j];
} else {
double xyz_cross = lower_left_[j] + (ijk0(j) - 1) * width_[j];
d(j) = (xyz_cross - r0[j]) / u[j];
}
}
j = xt::argmin(d)(0);
if (u[j] > 0.0) {
++ijk0(j);
} else {
--ijk0(j);
}
++search_iter;
}
// Advance position
r0 += d(j) * u;
}
while (true) {
// ========================================================================
// Compute the length of the track segment in the each mesh cell and return
double distance;
int j;
if (ijk0 == ijk1) {
// The track ends in this cell. Use the particle end location rather
// than the mesh surface.
distance = (r1 - r0).norm();
} else {
// The track exits this cell. Determine the distance to the closest mesh
// surface.
xt::xtensor<double, 1> d = xt::zeros<double>({n});
for (int j = 0; j < n; ++j) {
if (std::fabs(u[j]) < FP_PRECISION) {
d(j) = INFTY;
} else if (u[j] > 0) {
double xyz_cross = lower_left_[j] + ijk0(j) * width_[j];
d(j) = (xyz_cross - r0[j]) / u[j];
} else {
double xyz_cross = lower_left_[j] + (ijk0(j) - 1) * width_[j];
d(j) = (xyz_cross - r0[j]) / u[j];
}
}
j = xt::argmin(d)(0);
distance = d(j);
}
// Assign the next tally bin and the score.
int bin = get_bin_from_indices(ijk0.data());
bins.push_back(bin);
lengths.push_back(distance / total_distance);
// If the particle track ends in that bin, then we are done.
if (ijk0 == ijk1) break;
// Translate the starting coordintes by the distance to that face. This
// should be the xyz that we computed the distance to in the last
// iteration of the filter loop.
r0 += distance * u;
// Increment the indices into the next mesh cell.
if (u[j] > 0.0) {
++ijk0(j);
} else {
--ijk0(j);
}
// If the next indices are invalid, then the track has left the mesh and
// we are done.
if (xt::any(ijk0 < 1) || xt::any(ijk0 > shape_)) break;
}
}
void RegularMesh::surface_bins_crossed(const Particle* p, std::vector<int>& bins) const
{
// ========================================================================
// Determine if the track intersects the tally mesh.
// Copy the starting and ending coordinates of the particle.
Position r0 {p->last_xyz_current};
Position r1 {p->coord[0].xyz};
Direction u {p->coord[0].uvw};
// Determine indices for starting and ending location.
int n = n_dimension_;
xt::xtensor<int, 1> ijk0 = xt::empty<int>({n});
bool start_in_mesh;
get_indices(r0, ijk0.data(), &start_in_mesh);
xt::xtensor<int, 1> ijk1 = xt::empty<int>({n});
bool end_in_mesh;
get_indices(r1, ijk1.data(), &end_in_mesh);
// Check if the track intersects any part of the mesh.
if (!start_in_mesh && !end_in_mesh) {
if (!intersects(r0, r1)) return;
}
// ========================================================================
// Figure out which mesh cell to tally.
// Calculate number of surface crossings
int n_cross = xt::sum(xt::abs(ijk1 - ijk0))();
if (n_cross == 0) return;
// Bounding coordinates
Position xyz_cross;
for (int i = 0; i < n; ++i) {
if (u[i] > 0.0) {
xyz_cross[i] = lower_left_[i] + ijk0[i] * width_[i];
} else {
xyz_cross[i] = lower_left_[i] + (ijk0[i] - 1) * width_[i];
}
}
for (int j = 0; j < n_cross; ++j) {
// Set the distances to infinity
Position d {INFTY, INFTY, INFTY};
// Determine closest bounding surface. We need to treat
// special case where the cosine of the angle is zero since this would
// result in a divide-by-zero.
double distance = INFTY;
for (int i = 0; i < n; ++i) {
if (u[i] == 0) {
d[i] = INFINITY;
} else {
d[i] = (xyz_cross[i] - r0[i])/u[i];
}
distance = std::min(distance, d[i]);
}
// Loop over the dimensions
for (int i = 0; i < n; ++i) {
// Check whether distance is the shortest distance
if (distance == d[i]) {
// Check whether particle is moving in positive i direction
if (u[i] > 0) {
// Outward current on i max surface
if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_)) {
int i_surf = 4*i + 3;
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*(i_mesh - 1) + i_surf;
bins.push_back(i_bin);
}
// Advance position
++ijk0[i];
xyz_cross[i] += width_[i];
// If the particle crossed the surface, tally the inward current on
// i min surface
if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_)) {
int i_surf = 4*i + 2;
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*(i_mesh - 1) + i_surf;
bins.push_back(i_bin);
}
} else {
// The particle is moving in the negative i direction
// Outward current on i min surface
if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_) ){
int i_surf = 4*i + 1;
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*(i_mesh - 1) + i_surf;
bins.push_back(i_bin);
}
// Advance position
--ijk0[i];
xyz_cross[i] -= width_[i];
// If the particle crossed the surface, tally the inward current on
// i max surface
if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_)) {
int i_surf = 4*i + 4;
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*(i_mesh - 1) + i_surf;
bins.push_back(i_bin);
}
}
}
}
// Calculate new coordinates
r0 += distance * u;
}
}
void RegularMesh::to_hdf5(hid_t group) const
{
hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_));
write_dataset(mesh_group, "type", "regular");
write_dataset(mesh_group, "dimension", shape_);
write_dataset(mesh_group, "lower_left", lower_left_);
write_dataset(mesh_group, "upper_right", upper_right_);
write_dataset(mesh_group, "width", width_);
close_group(mesh_group);
}
xt::xarray<double> RegularMesh::count_sites(int64_t n, const Bank* bank,
int n_energy, const double* energies, bool* outside) const
{
// Determine shape of array for counts
std::size_t m = xt::prod(shape_)();
std::vector<std::size_t> shape;
if (n_energy > 0) {
shape = {m, static_cast<std::size_t>(n_energy - 1)};
} else {
shape = {m};
}
// Create array of zeros
xt::xarray<double> cnt {shape, 0.0};
bool outside_ = false;
for (int64_t i = 0; i < n; ++i) {
// determine scoring bin for entropy mesh
// TODO: off-by-one
int mesh_bin = get_bin({bank[i].xyz}) - 1;
// if outside mesh, skip particle
if (mesh_bin < 0) {
outside_ = true;
continue;
}
if (n_energy > 0) {
double E = bank[i].E;
if (E >= energies[0] && E <= energies[n_energy - 1]) {
// determine energy bin
int e_bin = lower_bound_index(energies, energies + n_energy, E);
// Add to appropriate bin
cnt(mesh_bin, e_bin) += bank[i].wgt;
}
} else {
// Add to appropriate bin
cnt(mesh_bin) += bank[i].wgt;
}
}
// Create copy of count data
int total = cnt.size();
double* cnt_reduced = new double[total];
#ifdef OPENMC_MPI
// collect values from all processors
MPI_Reduce(cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0,
mpi::intracomm);
// Check if there were sites outside the mesh for any processor
if (outside) {
MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
}
#else
std::copy(cnt.data(), cnt.data() + total, cnt_reduced);
if (outside) *outside = outside_;
#endif
// Adapt reduced values in array back into an xarray
auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape);
xt::xarray<double> counts = arr;
return counts;
}
//==============================================================================
// C API functions
//==============================================================================
//! Extend the meshes array by n elements
extern "C" int
openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end)
{
if (index_start) *index_start = meshes.size();
for (int i = 0; i < n; ++i) {
meshes.emplace_back(new RegularMesh{});
}
if (index_end) *index_end = meshes.size() - 1;
return 0;
}
//! Return the index in the meshes array of a mesh with a given ID
extern "C" int
openmc_get_mesh_index(int32_t id, int32_t* index)
{
auto pair = mesh_map.find(id);
if (pair == mesh_map.end()) {
set_errmsg("No mesh exists with ID=" + std::to_string(id) + ".");
return OPENMC_E_INVALID_ID;
}
*index = pair->second;
return 0;
}
// Return the ID of a mesh
extern "C" int
openmc_mesh_get_id(int32_t index, int32_t* id)
{
if (index < 0 || index >= meshes.size()) {
set_errmsg("Index in meshes array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
*id = meshes[index]->id_;
return 0;
}
//! Set the ID of a mesh
extern "C" int
openmc_mesh_set_id(int32_t index, int32_t id)
{
if (index < 0 || index >= meshes.size()) {
set_errmsg("Index in meshes array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
meshes[index]->id_ = id;
mesh_map[id] = index;
return 0;
}
//! Get the dimension of a mesh
extern "C" int
openmc_mesh_get_dimension(int32_t index, int** dims, int* n)
{
if (index < 0 || index >= meshes.size()) {
set_errmsg("Index in meshes array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
*dims = meshes[index]->shape_.data();
*n = meshes[index]->n_dimension_;
return 0;
}
//! Set the dimension of a mesh
extern "C" int
openmc_mesh_set_dimension(int32_t index, int n, const int* dims)
{
if (index < 0 || index >= meshes.size()) {
set_errmsg("Index in meshes array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
// Copy dimension
std::vector<std::size_t> shape = {static_cast<std::size_t>(n)};
auto& m = meshes[index];
m->shape_ = xt::adapt(dims, n, xt::no_ownership(), shape);
m->n_dimension_ = m->shape_.size();
return 0;
}
//! Get the mesh parameters
extern "C" int
openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n)
{
if (index < 0 || index >= meshes.size()) {
set_errmsg("Index in meshes array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
auto& m = meshes[index];
if (m->lower_left_.dimension() == 0) {
set_errmsg("Mesh parameters have not been set.");
return OPENMC_E_ALLOCATE;
}
*ll = m->lower_left_.data();
*ur = m->upper_right_.data();
*width = m->width_.data();
*n = m->n_dimension_;
return 0;
}
//! Set the mesh parameters
extern "C" int
openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur,
const double* width)
{
if (index < 0 || index >= meshes.size()) {
set_errmsg("Index in meshes array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
auto& m = meshes[index];
std::vector<std::size_t> shape = {static_cast<std::size_t>(n)};
if (ll && ur) {
m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape);
m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape);
m->width_ = (m->upper_right_ - m->lower_left_) / m->shape_;
} else if (ll && width) {
m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape);
m->width_ = xt::adapt(width, n, xt::no_ownership(), shape);
m->upper_right_ = m->lower_left_ + m->shape_ * m->width_;
} else if (ur && width) {
m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape);
m->width_ = xt::adapt(width, n, xt::no_ownership(), shape);
m->lower_left_ = m->upper_right_ - m->shape_ * m->width_;
} else {
set_errmsg("At least two parameters must be specified.");
return OPENMC_E_INVALID_ARGUMENT;
}
return 0;
}
//==============================================================================
// Non-member functions
//==============================================================================
void read_meshes(pugi::xml_node* root)
{
for (auto node : root->children("mesh")) {
// Read mesh and add to vector
meshes.emplace_back(new RegularMesh{node});
// Map ID to position in vector
mesh_map[meshes.back()->id_] = meshes.size() - 1;
}
}
void meshes_to_hdf5(hid_t group)
{
// Write number of meshes
hid_t meshes_group = create_group(group, "meshes");
int32_t n_meshes = meshes.size();
write_attribute(meshes_group, "n_meshes", n_meshes);
if (n_meshes > 0) {
// Write IDs of meshes
std::vector<int> ids;
for (const auto& m : meshes) {
m->to_hdf5(meshes_group);
ids.push_back(m->id_);
}
write_attribute(meshes_group, "ids", ids);
}
close_group(meshes_group);
}
//==============================================================================
// Fortran compatibility
//==============================================================================
extern "C" {
int n_meshes() { return meshes.size(); }
RegularMesh* mesh_ptr(int i) { return meshes.at(i).get(); }
int32_t mesh_id(RegularMesh* m) { return m->id_; }
double mesh_volume_frac(RegularMesh* m) { return m->volume_frac_; }
int mesh_n_dimension(RegularMesh* m) { return m->n_dimension_; }
int mesh_dimension(RegularMesh* m, int i) { return m->shape_(i - 1); }
double mesh_lower_left(RegularMesh* m, int i) { return m->lower_left_(i - 1); }
double mesh_upper_right(RegularMesh* m, int i) { return m->upper_right_(i - 1); }
double mesh_width(RegularMesh* m, int i) { return m->width_(i - 1); }
int mesh_get_bin(RegularMesh* m, const double* xyz)
{
return m->get_bin({xyz});
}
int mesh_get_bin_from_indices(RegularMesh* m, const int* ijk)
{
return m->get_bin_from_indices(ijk);
}
void mesh_get_indices(RegularMesh* m, const double* xyz, int* ijk, bool* in_mesh)
{
m->get_indices({xyz}, ijk, in_mesh);
}
void mesh_get_indices_from_bin(RegularMesh* m, int bin, int* ijk)
{
m->get_indices_from_bin(bin, ijk);
}
void mesh_bins_crossed(RegularMesh* m, const Particle* p,
TallyFilterMatch* match)
{
match->bins.clear();
match->weights.clear();
m->bins_crossed(p, match->bins, match->weights);
}
void mesh_surface_bins_crossed(RegularMesh* m, const Particle* p,
TallyFilterMatch* match)
{
match->bins.clear();
match->weights.clear();
m->surface_bins_crossed(p, match->bins);
for (auto b : match->bins) {
match->weights.push_back(1.0);
}
}
void free_memory_mesh()
{
meshes.clear();
mesh_map.clear();
}
}
} // namespace openmc

View file

@ -2,185 +2,242 @@ module mesh_header
use, intrinsic :: ISO_C_BINDING
use constants
use dict_header, only: DictIntInt
use error
use hdf5_interface
use string, only: to_str, to_lower
use xml_interface
implicit none
private
public :: free_memory_mesh
public :: openmc_extend_meshes
public :: openmc_get_mesh_index
public :: openmc_mesh_get_id
public :: openmc_mesh_get_dimension
public :: openmc_mesh_get_params
public :: openmc_mesh_set_id
public :: openmc_mesh_set_dimension
public :: openmc_mesh_set_params
!===============================================================================
! STRUCTUREDMESH represents a tessellation of n-dimensional Euclidean space by
! congruent squares or cubes
!===============================================================================
type, public :: RegularMesh
integer :: id = -1 ! user-specified id
integer :: type = MESH_REGULAR ! rectangular, hexagonal
integer(C_INT) :: n_dimension ! rank of mesh
real(8) :: volume_frac ! volume fraction of each cell
integer(C_INT), allocatable :: dimension(:) ! number of cells in each direction
real(C_DOUBLE), allocatable :: lower_left(:) ! lower-left corner of mesh
real(C_DOUBLE), allocatable :: upper_right(:) ! upper-right corner of mesh
real(C_DOUBLE), allocatable :: width(:) ! width of each mesh cell
type :: RegularMesh
type(C_PTR) :: ptr
contains
procedure :: from_xml => regular_from_xml
procedure :: id => regular_id
procedure :: volume_frac => regular_volume_frac
procedure :: n_dimension => regular_n_dimension
procedure :: dimension => regular_dimension
procedure :: lower_left => regular_lower_left
procedure :: upper_right => regular_upper_right
procedure :: width => regular_width
procedure :: get_bin => regular_get_bin
procedure :: get_indices => regular_get_indices
procedure :: get_bin_from_indices => regular_get_bin_from_indices
procedure :: get_indices_from_bin => regular_get_indices_from_bin
procedure :: intersects => regular_intersects
procedure :: to_hdf5 => regular_to_hdf5
end type RegularMesh
integer(C_INT32_T), public, bind(C) :: n_meshes = 0 ! # of structured meshes
interface
function openmc_extend_meshes(n, index_start, index_end) result(err) bind(C)
import C_INT32_T, C_INT
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
end function openmc_extend_meshes
type(RegularMesh), public, allocatable, target :: meshes(:)
function openmc_get_mesh_index(id, index) result(err) bind(C)
import C_INT32_T, C_INT
integer(C_INT32_T), value :: id
integer(C_INT32_T), intent(out) :: index
integer(C_INT) :: err
end function openmc_get_mesh_index
! Dictionary that maps user IDs to indices in 'meshes'
type(DictIntInt), public :: mesh_dict
function openmc_mesh_get_id(index, id) result(err) bind(C)
import C_INT32_T, C_INT
integer(C_INT32_T), value :: index
integer(C_INT32_T), intent(out) :: id
integer(C_INT) :: err
end function openmc_mesh_get_id
function openmc_mesh_set_id(index, id) result(err) bind(C)
import C_INT32_T, C_INT
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT32_T), value, intent(in) :: id
integer(C_INT) :: err
end function openmc_mesh_set_id
function openmc_mesh_get_dimension(index, dims, n) result(err) bind(C)
import C_INT32_T, C_PTR, C_INT
integer(C_INT32_T), value, intent(in) :: index
type(C_PTR), intent(out) :: dims
integer(C_INT), intent(out) :: n
integer(C_INT) :: err
end function openmc_mesh_get_dimension
function openmc_mesh_set_dimension(index, n, dims) result(err) bind(C)
import C_INT32_T, C_INT
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT), value, intent(in) :: n
integer(C_INT), intent(in) :: dims(n)
integer(C_INT) :: err
end function openmc_mesh_set_dimension
function openmc_mesh_get_params(index, ll, ur, width, n) result(err) bind(C)
import C_INT32_T, C_PTR, C_INT
integer(C_INT32_T), value, intent(in) :: index
type(C_PTR), intent(out) :: ll
type(C_PTR), intent(out) :: ur
type(C_PTR), intent(out) :: width
integer(C_INT), intent(out) :: n
integer(C_INT) :: err
end function openmc_mesh_get_params
function openmc_mesh_set_params(index, n, ll, ur, width) result(err) bind(C)
import C_INT32_T, C_INT, C_DOUBLE
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT), value, intent(in) :: n
real(C_DOUBLE), intent(in), optional :: ll(n)
real(C_DOUBLE), intent(in), optional :: ur(n)
real(C_DOUBLE), intent(in), optional :: width(n)
integer(C_INT) :: err
end function openmc_mesh_set_params
function mesh_id(ptr) result(id) bind(C)
import C_PTR, C_INT32_T
type(C_PTR), value :: ptr
integer(C_INT32_T) :: id
end function
function mesh_volume_frac(ptr) result(volume_frac) bind(C)
import C_PTR, C_DOUBLE
type(C_PTR), value :: ptr
real(C_DOUBLE) :: volume_frac
end function
function mesh_n_dimension(ptr) result(n) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT) :: n
end function
function mesh_dimension(ptr, i) result(d) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT), value :: i
integer(C_INT) :: d
end function
function mesh_lower_left(ptr, i) result(ll) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
integer(C_INT), value :: i
real(C_DOUBLE) :: ll
end function
function mesh_upper_right(ptr, i) result(ur) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
integer(C_INT), value :: i
real(C_DOUBLE) :: ur
end function
function mesh_width(ptr, i) result(w) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
integer(C_INT), value :: i
real(C_DOUBLE) :: w
end function
pure function mesh_get_bin(ptr, xyz) result(bin) bind(C)
import C_PTR, C_DOUBLE, C_INT
type(C_PTR), value :: ptr
real(C_DOUBLE), intent(in) :: xyz(*)
integer(C_INT) :: bin
end function
pure function mesh_get_bin_from_indices(ptr, ijk) result(bin) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT), intent(in) :: ijk(*)
integer(C_INT) :: bin
end function
pure subroutine mesh_get_indices(ptr, xyz, ijk, in_mesh) bind(C)
import C_PTR, C_DOUBLE, C_INT, C_BOOL
type(C_PTR), value :: ptr
real(C_DOUBLE), intent(in) :: xyz(*)
integer(C_INT), intent(out) :: ijk(*)
logical(C_BOOL), intent(out) :: in_mesh
end subroutine
pure subroutine mesh_get_indices_from_bin(ptr, bin, ijk) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT), value :: bin
integer(C_INT), intent(out) :: ijk(*)
end subroutine
function mesh_ptr(i) result(ptr) bind(C)
import C_INT, C_PTR
integer(C_INT), value :: i
type(C_PTR) :: ptr
end function
subroutine read_meshes(node_ptr) bind(C)
import C_PTR
type(C_PTR) :: node_ptr
end subroutine
function n_meshes() result(n) bind(C)
import C_INT
integer(C_INT) :: n
end function
end interface
contains
subroutine regular_from_xml(this, node)
class(RegularMesh), intent(inout) :: this
type(XMLNode), intent(in) :: node
function meshes(i) result(m)
integer, intent(in) :: i
type(RegularMesh) :: m
integer :: n
character(MAX_LINE_LEN) :: temp_str
m % ptr = mesh_ptr(i)
end function
! Copy mesh id
if (check_for_node(node, "id")) then
call get_node_value(node, "id", this % id)
function regular_id(this) result(id)
class(RegularMesh), intent(in) :: this
integer(C_INT32_T) :: id
id = mesh_id(this % ptr)
end function
! Check to make sure 'id' hasn't been used
if (mesh_dict % has(this % id)) then
call fatal_error("Two or more meshes use the same unique ID: " &
// to_str(this % id))
end if
end if
function regular_volume_frac(this) result(volume_frac)
class(RegularMesh), intent(in) :: this
real(C_DOUBLE) :: volume_frac
volume_frac = mesh_volume_frac(this % ptr)
end function
! Read mesh type
if (check_for_node(node, "type")) then
call get_node_value(node, "type", temp_str)
select case (to_lower(temp_str))
case ('rect', 'rectangle', 'rectangular')
call warning("Mesh type '" // trim(temp_str) // "' is deprecated. &
&Please use 'regular' instead.")
this % type = MESH_REGULAR
case ('regular')
this % type = MESH_REGULAR
case default
call fatal_error("Invalid mesh type: " // trim(temp_str))
end select
else
this % type = MESH_REGULAR
end if
function regular_n_dimension(this) result(n)
class(RegularMesh), intent(in) :: this
integer(C_INT) :: n
n = mesh_n_dimension(this % ptr)
end function
! Determine number of dimensions for mesh
if (check_for_node(node, "dimension")) then
n = node_word_count(node, "dimension")
if (n /= 1 .and. n /= 2 .and. n /= 3) then
call fatal_error("Mesh must be one, two, or three dimensions.")
end if
this % n_dimension = n
function regular_dimension(this, i) result(d)
class(RegularMesh), intent(in) :: this
integer(C_INT), intent(in) :: i
integer(C_INT) :: d
d = mesh_dimension(this % ptr, i)
end function
! Allocate attribute arrays
allocate(this % dimension(n))
function regular_lower_left(this, i) result(ll)
class(RegularMesh), intent(in) :: this
integer(C_INT), intent(in) :: i
real(C_DOUBLE) :: ll
ll = mesh_lower_left(this % ptr, i)
end function
! Check that dimensions are all greater than zero
call get_node_array(node, "dimension", this % dimension)
if (any(this % dimension <= 0)) then
call fatal_error("All entries on the <dimension> element for a tally &
&mesh must be positive.")
end if
end if
function regular_upper_right(this, i) result(ur)
class(RegularMesh), intent(in) :: this
integer(C_INT), intent(in) :: i
real(C_DOUBLE) :: ur
ur = mesh_upper_right(this % ptr, i)
end function
! Check for lower-left coordinates
if (check_for_node(node, "lower_left")) then
n = node_word_count(node, "lower_left")
allocate(this % lower_left(n))
! Read mesh lower-left corner location
call get_node_array(node, "lower_left", this % lower_left)
else
call fatal_error("Must specify <lower_left> on a mesh.")
end if
if (check_for_node(node, "width")) then
! Make sure both upper-right or width were specified
if (check_for_node(node, "upper_right")) then
call fatal_error("Cannot specify both <upper_right> and <width> on a &
&mesh.")
end if
n = node_word_count(node, "width")
allocate(this % width(n))
allocate(this % upper_right(n))
! Check to ensure width has same dimensions
if (n /= size(this % lower_left)) then
call fatal_error("Number of entries on <width> must be the same as &
&the number of entries on <lower_left>.")
end if
! Check for negative widths
call get_node_array(node, "width", this % width)
if (any(this % width < ZERO)) then
call fatal_error("Cannot have a negative <width> on a tally mesh.")
end if
! Set width and upper right coordinate
this % upper_right = this % lower_left + this % dimension * this % width
elseif (check_for_node(node, "upper_right")) then
n = node_word_count(node, "upper_right")
allocate(this % upper_right(n))
allocate(this % width(n))
! Check to ensure width has same dimensions
if (n /= size(this % lower_left)) then
call fatal_error("Number of entries on <upper_right> must be the &
&same as the number of entries on <lower_left>.")
end if
! Check that upper-right is above lower-left
call get_node_array(node, "upper_right", this % upper_right)
if (any(this % upper_right < this % lower_left)) then
call fatal_error("The <upper_right> coordinates must be greater than &
&the <lower_left> coordinates on a tally mesh.")
end if
! Set width and upper right coordinate
this % width = (this % upper_right - this % lower_left) / this % dimension
else
call fatal_error("Must specify either <upper_right> and <width> on a &
&mesh.")
end if
if (allocated(this % dimension)) then
if (size(this % dimension) /= size(this % lower_left)) then
call fatal_error("Number of entries on <lower_left> must be the same &
&as the number of entries on <dimension>.")
end if
! Set volume fraction
this % volume_frac = ONE/real(product(this % dimension),8)
end if
end subroutine regular_from_xml
function regular_width(this, i) result(w)
class(RegularMesh), intent(in) :: this
integer(C_INT), intent(in) :: i
real(C_DOUBLE) :: w
w = mesh_width(this % ptr, i)
end function
!===============================================================================
! GET_MESH_BIN determines the tally bin for a particle in a structured mesh
@ -191,38 +248,8 @@ contains
real(8), intent(in) :: xyz(:) ! coordinates
integer, intent(out) :: bin ! tally bin
integer :: n ! size of mesh
integer :: d ! mesh dimension index
integer :: ijk(3) ! indices in mesh
logical :: in_mesh ! was given coordinate in mesh at all?
! Get number of dimensions
n = this % n_dimension
! Loop over the dimensions of the mesh
do d = 1, n
! Check for cases where particle is outside of mesh
if (xyz(d) < this % lower_left(d)) then
bin = NO_BIN_FOUND
return
elseif (xyz(d) > this % upper_right(d)) then
bin = NO_BIN_FOUND
return
end if
end do
! Determine indices
call this % get_indices(xyz, ijk, in_mesh)
! Convert indices to bin
if (in_mesh) then
bin = this % get_bin_from_indices(ijk)
else
bin = NO_BIN_FOUND
end if
end subroutine regular_get_bin
bin = mesh_get_bin(this % ptr, xyz)
end subroutine
!===============================================================================
! GET_MESH_INDICES determines the indices of a particle in a structured mesh
@ -234,18 +261,10 @@ contains
integer, intent(out) :: ijk(:) ! indices in mesh
logical, intent(out) :: in_mesh ! were given coords in mesh?
! Find particle in mesh
ijk(:this % n_dimension) = ceiling((xyz(:this % n_dimension) - &
this % lower_left)/this % width)
! Determine if particle is in mesh
if (any(ijk(:this % n_dimension) < 1) .or. &
any(ijk(:this % n_dimension) > this % dimension)) then
in_mesh = .false.
else
in_mesh = .true.
end if
logical(C_BOOL) :: in_mesh_
call mesh_get_indices(this % ptr, xyz, ijk, in_mesh_)
in_mesh = in_mesh_
end subroutine regular_get_indices
!===============================================================================
@ -258,15 +277,7 @@ contains
integer, intent(in) :: ijk(:)
integer :: bin
if (this % n_dimension == 1) then
bin = ijk(1)
elseif (this % n_dimension == 2) then
bin = (ijk(2) - 1) * this % dimension(1) + ijk(1)
elseif (this % n_dimension == 3) then
bin = ((ijk(3) - 1) * this % dimension(2) + (ijk(2) - 1)) &
* this % dimension(1) + ijk(1)
end if
bin = mesh_get_bin_from_indices(this % ptr, ijk)
end function regular_get_bin_from_indices
!===============================================================================
@ -279,498 +290,7 @@ contains
integer, intent(in) :: bin
integer, intent(out) :: ijk(:)
if (this % n_dimension == 1) then
ijk(1) = bin
else if (this % n_dimension == 2) then
ijk(1) = mod(bin - 1, this % dimension(1)) + 1
ijk(2) = (bin - 1)/this % dimension(1) + 1
else if (this % n_dimension == 3) then
ijk(1) = mod(bin - 1, this % dimension(1)) + 1
ijk(2) = mod(bin - 1, this % dimension(1) * this % dimension(2)) &
/ this % dimension(1) + 1
ijk(3) = (bin - 1)/(this % dimension(1) * this % dimension(2)) + 1
end if
call mesh_get_indices_from_bin(this % ptr, bin, ijk)
end subroutine regular_get_indices_from_bin
!===============================================================================
! MESH_INTERSECTS determines if a line between xyz0 and xyz1 intersects the
! outer boundary of the given mesh. This is important for determining whether a
! track will score to a mesh tally.
!===============================================================================
pure function regular_intersects(this, xyz0, xyz1) result(intersects)
class(RegularMesh), intent(in) :: this
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
select case(this % n_dimension)
case (1)
intersects = mesh_intersects_1d(this, xyz0, xyz1)
case (2)
intersects = mesh_intersects_2d(this, xyz0, xyz1)
case (3)
intersects = mesh_intersects_3d(this, xyz0, xyz1)
end select
end function regular_intersects
pure function mesh_intersects_1d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
real(8) :: x0 ! track start point
real(8) :: x1 ! track end point
real(8) :: xm0 ! lower-left coordinates of mesh
real(8) :: xm1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
! Copy coordinates of ending point
x1 = xyz1(1)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
intersects = .true.
return
end if
! Check if line intersects right surface
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
intersects = .true.
return
end if
end function mesh_intersects_1d
pure function mesh_intersects_2d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
real(8) :: x0, y0 ! track start point
real(8) :: x1, y1 ! track end point
real(8) :: xi, yi ! track intersection point with mesh
real(8) :: xm0, ym0 ! lower-left coordinates of mesh
real(8) :: xm1, ym1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
y0 = xyz0(2)
! Copy coordinates of ending point
x1 = xyz1(1)
y1 = xyz1(2)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
ym0 = m % lower_left(2)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
ym1 = m % upper_right(2)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface -- calculate the intersection point
! y
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects back surface -- calculate the intersection point
! x
if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then
xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1) then
intersects = .true.
return
end if
end if
! Check if line intersects right surface -- calculate the intersection
! point y
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects front surface -- calculate the intersection point
! x
if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then
xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1) then
intersects = .true.
return
end if
end if
end function mesh_intersects_2d
pure function mesh_intersects_3d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
real(8) :: x0, y0, z0 ! track start point
real(8) :: x1, y1, z1 ! track end point
real(8) :: xi, yi, zi ! track intersection point with mesh
real(8) :: xm0, ym0, zm0 ! lower-left coordinates of mesh
real(8) :: xm1, ym1, zm1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
y0 = xyz0(2)
z0 = xyz0(3)
! Copy coordinates of ending point
x1 = xyz1(1)
y1 = xyz1(2)
z1 = xyz1(3)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
ym0 = m % lower_left(2)
zm0 = m % lower_left(3)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
ym1 = m % upper_right(2)
zm1 = m % upper_right(3)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface -- calculate the intersection point
! (y,z)
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0)
zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects back surface -- calculate the intersection point
! (x,z)
if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then
xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0)
zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects bottom surface -- calculate the intersection
! point (x,y)
if ((z0 < zm0 .and. z1 > zm0) .or. (z0 > zm0 .and. z1 < zm0)) then
xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0)
yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0)
if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects right surface -- calculate the intersection point
! (y,z)
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0)
zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects front surface -- calculate the intersection point
! (x,z)
if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then
xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0)
zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects top surface -- calculate the intersection point
! (x,y)
if ((z0 < zm1 .and. z1 > zm1) .or. (z0 > zm1 .and. z1 < zm1)) then
xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0)
yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0)
if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
end function mesh_intersects_3d
!===============================================================================
! TO_HDF5 writes the mesh data to an HDF5 group
!===============================================================================
subroutine regular_to_hdf5(this, group)
class(RegularMesh), intent(in) :: this
integer(HID_T), intent(in) :: group
integer(HID_T) :: mesh_group
mesh_group = create_group(group, "mesh " // trim(to_str(this % id)))
call write_dataset(mesh_group, "type", "regular")
call write_dataset(mesh_group, "dimension", this % dimension)
call write_dataset(mesh_group, "lower_left", this % lower_left)
call write_dataset(mesh_group, "upper_right", this % upper_right)
call write_dataset(mesh_group, "width", this % width)
call close_group(mesh_group)
end subroutine regular_to_hdf5
!===============================================================================
! FREE_MEMORY_MESH deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_mesh()
n_meshes = 0
if (allocated(meshes)) deallocate(meshes)
call mesh_dict % clear()
end subroutine free_memory_mesh
!===============================================================================
! C API FUNCTIONS
!===============================================================================
function openmc_extend_meshes(n, index_start, index_end) result(err) bind(C)
! Extend the meshes 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(RegularMesh), allocatable :: temp(:) ! temporary meshes array
if (n_meshes == 0) then
! Allocate meshes array
allocate(meshes(n))
else
! Allocate meshes array with increased size
allocate(temp(n_meshes + n))
! Copy original meshes to temporary array
temp(1:n_meshes) = meshes
! Move allocation from temporary array
call move_alloc(FROM=temp, TO=meshes)
end if
! Return indices in meshes array
if (present(index_start)) index_start = n_meshes + 1
if (present(index_end)) index_end = n_meshes + n
n_meshes = n_meshes + n
err = 0
end function openmc_extend_meshes
function openmc_get_mesh_index(id, index) result(err) bind(C)
! Return the index in the meshes array of a mesh with a given ID
integer(C_INT32_T), value :: id
integer(C_INT32_T), intent(out) :: index
integer(C_INT) :: err
if (allocated(meshes)) then
if (mesh_dict % has(id)) then
index = mesh_dict % get(id)
err = 0
else
err = E_INVALID_ID
call set_errmsg("No mesh exists with ID=" // trim(to_str(id)) // ".")
end if
else
err = E_ALLOCATE
call set_errmsg("Memory has not been allocated for meshes.")
end if
end function openmc_get_mesh_index
function openmc_mesh_get_id(index, id) result(err) bind(C)
! Return the ID of a mesh
integer(C_INT32_T), value :: index
integer(C_INT32_T), intent(out) :: id
integer(C_INT) :: err
if (index >= 1 .and. index <= size(meshes)) then
id = meshes(index) % id
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in meshes array is out of bounds.")
end if
end function openmc_mesh_get_id
function openmc_mesh_set_id(index, id) result(err) bind(C)
! Set the ID of a mesh
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT32_T), value, intent(in) :: id
integer(C_INT) :: err
if (index >= 1 .and. index <= n_meshes) then
meshes(index) % id = id
call mesh_dict % set(id, index)
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in meshes array is out of bounds.")
end if
end function openmc_mesh_set_id
function openmc_mesh_get_dimension(index, dims, n) result(err) bind(C)
! Get the dimension of a mesh
integer(C_INT32_T), value, intent(in) :: index
type(C_PTR), intent(out) :: dims
integer(C_INT), intent(out) :: n
integer(C_INT) :: err
if (index >= 1 .and. index <= n_meshes) then
dims = C_LOC(meshes(index) % dimension)
n = meshes(index) % n_dimension
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in meshes array is out of bounds.")
end if
end function openmc_mesh_get_dimension
function openmc_mesh_set_dimension(index, n, dims) result(err) bind(C)
! Set the dimension of a mesh
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT), value, intent(in) :: n
integer(C_INT), intent(in) :: dims(n)
integer(C_INT) :: err
if (index >= 1 .and. index <= n_meshes) then
associate (m => meshes(index))
if (allocated(m % dimension)) deallocate (m % dimension)
if (allocated(m % lower_left)) deallocate (m % lower_left)
if (allocated(m % upper_right)) deallocate (m % upper_right)
if (allocated(m % width)) deallocate (m % width)
m % n_dimension = n
allocate(m % dimension(n))
allocate(m % lower_left(n))
allocate(m % upper_right(n))
allocate(m % width(n))
! Copy dimension
m % dimension(:) = dims
end associate
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in meshes array is out of bounds.")
end if
end function openmc_mesh_set_dimension
function openmc_mesh_get_params(index, ll, ur, width, n) result(err) bind(C)
! Get the mesh parameters
integer(C_INT32_T), value, intent(in) :: index
type(C_PTR), intent(out) :: ll
type(C_PTR), intent(out) :: ur
type(C_PTR), intent(out) :: width
integer(C_INT), intent(out) :: n
integer(C_INT) :: err
err = 0
if (index >= 1 .and. index <= n_meshes) then
associate (m => meshes(index))
if (allocated(m % lower_left)) then
ll = C_LOC(m % lower_left(1))
ur = C_LOC(m % upper_right(1))
width = C_LOC(m % width(1))
n = m % n_dimension
else
err = E_ALLOCATE
call set_errmsg("Mesh parameters have not been set.")
end if
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in meshes array is out of bounds.")
end if
end function openmc_mesh_get_params
function openmc_mesh_set_params(index, n, ll, ur, width) result(err) bind(C)
! Set the mesh parameters
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT), value, intent(in) :: n
real(C_DOUBLE), intent(in), optional :: ll(n)
real(C_DOUBLE), intent(in), optional :: ur(n)
real(C_DOUBLE), intent(in), optional :: width(n)
integer(C_INT) :: err
err = 0
if (index >= 1 .and. index <= n_meshes) then
associate (m => meshes(index))
if (allocated(m % lower_left)) deallocate (m % lower_left)
if (allocated(m % upper_right)) deallocate (m % upper_right)
if (allocated(m % width)) deallocate (m % width)
allocate(m % lower_left(n))
allocate(m % upper_right(n))
allocate(m % width(n))
if (present(ll) .and. present(ur)) then
m % lower_left(:) = ll
m % upper_right(:) = ur
m % width(:) = (ur - ll) / m % dimension
elseif (present(ll) .and. present(width)) then
m % lower_left(:) = ll
m % width(:) = width
m % upper_right(:) = ll + width * m % dimension
elseif (present(ur) .and. present(width)) then
m % upper_right(:) = ur
m % width(:) = width
m % lower_left(:) = ur - width * m % dimension
else
err = E_INVALID_ARGUMENT
call set_errmsg("At least two parameters must be specified.")
end if
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in meshes array is out of bounds.")
end if
end function openmc_mesh_set_params
end module mesh_header

View file

@ -5,6 +5,7 @@ namespace mpi {
int rank {0};
int n_procs {1};
bool master {true};
#ifdef OPENMC_MPI
MPI_Comm intracomm;

View file

@ -3,12 +3,17 @@
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <valarray>
#include <sstream>
#ifdef _OPENMP
#include <omp.h>
#endif
#include "xtensor/xmath.hpp"
#include "xtensor/xsort.hpp"
#include "xtensor/xadapt.hpp"
#include "xtensor/xview.hpp"
#include "openmc/error.h"
#include "openmc/math_functions.h"
#include "openmc/random_lcg.h"
@ -28,14 +33,15 @@ std::vector<Mgxs> macro_xs;
void
Mgxs::init(const std::string& in_name, double in_awr,
const double_1dvec& in_kTs, bool in_fissionable, int in_scatter_format,
const std::vector<double>& in_kTs, bool in_fissionable, int in_scatter_format,
int in_num_groups, int in_num_delayed_groups, bool in_is_isotropic,
const double_1dvec& in_polar, const double_1dvec& in_azimuthal)
const std::vector<double>& in_polar, const std::vector<double>& in_azimuthal)
{
// Set the metadata
name = in_name;
awr = in_awr;
kTs = in_kTs;
//TODO: Remove adapt when in_KTs is an xtensor
kTs = xt::adapt(in_kTs);
fissionable = in_fissionable;
scatter_format = in_scatter_format;
num_groups = in_num_groups;
@ -61,8 +67,8 @@ Mgxs::init(const std::string& in_name, double in_awr,
void
Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
int in_num_delayed_groups, const double_1dvec& temperature,
double tolerance, int_1dvec& temps_to_read, int& order_dim, int& method)
int in_num_delayed_groups, const std::vector<double>& temperature,
double tolerance, std::vector<int>& temps_to_read, int& order_dim, int& method)
{
// get name
char char_name[MAX_WORD_LEN];
@ -87,7 +93,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
dset_names[i] = new char[151];
}
get_datasets(kT_group, dset_names);
double_1dvec available_temps(num_temps);
xt::xarray<double> available_temps(num_temps);
for (int i = 0; i < num_temps; i++) {
read_double(kT_group, dset_names[i], &available_temps[i], true);
@ -110,24 +116,20 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
switch(method) {
case TEMPERATURE_NEAREST:
// Find the minimum difference
for (int i = 0; i < temperature.size(); i++) {
std::valarray<double> temp_diff(available_temps.data(),
available_temps.size());
temp_diff = std::abs(temp_diff - temperature[i]);
int i_closest = std::min_element(std::begin(temp_diff), std::end(temp_diff)) -
std::begin(temp_diff);
// Determine actual temperatures to read
for (const auto& T : temperature) {
auto i_closest = xt::argmin(xt::abs(available_temps - T))[0];
double temp_actual = available_temps[i_closest];
if (std::abs(temp_actual - temperature[i]) < tolerance) {
if (std::find(temps_to_read.begin(), temps_to_read.end(),
std::round(temp_actual)) == temps_to_read.end()) {
if (std::fabs(temp_actual - T) < tolerance) {
if (std::find(temps_to_read.begin(), temps_to_read.end(), std::round(temp_actual))
== temps_to_read.end()) {
temps_to_read.push_back(std::round(temp_actual));
} else {
fatal_error("MGXS Library does not contain cross section for " +
in_name + " at or near " +
std::to_string(std::round(temperature[i])) + " K.");
}
} else {
std::stringstream msg;
msg << "MGXS library does not contain cross sections for "
<< in_name << " at or near " << std::round(T) << " K.";
fatal_error(msg);
}
}
break;
@ -160,7 +162,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
// Get the library's temperatures
int n_temperature = temps_to_read.size();
double_1dvec in_kTs(n_temperature);
std::vector<double> in_kTs(n_temperature);
for (int i = 0; i < n_temperature; i++) {
std::string temp_str(std::to_string(temps_to_read[i]) + "K");
@ -254,12 +256,12 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
}
// Set the angular bins to use equally-spaced bins
double_1dvec in_polar(in_n_pol);
std::vector<double> in_polar(in_n_pol);
double dangle = PI / in_n_pol;
for (int p = 0; p < in_n_pol; p++) {
in_polar[p] = (p + 0.5) * dangle;
}
double_1dvec in_azimuthal(in_n_azi);
std::vector<double> in_azimuthal(in_n_azi);
dangle = 2. * PI / in_n_azi;
for (int a = 0; a < in_n_azi; a++) {
in_azimuthal[a] = (a + 0.5) * dangle - PI;
@ -274,12 +276,12 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
//==============================================================================
Mgxs::Mgxs(hid_t xs_id, int energy_groups, int delayed_groups,
const double_1dvec& temperature, double tolerance, int max_order,
const std::vector<double>& temperature, double tolerance, int max_order,
bool legendre_to_tabular, int legendre_to_tabular_points, int& method)
{
// Call generic data gathering routine (will populate the metadata)
int order_data;
int_1dvec temps_to_read;
std::vector<int> temps_to_read;
metadata_from_hdf5(xs_id, energy_groups, delayed_groups, temperature,
tolerance, temps_to_read, order_data, method);
@ -310,8 +312,8 @@ Mgxs::Mgxs(hid_t xs_id, int energy_groups, int delayed_groups,
//==============================================================================
Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs,
const std::vector<Mgxs*>& micros, const double_1dvec& atom_densities,
Mgxs::Mgxs(const std::string& in_name, const std::vector<double>& mat_kTs,
const std::vector<Mgxs*>& micros, const std::vector<double>& atom_densities,
double tolerance, int& method)
{
// Get the minimum data needed to initialize:
@ -328,8 +330,8 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs,
int in_num_groups = micros[0]->num_groups;
int in_num_delayed_groups = micros[0]->num_delayed_groups;
bool in_is_isotropic = micros[0]->is_isotropic;
double_1dvec in_polar = micros[0]->polar;
double_1dvec in_azimuthal = micros[0]->azimuthal;
std::vector<double> in_polar = micros[0]->polar;
std::vector<double> in_azimuthal = micros[0]->azimuthal;
init(in_name, in_awr, mat_kTs, in_fissionable, in_scatter_format,
in_num_groups, in_num_delayed_groups, in_is_isotropic, in_polar,
@ -345,33 +347,27 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs,
// Create the list of temperature indices and interpolation factors for
// each microscopic data at the material temperature
int_1dvec micro_t(micros.size(), 0);
double_1dvec micro_t_interp(micros.size(), 0.);
std::vector<int> micro_t(micros.size(), 0);
std::vector<double> micro_t_interp(micros.size(), 0.);
for (int m = 0; m < micros.size(); m++) {
switch(method) {
case TEMPERATURE_NEAREST:
{
// Find the nearest temperature
std::valarray<double> temp_diff(micros[m]->kTs.data(),
micros[m]->kTs.size());
temp_diff = std::abs(temp_diff - temp_desired);
micro_t[m] = std::min_element(std::begin(temp_diff),
std::end(temp_diff)) -
std::begin(temp_diff);
double temp_actual = micros[m]->kTs[micro_t[m]];
micro_t[m] = xt::argmin(xt::abs(micros[m]->kTs - temp_desired))[0];
auto temp_actual = micros[m]->kTs[micro_t[m]];
if (std::abs(temp_actual - temp_desired) >= K_BOLTZMANN * tolerance) {
fatal_error("MGXS Library does not contain cross section for " +
name + " at or near " +
std::to_string(std::round(temp_desired / K_BOLTZMANN))
+ " K.");
std::stringstream msg;
msg << "MGXS Library does not contain cross section for " << name
<< " at or near " << std::round(temp_desired / K_BOLTZMANN) << "K.";
fatal_error(msg);
}
}
break;
case TEMPERATURE_INTERPOLATION:
// Get a list of bounding temperatures for each actual temperature
// present in the model
for (int k = 0; k < micros[m]->kTs.size() - 1; k++) {
for (int k = 0; k < micros[m]->kTs.shape()[0] - 1; k++) {
if ((micros[m]->kTs[k] <= temp_desired) &&
(temp_desired < micros[m]->kTs[k + 1])) {
micro_t[m] = k;
@ -394,8 +390,8 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs,
int num_interp_points = 2;
if (method == TEMPERATURE_NEAREST) num_interp_points = 1;
for (int interp_point = 0; interp_point < num_interp_points; interp_point++) {
double_1dvec interp(micros.size());
double_1dvec temp_indices(micros.size());
std::vector<double> interp(micros.size());
std::vector<double> temp_indices(micros.size());
for (int m = 0; m < micros.size(); m++) {
interp[m] = (1. - micro_t_interp[m]) * atom_densities[m];
temp_indices[m] = micro_t[m] + interp_point;
@ -409,8 +405,8 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs,
//==============================================================================
void
Mgxs::combine(const std::vector<Mgxs*>& micros, const double_1dvec& scalars,
const int_1dvec& micro_ts, int this_t)
Mgxs::combine(const std::vector<Mgxs*>& micros, const std::vector<double>& scalars,
const std::vector<int>& micro_ts, int this_t)
{
// Build the vector of pointers to the xs objects within micros
std::vector<XsData*> those_xs(micros.size());
@ -441,19 +437,19 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg)
double val;
switch(xstype) {
case MG_GET_XS_TOTAL:
val = xs_t->total[a][gin];
val = xs_t->total(a, gin);
break;
case MG_GET_XS_NU_FISSION:
val = fissionable ? xs_t->nu_fission[a][gin] : 0.;
val = fissionable ? xs_t->nu_fission(a, gin) : 0.;
break;
case MG_GET_XS_ABSORPTION:
val = xs_t->absorption[a][gin];
val = xs_t->absorption(a, gin);;
break;
case MG_GET_XS_FISSION:
val = fissionable ? xs_t->fission[a][gin] : 0.;
val = fissionable ? xs_t->fission(a, gin) : 0.;
break;
case MG_GET_XS_KAPPA_FISSION:
val = fissionable ? xs_t->kappa_fission[a][gin] : 0.;
val = fissionable ? xs_t->kappa_fission(a, gin) : 0.;
break;
case MG_GET_XS_SCATTER:
case MG_GET_XS_SCATTER_MULT:
@ -462,16 +458,16 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg)
val = xs_t->scatter[a]->get_xs(xstype, gin, gout, mu);
break;
case MG_GET_XS_PROMPT_NU_FISSION:
val = fissionable ? xs_t->prompt_nu_fission[a][gin] : 0.;
val = fissionable ? xs_t->prompt_nu_fission(a, gin) : 0.;
break;
case MG_GET_XS_DELAYED_NU_FISSION:
if (fissionable) {
if (dg != nullptr) {
val = xs_t->delayed_nu_fission[a][gin][*dg];
val = xs_t->delayed_nu_fission(a, *dg, gin);
} else {
val = 0.;
for (auto& num : xs_t->delayed_nu_fission[a][gin]) {
val += num;
for (int d = 0; d < xs_t->delayed_nu_fission.shape()[2]; d++) {
val += xs_t->delayed_nu_fission(a, d, gin);
}
}
} else {
@ -481,12 +477,12 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg)
case MG_GET_XS_CHI_PROMPT:
if (fissionable) {
if (gout != nullptr) {
val = xs_t->chi_prompt[a][gin][*gout];
val = xs_t->chi_prompt(a, gin, *gout);
} else {
// provide an outgoing group-wise sum
val = 0.;
for (auto& num : xs_t->chi_prompt[a][gin]) {
val += num;
for (int g = 0; g < xs_t->chi_prompt.shape()[2]; g++) {
val += xs_t->chi_prompt(a, gin, g);
}
}
} else {
@ -497,21 +493,21 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg)
if (fissionable) {
if (gout != nullptr) {
if (dg != nullptr) {
val = xs_t->chi_delayed[a][gin][*gout][*dg];
val = xs_t->chi_delayed(a, *dg, gin, *gout);
} else {
val = xs_t->chi_delayed[a][gin][*gout][0];
val = xs_t->chi_delayed(a, 0, gin, *gout);
}
} else {
if (dg != nullptr) {
val = 0.;
for (int i = 0; i < xs_t->chi_delayed[a][gin].size(); i++) {
val += xs_t->chi_delayed[a][gin][i][*dg];
for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) {
val += xs_t->delayed_nu_fission(a, *dg, gin, g);
}
} else {
val = 0.;
for (int i = 0; i < xs_t->chi_delayed[a][gin].size(); i++) {
for (auto& num : xs_t->chi_delayed[a][gin][i]) {
val += num;
for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) {
for (int d = 0; d < xs_t->delayed_nu_fission.shape()[3]; d++) {
val += xs_t->delayed_nu_fission(a, d, gin, g);
}
}
}
@ -521,13 +517,13 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg)
}
break;
case MG_GET_XS_INVERSE_VELOCITY:
val = xs_t->inverse_velocity[a][gin];
val = xs_t->inverse_velocity(a, gin);
break;
case MG_GET_XS_DECAY_RATE:
if (dg != nullptr) {
val = xs_t->decay_rate[a][*dg + 1];
val = xs_t->decay_rate(a, *dg + 1);
} else {
val = xs_t->decay_rate[a][0];
val = xs_t->decay_rate(a, 0);
}
break;
default:
@ -548,11 +544,11 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout)
int tid = 0;
#endif
XsData* xs_t = &xs[cache[tid].t];
double nu_fission = xs_t->nu_fission[cache[tid].a][gin];
double nu_fission = xs_t->nu_fission(cache[tid].a, gin);
// Find the probability of having a prompt neutron
double prob_prompt =
xs_t->prompt_nu_fission[cache[tid].a][gin];
xs_t->prompt_nu_fission(cache[tid].a, gin);
// sample random numbers
double xi_pd = prn() * nu_fission;
@ -568,10 +564,10 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout)
// sample the outgoing energy group
gout = 0;
double prob_gout =
xs_t->chi_prompt[cache[tid].a][gin][gout];
xs_t->chi_prompt(cache[tid].a, gin, gout);
while (prob_gout < xi_gout) {
gout++;
prob_gout += xs_t->chi_prompt[cache[tid].a][gin][gout];
prob_gout += xs_t->chi_prompt(cache[tid].a, gin, gout);
}
} else {
@ -582,7 +578,7 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout)
while (xi_pd >= prob_prompt) {
dg++;
prob_prompt +=
xs_t->delayed_nu_fission[cache[tid].a][gin][dg];
xs_t->delayed_nu_fission(cache[tid].a, dg, gin);
}
// adjust dg in case of round-off error
@ -591,11 +587,11 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout)
// sample the outgoing energy group
gout = 0;
double prob_gout =
xs_t->chi_delayed[cache[tid].a][gin][gout][dg];
xs_t->chi_delayed(cache[tid].a, dg, gin, gout);
while (prob_gout < xi_gout) {
gout++;
prob_gout +=
xs_t->chi_delayed[cache[tid].a][gin][gout][dg];
xs_t->chi_delayed(cache[tid].a, dg, gin, gout);
}
}
}
@ -630,10 +626,10 @@ Mgxs::calculate_xs(int gin, double sqrtkT, const double uvw[3],
set_temperature_index(sqrtkT);
set_angle_index(uvw);
XsData* xs_t = &xs[cache[tid].t];
total_xs = xs_t->total[cache[tid].a][gin];
abs_xs = xs_t->absorption[cache[tid].a][gin];
total_xs = xs_t->total(cache[tid].a, gin);
abs_xs = xs_t->absorption(cache[tid].a, gin);
nu_fiss_xs = fissionable ? xs_t->nu_fission[cache[tid].a][gin] : 0.;
nu_fiss_xs = fissionable ? xs_t->nu_fission(cache[tid].a, gin) : 0.;
}
//==============================================================================
@ -662,17 +658,7 @@ Mgxs::set_temperature_index(double sqrtkT)
int tid = 0;
#endif
if (sqrtkT != cache[tid].sqrtkT) {
double kT = sqrtkT * sqrtkT;
// initialize vector for storage of the differences
std::valarray<double> temp_diff(kTs.data(), kTs.size());
// Find the minimum difference of kT and kTs
temp_diff = std::abs(temp_diff - kT);
cache[tid].t = std::min_element(std::begin(temp_diff), std::end(temp_diff)) -
std::begin(temp_diff);
// store this temperature as the last one used
cache[tid].t = xt::argmin(xt::abs(kTs - sqrtkT * sqrtkT))[0];
cache[tid].sqrtkT = sqrtkT;
}
}

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

View file

@ -19,7 +19,7 @@ add_mgxs_c(hid_t file_id, const char* name, int energy_groups,
int& method)
{
// Convert temps to a vector for the from_hdf5 function
double_1dvec temperature(temps, temps + n_temps);
std::vector<double> temperature(temps, temps + n_temps);
write_message("Loading " + std::string(name) + " data...", 6);
@ -60,10 +60,10 @@ create_macro_xs_c(const char* mat_name, int n_nuclides, const int i_nuclides[],
{
if (n_temps > 0) {
// // Convert temps to a vector
double_1dvec temperature(temps, temps + n_temps);
std::vector<double> temperature(temps, temps + n_temps);
// Convert atom_densities to a vector
double_1dvec atom_densities_vec(atom_densities,
std::vector<double> atom_densities_vec(atom_densities,
atom_densities + n_nuclides);
// Build array of pointers to nuclides_MG's Mgxs objects needed for this

View file

@ -1,7 +1,6 @@
module multipole_header
use constants
use dict_header, only: DictIntInt
use error, only: fatal_error
use hdf5_interface
@ -74,12 +73,11 @@ contains
character(len=*), intent(in) :: filename
character(len=10) :: version
integer :: i, n_poles, n_residues, n_windows
integer :: n_poles, n_residues, n_windows
integer(HSIZE_T) :: dims_1d(1), dims_2d(2), dims_3d(3)
integer(HID_T) :: file_id
integer(HID_T) :: group_id
integer(HID_T) :: dset
type(DictIntInt) :: l_val_dict
! Open file for reading and move into the /isotope group
file_id = file_open(filename, 'r', parallel=.true.)

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

@ -600,7 +600,6 @@ contains
integer :: i, j, k, l
integer :: t
integer :: m
integer :: n
integer :: n_grid
integer :: i_fission

View file

@ -10,7 +10,6 @@ module output
use error, only: fatal_error, warning
use geometry_header
use math, only: t_percentile
use mesh_header, only: RegularMesh, meshes
use message_passing, only: master, n_procs
use mgxs_interface
use nuclide_header
@ -34,6 +33,14 @@ module output
integer :: ou = OUTPUT_UNIT
integer :: eu = ERROR_UNIT
interface
function entropy(i) result(h) bind(C, name='entropy_c')
import C_INT, C_DOUBLE
integer(C_INT), value :: i
real(C_DOUBLE) :: h
end function
end interface
contains
!===============================================================================
@ -41,7 +48,7 @@ contains
! developers, version, and date/time which the problem was run.
!===============================================================================
subroutine title()
subroutine title() bind(C)
#ifdef _OPENMP
use omp_lib
@ -211,7 +218,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 +238,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(&
@ -337,7 +343,7 @@ contains
! write out entropy info
if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') &
entropy % data(i)
entropy(i)
if (n > 1) then
write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5," +/-",F8.5)', ADVANCE='NO') &
@ -371,7 +377,7 @@ contains
! write out entropy info
if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') &
entropy % data(i)
entropy(i)
! write out accumulated k-effective if after first active batch
if (n > 1) then
@ -622,42 +628,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 (settings::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;
@ -130,7 +130,7 @@ Particle::mark_as_lost(const char* message)
openmc_n_lost_particles += 1;
// Count the total number of simulated particles (on this processor)
auto n = openmc_current_batch * gen_per_batch * openmc_work;
auto n = openmc_current_batch * settings::gen_per_batch * openmc_work;
// Abort the simulation if the maximum number of lost particles has been
// reached
@ -141,14 +141,15 @@ Particle::mark_as_lost(const char* message)
}
void
Particle::write_restart()
Particle::write_restart() const
{
// Dont write another restart file if in particle restart mode
if (openmc_run_mode == RUN_MODE_PARTICLE) return;
if (settings::run_mode == RUN_MODE_PARTICLE) return;
// Set up file name
std::stringstream filename;
filename << path_output << "particle_" << openmc_current_batch << '_' << id << ".h5";
filename << settings::path_output << "particle_" << openmc_current_batch
<< '_' << id << ".h5";
#pragma omp critical (WriteParticleRestart)
{
@ -165,10 +166,10 @@ Particle::write_restart()
// Write data to file
write_dataset(file_id, "current_batch", openmc_current_batch);
write_dataset(file_id, "generations_per_batch", gen_per_batch);
write_dataset(file_id, "generations_per_batch", settings::gen_per_batch);
write_dataset(file_id, "current_generation", openmc_current_gen);
write_dataset(file_id, "n_particles", n_particles);
switch (openmc_run_mode) {
write_dataset(file_id, "n_particles", settings::n_particles);
switch (settings::run_mode) {
case RUN_MODE_FIXEDSOURCE:
write_dataset(file_id, "run_mode", "fixed source");
break;

View file

@ -6,7 +6,6 @@ module physics
use error, only: fatal_error, warning, write_message
use material_header, only: Material, materials
use math
use mesh_header, only: meshes
use message_passing
use nuclide_header
use particle_header
@ -1182,11 +1181,18 @@ contains
integer :: nu_d(MAX_DELAYED_GROUPS) ! number of delayed neutrons born
integer :: i ! loop index
integer :: nu ! actual number of neutrons produced
integer :: mesh_bin ! mesh bin for source site
real(8) :: nu_t ! total nu
real(8) :: weight ! weight adjustment for ufs method
type(Nuclide), pointer :: nuc
interface
function ufs_get_weight(p) result(weight) bind(C)
import Particle, C_DOUBLE
type(Particle), intent(in) :: p
real(C_DOUBLE) :: WEIGHT
end function
end interface
! Get pointers
nuc => nuclides(i_nuclide)
@ -1196,20 +1202,7 @@ contains
! the expected number of fission sites produced
if (ufs) then
associate (m => meshes(index_ufs_mesh))
! Determine indices on ufs mesh for current location
call m % get_bin(p % coord(1) % xyz, mesh_bin)
if (mesh_bin == NO_BIN_FOUND) then
call particle_write_restart(p)
call fatal_error("Source site outside UFS mesh!")
end if
if (source_frac(1, mesh_bin) /= ZERO) then
weight = m % volume_frac / source_frac(1, mesh_bin)
else
weight = ONE
end if
end associate
weight = ufs_get_weight(p)
else
weight = ONE
end if

View file

@ -7,7 +7,6 @@ module physics_mg
use error, only: fatal_error, warning, write_message
use material_header, only: Material, materials
use math, only: rotate_angle
use mesh_header, only: meshes
use mgxs_interface
use message_passing
use nuclide_header, only: material_xs
@ -168,33 +167,26 @@ contains
integer :: dg ! delayed group
integer :: gout ! group out
integer :: nu ! actual number of neutrons produced
integer :: mesh_bin ! mesh bin for source site
real(8) :: nu_t ! total nu
real(8) :: mu ! fission neutron angular cosine
real(8) :: phi ! fission neutron azimuthal angle
real(8) :: weight ! weight adjustment for ufs method
interface
function ufs_get_weight(p) result(weight) bind(C)
import Particle, C_DOUBLE
type(Particle), intent(in) :: p
real(C_DOUBLE) :: WEIGHT
end function
end interface
! TODO: Heat generation from fission
! If uniform fission source weighting is turned on, we increase of decrease
! the expected number of fission sites produced
if (ufs) then
associate (m => meshes(index_ufs_mesh))
! Determine indices on ufs mesh for current location
call m % get_bin(p % coord(1) % xyz, mesh_bin)
if (mesh_bin == NO_BIN_FOUND) then
call particle_write_restart(p)
call fatal_error("Source site outside UFS mesh!")
end if
if (source_frac(1, mesh_bin) /= ZERO) then
weight = m % volume_frac / source_frac(1, mesh_bin)
else
weight = ONE
end if
end associate
weight = ufs_get_weight(p)
else
weight = ONE
end if

View file

@ -9,6 +9,7 @@ module plot
use hdf5_interface
use output, only: time_stamp
use material_header, only: materials
use mesh_header, only: meshes, RegularMesh
use particle_header
use plot_header
use progress_header, only: ProgressBar
@ -84,7 +85,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 +101,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
@ -184,7 +185,7 @@ contains
!$omp end parallel do
! Draw tally mesh boundaries on the image if requested
if (associated(pl % meshlines_mesh)) call draw_mesh_lines(pl, data)
if (pl % index_meshlines_mesh >= 0) call draw_mesh_lines(pl, data)
! Write out the ppm to a file
call output_ppm(pl, data)
@ -214,6 +215,7 @@ contains
real(8) :: xyz_ur_plot(3) ! upper right xyz of plot image
real(8) :: xyz_ll(3) ! lower left xyz
real(8) :: xyz_ur(3) ! upper right xyz
type(RegularMesh) :: m
rgb(:) = pl % meshlines_color % rgb
@ -239,57 +241,56 @@ contains
width = xyz_ur_plot - xyz_ll_plot
associate (m => pl % meshlines_mesh)
call m % get_indices(xyz_ll_plot, ijk_ll(:m % n_dimension), in_mesh)
call m % get_indices(xyz_ur_plot, ijk_ur(:m % n_dimension), in_mesh)
m = meshes(pl % index_meshlines_mesh)
call m % get_indices(xyz_ll_plot, ijk_ll(:m % n_dimension()), in_mesh)
call m % get_indices(xyz_ur_plot, ijk_ur(:m % n_dimension()), in_mesh)
! sweep through all meshbins on this plane and draw borders
do i = ijk_ll(outer), ijk_ur(outer)
do j = ijk_ll(inner), ijk_ur(inner)
! check if we're in the mesh for this ijk
if (i > 0 .and. i <= m % dimension(outer) .and. &
j > 0 .and. j <= m % dimension(inner)) then
! sweep through all meshbins on this plane and draw borders
do i = ijk_ll(outer), ijk_ur(outer)
do j = ijk_ll(inner), ijk_ur(inner)
! check if we're in the mesh for this ijk
if (i > 0 .and. i <= m % dimension(outer) .and. &
j > 0 .and. j <= m % dimension(inner)) then
! get xyz's of lower left and upper right of this mesh cell
xyz_ll(outer) = m % lower_left(outer) + m % width(outer) * (i - 1)
xyz_ll(inner) = m % lower_left(inner) + m % width(inner) * (j - 1)
xyz_ur(outer) = m % lower_left(outer) + m % width(outer) * i
xyz_ur(inner) = m % lower_left(inner) + m % width(inner) * j
! get xyz's of lower left and upper right of this mesh cell
xyz_ll(outer) = m % lower_left(outer) + m % width(outer) * (i - 1)
xyz_ll(inner) = m % lower_left(inner) + m % width(inner) * (j - 1)
xyz_ur(outer) = m % lower_left(outer) + m % width(outer) * i
xyz_ur(inner) = m % lower_left(inner) + m % width(inner) * j
! map the xyz ranges to pixel ranges
! map the xyz ranges to pixel ranges
frac = (xyz_ll(outer) - xyz_ll_plot(outer)) / width(outer)
outrange(1) = int(frac * real(pl % pixels(1), 8))
frac = (xyz_ur(outer) - xyz_ll_plot(outer)) / width(outer)
outrange(2) = int(frac * real(pl % pixels(1), 8))
frac = (xyz_ll(outer) - xyz_ll_plot(outer)) / width(outer)
outrange(1) = int(frac * real(pl % pixels(1), 8))
frac = (xyz_ur(outer) - xyz_ll_plot(outer)) / width(outer)
outrange(2) = int(frac * real(pl % pixels(1), 8))
frac = (xyz_ur(inner) - xyz_ll_plot(inner)) / width(inner)
inrange(1) = int((ONE - frac) * real(pl % pixels(2), 8))
frac = (xyz_ll(inner) - xyz_ll_plot(inner)) / width(inner)
inrange(2) = int((ONE - frac) * real(pl % pixels(2), 8))
frac = (xyz_ur(inner) - xyz_ll_plot(inner)) / width(inner)
inrange(1) = int((ONE - frac) * real(pl % pixels(2), 8))
frac = (xyz_ll(inner) - xyz_ll_plot(inner)) / width(inner)
inrange(2) = int((ONE - frac) * real(pl % pixels(2), 8))
! draw lines
do out_ = outrange(1), outrange(2)
do plus = 0, pl % meshlines_width
data(:, out_ + 1, inrange(1) + plus + 1) = rgb
data(:, out_ + 1, inrange(2) + plus + 1) = rgb
data(:, out_ + 1, inrange(1) - plus + 1) = rgb
data(:, out_ + 1, inrange(2) - plus + 1) = rgb
end do
! draw lines
do out_ = outrange(1), outrange(2)
do plus = 0, pl % meshlines_width
data(:, out_ + 1, inrange(1) + plus + 1) = rgb
data(:, out_ + 1, inrange(2) + plus + 1) = rgb
data(:, out_ + 1, inrange(1) - plus + 1) = rgb
data(:, out_ + 1, inrange(2) - plus + 1) = rgb
end do
do in_ = inrange(1), inrange(2)
do plus = 0, pl % meshlines_width
data(:, outrange(1) + plus + 1, in_ + 1) = rgb
data(:, outrange(2) + plus + 1, in_ + 1) = rgb
data(:, outrange(1) - plus + 1, in_ + 1) = rgb
data(:, outrange(2) - plus + 1, in_ + 1) = rgb
end do
end do
do in_ = inrange(1), inrange(2)
do plus = 0, pl % meshlines_width
data(:, outrange(1) + plus + 1, in_ + 1) = rgb
data(:, outrange(2) + plus + 1, in_ + 1) = rgb
data(:, outrange(1) - plus + 1, in_ + 1) = rgb
data(:, outrange(2) - plus + 1, in_ + 1) = rgb
end do
end do
end if
end do
end if
end do
end associate
end do
end subroutine draw_mesh_lines

View file

@ -4,7 +4,6 @@ module plot_header
use constants
use dict_header, only: DictIntInt
use mesh_header, only: RegularMesh
implicit none
@ -31,7 +30,7 @@ module plot_header
integer :: pixels(3) ! pixel width/height of plot slice
integer :: meshlines_width ! pixel width of meshlines
integer :: level ! universe depth to plot the cells of
type(RegularMesh), pointer :: meshlines_mesh => null() ! mesh to plot
integer :: index_meshlines_mesh = -1 ! index of mesh to plot
type(ObjectColor) :: meshlines_color ! Color for meshlines
type(ObjectColor) :: not_found ! color for positions where no cell found
type(ObjectColor), allocatable :: colors(:) ! colors of cells/mats

View file

@ -4,6 +4,8 @@
#include <numeric>
#include <cmath>
#include "xtensor/xbuilder.hpp"
#include "openmc/constants.h"
#include "openmc/error.h"
#include "openmc/math_functions.h"
@ -16,11 +18,11 @@ namespace openmc {
//==============================================================================
void
ScattData::base_init(int order, const int_1dvec& in_gmin,
const int_1dvec& in_gmax, const double_2dvec& in_energy,
ScattData::base_init(int order, const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_energy,
const double_2dvec& in_mult)
{
int groups = in_energy.size();
size_t groups = in_energy.size();
gmin = in_gmin;
gmax = in_gmax;
@ -51,18 +53,17 @@ ScattData::base_init(int order, const int_1dvec& in_gmin,
//==============================================================================
void
ScattData::base_combine(int max_order,
const std::vector<ScattData*>& those_scatts, const double_1dvec& scalars,
int_1dvec& in_gmin, int_1dvec& in_gmax, double_2dvec& sparse_mult,
ScattData::base_combine(size_t max_order,
const std::vector<ScattData*>& those_scatts, const std::vector<double>& scalars,
xt::xtensor<int, 1>& in_gmin, xt::xtensor<int, 1>& in_gmax, double_2dvec& sparse_mult,
double_3dvec& sparse_scatter)
{
int groups = those_scatts[0] -> energy.size();
size_t groups = those_scatts[0] -> energy.size();
// Now allocate and zero our storage spaces
double_3dvec this_matrix = double_3dvec(groups, double_2dvec(groups,
double_1dvec(max_order, 0.)));
double_2dvec mult_numer(groups, double_1dvec(groups, 0.));
double_2dvec mult_denom(groups, double_1dvec(groups, 0.));
xt::xtensor<double, 3> this_matrix({groups, groups, max_order}, 0.);
xt::xtensor<double, 2> mult_numer({groups, groups}, 0.);
xt::xtensor<double, 2> mult_denom({groups, groups}, 0.);
// Build the dense scattering and multiplicity matrices
// Get the multiplicity_matrix
@ -80,26 +81,26 @@ ScattData::base_combine(int max_order,
ScattData* that = those_scatts[i];
// Build the dense matrix for that object
double_3dvec that_matrix = that->get_matrix(max_order);
xt::xtensor<double, 3> that_matrix = that->get_matrix(max_order);
// Now add that to this for the scattering and multiplicity
for (int gin = 0; gin < groups; gin++) {
// Only spend time adding that's gmin to gmax data since the rest will
// be zeros
int i_gout = 0;
for (int gout = that->gmin[gin]; gout <= that->gmax[gin]; gout++) {
for (int gout = that->gmin(gin); gout <= that->gmax(gin); gout++) {
// Do the scattering matrix
for (int l = 0; l < max_order; l++) {
this_matrix[gin][gout][l] += scalars[i] * that_matrix[gin][gout][l];
this_matrix(gin, gout, l) += scalars[i] * that_matrix(gin, gout, l);
}
// Incorporate that's contribution to the multiplicity matrix data
double nuscatt = that->scattxs[gin] * that->energy[gin][i_gout];
mult_numer[gin][gout] += scalars[i] * nuscatt;
double nuscatt = that->scattxs(gin) * that->energy[gin][i_gout];
mult_numer(gin, gout) += scalars[i] * nuscatt;
if (that->mult[gin][i_gout] > 0.) {
mult_denom[gin][gout] += scalars[i] * nuscatt / that->mult[gin][i_gout];
mult_denom(gin, gout) += scalars[i] * nuscatt / that->mult[gin][i_gout];
} else {
mult_denom[gin][gout] += scalars[i];
mult_denom(gin, gout) += scalars[i];
}
i_gout++;
}
@ -107,16 +108,8 @@ ScattData::base_combine(int max_order,
}
// Combine mult_numer and mult_denom into the combined multiplicity matrix
double_2dvec this_mult(groups, double_1dvec(groups, 1.));
for (int gin = 0; gin < groups; gin++) {
for (int gout = 0; gout < groups; gout++) {
if (mult_denom[gin][gout] > 0.) {
this_mult[gin][gout] = mult_numer[gin][gout] / mult_denom[gin][gout];
}
}
}
mult_numer.clear();
mult_denom.clear();
xt::xtensor<double, 2> this_mult({groups, groups}, 1.);
this_mult = xt::nan_to_num(mult_numer / mult_denom);
// We have the data, now we need to convert to a jagged array and then use
// the initialize function to store it on the object.
@ -125,8 +118,8 @@ ScattData::base_combine(int max_order,
int gmin_;
for (gmin_ = 0; gmin_ < groups; gmin_++) {
bool non_zero = false;
for (int l = 0; l < this_matrix[gin][gmin_].size(); l++) {
if (this_matrix[gin][gmin_][l] != 0.) {
for (int l = 0; l < this_matrix.shape()[2]; l++) {
if (this_matrix(gin, gmin_, l) != 0.) {
non_zero = true;
break;
}
@ -136,8 +129,8 @@ ScattData::base_combine(int max_order,
int gmax_;
for (gmax_ = groups - 1; gmax_ >= 0; gmax_--) {
bool non_zero = false;
for (int l = 0; l < this_matrix[gin][gmax_].size(); l++) {
if (this_matrix[gin][gmax_][l] != 0.) {
for (int l = 0; l < this_matrix.shape()[2]; l++) {
if (this_matrix(gin, gmax_, l) != 0.) {
non_zero = true;
break;
}
@ -160,8 +153,11 @@ ScattData::base_combine(int max_order,
sparse_mult[gin].resize(gmax_ - gmin_ + 1);
int i_gout = 0;
for (int gout = gmin_; gout <= gmax_; gout++) {
sparse_scatter[gin][i_gout] = this_matrix[gin][gout];
sparse_mult[gin][i_gout] = this_mult[gin][gout];
sparse_scatter[gin][i_gout].resize(this_matrix.shape()[2]);
for (int l = 0; l < this_matrix.shape()[2]; l++) {
sparse_scatter[gin][i_gout][l] = this_matrix(gin, gout, l);
}
sparse_mult[gin][i_gout] = this_mult(gin, gout);
i_gout++;
}
}
@ -241,21 +237,21 @@ ScattData::get_xs(int xstype, int gin, const int* gout, const double* mu)
//==============================================================================
void
ScattDataLegendre::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
const double_2dvec& in_mult, const double_3dvec& coeffs)
ScattDataLegendre::init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs)
{
int groups = coeffs.size();
int order = coeffs[0][0].size();
size_t groups = coeffs.size();
size_t order = coeffs[0][0].size();
// make a copy of coeffs that we can use to both extract data and normalize
double_3dvec matrix = coeffs;
// Get the scattering cross section value by summing the un-normalized P0
// coefficient in the variable matrix over all outgoing groups.
scattxs.resize(groups);
scattxs = xt::zeros<double>({groups});
for (int gin = 0; gin < groups; gin++) {
int num_groups = in_gmax[gin] - in_gmin[gin] + 1;
scattxs[gin] = 0.;
for (int i_gout = 0; i_gout < num_groups; i_gout++) {
scattxs[gin] += matrix[gin][i_gout][0];
}
@ -301,7 +297,7 @@ ScattDataLegendre::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
void
ScattDataLegendre::update_max_val()
{
int groups = max_val.size();
size_t groups = max_val.size();
// Step through the polynomial with fixed number of points to identify the
// maximal value
int Nmu = 1001;
@ -384,25 +380,25 @@ ScattDataLegendre::sample(int gin, int& gout, double& mu, double& wgt)
void
ScattDataLegendre::combine(const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars)
const std::vector<double>& scalars)
{
// Find the max order in the data set and make sure we can combine the sets
int max_order = 0;
size_t max_order = 0;
for (int i = 0; i < those_scatts.size(); i++) {
// Lets also make sure these items are combineable
ScattDataLegendre* that = dynamic_cast<ScattDataLegendre*>(those_scatts[i]);
if (!that) {
fatal_error("Cannot combine the ScattData objects!");
}
int that_order = that->get_order();
size_t that_order = that->get_order();
if (that_order > max_order) max_order = that_order;
}
max_order++; // Add one since this is a Legendre
int groups = those_scatts[0] -> energy.size();
size_t groups = those_scatts[0] -> energy.size();
int_1dvec in_gmin(groups);
int_1dvec in_gmax(groups);
xt::xtensor<int, 1> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups);
@ -418,20 +414,19 @@ ScattDataLegendre::combine(const std::vector<ScattData*>& those_scatts,
//==============================================================================
double_3dvec
ScattDataLegendre::get_matrix(int max_order)
xt::xtensor<double, 3>
ScattDataLegendre::get_matrix(size_t max_order)
{
// Get the sizes and initialize the data to 0
int groups = energy.size();
int order_dim = max_order + 1;
double_3dvec matrix = double_3dvec(groups, double_2dvec(groups,
double_1dvec(order_dim, 0.)));
size_t groups = energy.size();
size_t order_dim = max_order + 1;
xt::xtensor<double, 3> matrix({groups, groups, order_dim}, 0.);
for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) {
int gout = i_gout + gmin[gin];
for (int l = 0; l < order_dim; l++) {
matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] *
matrix(gin, gout, l) = scattxs[gin] * energy[gin][i_gout] *
dist[gin][i_gout][l];
}
}
@ -444,20 +439,20 @@ ScattDataLegendre::get_matrix(int max_order)
//==============================================================================
void
ScattDataHistogram::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
const double_2dvec& in_mult, const double_3dvec& coeffs)
ScattDataHistogram::init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs)
{
int groups = coeffs.size();
int order = coeffs[0][0].size();
size_t groups = coeffs.size();
size_t order = coeffs[0][0].size();
// make a copy of coeffs that we can use to both extract data and normalize
double_3dvec matrix = coeffs;
// Get the scattering cross section value by summing the distribution
// over all the histogram bins in angle and outgoing energy groups
scattxs.resize(groups);
scattxs = xt::zeros<double>({groups});
for (int gin = 0; gin < groups; gin++) {
scattxs[gin] = 0.;
for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
scattxs[gin] += std::accumulate(matrix[gin][i_gout].begin(),
matrix[gin][i_gout].end(), 0.);
@ -484,12 +479,8 @@ ScattDataHistogram::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
ScattData::base_init(order, in_gmin, in_gmax, in_energy, in_mult);
// Build the angular distribution mu values
mu = double_1dvec(order);
mu = xt::linspace(-1., 1., order + 1);
dmu = 2. / order;
mu[0] = -1.;
for (int imu = 1; imu < order; imu++) {
mu[imu] = -1. + imu * dmu;
}
// Calculate f(mu) and integrate it so we can avoid rejection sampling
fmu.resize(groups);
@ -534,7 +525,7 @@ ScattDataHistogram::calc_f(int gin, int gout, double mu)
int imu;
if (mu == 1.) {
// use size -2 to have the index one before the end
imu = this->mu.size() - 2;
imu = this->mu.shape()[0] - 2;
} else {
imu = std::floor((mu + 1.) / dmu + 1.) - 1;
}
@ -560,7 +551,6 @@ ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt)
if (xi < dist[gin][i_gout][0]) {
imu = 0;
} else {
// TODO lower_bound? + 1?
imu = std::upper_bound(dist[gin][i_gout].begin(),
dist[gin][i_gout].end(), xi) -
dist[gin][i_gout].begin();
@ -581,21 +571,20 @@ ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt)
//==============================================================================
double_3dvec
ScattDataHistogram::get_matrix(int max_order)
xt::xtensor<double, 3>
ScattDataHistogram::get_matrix(size_t max_order)
{
// Get the sizes and initialize the data to 0
int groups = energy.size();
size_t groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations
int order_dim = get_order();
double_3dvec matrix = double_3dvec(groups, double_2dvec(groups,
double_1dvec(order_dim, 0.)));
size_t order_dim = get_order();
xt::xtensor<double, 3> matrix({groups, groups, order_dim}, 0);
for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) {
int gout = i_gout + gmin[gin];
for (int l = 0; l < order_dim; l++) {
matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] *
matrix(gin, gout, l) = scattxs[gin] * energy[gin][i_gout] *
fmu[gin][i_gout][l];
}
}
@ -607,10 +596,10 @@ ScattDataHistogram::get_matrix(int max_order)
void
ScattDataHistogram::combine(const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars)
const std::vector<double>& scalars)
{
// Find the max order in the data set and make sure we can combine the sets
int max_order = those_scatts[0]->get_order();
size_t max_order = those_scatts[0]->get_order();
for (int i = 0; i < those_scatts.size(); i++) {
// Lets also make sure these items are combineable
ScattDataHistogram* that = dynamic_cast<ScattDataHistogram*>(those_scatts[i]);
@ -622,10 +611,10 @@ ScattDataHistogram::combine(const std::vector<ScattData*>& those_scatts,
}
}
int groups = those_scatts[0] -> energy.size();
size_t groups = those_scatts[0] -> energy.size();
int_1dvec in_gmin(groups);
int_1dvec in_gmax(groups);
xt::xtensor<int, 1> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups);
@ -633,7 +622,7 @@ ScattDataHistogram::combine(const std::vector<ScattData*>& those_scatts,
// so we use a base class method to sum up xs and create new energy and mult
// matrices
ScattData::base_combine(max_order, those_scatts, scalars, in_gmin, in_gmax,
sparse_mult, sparse_scatter);
sparse_mult, sparse_scatter);
// Got everything we need, store it.
init(in_gmin, in_gmax, sparse_mult, sparse_scatter);
@ -644,29 +633,24 @@ ScattDataHistogram::combine(const std::vector<ScattData*>& those_scatts,
//==============================================================================
void
ScattDataTabular::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax,
const double_2dvec& in_mult, const double_3dvec& coeffs)
ScattDataTabular::init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs)
{
int groups = coeffs.size();
int order = coeffs[0][0].size();
size_t groups = coeffs.size();
size_t order = coeffs[0][0].size();
// make a copy of coeffs that we can use to both extract data and normalize
double_3dvec matrix = coeffs;
// Build the angular distribution mu values
mu = double_1dvec(order);
mu = xt::linspace(-1., 1., order);
dmu = 2. / (order - 1);
mu[0] = -1.;
for (int imu = 1; imu < order - 1; imu++) {
mu[imu] = -1. + imu * dmu;
}
mu[order - 1] = 1.;
// Get the scattering cross section value by integrating the distribution
// over all mu points and then combining over all outgoing groups
scattxs.resize(groups);
scattxs = xt::zeros<double>({groups});
for (int gin = 0; gin < groups; gin++) {
scattxs[gin] = 0.;
for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
for (int imu = 1; imu < order; imu++) {
scattxs[gin] += 0.5 * dmu * (matrix[gin][i_gout][imu - 1] +
@ -743,7 +727,7 @@ ScattDataTabular::calc_f(int gin, int gout, double mu)
int imu;
if (mu == 1.) {
// use size -2 to have the index one before the end
imu = this->mu.size() - 2;
imu = this->mu.shape()[0] - 2;
} else {
imu = std::floor((mu + 1.) / dmu + 1.) - 1;
}
@ -764,7 +748,7 @@ ScattDataTabular::sample(int gin, int& gout, double& mu, double& wgt)
sample_energy(gin, gout, i_gout);
// Determine the outgoing cosine bin
int NP = this->mu.size();
int NP = this->mu.shape()[0];
double xi = prn();
double c_k = dist[gin][i_gout][0];
@ -804,21 +788,20 @@ ScattDataTabular::sample(int gin, int& gout, double& mu, double& wgt)
//==============================================================================
double_3dvec
ScattDataTabular::get_matrix(int max_order)
xt::xtensor<double, 3>
ScattDataTabular::get_matrix(size_t max_order)
{
// Get the sizes and initialize the data to 0
int groups = energy.size();
size_t groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations
int order_dim = get_order();
double_3dvec matrix = double_3dvec(groups, double_2dvec(groups,
double_1dvec(order_dim, 0.)));
size_t order_dim = get_order();
xt::xtensor<double, 3> matrix({groups, groups, order_dim}, 0.);
for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) {
int gout = i_gout + gmin[gin];
for (int l = 0; l < order_dim; l++) {
matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] *
matrix(gin, gout, l) = scattxs[gin] * energy[gin][i_gout] *
fmu[gin][i_gout][l];
}
}
@ -830,10 +813,10 @@ ScattDataTabular::get_matrix(int max_order)
void
ScattDataTabular::combine(const std::vector<ScattData*>& those_scatts,
const double_1dvec& scalars)
const std::vector<double>& scalars)
{
// Find the max order in the data set and make sure we can combine the sets
int max_order = those_scatts[0]->get_order();
size_t max_order = those_scatts[0]->get_order();
for (int i = 0; i < those_scatts.size(); i++) {
// Lets also make sure these items are combineable
ScattDataTabular* that = dynamic_cast<ScattDataTabular*>(those_scatts[i]);
@ -845,10 +828,10 @@ ScattDataTabular::combine(const std::vector<ScattData*>& those_scatts,
}
}
int groups = those_scatts[0] -> energy.size();
size_t groups = those_scatts[0] -> energy.size();
int_1dvec in_gmin(groups);
int_1dvec in_gmax(groups);
xt::xtensor<int, 1> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups);
@ -856,7 +839,7 @@ ScattDataTabular::combine(const std::vector<ScattData*>& those_scatts,
// so we use a base class method to sum up xs and create new energy and mult
// matrices
ScattData::base_combine(max_order, those_scatts, scalars, in_gmin, in_gmax,
sparse_mult, sparse_scatter);
sparse_mult, sparse_scatter);
// Got everything we need, store it.
init(in_gmin, in_gmax, sparse_mult, sparse_scatter);
@ -885,16 +868,11 @@ convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab,
tab.scattxs = leg.scattxs;
// Build mu and dmu
tab.mu = double_1dvec(n_mu);
tab.mu = xt::linspace(-1., 1., n_mu);
tab.dmu = 2. / (n_mu - 1);
tab.mu[0] = -1.;
for (int imu = 1; imu < n_mu - 1; imu++) {
tab.mu[imu] = -1. + imu * tab.dmu;
}
tab.mu[n_mu - 1] = 1.;
// Calculate f(mu) and integrate it so we can avoid rejection sampling
int groups = tab.energy.size();
size_t groups = tab.energy.size();
tab.fmu.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = tab.gmax[gin] - tab.gmin[gin] + 1;

View file

@ -9,105 +9,103 @@ module settings
! ============================================================================
! ENERGY TREATMENT RELATED VARIABLES
logical(C_BOOL) :: run_CE = .true. ! Run in CE mode?
logical(C_BOOL), bind(C, name='run_CE') :: run_CE ! Run in CE mode?
! ============================================================================
! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES
! Unreoslved resonance probablity tables
logical :: urr_ptables_on = .true.
logical(C_BOOL), bind(C) :: urr_ptables_on
! Default temperature and method for choosing temperatures
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(8) :: temperature_range(2) = [ZERO, ZERO]
integer(C_INT), bind(C) :: temperature_method
logical(C_BOOL), bind(C) :: temperature_multipole
real(C_DOUBLE), bind(C) :: temperature_tolerance
real(C_DOUBLE), bind(C) :: temperature_default
real(C_DOUBLE), bind(C) :: temperature_range(2)
integer :: n_log_bins ! number of bins for logarithmic grid
integer(C_INT), bind(C) :: n_log_bins ! number of bins for logarithmic grid
logical :: photon_transport = .false.
integer :: electron_treatment = ELECTRON_TTB
logical(C_BOOL), bind(C) :: photon_transport
integer(C_INT), bind(C) :: electron_treatment
! ============================================================================
! MULTI-GROUP CROSS SECTION RELATED VARIABLES
! Maximum Data Order
integer(C_INT) :: max_order
integer(C_INT), bind(C) :: max_order
! Whether or not to convert Legendres to tabulars
logical :: legendre_to_tabular = .true.
logical(C_BOOL), bind(C) :: legendre_to_tabular
! Number of points to use in the Legendre to tabular conversion
integer(C_INT) :: legendre_to_tabular_points = C_NONE
integer(C_INT), bind(C) :: legendre_to_tabular_points
! ============================================================================
! SIMULATION VARIABLES
! Assume all tallies are spatially distinct
logical :: assume_separate = .false.
logical(C_BOOL), bind(C) :: assume_separate
! Use confidence intervals for results instead of standard deviations
logical :: confidence_intervals = .false.
logical(C_BOOL), bind(C) :: confidence_intervals
integer(C_INT64_T), bind(C) :: n_particles = 0 ! # of particles per generation
integer(C_INT32_T), bind(C) :: n_batches ! # of batches
integer(C_INT32_T), bind(C) :: n_inactive ! # of inactive batches
integer(C_INT32_T), bind(C) :: gen_per_batch = 1 ! # of generations per batch
integer(C_INT64_T), bind(C) :: n_particles ! # of particles per generation
integer(C_INT32_T), bind(C) :: n_batches ! # of batches
integer(C_INT32_T), bind(C) :: n_inactive ! # of inactive batches
integer(C_INT32_T), bind(C) :: gen_per_batch ! # of generations per batch
integer :: n_max_batches ! max # of batches
integer :: n_batch_interval = 1 ! batch interval for triggers
logical :: pred_batches = .false. ! predict batches for triggers
logical :: trigger_on = .false. ! flag for turning triggers on/off
integer(C_INT), bind(C) :: n_max_batches ! max # of batches
integer(C_INT), bind(C, name='trigger_batch_interval') :: n_batch_interval ! batch interval for triggers
logical(C_BOOL), bind(C, name='trigger_predict') :: pred_batches ! predict batches for triggers
logical(C_BOOL), bind(C) :: trigger_on ! flag for turning triggers on/off
logical :: entropy_on = .false.
integer :: index_entropy_mesh = -1
logical(C_BOOL), bind(C) :: entropy_on
integer(C_INT32_T), bind(C) :: index_entropy_mesh
logical :: ufs = .false.
integer :: index_ufs_mesh = -1
logical(C_BOOL), bind(C, name='ufs_on') :: ufs
integer(C_INT32_T), bind(C) :: index_ufs_mesh
! Write source at end of simulation
logical :: source_separate = .false.
logical :: source_write = .true.
logical :: source_latest = .false.
logical(C_BOOL), bind(C) :: source_separate
logical(C_BOOL), bind(C) :: source_write
logical(C_BOOL), bind(C) :: source_latest
! Variance reduction settins
logical :: survival_biasing = .false.
real(8) :: weight_cutoff = 0.25_8
real(8) :: energy_cutoff(4) = [ZERO, 1000.0_8, ZERO, ZERO]
real(8) :: weight_survive = ONE
logical(C_BOOL), bind(C) :: survival_biasing
real(C_DOUBLE), bind(C) :: weight_cutoff
real(C_DOUBLE), bind(C) :: energy_cutoff(4)
real(C_DOUBLE), bind(C) :: weight_survive
! Mode to run in (fixed source, eigenvalue, plotting, etc)
integer(C_INT), bind(C, name='openmc_run_mode') :: run_mode = NONE
integer(C_INT), bind(C) :: run_mode
! Restart run
logical(C_BOOL), bind(C, name='openmc_restart_run') :: restart_run = .false.
logical(C_BOOL), bind(C) :: restart_run
! The verbosity controls how much information will be printed to the screen
! and in logs
integer(C_INT), bind(C, name='openmc_verbosity') :: verbosity = 7
integer(C_INT), bind(C) :: verbosity
logical(C_BOOL), bind(C, name='openmc_check_overlaps') :: check_overlaps = .false.
logical(C_BOOL), bind(C) :: check_overlaps
! Trace for single particle
integer :: trace_batch
integer :: trace_gen
integer(8) :: trace_particle
integer(C_INT), bind(C) :: trace_batch
integer(C_INT), bind(C) :: trace_gen
integer(C_INT64_T), bind(C) :: trace_particle
! Particle tracks
logical(C_BOOL), bind(C, name='openmc_write_all_tracks') :: &
write_all_tracks = .false.
logical(C_BOOL), bind(C) :: write_all_tracks
integer, allocatable :: track_identifiers(:,:)
! Particle restart run
logical(C_BOOL), bind(C, name='openmc_particle_restart_run') :: &
particle_restart_run = .false.
logical(C_BOOL), bind(C) :: particle_restart_run
! Write out initial source
logical :: write_initial_source = .false.
logical(C_BOOL), bind(C) :: write_initial_source
! Whether create fission neutrons or not. Only applied for MODE_FIXEDSOURCE
logical :: create_fission_neutrons = .true.
logical(C_BOOL), bind(C) :: create_fission_neutrons
! Information about state points to be written
integer :: n_state_points = 0
@ -120,28 +118,27 @@ 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
character(MAX_FILE_LEN) :: path_output = '' ! Path to output directory
! Various output options
logical :: output_summary = .true.
logical :: output_tallies = .true.
logical(C_BOOL), bind(C) :: output_summary
logical(C_BOOL), bind(C) :: output_tallies
! Resonance scattering settings
logical :: res_scat_on = .false. ! is resonance scattering treated?
integer :: res_scat_method = RES_SCAT_ARES ! resonance scattering method
real(8) :: res_scat_energy_min = 0.01_8
real(8) :: res_scat_energy_max = 1000.0_8
logical(C_BOOL), bind(C) :: res_scat_on ! is resonance scattering treated?
integer(C_INT), bind(C) :: res_scat_method ! resonance scattering method
real(C_DOUBLE), bind(C) :: res_scat_energy_min
real(C_DOUBLE), bind(C) :: res_scat_energy_max
character(10), allocatable :: res_scat_nuclides(:)
! Is CMFD active
logical :: cmfd_run = .false.
logical(C_BOOL), bind(C) :: cmfd_run
! No reduction at end of batch
logical :: reduce_tallies = .true.
logical(C_BOOL), bind(C) :: reduce_tallies
contains

View file

@ -1,8 +1,23 @@
#include "openmc/settings.h"
#include <cmath> // for ceil, pow
#include <limits> // for numeric_limits
#include <sstream>
#include <string>
#include <omp.h>
#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/file_utils.h"
#include "openmc/mesh.h"
#include "openmc/output.h"
#include "openmc/random_lcg.h"
#include "openmc/source.h"
#include "openmc/string_utils.h"
#include "openmc/xml_interface.h"
@ -12,57 +27,599 @@ namespace openmc {
// Global variables
//==============================================================================
char* openmc_path_input;
char* openmc_path_statepoint;
char* openmc_path_sourcepoint;
char* openmc_path_particle_restart;
namespace settings {
// Default values for boolean flags
bool assume_separate {false};
bool check_overlaps {false};
bool cmfd_run {false};
bool confidence_intervals {false};
bool create_fission_neutrons {true};
bool entropy_on {false};
bool legendre_to_tabular {true};
bool output_summary {true};
bool output_tallies {true};
bool particle_restart_run {false};
bool photon_transport {false};
bool reduce_tallies {true};
bool res_scat_on {false};
bool restart_run {false};
bool run_CE {true};
bool source_latest {false};
bool source_separate {false};
bool source_write {true};
bool survival_biasing {false};
bool temperature_multipole {false};
bool trigger_on {false};
bool trigger_predict {false};
bool ufs_on {false};
bool urr_ptables_on {true};
bool write_all_tracks {false};
bool write_initial_source {false};
std::string path_cross_sections;
std::string path_input;
std::string path_multipole;
std::string path_output;
std::string path_particle_restart;
std::string path_source;
std::string path_sourcepoint;
std::string path_statepoint;
int32_t index_entropy_mesh {-1};
int32_t index_ufs_mesh {-1};
int32_t n_batches;
int32_t n_inactive {0};
int32_t gen_per_batch {1};
int64_t n_particles {-1};
int electron_treatment {ELECTRON_TTB};
double energy_cutoff[4] {0.0, 1000.0, 0.0, 0.0};
int legendre_to_tabular_points {C_NONE};
int max_order {0};
int n_log_bins {8000};
int n_max_batches;
int res_scat_method {RES_SCAT_ARES};
double res_scat_energy_min {0.01};
double res_scat_energy_max {1000.0};
int run_mode {-1};
int temperature_method {TEMPERATURE_NEAREST};
bool temperature_multipole {false};
double temperature_tolerance {10.0};
double temperature_default {293.6};
std::array<double, 2> temperature_range {0.0, 0.0};
double temperature_range[2] {0.0, 0.0};
int trace_batch;
int trace_gen;
int64_t trace_particle;
int trigger_batch_interval {1};
int verbosity {7};
double weight_cutoff {0.25};
double weight_survive {1.0};
// TODO: Move to separate file
struct KTrigger {
int type;
double threshold;
};
extern "C" KTrigger keff_trigger;
} // namespace settings
//==============================================================================
// Functions
//==============================================================================
void read_settings(pugi::xml_node* root)
void get_run_parameters(pugi::xml_node node_base)
{
using namespace settings;
using namespace pugi;
// Check number of particles
if (!check_for_node(node_base, "particles")) {
fatal_error("Need to specify number of particles.");
}
// Get number of particles if it wasn't specified as a command-line argument
if (n_particles == -1) {
n_particles = std::stoll(get_node_value(node_base, "particles"));
}
// Get number of basic batches
if (check_for_node(node_base, "batches")) {
n_batches = std::stoi(get_node_value(node_base, "batches"));
}
if (!trigger_on) n_max_batches = n_batches;
// Get number of inactive batches
if (run_mode == RUN_MODE_EIGENVALUE) {
if (check_for_node(node_base, "inactive")) {
n_inactive = std::stoi(get_node_value(node_base, "inactive"));
}
if (check_for_node(node_base, "generations_per_batch")) {
gen_per_batch = std::stoi(get_node_value(node_base, "generations_per_batch"));
}
// TODO: Preallocate space for keff and entropy by generation
// Get the trigger information for keff
if (check_for_node(node_base, "keff_trigger")) {
xml_node node_keff_trigger = node_base.child("keff_trigger");
if (check_for_node(node_keff_trigger, "type")) {
auto temp = get_node_value(node_keff_trigger, "type", true, true);
if (temp == "std_dev") {
keff_trigger.type = STANDARD_DEVIATION;
} else if (temp == "variance") {
keff_trigger.type = VARIANCE;
} else if (temp == "rel_err") {
keff_trigger.type = RELATIVE_ERROR;
} else {
fatal_error("Unrecognized keff trigger type " + temp);
}
} else {
fatal_error("Specify keff trigger type in settings XML");
}
if (check_for_node(node_keff_trigger, "threshold")) {
keff_trigger.threshold = std::stod(get_node_value(
node_keff_trigger, "threshold"));
} else {
fatal_error("Specify keff trigger threshold in settings XML");
}
}
}
}
void read_settings_xml()
{
using namespace settings;
using namespace pugi;
// Check if settings.xml exists
std::string filename = std::string(path_input) + "settings.xml";
if (!file_exists(filename)) {
if (run_mode != RUN_MODE_PLOTTING) {
std::stringstream msg;
msg << "Settings XML file '" << filename << "' does not exist! In order "
"to run OpenMC, you first need a set of input files; at a minimum, this "
"includes settings.xml, geometry.xml, and materials.xml. Please consult "
"the user's guide at http://openmc.readthedocs.io for further "
"information.";
fatal_error(msg);
} else {
// The settings.xml file is optional if we just want to make a plot.
return;
}
}
// Parse settings.xml file
xml_document doc;
auto result = doc.load_file("settings.xml");
if (!result) {
fatal_error("Error processing settings.xml file.");
}
// Get root element
xml_node root = doc.document_element();
// Verbosity
if (check_for_node(root, "verbosity")) {
verbosity = std::stoi(get_node_value(root, "verbosity"));
}
// To this point, we haven't displayed any output since we didn't know what
// the verbosity is. Now that we checked for it, show the title if necessary
if (openmc_master) {
if (verbosity >= 2) title();
}
write_message("Reading settings XML file...", 5);
// Find if a multi-group or continuous-energy simulation is desired
if (check_for_node(root, "energy_mode")) {
std::string temp_str = get_node_value(root, "energy_mode", true, true);
if (temp_str == "mg" || temp_str == "multi-group") {
run_CE = false;
} else if (temp_str == "ce" || temp_str == "continuous-energy") {
run_CE = true;
}
}
// Look for deprecated cross_sections.xml file in settings.xml
if (check_for_node(*root, "cross_sections")) {
if (check_for_node(root, "cross_sections")) {
warning("Setting cross_sections in settings.xml has been deprecated."
" The cross_sections are now set in materials.xml and the "
"cross_sections input to materials.xml and the OPENMC_CROSS_SECTIONS"
" environment variable will take precendent over setting "
"cross_sections in settings.xml.");
path_cross_sections = get_node_value(*root, "cross_sections");
path_cross_sections = get_node_value(root, "cross_sections");
}
// Look for deprecated windowed_multipole file in settings.xml
if (openmc_run_mode != RUN_MODE_PLOTTING) {
if (check_for_node(*root, "multipole_library")) {
if (run_mode != RUN_MODE_PLOTTING) {
if (check_for_node(root, "multipole_library")) {
warning("Setting multipole_library in settings.xml has been "
"deprecated. The multipole_library is now set in materials.xml and"
" the multipole_library input to materials.xml and the "
"OPENMC_MULTIPOLE_LIBRARY environment variable will take "
"precendent over setting multipole_library in settings.xml.");
path_multipole = get_node_value(*root, "multipole_library");
path_multipole = get_node_value(root, "multipole_library");
}
if (!ends_with(path_multipole, "/")) {
path_multipole += "/";
}
}
// Check for output options
if (check_for_node(*root, "output")) {
if (!run_CE) {
// Scattering Treatments
if (check_for_node(root, "max_order")) {
max_order = std::stoi(get_node_value(root, "max_order"));
} else {
// Set to default of largest int - 1, which means to use whatever is
// contained in library. This is largest int - 1 because for legendre
// scattering, a value of 1 is added to the order; adding 1 to the largest
// int gets you the largest negative integer, which is not what we want.
max_order = std::numeric_limits<int>::max() - 1;
}
}
// Check for a trigger node and get trigger information
if (check_for_node(root, "trigger")) {
xml_node node_trigger = root.child("trigger");
// Check if trigger(s) are to be turned on
trigger_on = get_node_value_bool(node_trigger, "active");
if (trigger_on) {
if (check_for_node(node_trigger, "max_batches") ){
n_max_batches = std::stoi(get_node_value(node_trigger, "max_batches"));
} else {
fatal_error("<max_batches> must be specified with triggers");
}
// Get the batch interval to check triggers
if (!check_for_node(node_trigger, "batch_interval")){
trigger_predict = true;
} else {
trigger_batch_interval = std::stoi(get_node_value(node_trigger, "batch_interval"));
if (trigger_batch_interval <= 0) {
fatal_error("Trigger batch interval must be greater than zero");
}
}
}
}
// Check run mode if it hasn't been set from the command line
xml_node node_mode;
if (run_mode == C_NONE) {
if (check_for_node(root, "run_mode")) {
std::string temp_str = get_node_value(root, "run_mode", true, true);
if (temp_str == "eigenvalue") {
run_mode = RUN_MODE_EIGENVALUE;
} else if (temp_str == "fixed source") {
run_mode = RUN_MODE_FIXEDSOURCE;
} else if (temp_str == "plot") {
run_mode = RUN_MODE_PLOTTING;
} else if (temp_str == "particle restart") {
run_mode = RUN_MODE_PARTICLE;
} else if (temp_str == "volume") {
run_mode = RUN_MODE_VOLUME;
} else {
fatal_error("Unrecognized run mode: " + temp_str);
}
// Assume XML specifies <particles>, <batches>, etc. directly
node_mode = root;
} else {
warning("<run_mode> should be specified.");
// Make sure that either eigenvalue or fixed source was specified
node_mode = root.child("eigenvalue");
if (node_mode) {
run_mode = RUN_MODE_EIGENVALUE;
} else {
node_mode = root.child("fixed_source");
if (node_mode) {
run_mode = RUN_MODE_FIXEDSOURCE;
} else {
fatal_error("<eigenvalue> or <fixed_source> not specified.");
}
}
}
}
if (run_mode == RUN_MODE_EIGENVALUE || run_mode == RUN_MODE_FIXEDSOURCE) {
// Read run parameters
get_run_parameters(node_mode);
// Check number of active batches, inactive batches, and particles
if (n_batches <= n_inactive) {
fatal_error("Number of active batches must be greater than zero.");
} else if (n_inactive < 0) {
fatal_error("Number of inactive batches must be non-negative.");
} else if (n_particles <= 0) {
fatal_error("Number of particles must be greater than zero.");
}
}
// Copy random number seed if specified
if (check_for_node(root, "seed")) {
auto seed = std::stoll(get_node_value(root, "seed"));
openmc_set_seed(seed);
}
// Check for electron treatment
if (check_for_node(root, "electron_treatment")) {
auto temp_str = get_node_value(root, "electron_treatment", true, true);
if (temp_str == "led") {
electron_treatment = ELECTRON_LED;
} else if (temp_str == "ttb") {
electron_treatment = ELECTRON_TTB;
} else {
fatal_error("Unrecognized electron treatment: " + temp_str + ".");
}
}
// Check for photon transport
if (check_for_node(root, "photon_transport")) {
photon_transport = get_node_value_bool(root, "photon_transport");
if (!run_CE && photon_transport) {
fatal_error("Photon transport is not currently supported in "
"multigroup mode");
}
}
// Number of bins for logarithmic grid
if (check_for_node(root, "log_grid_bins")) {
n_log_bins = std::stoi(get_node_value(root, "log_grid_bins"));
if (n_log_bins < 1) {
fatal_error("Number of bins for logarithmic grid must be greater "
"than zero.");
}
}
// Number of OpenMP threads
if (check_for_node(root, "threads")) {
#ifdef _OPENMP
if (openmc_n_threads == 0) {
openmc_n_threads = std::stoi(get_node_value(root, "threads"));
if (openmc_n_threads < 1) {
std::stringstream msg;
msg << "Invalid number of threads: " << openmc_n_threads;
fatal_error(msg);
}
omp_set_num_threads(openmc_n_threads);
}
#else
if (openmc_master) warning("OpenMC was not compiled with OpenMP support; "
"ignoring number of threads.");
#endif
}
// ==========================================================================
// 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));
}
// Check if we want to write out source
if (check_for_node(root, "write_initial_source")) {
write_initial_source = get_node_value_bool(root, "write_initial_source");
}
// Survival biasing
if (check_for_node(root, "survival_biasing")) {
survival_biasing = get_node_value_bool(root, "survival_biasing");
}
// Probability tables
if (check_for_node(root, "ptables")) {
urr_ptables_on = get_node_value_bool(root, "ptables");
}
// Cutoffs
if (check_for_node(root, "cutoff")) {
xml_node node_cutoff = root.child("cutoff");
if (check_for_node(node_cutoff, "weight")) {
weight_cutoff = std::stod(get_node_value(node_cutoff, "weight"));
}
if (check_for_node(node_cutoff, "weight_avg")) {
weight_survive = std::stod(get_node_value(node_cutoff, "weight_avg"));
}
if (check_for_node(node_cutoff, "energy_neutron")) {
energy_cutoff[0] = std::stod(get_node_value(node_cutoff, "energy_neutron"));
} else if (check_for_node(node_cutoff, "energy")) {
warning("The use of an <energy> cutoff is deprecated and should "
"be replaced by <energy_neutron>.");
energy_cutoff[0] = std::stod(get_node_value(node_cutoff, "energy"));
}
if (check_for_node(node_cutoff, "energy_photon")) {
energy_cutoff[1] = std::stod(get_node_value(node_cutoff, "energy_photon"));
}
if (check_for_node(node_cutoff, "energy_electron")) {
energy_cutoff[2] = std::stof(get_node_value(node_cutoff, "energy_electron"));
}
if (check_for_node(node_cutoff, "energy_positron")) {
energy_cutoff[3] = std::stod(get_node_value(node_cutoff, "energy_positron"));
}
}
// Particle trace
if (check_for_node(root, "trace")) {
auto temp = get_node_array<int64_t>(root, "trace");
if (temp.size() != 3) {
fatal_error("Must provide 3 integers for <trace> that specify the "
"batch, generation, and particle number.");
}
trace_batch = temp.at(0);
trace_gen = temp.at(1);
trace_particle = temp.at(2);
}
// Particle tracks
if (check_for_node(root, "track")) {
// Get values and make sure there are three per particle
auto temp = get_node_array<int64_t>(root, "track");
if (temp.size() % 3 != 0) {
fatal_error("Number of integers specified in 'track' is not "
"divisible by 3. Please provide 3 integers per particle to be "
"tracked.");
}
// Reshape into track_identifiers
//allocate(track_identifiers(3, n_tracks/3))
//track_identifiers = reshape(temp_int_array, [3, n_tracks/3])
}
// Read meshes
read_meshes(&root);
// Shannon Entropy mesh
if (check_for_node(root, "entropy_mesh")) {
int temp = std::stoi(get_node_value(root, "entropy_mesh"));
if (mesh_map.find(temp) == mesh_map.end()) {
std::stringstream msg;
msg << "Mesh " << temp << " specified for Shannon entropy does not exist.";
fatal_error(msg);
}
index_entropy_mesh = mesh_map.at(temp);
} else if (check_for_node(root, "entropy")) {
warning("Specifying a Shannon entropy mesh via the <entropy> element "
"is deprecated. Please create a mesh using <mesh> and then reference "
"it by specifying its ID in an <entropy_mesh> element.");
// Read entropy mesh from <entropy>
auto node_entropy = root.child("entropy");
meshes.emplace_back(new RegularMesh{node_entropy});
// Set entropy mesh index
index_entropy_mesh = meshes.size() - 1;
// Assign ID and set mapping
meshes.back()->id_ = 10000;
mesh_map[10000] = index_entropy_mesh;
}
if (index_entropy_mesh >= 0) {
auto& m = *meshes[index_entropy_mesh];
if (m.shape_.dimension() == 0) {
// If the user did not specify how many mesh cells are to be used in
// each direction, we automatically determine an appropriate number of
// cells
int n = std::ceil(std::pow(settings::n_particles / 20.0, 1.0/3.0));
m.shape_ = {n, n, n};
m.n_dimension_ = 3;
// Calculate width
m.width_ = (m.upper_right_ - m.lower_left_) / m.shape_;
}
// Turn on Shannon entropy calculation
settings::entropy_on = true;
}
// Uniform fission source weighting mesh
if (check_for_node(root, "ufs_mesh")) {
auto temp = std::stoi(get_node_value(root, "ufs_mesh"));
if (mesh_map.find(temp) == mesh_map.end()) {
std::stringstream msg;
msg << "Mesh " << temp << " specified for uniform fission site method "
"does not exist.";
fatal_error(msg);
}
index_ufs_mesh = mesh_map.at(temp);
} else if (check_for_node(root, "uniform_fs")) {
warning("Specifying a UFS mesh via the <uniform_fs> element "
"is deprecated. Please create a mesh using <mesh> and then reference "
"it by specifying its ID in a <ufs_mesh> element.");
// Read entropy mesh from <entropy>
auto node_ufs = root.child("uniform_fs");
meshes.emplace_back(new RegularMesh{node_ufs});
// Set entropy mesh index
index_ufs_mesh = meshes.size() - 1;
// Assign ID and set mapping
meshes.back()->id_ = 10001;
mesh_map[10001] = index_entropy_mesh;
}
if (index_ufs_mesh >= 0) {
// Turn on uniform fission source weighting
settings::ufs_on = true;
}
// TODO: Read <state_point>
// Check if the user has specified to write source points
if (check_for_node(root, "source_point")) {
// Get source_point node
xml_node node_sp = root.child("source_point");
// TODO: Read source point batches
// Check if the user has specified to write binary source file
if (check_for_node(node_sp, "separate")) {
source_separate = get_node_value_bool(node_sp, "separate");
}
if (check_for_node(node_sp, "write")) {
source_write = get_node_value_bool(node_sp, "write");
}
if (check_for_node(node_sp, "overwrite_latest")) {
source_latest = get_node_value_bool(node_sp, "overwrite_latest");
source_separate = source_latest;
}
} else {
// If no <source_point> tag was present, by default we keep source bank in
// statepoint file and write it out at statepoints intervals
source_separate = false;
// TODO: add defaults
}
// TODO: Check source points are subset
// Check if the user has specified to not reduce tallies at the end of every
// batch
if (check_for_node(root, "no_reduce")) {
reduce_tallies = get_node_value_bool(root, "no_reduce");
}
// Check if the user has specified to use confidence intervals for
// uncertainties rather than standard deviations
if (check_for_node(root, "confidence_intervals")) {
confidence_intervals = get_node_value_bool(root, "confidence_intervals");
}
// Check for output options
if (check_for_node(root, "output")) {
// Get pointer to output node
pugi::xml_node node_output = root->child("output");
pugi::xml_node node_output = root.child("output");
// Check for summary option
if (check_for_node(node_output, "summary")) {
output_summary = get_node_value_bool(node_output, "summary");
}
// Check for ASCII tallies output option
if (check_for_node(node_output, "tallies")) {
output_tallies = get_node_value_bool(node_output, "tallies");
}
// Set output directory if a path has been specified
if (check_for_node(node_output, "path")) {
@ -73,30 +630,133 @@ void read_settings(pugi::xml_node* root)
}
}
// Get temperature settings
if (check_for_node(*root, "temperature_default")) {
temperature_default = std::stod(get_node_value(*root, "temperature_default"));
// Check for cmfd run
if (check_for_node(root, "run_cmfd")) {
cmfd_run = get_node_value_bool(root, "run_cmfd");
}
if (check_for_node(*root, "temperature_method")) {
auto temp_str = get_node_value(*root, "temperature_method", true, true);
if (temp_str == "nearest") {
// Resonance scattering parameters
if (check_for_node(root, "resonance_scattering")) {
xml_node node_res_scat = root.child("resonance_scattering");
// See if resonance scattering is enabled
if (check_for_node(node_res_scat, "enable")) {
res_scat_on = get_node_value_bool(node_res_scat, "enable");
} else {
res_scat_on = true;
}
// Determine what method is used
if (check_for_node(node_res_scat, "method")) {
auto temp = get_node_value(node_res_scat, "method", true, true);
if (temp == "ares") {
res_scat_method = RES_SCAT_ARES;
} else if (temp == "dbrc") {
res_scat_method = RES_SCAT_DBRC;
} else if (temp == "wcm") {
res_scat_method = RES_SCAT_WCM;
} else {
fatal_error("Unrecognized resonance elastic scattering method: "
+ temp + ".");
}
}
// Minimum energy for resonance scattering
if (check_for_node(node_res_scat, "energy_min")) {
res_scat_energy_min = std::stod(get_node_value(node_res_scat, "energy_min"));
}
if (res_scat_energy_min < 0.0) {
fatal_error("Lower resonance scattering energy bound is negative");
}
// Maximum energy for resonance scattering
if (check_for_node(node_res_scat, "energy_max")) {
res_scat_energy_max = std::stod(get_node_value(node_res_scat, "energy_max"));
}
if (res_scat_energy_max < res_scat_energy_min) {
fatal_error("Upper resonance scattering energy bound is below the "
"lower resonance scattering energy bound.");
}
// TODO: Get resonance scattering nuclides
}
// TODO: Get volume calculations
// Get temperature settings
if (check_for_node(root, "temperature_default")) {
temperature_default = std::stod(get_node_value(root, "temperature_default"));
}
if (check_for_node(root, "temperature_method")) {
auto temp = get_node_value(root, "temperature_method", true, true);
if (temp == "nearest") {
temperature_method = TEMPERATURE_NEAREST;
} else if (temp_str == "interpolation") {
} else if (temp == "interpolation") {
temperature_method = TEMPERATURE_INTERPOLATION;
} else {
fatal_error("Unknown temperature method: " + temp_str);
fatal_error("Unknown temperature method: " + temp);
}
}
if (check_for_node(*root, "temperature_tolerance")) {
temperature_tolerance = std::stod(get_node_value(*root, "temperature_tolerance"));
if (check_for_node(root, "temperature_tolerance")) {
temperature_tolerance = std::stod(get_node_value(root, "temperature_tolerance"));
}
if (check_for_node(*root, "temperature_multipole")) {
temperature_multipole = get_node_value_bool(*root, "temperature_multipole");
if (check_for_node(root, "temperature_multipole")) {
temperature_multipole = get_node_value_bool(root, "temperature_multipole");
}
if (check_for_node(*root, "temperature_range")) {
auto range = get_node_array<double>(*root, "temperature_range");
temperature_range[0] = range[0];
temperature_range[1] = range[1];
if (check_for_node(root, "temperature_range")) {
auto range = get_node_array<double>(root, "temperature_range");
temperature_range[0] = range.at(0);
temperature_range[1] = range.at(1);
}
// Check for tabular_legendre options
if (check_for_node(root, "tabular_legendre")) {
// Get pointer to tabular_legendre node
xml_node node_tab_leg = root.child("tabular_legendre");
// Check for enable option
if (check_for_node(node_tab_leg, "enable")) {
legendre_to_tabular = get_node_value_bool(node_tab_leg, "enable");
}
// Check for the number of points
if (check_for_node(node_tab_leg, "num_points")) {
legendre_to_tabular_points = std::stoi(get_node_value(
node_tab_leg, "num_points"));
if (legendre_to_tabular_points <= 1 && !run_CE) {
fatal_error("The 'num_points' subelement/attribute of the "
"<tabular_legendre> element must contain a value greater than 1");
}
}
}
// Check whether create fission sites
if (run_mode == RUN_MODE_FIXEDSOURCE) {
if (check_for_node(root, "create_fission_neutrons")) {
create_fission_neutrons = get_node_value_bool(root, "create_fission_neutrons");
}
}
// Read remaining settings from Fortran side
read_settings_xml_f(root.internal_object());
}
//==============================================================================
// Fortran compatibility functions
//==============================================================================
extern "C" {
const char* openmc_path_input() {
return settings::path_input.c_str();
}
const char* openmc_path_statepoint() {
return settings::path_statepoint.c_str();
}
const char* openmc_path_sourcepoint() {
return settings::path_sourcepoint.c_str();
}
const char* openmc_path_particle_restart() {
return settings::path_particle_restart.c_str();
}
}

View file

@ -10,8 +10,7 @@ module simulation
use cmfd_execute, only: cmfd_init_batch, cmfd_tally_init, execute_cmfd
use cmfd_header, only: cmfd_on
use constants, only: ZERO
use eigenvalue, only: count_source_for_ufs, calculate_average_keff, &
calculate_generation_keff, shannon_entropy, &
use eigenvalue, only: calculate_average_keff, calculate_generation_keff, &
synchronize_bank, keff_generation, k_sum
#ifdef _OPENMP
use eigenvalue, only: join_bank_from_threads
@ -24,13 +23,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 +50,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
!===============================================================================
@ -222,12 +233,17 @@ contains
subroutine initialize_generation()
interface
subroutine ufs_count_sites() bind(C)
end subroutine
end interface
if (run_mode == MODE_EIGENVALUE) then
! Reset number of fission bank sites
n_bank = 0
! Count source sites if using uniform fission source weighting
if (ufs) call count_source_for_ufs()
if (ufs) call ufs_count_sites()
! Store current value of tracklength k
keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH)
@ -241,7 +257,13 @@ contains
subroutine finalize_generation()
integer(8) :: i
interface
subroutine fill_source_bank_fixedsource() bind(C)
end subroutine
subroutine shannon_entropy() bind(C)
end subroutine
end interface
! Update global tallies with the omp private accumulation variables
!$omp parallel
@ -294,13 +316,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
@ -420,16 +436,14 @@ contains
integer :: i
interface
subroutine openmc_simulation_init_c() bind(C)
end subroutine openmc_simulation_init_c
end interface
err = 0
! 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()
@ -455,13 +469,19 @@ contains
! Allocate array for microscopic cross section cache
allocate(micro_xs(n_nuclides))
allocate(micro_photon_xs(n_elements))
! Allocate array for matching filter bins
allocate(filter_matches(n_filters))
do i = 1, n_filters
filter_matches(i) % ptr = filter_match_pointer(i - 1)
end do
!$omp end parallel
! Reset global variables -- this is done before loading state point (as that
! will potentially populate k_generation and entropy)
current_batch = 0
call k_generation % clear()
call entropy % clear()
call entropy_clear()
need_depletion_rx = .false.
! If this is a restart run, load the state point data and binary source
@ -495,16 +515,6 @@ contains
end if
end if
call openmc_simulation_init_c()
!$omp parallel
! Allocate array for matching filter bins
allocate(filter_matches(n_filters))
do i = 1, n_filters
filter_matches(i) % ptr = filter_match_pointer(i - 1)
end do
!$omp end parallel
! Set flag indicating initialization is done
simulation_initialized = .true.
@ -523,7 +533,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
@ -535,6 +544,9 @@ contains
interface
subroutine openmc_simulation_finalize_c() bind(C)
end subroutine openmc_simulation_finalize_c
subroutine print_overlap_check() bind(C)
end subroutine print_overlap_check
end interface
err = 0
@ -589,12 +601,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
@ -615,8 +621,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,8 @@
#include "openmc/simulation.h"
#include "openmc/capi.h"
#include "openmc/message_passing.h"
#include "openmc/settings.h"
#include "openmc/tallies/tally_filter.h"
// OPENMC_RUN encompasses all the main logic where iterations are performed
@ -21,15 +25,52 @@ int openmc_run()
namespace openmc {
extern "C" void
openmc_simulation_init_c()
//==============================================================================
// Global variables
//==============================================================================
std::vector<int64_t> work_index;
//==============================================================================
// Functions
//==============================================================================
void openmc_simulation_init_c()
{
// Determine how much work each process should do
calculate_work();
// Allocate array for matching filter bins
#pragma omp parallel
{
filter_matches.resize(n_filters);
}
}
void calculate_work()
{
// Determine minimum amount of particles to simulate on each processor
int64_t min_work = settings::n_particles / mpi::n_procs;
// Determine number of processors that have one extra particle
int64_t remainder = settings::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);
}
}
extern "C" void
openmc_simulation_finalize_c()
{

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?
@ -47,13 +47,6 @@ module simulation_header
real(8) :: k_col_tra = ZERO ! sum over batches of k_collision * k_tracklength
real(8) :: k_abs_tra = ZERO ! sum over batches of k_absorption * k_tracklength
! Shannon entropy
type(VectorReal) :: entropy ! shannon entropy at each generation
real(8), allocatable :: entropy_p(:,:) ! % of source sites in each cell
! Uniform fission source weighting
real(8), allocatable :: source_frac(:,:)
! ============================================================================
! PARALLEL PROCESSING VARIABLES
@ -67,21 +60,24 @@ 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)
interface
subroutine entropy_clear() bind(C)
end subroutine
end interface
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,15 +86,12 @@ 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)
call k_generation % clear()
call k_generation % shrink_to_fit()
call entropy % clear()
call entropy % shrink_to_fit()
call entropy_clear()
end subroutine free_memory_simulation
end module simulation_header

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

379
src/source.cpp Normal file
View file

@ -0,0 +1,379 @@
#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;
settings::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
settings::path_source = get_node_value(node, "file", false, true);
// Check if source file exists
if (!file_exists(settings::path_source)) {
std::stringstream msg;
msg << "Source file '" << settings::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 (settings::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 " << settings::path_source << "...";
write_message(msg, 6);
// Open the binary file
hid_t file_id = file_open(settings::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*settings::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 (settings::write_initial_source) {
write_message("Writing out initial source...", 5);
std::string filename = settings::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 (!settings::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 (settings::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()) *
settings::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

@ -20,7 +20,6 @@ module state_point
use endf, only: reaction_name
use error, only: fatal_error, warning, write_message
use hdf5_interface
use mesh_header, only: RegularMesh, meshes, n_meshes
use message_passing
use mgxs_interface
use nuclide_header, only: nuclides
@ -68,7 +67,7 @@ contains
integer :: i_xs
integer, allocatable :: id_array(:)
integer(HID_T) :: file_id
integer(HID_T) :: cmfd_group, tallies_group, tally_group, meshes_group, &
integer(HID_T) :: cmfd_group, tallies_group, tally_group, &
filters_group, filter_group, derivs_group, &
deriv_group, runtime_group
integer(C_INT) :: ignored_err
@ -79,6 +78,17 @@ contains
character(MAX_WORD_LEN, kind=C_CHAR) :: temp_name
logical :: parallel
interface
subroutine meshes_to_hdf5(group) bind(C)
import HID_T
integer(HID_T), value :: group
end subroutine
subroutine entropy_to_hdf5(group) bind(C)
import HID_T
integer(HID_T), value :: group
end subroutine
end interface
err = 0
! Set the filename
@ -163,9 +173,7 @@ contains
call write_dataset(file_id, "generations_per_batch", gen_per_batch)
k = k_generation % size()
call write_dataset(file_id, "k_generation", k_generation % data(1:k))
if (entropy_on) then
call write_dataset(file_id, "entropy", entropy % data(1:k))
end if
call entropy_to_hdf5(file_id)
call write_dataset(file_id, "k_col_abs", k_col_abs)
call write_dataset(file_id, "k_col_tra", k_col_tra)
call write_dataset(file_id, "k_abs_tra", k_abs_tra)
@ -192,26 +200,8 @@ contains
tallies_group = create_group(file_id, "tallies")
! Write number of meshes
meshes_group = create_group(tallies_group, "meshes")
call write_attribute(meshes_group, "n_meshes", n_meshes)
if (n_meshes > 0) then
! Write IDs of meshes
allocate(id_array(n_meshes))
do i = 1, n_meshes
id_array(i) = meshes(i) % id
end do
call write_attribute(meshes_group, "ids", id_array)
deallocate(id_array)
! Write information for meshes
MESH_LOOP: do i = 1, n_meshes
call meshes(i) % to_hdf5(meshes_group)
end do MESH_LOOP
end if
call close_group(meshes_group)
! Write meshes
call meshes_to_hdf5(tallies_group)
! Write information for derivatives.
if (size(tally_derivs) > 0) then
@ -641,6 +631,11 @@ contains
logical :: source_present
character(MAX_WORD_LEN) :: word
interface
subroutine entropy_from_hdf5() bind(C)
end subroutine
end interface
! Write message
call write_message("Loading state point " // trim(path_state_point) &
// "...", 5)
@ -722,10 +717,7 @@ contains
call k_generation % resize(n)
call read_dataset(k_generation % data(1:n), file_id, "k_generation")
if (entropy_on) then
call entropy % resize(n)
call read_dataset(entropy % data(1:n), file_id, "entropy")
end if
call entropy_from_hdf5()
call read_dataset(k_col_abs, file_id, "k_col_abs")
call read_dataset(k_col_tra, file_id, "k_col_tra")
call read_dataset(k_abs_tra, file_id, "k_abs_tra")

View file

@ -10,6 +10,7 @@
#include "openmc/capi.h"
#include "openmc/error.h"
#include "openmc/message_passing.h"
#include "openmc/settings.h"
namespace openmc {
@ -39,7 +40,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank)
#ifdef PHDF5
// Set size of total dataspace for all procs and rank
hsize_t dims[] {static_cast<hsize_t>(n_particles)};
hsize_t dims[] {static_cast<hsize_t>(settings::n_particles)};
hid_t dspace = H5Screate_simple(1, dims, nullptr);
hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace,
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
@ -69,7 +70,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank)
if (openmc_master) {
// Create dataset big enough to hold all source sites
hsize_t dims[] {static_cast<hsize_t>(n_particles)};
hsize_t dims[] {static_cast<hsize_t>(settings::n_particles)};
hid_t dspace = H5Screate_simple(1, dims, nullptr);
hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace,
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);

View file

@ -35,6 +35,8 @@ module stl_vector
!
! size -- Returns the number of elements in the vector.
use, intrinsic :: ISO_C_BINDING
implicit none
private
@ -521,4 +523,28 @@ contains
size = this%size_
end function size_char
!===============================================================================
! Procedures to be called from C++
!===============================================================================
subroutine vector_int_push_back(ptr, val) bind(C)
type(C_PTR), value :: ptr
integer(C_INT), value :: val
type(VectorInt), pointer :: vec
call C_F_POINTER(ptr, vec)
call vec % push_back(val)
end subroutine
subroutine vector_real_push_back(ptr, val) bind(C)
type(C_PTR), value :: ptr
real(C_DOUBLE), value :: val
type(VectorReal), pointer :: vec
call C_F_POINTER(ptr, vec)
call vec % push_back(val)
end subroutine
end module stl_vector

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

@ -6,7 +6,6 @@ module summary
use geometry_header
use hdf5_interface
use material_header, only: Material, n_materials, openmc_material_get_volume
use mesh_header, only: RegularMesh
use message_passing
use mgxs_interface
use nuclide_header
@ -29,6 +28,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 +160,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();
}

Some files were not shown because too many files have changed in this diff Show more