Merge branch 'develop' into photon-production-fix

This commit is contained in:
amandalund 2018-09-11 08:52:23 -05:00
commit 7d163b256e
96 changed files with 4264 additions and 3912 deletions

View file

@ -312,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
@ -380,11 +379,13 @@ 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
@ -394,6 +395,7 @@ add_library(libopenmc SHARED
src/lattice.cpp
src/material.cpp
src/math_functions.cpp
src/mesh.cpp
src/message_passing.cpp
src/mgxs.cpp
src/mgxs_interface.cpp

View file

@ -37,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);
@ -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);
@ -135,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;
@ -152,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)
@ -164,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

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

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

@ -14,6 +14,7 @@
#include "xtensor/xarray.hpp"
#include "openmc/position.h"
#include "openmc/error.h"
namespace openmc {
@ -46,39 +47,6 @@ 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);
@ -236,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>
@ -294,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
//==============================================================================
@ -317,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
//==============================================================================
@ -347,6 +357,15 @@ write_dataset(hid_t obj_id, const char* name, const std::vector<T>& 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 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)
{

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

@ -22,5 +22,7 @@ void header(const char* msg, int level);
extern "C" void print_overlap_check();
extern "C" void title();
} // namespace openmc
#endif // OPENMC_OUTPUT_H

View file

@ -161,7 +161,7 @@ extern "C" {
{mark_as_lost(message.str());}
//! create a particle restart HDF5 file
void write_restart();
void write_restart() const;
};

View file

@ -1,6 +1,7 @@
#ifndef OPENMC_POSITION_H
#define OPENMC_POSITION_H
#include <cmath>
#include <vector>
namespace openmc {
@ -46,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.;

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,39 +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_photon_transport;
extern "C" bool openmc_restart_run;
extern "C" bool openmc_run_CE;
extern "C" int openmc_verbosity;
extern "C" bool openmc_write_all_tracks;
extern "C" bool openmc_write_initial_source;
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,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

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

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

@ -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
@ -132,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
@ -149,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.
@ -305,6 +304,9 @@ contains
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()

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

@ -620,7 +620,7 @@ read_cells(pugi::xml_node* node)
universes.shrink_to_fit();
// Allocate the cell overlap count if necessary.
if (openmc_check_overlaps) overlap_check_count.resize(n_cells, 0);
if (settings::check_overlaps) overlap_check_count.resize(n_cells, 0);
}
//==============================================================================

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

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

@ -7,6 +7,7 @@
#include "openmc/constants.h"
#include "openmc/error.h"
#include "openmc/lattice.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
#include "openmc/surface.h"
@ -90,7 +91,7 @@ find_cell(Particle* p, int search_surf) {
if (cells[i_cell]->contains(r, u, surf)) {
p->coord[p->n_coord-1].cell = i_cell;
if (openmc_verbosity >= 10 || openmc_trace) {
if (settings::verbosity >= 10 || openmc_trace) {
std::stringstream msg;
msg << " Entering cell " << cells[i_cell]->id_;
write_message(msg, 1);
@ -243,6 +244,8 @@ find_cell(Particle* p, int search_surf) {
return find_cell(p, 0);
}
}
return found;
}
//==============================================================================
@ -252,7 +255,7 @@ cross_lattice(Particle* p, int lattice_translation[3])
{
Lattice& lat {*lattices[p->coord[p->n_coord-1].lattice-1]};
if (openmc_verbosity >= 10 || openmc_trace) {
if (settings::verbosity >= 10 || openmc_trace) {
std::stringstream msg;
msg << " Crossing lattice " << lat.id_ << ". Current position ("
<< p->coord[p->n_coord-1].lattice_x << ","

View file

@ -100,7 +100,8 @@ assign_temperatures()
c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * T));
} else {
// Use the global default temperature.
c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * temperature_default));
c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN *
settings::temperature_default));
}
}
}

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

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

@ -10,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();
@ -59,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;
@ -67,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;
@ -86,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[])
{
@ -108,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;
@ -127,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 << ".";
@ -140,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")) {
@ -156,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;
@ -200,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';
@ -213,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;
}

View file

@ -4,7 +4,7 @@ module input_xml
use algorithm, only: find
use cmfd_input, only: configure_cmfd
use cmfd_header, only: cmfd_mesh
use cmfd_header, only: index_cmfd_mesh
use constants
use dict_header, only: DictIntInt, DictCharInt, DictEntryCI
use endf, only: reaction_name
@ -22,7 +22,7 @@ module input_xml
use output, only: title, header, print_plot
use photon_header
use plot_header
use random_lcg, only: prn, openmc_set_seed
use random_lcg, only: prn
use surface_header
use set_header, only: SetChar
use settings
@ -78,10 +78,8 @@ module input_xml
type(C_PTR) :: node_ptr
end subroutine read_lattices
subroutine read_settings(node_ptr) bind(C)
import C_PTR
type(C_PTR) :: node_ptr
end subroutine read_settings
subroutine read_settings_xml() bind(C)
end subroutine read_settings_xml
subroutine read_materials(node_ptr) bind(C)
import C_PTR
@ -183,7 +181,7 @@ contains
! Assign temperatures to cells that don't have temperatures already assigned
call assign_temperatures()
! Determine desired txemperatures for each nuclide and S(a,b) table
! Determine desired temperatures for each nuclide and S(a,b) table
call get_temperatures(nuc_temps, sab_temps)
! Check to make sure there are not too many nested coordinate levels in the
@ -202,311 +200,25 @@ contains
! for errors and placing properly-formatted data in the right data structures
!===============================================================================
subroutine read_settings_xml()
subroutine read_settings_xml_f(root_ptr) bind(C)
type(C_PTR), value :: root_ptr
character(MAX_LINE_LEN) :: temp_str
integer :: i
integer :: n
integer :: temp_int
integer :: temp_int_array3(3)
integer(C_INT32_T) :: i_start, i_end
integer(C_INT64_T) :: seed
integer(C_INT) :: err
integer, allocatable :: temp_int_array(:)
integer :: n_tracks
logical :: file_exists
character(MAX_LINE_LEN) :: filename
type(XMLDocument) :: doc
type(XMLNode) :: root
type(XMLNode) :: node_mode
type(XMLNode) :: node_cutoff
type(XMLNode) :: node_entropy
type(XMLNode) :: node_ufs
type(XMLNode) :: node_sp
type(XMLNode) :: node_output
type(XMLNode) :: node_res_scat
type(XMLNode) :: node_trigger
type(XMLNode) :: node_vol
type(XMLNode) :: node_tab_leg
type(XMLNode), allocatable :: node_mesh_list(:)
type(XMLNode), allocatable :: node_vol_list(:)
! Check if settings.xml exists
filename = trim(path_input) // "settings.xml"
inquire(FILE=filename, EXIST=file_exists)
if (.not. file_exists) then
if (run_mode /= MODE_PLOTTING) then
call fatal_error("Settings XML file '" // trim(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.")
else
! The settings.xml file is optional if we just want to make a plot.
return
end if
end if
! Get proper XMLNode type given pointer
root % ptr = root_ptr
! Parse settings.xml file
call doc % load_file(filename)
root = doc % document_element()
! Read settings from C++ side
call read_settings(root % ptr)
! Verbosity
if (check_for_node(root, "verbosity")) then
call get_node_value(root, "verbosity", verbosity)
end if
! 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 (master) then
if (verbosity >= 2) call title()
end if
call 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")) then
call get_node_value(root, "energy_mode", temp_str)
temp_str = trim(to_lower(temp_str))
if (temp_str == "mg" .or. temp_str == "multi-group") then
run_CE = .false.
else if (temp_str == "ce" .or. temp_str == "continuous-energy") then
run_CE = .true.
end if
end if
! Look for deprecated cross_sections.xml file in settings.xml
if (check_for_node(root, "cross_sections")) then
call 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.")
call get_node_value(root, "cross_sections", path_cross_sections)
end if
! Look for deprecated windowed_multipole file in settings.xml
if (run_mode /= MODE_PLOTTING) then
if (check_for_node(root, "multipole_library")) then
call 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.")
call get_node_value(root, "multipole_library", path_multipole)
end if
if (.not. ends_with(path_multipole, "/")) &
path_multipole = trim(path_multipole) // "/"
end if
if (.not. run_CE) then
! Scattering Treatments
if (check_for_node(root, "max_order")) then
call get_node_value(root, "max_order", 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 huge(0) gets you the largest
! negative integer, which is not what we want.
max_order = huge(0) - 1
end if
else
max_order = 0
end if
! Check for a trigger node and get trigger information
if (check_for_node(root, "trigger")) then
node_trigger = root % child("trigger")
! Check if trigger(s) are to be turned on
call get_node_value(node_trigger, "active", trigger_on)
if (trigger_on) then
if (check_for_node(node_trigger, "max_batches") )then
call get_node_value(node_trigger, "max_batches", n_max_batches)
else
call fatal_error("The max_batches must be specified with triggers")
end if
! Get the batch interval to check triggers
if (.not. check_for_node(node_trigger, "batch_interval"))then
pred_batches = .true.
else
call get_node_value(node_trigger, "batch_interval", temp_int)
n_batch_interval = temp_int
if (n_batch_interval <= 0) then
call fatal_error("The batch interval must be greater than zero")
end if
end if
end if
end if
! Check run mode if it hasn't been set from the command line
if (run_mode == NONE) then
if (check_for_node(root, "run_mode")) then
call get_node_value(root, "run_mode", temp_str)
select case (to_lower(temp_str))
case ("eigenvalue")
run_mode = MODE_EIGENVALUE
case ("fixed source")
run_mode = MODE_FIXEDSOURCE
case ("plot")
run_mode = MODE_PLOTTING
case ("particle restart")
run_mode = MODE_PARTICLE
case ("volume")
run_mode = MODE_VOLUME
case default
call fatal_error("Unrecognized run mode: " // &
trim(temp_str) // ".")
end select
! Assume XML specifics <particles>, <batches>, etc. directly
node_mode = root
else
call warning("<run_mode> should be specified.")
! Make sure that either eigenvalue or fixed source was specified
node_mode = root % child("eigenvalue")
if (node_mode % associated()) then
if (run_mode == NONE) run_mode = MODE_EIGENVALUE
else
node_mode = root % child("fixed_source")
if (node_mode % associated()) then
if (run_mode == NONE) run_mode = MODE_FIXEDSOURCE
else
call fatal_error("<eigenvalue> or <fixed_source> not specified.")
end if
end if
end if
end if
if (run_mode == MODE_EIGENVALUE .or. run_mode == MODE_FIXEDSOURCE) then
! Read run parameters
call get_run_parameters(node_mode)
! Check number of active batches, inactive batches, and particles
if (n_batches <= n_inactive) then
call fatal_error("Number of active batches must be greater than zero.")
elseif (n_inactive < 0) then
call fatal_error("Number of inactive batches must be non-negative.")
elseif (n_particles <= 0) then
call fatal_error("Number of particles must be greater than zero.")
end if
end if
! Copy random number seed if specified
if (check_for_node(root, "seed")) then
call get_node_value(root, "seed", seed)
call openmc_set_seed(seed)
end if
! Check for electron treatment
if (check_for_node(root, "electron_treatment")) then
call get_node_value(root, "electron_treatment", temp_str)
select case (to_lower(temp_str))
case ("led")
electron_treatment = ELECTRON_LED
case ("ttb")
electron_treatment = ELECTRON_TTB
case default
call fatal_error("Unrecognized electron treatment: " // &
trim(temp_str) // ".")
end select
end if
! Check for photon transport
if (check_for_node(root, "photon_transport")) then
call get_node_value(root, "photon_transport", photon_transport)
if (.not. run_CE .and. photon_transport) then
call fatal_error("Photon transport is not currently supported &
&in Multi-group mode")
end if
end if
! Number of bins for logarithmic grid
if (check_for_node(root, "log_grid_bins")) then
call get_node_value(root, "log_grid_bins", n_log_bins)
if (n_log_bins < 1) then
call fatal_error("Number of bins for logarithmic grid must be &
&greater than zero.")
end if
else
n_log_bins = 8000
end if
! Number of OpenMP threads
if (check_for_node(root, "threads")) then
#ifdef _OPENMP
if (n_threads == NONE) then
call get_node_value(root, "threads", n_threads)
if (n_threads < 1) then
call fatal_error("Invalid number of threads: " // to_str(n_threads))
end if
call omp_set_num_threads(n_threads)
end if
#else
if (master) call warning("Ignoring number of threads.")
#endif
end if
! ==========================================================================
! EXTERNAL SOURCE
! Handled on C++ side
! Check if we want to write out source
if (check_for_node(root, "write_initial_source")) then
call get_node_value(root, "write_initial_source", write_initial_source)
end if
! Survival biasing
if (check_for_node(root, "survival_biasing")) then
call get_node_value(root, "survival_biasing", survival_biasing)
end if
! Probability tables
if (check_for_node(root, "ptables")) then
call get_node_value(root, "ptables", urr_ptables_on)
end if
! Cutoffs
if (check_for_node(root, "cutoff")) then
node_cutoff = root % child("cutoff")
if (check_for_node(node_cutoff, "weight")) then
call get_node_value(node_cutoff, "weight", weight_cutoff)
end if
if (check_for_node(node_cutoff, "weight_avg")) then
call get_node_value(node_cutoff, "weight_avg", weight_survive)
end if
if (check_for_node(node_cutoff, "energy_neutron")) then
call get_node_value(node_cutoff, "energy_neutron", energy_cutoff(1))
elseif (check_for_node(node_cutoff, "energy")) then
call warning("The use of an <energy> cutoff is deprecated and should &
&be replaced by <energy_neutron>.")
call get_node_value(node_cutoff, "energy", energy_cutoff(1))
end if
if (check_for_node(node_cutoff, "energy_photon")) then
call get_node_value(node_cutoff, "energy_photon", energy_cutoff(2))
end if
if (check_for_node(node_cutoff, "energy_electron")) then
call get_node_value(node_cutoff, "energy_electron", energy_cutoff(3))
end if
if (check_for_node(node_cutoff, "energy_positron")) then
call get_node_value(node_cutoff, "energy_positron", energy_cutoff(4))
end if
end if
! Particle trace
if (check_for_node(root, "trace")) then
call get_node_array(root, "trace", temp_int_array3)
trace_batch = temp_int_array3(1)
trace_gen = temp_int_array3(2)
trace_particle = int(temp_int_array3(3), 8)
if (run_mode == MODE_EIGENVALUE) then
! Preallocate space for keff and entropy by generation
call k_generation % reserve(n_max_batches*gen_per_batch)
end if
! Particle tracks
@ -528,110 +240,6 @@ contains
track_identifiers = reshape(temp_int_array, [3, n_tracks/3])
end if
! Read meshes
call get_node_list(root, "mesh", node_mesh_list)
! Check for user meshes and allocate
n = size(node_mesh_list)
if (n > 0) then
err = openmc_extend_meshes(n, i_start, i_end)
end if
do i = 1, n
associate (m => meshes(i_start + i - 1))
! Instantiate mesh from XML node
call m % from_xml(node_mesh_list(i))
! Add mesh to dictionary
call mesh_dict % set(m % id, i_start + i - 1)
end associate
end do
! Shannon Entropy mesh
if (check_for_node(root, "entropy_mesh")) then
call get_node_value(root, "entropy_mesh", temp_int)
if (mesh_dict % has(temp_int)) then
index_entropy_mesh = mesh_dict % get(temp_int)
else
call fatal_error("Mesh " // to_str(temp_int) // " specified for &
&Shannon entropy does not exist.")
end if
elseif (check_for_node(root, "entropy")) then
call 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.")
! Get pointer to entropy node
node_entropy = root % child("entropy")
err = openmc_extend_meshes(1, index_entropy_mesh)
associate (m => meshes(index_entropy_mesh))
! Assign ID
m % id = 10000
call m % from_xml(node_entropy)
end associate
end if
if (index_entropy_mesh > 0) then
associate(m => meshes(index_entropy_mesh))
if (.not. allocated(m % dimension)) then
! 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
m % n_dimension = 3
allocate(m % dimension(3))
m % dimension = ceiling((n_particles/20)**(ONE/THREE))
! Calculate width
m % width = (m % upper_right - m % lower_left) / m % dimension
end if
! Allocate space for storing number of fission sites in each mesh cell
allocate(entropy_p(1, product(m % dimension)))
end associate
! Turn on Shannon entropy calculation
entropy_on = .true.
end if
! Uniform fission source weighting mesh
if (check_for_node(root, "ufs_mesh")) then
call get_node_value(root, "ufs_mesh", temp_int)
if (mesh_dict % has(temp_int)) then
index_ufs_mesh = mesh_dict % get(temp_int)
else
call fatal_error("Mesh " // to_str(temp_int) // " specified for &
&uniform fission site method does not exist.")
end if
elseif (check_for_node(root, "uniform_fs")) then
call 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.")
! Get pointer to ufs node
node_ufs = root % child("uniform_fs")
err = openmc_extend_meshes(1, index_ufs_mesh)
! Allocate mesh object and coordinates on mesh
associate (m => meshes(index_ufs_mesh))
! Assign ID
m % id = 10001
call m % from_xml(node_ufs)
end associate
end if
if (index_ufs_mesh > 0) then
! Allocate array to store source fraction for UFS
allocate(source_frac(1, product(meshes(index_ufs_mesh) % dimension)))
! Turn on uniform fission source weighting
ufs = .true.
end if
! Check if the user has specified to write state points
if (check_for_node(root, "state_point")) then
@ -693,22 +301,9 @@ contains
call sourcepoint_batch % add(statepoint_batch % get_item(i))
end do
end if
! Check if the user has specified to write binary source file
if (check_for_node(node_sp, "separate")) then
call get_node_value(node_sp, "separate", source_separate)
end if
if (check_for_node(node_sp, "write")) then
call get_node_value(node_sp, "write", source_write)
end if
if (check_for_node(node_sp, "overwrite_latest")) then
call get_node_value(node_sp, "overwrite_latest", source_latest)
source_separate = source_latest
end if
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.
n_source_points = n_state_points
do i = 1, n_state_points
call sourcepoint_batch % add(statepoint_batch % get_item(i))
@ -728,91 +323,10 @@ contains
end do
end if
! Check if the user has specified to not reduce tallies at the end of every
! batch
if (check_for_node(root, "no_reduce")) then
call get_node_value(root, "no_reduce", reduce_tallies)
end if
! Check if the user has specified to use confidence intervals for
! uncertainties rather than standard deviations
if (check_for_node(root, "confidence_intervals")) then
call get_node_value(root, "confidence_intervals", confidence_intervals)
end if
! Check for output options
if (check_for_node(root, "output")) then
! Get pointer to output node
node_output = root % child("output")
! Check for summary option
if (check_for_node(node_output, "summary")) then
call get_node_value(node_output, "summary", output_summary)
end if
! Check for ASCII tallies output option
if (check_for_node(node_output, "tallies")) then
call get_node_value(node_output, "tallies", output_tallies)
end if
! Set output directory if a path has been specified
if (check_for_node(node_output, "path")) then
call get_node_value(node_output, "path", path_output)
if (.not. ends_with(path_output, "/")) &
path_output = trim(path_output) // "/"
end if
end if
! Check for cmfd run
if (check_for_node(root, "run_cmfd")) then
call get_node_value(root, "run_cmfd", cmfd_run)
end if
! Resonance scattering parameters
if (check_for_node(root, "resonance_scattering")) then
node_res_scat = root % child("resonance_scattering")
! See if resonance scattering is enabled
if (check_for_node(node_res_scat, "enable")) then
call get_node_value(node_res_scat, "enable", res_scat_on)
else
res_scat_on = .true.
end if
! Determine what method is used
if (check_for_node(node_res_scat, "method")) then
call get_node_value(node_res_scat, "method", temp_str)
select case(to_lower(temp_str))
case ('ares')
res_scat_method = RES_SCAT_ARES
case ('dbrc')
res_scat_method = RES_SCAT_DBRC
case ('wcm')
res_scat_method = RES_SCAT_WCM
case default
call fatal_error("Unrecognized resonance elastic scattering method: " &
// trim(temp_str) // ".")
end select
end if
! Minimum energy for resonance scattering
if (check_for_node(node_res_scat, "energy_min")) then
call get_node_value(node_res_scat, "energy_min", res_scat_energy_min)
end if
if (res_scat_energy_min < ZERO) then
call fatal_error("Lower resonance scattering energy bound is negative")
end if
! Maximum energy for resonance scattering
if (check_for_node(node_res_scat, "energy_max")) then
call get_node_value(node_res_scat, "energy_max", res_scat_energy_max)
end if
if (res_scat_energy_max < res_scat_energy_min) then
call fatal_error("Upper resonance scattering energy bound is below the &
&lower resonance scattering energy bound.")
end if
! Get nuclides that resonance scattering should be applied to
if (check_for_node(node_res_scat, "nuclides")) then
n = node_word_count(node_res_scat, "nuclides")
@ -831,138 +345,7 @@ contains
call volume_calcs(i) % from_xml(node_vol)
end do
! Get temperature settings
if (check_for_node(root, "temperature_default")) then
call get_node_value(root, "temperature_default", temperature_default)
end if
if (check_for_node(root, "temperature_method")) then
call get_node_value(root, "temperature_method", temp_str)
select case (to_lower(temp_str))
case ('nearest')
temperature_method = TEMPERATURE_NEAREST
case ('interpolation')
temperature_method = TEMPERATURE_INTERPOLATION
case default
call fatal_error("Unknown temperature method: " // trim(temp_str))
end select
end if
if (check_for_node(root, "temperature_tolerance")) then
call get_node_value(root, "temperature_tolerance", temperature_tolerance)
end if
if (check_for_node(root, "temperature_multipole")) then
call get_node_value(root, "temperature_multipole", temperature_multipole)
end if
if (check_for_node(root, "temperature_range")) then
call get_node_array(root, "temperature_range", temperature_range)
end if
! Check for tabular_legendre options
if (check_for_node(root, "tabular_legendre")) then
! Get pointer to tabular_legendre node
node_tab_leg = root % child("tabular_legendre")
! Check for enable option
if (check_for_node(node_tab_leg, "enable")) then
call get_node_value(node_tab_leg, "enable", legendre_to_tabular)
end if
! Check for the number of points
if (check_for_node(node_tab_leg, "num_points")) then
call get_node_value(node_tab_leg, "num_points", &
legendre_to_tabular_points)
if (legendre_to_tabular_points <= 1 .and. (.not. run_CE)) then
call fatal_error("The 'num_points' subelement/attribute of the &
&'tabular_legendre' element must contain a value greater than 1")
end if
end if
end if
! Check whether create fission sites
if (run_mode == MODE_FIXEDSOURCE) then
if (check_for_node(root, "create_fission_neutrons")) then
call get_node_value(root, "create_fission_neutrons", &
create_fission_neutrons)
end if
end if
! Close settings XML file
call doc % clear()
end subroutine read_settings_xml
!===============================================================================
! GET_RUN_PARAMETERS
!===============================================================================
subroutine get_run_parameters(node_base)
type(XMLNode), intent(in) :: node_base
character(MAX_LINE_LEN) :: temp_str
type(XMLNode) :: node_keff_trigger
! Check number of particles
if (.not. check_for_node(node_base, "particles")) then
call fatal_error("Need to specify number of particles.")
end if
! Get number of particles if it wasn't specified as a command-line argument
if (n_particles == 0) then
call get_node_value(node_base, "particles", n_particles)
end if
! Get number of basic batches
call get_node_value(node_base, "batches", n_batches)
if (.not. trigger_on) then
n_max_batches = n_batches
end if
n_inactive = 0
gen_per_batch = 1
! Get number of inactive batches
if (run_mode == MODE_EIGENVALUE) then
call get_node_value(node_base, "inactive", n_inactive)
if (check_for_node(node_base, "generations_per_batch")) then
call get_node_value(node_base, "generations_per_batch", gen_per_batch)
end if
! Preallocate space for keff and entropy by generation
call k_generation % reserve(n_max_batches*gen_per_batch)
call entropy % reserve(n_max_batches*gen_per_batch)
! Get the trigger information for keff
if (check_for_node(node_base, "keff_trigger")) then
node_keff_trigger = node_base % child("keff_trigger")
if (check_for_node(node_keff_trigger, "type")) then
call get_node_value(node_keff_trigger, "type", temp_str)
temp_str = trim(to_lower(temp_str))
select case (temp_str)
case ('std_dev')
keff_trigger % trigger_type = STANDARD_DEVIATION
case ('variance')
keff_trigger % trigger_type = VARIANCE
case ('rel_err')
keff_trigger % trigger_type = RELATIVE_ERROR
case default
call fatal_error("Unrecognized keff trigger type " // temp_str)
end select
else
call fatal_error("Specify keff trigger type in settings XML")
end if
if (check_for_node(node_keff_trigger, "threshold")) then
call get_node_value(node_keff_trigger, "threshold", &
keff_trigger % threshold)
else
call fatal_error("Specify keff trigger threshold in settings XML")
end if
end if
end if
end subroutine get_run_parameters
end subroutine read_settings_xml_f
!===============================================================================
! READ_GEOMETRY_XML reads data from a geometry.xml file and parses it, checking
@ -1773,13 +1156,11 @@ contains
character(MAX_WORD_LEN), allocatable :: sarray(:)
type(DictCharInt) :: trigger_scores
type(TallyFilterContainer), pointer :: f
type(RegularMesh), pointer :: m
type(XMLDocument) :: doc
type(XMLNode) :: root
type(XMLNode) :: node_tal
type(XMLNode) :: node_filt
type(XMLNode) :: node_trigger
type(XMLNode), allocatable :: node_mesh_list(:)
type(XMLNode), allocatable :: node_tal_list(:)
type(XMLNode), allocatable :: node_filt_list(:)
type(XMLNode), allocatable :: node_trigger_list(:)
@ -1810,9 +1191,6 @@ contains
! ==========================================================================
! DETERMINE SIZE OF ARRAYS AND ALLOCATE
! Get pointer list to XML <mesh>
call get_node_list(root, "mesh", node_mesh_list)
! Get pointer list to XML <filter>
call get_node_list(root, "filter", node_filt_list)
@ -1828,20 +1206,7 @@ contains
! READ MESH DATA
! Check for user meshes and allocate
n = size(node_mesh_list)
if (n > 0) then
err = openmc_extend_meshes(n, i_start, i_end)
end if
do i = 1, n
m => meshes(i_start + i - 1)
! Instantiate mesh from XML node
call m % from_xml(node_mesh_list(i))
! Add mesh to dictionary
call mesh_dict % set(m % id, i_start + i - 1)
end do
call read_meshes(root % ptr)
! We only need the mesh info for plotting
if (run_mode == MODE_PLOTTING) then
@ -2683,6 +2048,7 @@ contains
integer :: i, j
integer :: n_cols, col_id, n_comp, n_masks, n_meshlines
integer :: meshid
integer(C_INT) :: err, idx
integer, allocatable :: iarray(:)
logical :: file_exists ! does plots.xml file exist?
character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml
@ -3005,7 +2371,7 @@ contains
// trim(to_str(pl % id)))
end if
pl % meshlines_mesh => meshes(index_ufs_mesh)
pl % index_meshlines_mesh = index_ufs_mesh
case ('cmfd')
@ -3014,7 +2380,7 @@ contains
&meshlines on plot " // trim(to_str(pl % id)))
end if
pl % meshlines_mesh => cmfd_mesh
pl % index_meshlines_mesh = index_cmfd_mesh
case ('entropy')
@ -3023,7 +2389,7 @@ contains
// trim(to_str(pl % id)))
end if
pl % meshlines_mesh => meshes(index_entropy_mesh)
pl % index_meshlines_mesh = index_entropy_mesh
case ('tally')
@ -3036,17 +2402,13 @@ contains
end if
! Check if the specified tally mesh exists
if (mesh_dict % has(meshid)) then
pl % meshlines_mesh => meshes(mesh_dict % get(meshid))
if (meshes(meshid) % type /= MESH_REGULAR) then
call fatal_error("Non-rectangular mesh specified in &
&meshlines for plot " // trim(to_str(pl % id)))
end if
else
err = openmc_get_mesh_index(meshid, idx)
if (err /= 0) then
call fatal_error("Could not find mesh " &
// trim(to_str(meshid)) // " specified in meshlines for &
&plot " // trim(to_str(pl % id)))
end if
pl % index_meshlines_mesh = idx
case default
call fatal_error("Invalid type for meshlines on plot " &

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

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

976
src/mesh.cpp Normal file
View file

@ -0,0 +1,976 @@
#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/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" {
// Declaration of Fortran procedures
void vector_int_push_back(void* ptr, int value);
void vector_real_push_back(void* ptr, double value);
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, void* match_bins,
void* match_weights)
{
// Get bins crossed
std::vector<int> bins;
std::vector<double> lengths;
m->bins_crossed(p, bins, lengths);
// Call bindings for VectorInt and VectorReal on Fortran side
for (int i = 0; i < bins.size(); ++i) {
vector_int_push_back(match_bins, bins[i]);
vector_real_push_back(match_weights, lengths[i]);
}
}
void mesh_surface_bins_crossed(RegularMesh* m, const Particle* p,
void* match_bins, void* match_weights)
{
// Get surface bins crossed
std::vector<int> bins;
m->surface_bins_crossed(p, bins);
// Call bindings for VectorInt and VectorReal
for (auto b : bins) {
vector_int_push_back(match_bins, b);
vector_real_push_back(match_weights, 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

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

View file

@ -601,7 +601,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
@ -336,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') &
@ -370,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
@ -653,6 +660,10 @@ contains
if (n_tallies == 0) return
allocate(matches(n_filters))
do i = 1, n_filters
allocate(matches(i) % bins)
allocate(matches(i) % weights)
end do
! Initialize names for scores
score_names(abs(SCORE_FLUX)) = "Flux"
@ -848,6 +859,11 @@ contains
close(UNIT=unit_tally)
do i = 1, n_filters
deallocate(matches(i) % bins)
deallocate(matches(i) % weights)
end do
end subroutine write_tallies
!===============================================================================

View file

@ -34,7 +34,7 @@ header(const char* msg, int level) {
for (int i = 0; i < n_suffix; i++) out << '=';
// Print header based on verbosity level.
if (openmc_verbosity >= level) {
if (settings::verbosity >= level) {
std::cout << out.str() << std::endl << std::endl;
}
}

View file

@ -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
@ -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,106 +9,103 @@ module settings
! ============================================================================
! ENERGY TREATMENT RELATED VARIABLES
logical(C_BOOL), bind(C, name='openmc_run_CE') :: 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(C_DOUBLE) :: 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(C_BOOL), bind(C, name='openmc_photon_transport') :: 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(C_BOOL), bind(C, name='openmc_write_initial_source') :: &
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
@ -127,21 +124,21 @@ module settings
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,11 +1,22 @@
#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"
@ -16,98 +27,380 @@ 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;
}
}
// Get pointer to output node
pugi::xml_node node_output = root->child("output");
// Check for a trigger node and get trigger information
if (check_for_node(root, "trigger")) {
xml_node node_trigger = root.child("trigger");
// Set output directory if a path has been specified
if (check_for_node(node_output, "path")) {
path_output = get_node_value(node_output, "path");
if (!ends_with(path_output, "/")) {
path_output += "/";
// 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");
}
}
}
}
// 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_str = get_node_value(*root, "temperature_method", true, true);
if (temp_str == "nearest") {
temperature_method = TEMPERATURE_NEAREST;
} else if (temp_str == "interpolation") {
temperature_method = TEMPERATURE_INTERPOLATION;
// 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 {
fatal_error("Unknown temperature method: " + temp_str);
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 (check_for_node(*root, "temperature_tolerance")) {
temperature_tolerance = std::stod(get_node_value(*root, "temperature_tolerance"));
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.");
}
}
if (check_for_node(*root, "temperature_multipole")) {
temperature_multipole = get_node_value_bool(*root, "temperature_multipole");
// 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);
}
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];
// 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")) {
for (pugi::xml_node node : root.children("source")) {
external_sources.emplace_back(node);
}
@ -120,6 +413,351 @@ void read_settings(pugi::xml_node* root)
};
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");
// 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")) {
path_output = get_node_value(node_output, "path");
if (!ends_with(path_output, "/")) {
path_output += "/";
}
}
}
// Check for cmfd run
if (check_for_node(root, "run_cmfd")) {
cmfd_run = get_node_value_bool(root, "run_cmfd");
}
// 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 == "interpolation") {
temperature_method = TEMPERATURE_INTERPOLATION;
} else {
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_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.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();
}
}
} // namespace openmc

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
@ -234,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)
@ -256,6 +260,9 @@ contains
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
@ -465,13 +472,17 @@ contains
! Allocate array for matching filter bins
allocate(filter_matches(n_filters))
do i = 1, n_filters
allocate(filter_matches(i) % bins)
allocate(filter_matches(i) % weights)
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
@ -550,6 +561,10 @@ contains
deallocate(materials(i) % mat_nuclide_index)
end do
!$omp parallel
do i = 1, size(filter_matches)
deallocate(filter_matches(i) % bins)
deallocate(filter_matches(i) % weights)
end do
deallocate(micro_xs, micro_photon_xs, filter_matches)
!$omp end parallel

View file

@ -2,6 +2,7 @@
#include "openmc/capi.h"
#include "openmc/message_passing.h"
#include "openmc/settings.h"
// OPENMC_RUN encompasses all the main logic where iterations are performed
// over the batches, generations, and histories in a fixed source or k-eigenvalue
@ -41,10 +42,10 @@ void openmc_simulation_init_c()
void calculate_work()
{
// Determine minimum amount of particles to simulate on each processor
int64_t min_work = n_particles/mpi::n_procs;
int64_t min_work = settings::n_particles / mpi::n_procs;
// Determine number of processors that have one extra particle
int64_t remainder = n_particles % mpi::n_procs;
int64_t remainder = settings::n_particles % mpi::n_procs;
int64_t i_bank = 0;
work_index.reserve(mpi::n_procs);

View file

@ -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
@ -71,6 +64,12 @@ module simulation_header
!$omp threadprivate(trace, thread_id, current_work)
interface
subroutine entropy_clear() bind(C)
end subroutine
end interface
contains
!===============================================================================
@ -87,14 +86,12 @@ contains
!===============================================================================
subroutine free_memory_simulation()
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

@ -45,7 +45,7 @@ SourceDistribution::SourceDistribution(pugi::xml_node node)
particle_ = ParticleType::neutron;
} else if (temp_str == "photon") {
particle_ = ParticleType::photon;
openmc_photon_transport = true;
settings::photon_transport = true;
} else {
fatal_error(std::string("Unknown source particle type: ") + temp_str);
}
@ -59,12 +59,12 @@ SourceDistribution::SourceDistribution(pugi::xml_node node)
// Check for external source file
if (check_for_node(node, "file")) {
// Copy path of source file
path_source = get_node_value(node, "file", false, true);
settings::path_source = get_node_value(node, "file", false, true);
// Check if source file exists
if (!file_exists(path_source)) {
if (!file_exists(settings::path_source)) {
std::stringstream msg;
msg << "Source file '" << path_source << "' does not exist.";
msg << "Source file '" << settings::path_source << "' does not exist.";
fatal_error(msg);
}
@ -243,16 +243,16 @@ void initialize_source()
int64_t n;
openmc_source_bank(&source_bank, &n);
if (path_source != "") {
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 " << path_source << "...";
msg << "Reading source file from " << settings::path_source << "...";
write_message(msg, 6);
// Open the binary file
hid_t file_id = file_open(path_source, 'r', true);
hid_t file_id = file_open(settings::path_source, 'r', true);
// Read the file type
std::string filetype;
@ -273,7 +273,8 @@ void initialize_source()
// Generation source sites from specified distribution in user input
for (int64_t i = 0; i < openmc_work; ++i) {
// initialize random number seed
int64_t id = openmc_total_gen*n_particles + work_index[openmc::mpi::rank] + i + 1;
int64_t id = openmc_total_gen*settings::n_particles +
work_index[openmc::mpi::rank] + i + 1;
set_particle_seed(id);
// sample external source distribution
@ -282,9 +283,9 @@ void initialize_source()
}
// Write out initial source
if (openmc_write_initial_source) {
if (settings::write_initial_source) {
write_message("Writing out initial source...", 5);
std::string filename = path_output + "initial_source.h5";
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);
@ -318,7 +319,7 @@ Bank sample_external_source()
Bank site {external_sources[i].sample()};
// If running in MG, convert site % E to group
if (!openmc_run_CE) {
if (!settings::run_CE) {
// Get pointer to rev_energy_bins array on Fortran side
double* rev_energy_bins = rev_energy_bins_ptr();
@ -357,7 +358,7 @@ extern "C" int overall_generation();
//! Fill source bank at end of generation for fixed source simulations
extern "C" void fill_source_bank_fixedsource()
{
if (path_source.empty()) {
if (settings::path_source.empty()) {
// Get pointer to source bank
Bank* source_bank;
int64_t n;
@ -365,8 +366,8 @@ extern "C" void fill_source_bank_fixedsource()
for (int64_t i = 0; i < openmc_work; ++i) {
// initialize random number seed
int64_t id = (openmc_total_gen + overall_generation())*n_particles +
work_index[openmc::mpi::rank] + i + 1;
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

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

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

View file

@ -8,7 +8,6 @@ module tally
use error, only: fatal_error
use geometry_header
use math, only: t_percentile
use mesh_header, only: RegularMesh, meshes
use message_passing
use mgxs_interface
use nuclide_header

View file

@ -28,8 +28,8 @@ module tally_filter_header
type, public :: TallyFilterMatch
! Index of the bin and weight being used in the current filter combination
integer :: i_bin
type(VectorInt) :: bins
type(VectorReal) :: weights
type(VectorInt), pointer :: bins
type(VectorReal), pointer :: weights
! Indicates whether all valid bins for this filter have been found
logical :: bins_present = .false.

View file

@ -5,7 +5,7 @@ module tally_filter_mesh
use constants
use dict_header, only: EMPTY
use error
use mesh_header, only: RegularMesh, meshes, n_meshes, mesh_dict
use mesh_header
use hdf5_interface
use particle_header, only: Particle
use string, only: to_str
@ -38,10 +38,11 @@ contains
class(MeshFilter), intent(inout) :: this
type(XMLNode), intent(in) :: node
integer :: i_mesh
integer :: i
integer :: id
integer :: n
integer :: val
integer(C_INT) :: err
type(RegularMesh) :: m
n = node_word_count(node, "bins")
@ -52,19 +53,18 @@ contains
call get_node_value(node, "bins", id)
! Get pointer to mesh
val = mesh_dict % get(id)
if (val /= EMPTY) then
i_mesh = val
else
err = openmc_get_mesh_index(id, this % mesh)
if (err /= 0) then
call fatal_error("Could not find mesh " // trim(to_str(id)) &
// " specified on filter.")
end if
! Determine number of bins
this % n_bins = product(meshes(i_mesh) % dimension)
! Store the index of the mesh
this % mesh = i_mesh
m = meshes(this % mesh)
this % n_bins = 1
do i = 1, m % n_dimension()
this % n_bins = this % n_bins * m % dimension(i)
end do
end subroutine from_xml
subroutine get_all_bins_mesh(this, p, estimator, match)
@ -73,184 +73,50 @@ contains
integer, intent(in) :: estimator
type(TallyFilterMatch), intent(inout) :: match
integer, parameter :: MAX_SEARCH_ITER = 100 ! Maximum number of times we can
! can loop while trying to find
! the first intersection.
integer :: j ! loop index for direction
integer :: n
integer :: ijk0(3) ! indices of starting coordinates
integer :: ijk1(3) ! indices of ending coordinates
integer :: search_iter ! loop count for intersection search
integer :: bin
real(8) :: uvw(3) ! cosine of angle of particle
real(8) :: xyz0(3) ! starting/intermediate coordinates
real(8) :: xyz1(3) ! ending coordinates of particle
real(8) :: xyz_cross ! coordinates of next boundary
real(8) :: d(3) ! distance to each bounding surface
real(8) :: total_distance ! distance of entire particle track
real(8) :: distance ! distance traveled in mesh cell
logical :: start_in_mesh ! starting coordinates inside mesh?
logical :: end_in_mesh ! ending coordinates inside mesh?
type(RegularMesh), pointer :: m
type(RegularMesh) :: m
type(C_PTR) :: ptr_bins, ptr_weights
interface
subroutine mesh_bins_crossed(m, p, bins, weights) bind(C)
import C_PTR, Particle
type(C_PTR), value :: m
type(Particle), intent(in) :: p
type(C_PTR), value :: bins
type(C_PTR), value :: weights
end subroutine
end interface
! Get a pointer to the mesh.
m => meshes(this % mesh)
n = m % n_dimension
m = meshes(this % mesh)
if (estimator /= ESTIMATOR_TRACKLENGTH) then
! If this is an analog or collision tally, then there can only be one
! valid mesh bin.
call m % get_bin(p % coord(1) % xyz, bin)
if (bin /= NO_BIN_FOUND) then
if (bin >= 0) then
call match % bins % push_back(bin)
call match % weights % push_back(ONE)
end if
return
else
ptr_bins = C_LOC(match % bins)
ptr_weights = C_LOC(match % weights)
call mesh_bins_crossed(m % ptr, p, ptr_bins, ptr_weights)
end if
! A track can span multiple mesh bins so we need to handle a lot of
! intersection logic for tracklength tallies.
! ========================================================================
! 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.
xyz0 = p % last_xyz + TINY_BIT * p % coord(1) % uvw
xyz1 = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
! Determine indices for starting and ending location.
call m % get_indices(xyz0, ijk0(:n), start_in_mesh)
call m % get_indices(xyz1, ijk1(:n), end_in_mesh)
! If this is the first iteration of the filter loop, check if the track
! intersects any part of the mesh.
if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then
if (.not. m % intersects(xyz0, xyz1)) return
end if
! ========================================================================
! Figure out which mesh cell to tally.
! Copy the un-modified coordinates the particle direction.
xyz0 = p % last_xyz
xyz1 = p % coord(1) % xyz
uvw = p % coord(1) % uvw
! Compute the length of the entire track.
total_distance = sqrt(sum((xyz1 - xyz0)**2))
! We are looking for the first valid mesh bin. Check to see if the
! particle starts inside the mesh.
if (any(ijk0(:n) < 1) .or. any(ijk0(:n) > m % dimension)) then
! 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.
search_iter = 0
do while (any(ijk0(:n) < 1) .or. any(ijk0(:n) > m % dimension))
if (search_iter == MAX_SEARCH_ITER) then
call warning("Failed to find a mesh intersection on a tally mesh &
&filter.")
return
end if
do j = 1, n
if (abs(uvw(j)) < FP_PRECISION) then
d(j) = INFINITY
else if (uvw(j) > 0) then
xyz_cross = m % lower_left(j) + ijk0(j) * m % width(j)
d(j) = (xyz_cross - xyz0(j)) / uvw(j)
else
xyz_cross = m % lower_left(j) + (ijk0(j) - 1) * m % width(j)
d(j) = (xyz_cross - xyz0(j)) / uvw(j)
end if
end do
j = minloc(d(:n), 1)
if (uvw(j) > ZERO) then
ijk0(j) = ijk0(j) + 1
else
ijk0(j) = ijk0(j) - 1
end if
search_iter = search_iter + 1
end do
distance = d(j)
xyz0 = xyz0 + distance * uvw
end if
do
! ========================================================================
! Compute the length of the track segment in the appropiate mesh cell and
! return.
if (all(ijk0(:n) == ijk1(:n))) then
! The track ends in this cell. Use the particle end location rather
! than the mesh surface.
distance = sqrt(sum((xyz1 - xyz0)**2))
else
! The track exits this cell. Determine the distance to the closest mesh
! surface.
do j = 1, n
if (abs(uvw(j)) < FP_PRECISION) then
d(j) = INFINITY
else if (uvw(j) > 0) then
xyz_cross = m % lower_left(j) + ijk0(j) * m % width(j)
d(j) = (xyz_cross - xyz0(j)) / uvw(j)
else
xyz_cross = m % lower_left(j) + (ijk0(j) - 1) * m % width(j)
d(j) = (xyz_cross - xyz0(j)) / uvw(j)
end if
end do
j = minloc(d(:n), 1)
distance = d(j)
end if
! Assign the next tally bin and the score.
bin = m % get_bin_from_indices(ijk0(:n))
call match % bins % push_back(bin)
call match % weights % push_back(distance / total_distance)
! Find the next mesh cell that the particle enters.
! If the particle track ends in that bin, then we are done.
if (all(ijk0(:n) == ijk1(:n))) exit
! 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.
xyz0 = xyz0 + distance * uvw
! Increment the indices into the next mesh cell.
if (uvw(j) > ZERO) then
ijk0(j) = ijk0(j) + 1
else
ijk0(j) = ijk0(j) - 1
end if
! If the next indices are invalid, then the track has left the mesh and
! we are done.
if (any(ijk0(:n) < 1) .or. any(ijk0(:n) > m % dimension)) exit
end do
end subroutine get_all_bins_mesh
subroutine to_statepoint_mesh(this, filter_group)
class(MeshFilter), intent(in) :: this
integer(HID_T), intent(in) :: filter_group
type(RegularMesh) :: m
m = meshes(this % mesh)
call write_dataset(filter_group, "type", "mesh")
call write_dataset(filter_group, "n_bins", this % n_bins)
call write_dataset(filter_group, "bins", meshes(this % mesh) % id)
call write_dataset(filter_group, "bins", m % id())
end subroutine to_statepoint_mesh
function text_label_mesh(this, bin) result(label)
@ -259,20 +125,20 @@ contains
character(MAX_LINE_LEN) :: label
integer, allocatable :: ijk(:)
type(RegularMesh) :: m
associate (m => meshes(this % mesh))
allocate(ijk(m % n_dimension))
call m % get_indices_from_bin(bin, ijk)
if (m % n_dimension == 1) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ")"
elseif (m % n_dimension == 2) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ")"
elseif (m % n_dimension == 3) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")"
end if
end associate
m = meshes(this % mesh)
allocate(ijk(m % n_dimension()))
call m % get_indices_from_bin(bin, ijk)
if (m % n_dimension() == 1) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ")"
elseif (m % n_dimension() == 2) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ")"
elseif (m % n_dimension() == 3) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")"
end if
end function text_label_mesh
!===============================================================================
@ -304,18 +170,25 @@ contains
integer(C_INT32_T), value, intent(in) :: index_mesh
integer(C_INT) :: err
type(RegularMesh) :: m
integer :: i
err = verify_filter(index)
if (err == 0) then
select type (f => filters(index) % obj)
type is (MeshFilter)
if (index_mesh >= 1 .and. index_mesh <= n_meshes) then
if (index_mesh >= 0 .and. index_mesh < n_meshes()) then
f % mesh = index_mesh
f % n_bins = product(meshes(index_mesh) % dimension)
f % n_bins = 1
m = meshes(index_mesh)
do i = 1, m % n_dimension()
f % n_bins = f % n_bins * m % dimension(i)
end do
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in 'meshes' array is out of bounds.")
end if
class default
class default
err = E_INVALID_TYPE
call set_errmsg("Tried to set mesh on a non-mesh filter.")
end select

View file

@ -3,9 +3,8 @@ module tally_filter_meshsurface
use, intrinsic :: ISO_C_BINDING
use constants
use dict_header, only: EMPTY
use error
use mesh_header, only: RegularMesh, meshes, n_meshes, mesh_dict
use mesh_header
use hdf5_interface
use particle_header, only: Particle
use string, only: to_str
@ -38,11 +37,11 @@ contains
class(MeshSurfaceFilter), intent(inout) :: this
type(XMLNode), intent(in) :: node
integer :: i_mesh
integer :: i
integer :: id
integer :: n
integer :: n_dim
integer :: val
integer(C_INT) :: err
type(RegularMesh) :: m
n = node_word_count(node, "bins")
@ -53,20 +52,18 @@ contains
call get_node_value(node, "bins", id)
! Get pointer to mesh
val = mesh_dict % get(id)
if (val /= EMPTY) then
i_mesh = val
else
err = openmc_get_mesh_index(id, this % mesh)
if (err /= 0) then
call fatal_error("Could not find mesh " // trim(to_str(id)) &
// " specified on filter.")
end if
! Determine number of bins
n_dim = meshes(i_mesh) % n_dimension
this % n_bins = 4*n_dim*product(meshes(i_mesh) % dimension)
! Store the index of the mesh
this % mesh = i_mesh
m = meshes(this % mesh)
this % n_bins = 4 * m % n_dimension()
do i = 1, m % n_dimension()
this % n_bins = this % n_bins * m % dimension(i)
end do
end subroutine from_xml
subroutine get_all_bins(this, p, estimator, match)
@ -75,150 +72,25 @@ contains
integer, intent(in) :: estimator
type(TallyFilterMatch), intent(inout) :: match
integer :: j ! loop indices
integer :: n_dim ! num dimensions of the mesh
integer :: d1 ! dimension index
integer :: ijk0(3) ! indices of starting coordinates
integer :: ijk1(3) ! indices of ending coordinates
integer :: n_cross ! number of surface crossings
integer :: i_mesh ! flattened mesh bin index
integer :: i_surf ! surface index (1--12)
integer :: i_bin ! actual index for filter
real(8) :: uvw(3) ! cosine of angle of particle
real(8) :: xyz0(3) ! starting/intermediate coordinates
real(8) :: xyz1(3) ! ending coordinates of particle
real(8) :: xyz_cross(3) ! coordinates of bounding surfaces
real(8) :: d(3) ! distance to each bounding surface
real(8) :: distance ! actual distance traveled
logical :: start_in_mesh ! particle's starting xyz in mesh?
logical :: end_in_mesh ! particle's ending xyz in mesh?
type(RegularMesh) :: m
type(C_PTR) :: ptr_bins, ptr_weights
! Copy starting and ending location of particle
xyz0 = p % last_xyz_current
xyz1 = p % coord(1) % xyz
interface
subroutine mesh_surface_bins_crossed(m, p, bins, weights) bind(C)
import C_PTR, Particle
type(C_PTR), value :: m
type(Particle), intent(in) :: p
type(C_PTR), value :: bins
type(C_PTR), value :: weights
end subroutine
end interface
associate (m => meshes(this % mesh))
n_dim = m % n_dimension
! Get a pointer to the mesh.
m = meshes(this % mesh)
! Determine indices for starting and ending location
call m % get_indices(xyz0, ijk0, start_in_mesh)
call m % get_indices(xyz1, ijk1, end_in_mesh)
! Check to see if start or end is in mesh -- if not, check if track still
! intersects with mesh
if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then
if (.not. m % intersects(xyz0, xyz1)) return
end if
! Calculate number of surface crossings
n_cross = sum(abs(ijk1(:n_dim) - ijk0(:n_dim)))
if (n_cross == 0) return
! Copy particle's direction
uvw = p % coord(1) % uvw
! Bounding coordinates
do d1 = 1, n_dim
if (uvw(d1) > 0) then
xyz_cross(d1) = m % lower_left(d1) + ijk0(d1) * m % width(d1)
else
xyz_cross(d1) = m % lower_left(d1) + (ijk0(d1) - 1) * m % width(d1)
end if
end do
do j = 1, n_cross
! Set the distances to infinity
d = INFINITY
! Calculate distance to each 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.
do d1 = 1, n_dim
if (uvw(d1) == 0) then
d(d1) = INFINITY
else
d(d1) = (xyz_cross(d1) - xyz0(d1))/uvw(d1)
end if
end do
! Determine the closest bounding surface of the mesh cell by
! calculating the minimum distance. Then use the minimum distance and
! direction of the particle to determine which surface was crossed.
distance = minval(d)
! Loop over the dimensions
do d1 = 1, n_dim
! Check whether distance is the shortest distance
if (distance == d(d1)) then
! Check whether particle is moving in positive d1 direction
if (uvw(d1) > 0) then
! Outward current on d1 max surface
if (all(ijk0(:n_dim) >= 1) .and. &
all(ijk0(:n_dim) <= m % dimension)) then
i_surf = d1 * 4 - 1
i_mesh = m % get_bin_from_indices(ijk0)
i_bin = 4*n_dim*(i_mesh - 1) + i_surf
call match % bins % push_back(i_bin)
call match % weights % push_back(ONE)
end if
! Advance position
ijk0(d1) = ijk0(d1) + 1
xyz_cross(d1) = xyz_cross(d1) + m % width(d1)
! If the particle crossed the surface, tally the inward current on
! d1 min surface
if (all(ijk0(:n_dim) >= 1) .and. &
all(ijk0(:n_dim) <= m % dimension)) then
i_surf = d1 * 4 - 2
i_mesh = m % get_bin_from_indices(ijk0)
i_bin = 4*n_dim*(i_mesh - 1) + i_surf
call match % bins % push_back(i_bin)
call match % weights % push_back(ONE)
end if
else
! The particle is moving in the negative d1 direction
! Outward current on d1 min surface
if (all(ijk0(:n_dim) >= 1) .and. &
all(ijk0(:n_dim) <= m % dimension)) then
i_surf = d1 * 4 - 3
i_mesh = m % get_bin_from_indices(ijk0)
i_bin = 4*n_dim*(i_mesh - 1) + i_surf
call match % bins % push_back(i_bin)
call match % weights % push_back(ONE)
end if
! Advance position
ijk0(d1) = ijk0(d1) - 1
xyz_cross(d1) = xyz_cross(d1) - m % width(d1)
! If the particle crossed the surface, tally the inward current on
! d1 max surface
if (all(ijk0(:n_dim) >= 1) .and. &
all(ijk0(:n_dim) <= m % dimension)) then
i_surf = d1 * 4
i_mesh = m % get_bin_from_indices(ijk0)
i_bin = 4*n_dim*(i_mesh - 1) + i_surf
call match % bins % push_back(i_bin)
call match % weights % push_back(ONE)
end if
end if
end if
end do
! Calculate new coordinates
xyz0 = xyz0 + distance * uvw
end do
end associate
ptr_bins = C_LOC(match % bins)
ptr_weights = C_LOC(match % weights)
call mesh_surface_bins_crossed(m % ptr, p, ptr_bins, ptr_weights)
end subroutine get_all_bins
@ -226,9 +98,12 @@ contains
class(MeshSurfaceFilter), intent(in) :: this
integer(HID_T), intent(in) :: filter_group
type(RegularMesh) :: m
m = meshes(this % mesh)
call write_dataset(filter_group, "type", "meshsurface")
call write_dataset(filter_group, "n_bins", this % n_bins)
call write_dataset(filter_group, "bins", meshes(this % mesh) % id)
call write_dataset(filter_group, "bins", m % id())
end subroutine to_statepoint
function text_label(this, bin) result(label)
@ -240,55 +115,55 @@ contains
integer :: i_surf
integer :: n_dim
integer, allocatable :: ijk(:)
type(RegularMesh) :: m
associate (m => meshes(this % mesh))
n_dim = m % n_dimension
allocate(ijk(n_dim))
m = meshes(this % mesh)
n_dim = m % n_dimension()
allocate(ijk(n_dim))
! Get flattend mesh index and surface index
i_mesh = (bin - 1) / (4*n_dim) + 1
i_surf = mod(bin - 1, 4*n_dim) + 1
! Get flattend mesh index and surface index
i_mesh = (bin - 1) / (4*n_dim) + 1
i_surf = mod(bin - 1, 4*n_dim) + 1
! Get mesh index part of label
call m % get_indices_from_bin(i_mesh, ijk)
if (m % n_dimension == 1) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ")"
elseif (m % n_dimension == 2) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ")"
elseif (m % n_dimension == 3) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")"
end if
! Get mesh index part of label
call m % get_indices_from_bin(i_mesh, ijk)
if (m % n_dimension() == 1) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ")"
elseif (m % n_dimension() == 2) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ")"
elseif (m % n_dimension() == 3) then
label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // &
trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")"
end if
! Get surface part of label
select case (i_surf)
case (OUT_LEFT)
label = trim(label) // " Outgoing, x-min"
case (IN_LEFT)
label = trim(label) // " Incoming, x-min"
case (OUT_RIGHT)
label = trim(label) // " Outgoing, x-max"
case (IN_RIGHT)
label = trim(label) // " Incoming, x-max"
case (OUT_BACK)
label = trim(label) // " Outgoing, y-min"
case (IN_BACK)
label = trim(label) // " Incoming, y-min"
case (OUT_FRONT)
label = trim(label) // " Outgoing, y-max"
case (IN_FRONT)
label = trim(label) // " Incoming, y-max"
case (OUT_BOTTOM)
label = trim(label) // " Outgoing, z-min"
case (IN_BOTTOM)
label = trim(label) // " Incoming, z-min"
case (OUT_TOP)
label = trim(label) // " Outgoing, z-max"
case (IN_TOP)
label = trim(label) // " Incoming, z-max"
end select
end associate
! Get surface part of label
select case (i_surf)
case (OUT_LEFT)
label = trim(label) // " Outgoing, x-min"
case (IN_LEFT)
label = trim(label) // " Incoming, x-min"
case (OUT_RIGHT)
label = trim(label) // " Outgoing, x-max"
case (IN_RIGHT)
label = trim(label) // " Incoming, x-max"
case (OUT_BACK)
label = trim(label) // " Outgoing, y-min"
case (IN_BACK)
label = trim(label) // " Incoming, y-min"
case (OUT_FRONT)
label = trim(label) // " Outgoing, y-max"
case (IN_FRONT)
label = trim(label) // " Incoming, y-max"
case (OUT_BOTTOM)
label = trim(label) // " Outgoing, z-min"
case (IN_BOTTOM)
label = trim(label) // " Incoming, z-min"
case (OUT_TOP)
label = trim(label) // " Outgoing, z-max"
case (IN_TOP)
label = trim(label) // " Incoming, z-max"
end select
end function text_label
!===============================================================================
@ -320,16 +195,22 @@ contains
integer(C_INT32_T), value, intent(in) :: index_mesh
integer(C_INT) :: err
integer :: i
integer :: n_dim
type(RegularMesh) :: m
err = verify_filter(index)
if (err == 0) then
select type (f => filters(index) % obj)
type is (MeshSurfaceFilter)
if (index_mesh >= 1 .and. index_mesh <= n_meshes) then
if (index_mesh >= 0 .and. index_mesh < n_meshes()) then
f % mesh = index_mesh
n_dim = meshes(index_mesh) % n_dimension
f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension)
m = meshes(index_mesh)
n_dim = m % n_dimension()
f % n_bins = 4*n_dim
do i = 1, n_dim
f % n_bins = f % n_bins * m % dimension(i)
end do
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in 'meshes' array is out of bounds.")

View file

@ -257,14 +257,14 @@ contains
logical :: print_ebin ! should incoming energy bin be displayed?
real(8) :: rel_err = ZERO ! temporary relative error of result
real(8) :: std_dev = ZERO ! temporary standard deviration of result
type(RegularMesh), pointer :: m ! surface current mesh
type(RegularMesh) :: m ! surface current mesh
! Get pointer to mesh
i_filter_mesh = t % filter(t % find_filter(FILTER_MESH))
i_filter_surf = t % filter(t % find_filter(FILTER_SURFACE))
select type(filt => filters(i_filter_mesh) % obj)
type is (MeshFilter)
m => meshes(filt % mesh)
m = meshes(filt % mesh)
end select
! initialize bins array
@ -285,8 +285,11 @@ contains
end if
! Get the dimensions and number of cells in the mesh
n_dim = m % n_dimension
n_cells = product(m % dimension)
n_dim = m % n_dimension()
n_cells = 1
do j = 1, n_dim
n_cells = n_cells * m % dimension(j)
end do
! Loop over all the mesh cells
do i = 1, n_cells

View file

@ -1,5 +1,7 @@
module trigger_header
use, intrinsic :: ISO_C_BINDING
use constants, only: NONE, N_FILTER_TYPES, ZERO
implicit none
@ -22,11 +24,11 @@ module trigger_header
!===============================================================================
! KTRIGGER describes a user-specified precision trigger for k-effective
!===============================================================================
type, public :: KTrigger
integer :: trigger_type = 0
real(8) :: threshold = ZERO
type, public, bind(C) :: KTrigger
integer(C_INT) :: trigger_type = 0
real(C_DOUBLE) :: threshold = ZERO
end type KTrigger
type(KTrigger), public :: keff_trigger ! trigger for k-effective
type(KTrigger), public, bind(C) :: keff_trigger ! trigger for k-effective
end module trigger_header

View file

@ -153,10 +153,10 @@ ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp,
// Determine temperature for S(a,b) table
double kT = sqrtkT*sqrtkT;
int i;
if (temperature_method == TEMPERATURE_NEAREST) {
if (settings::temperature_method == TEMPERATURE_NEAREST) {
// If using nearest temperature, do linear search on temperature
for (i = 0; i < kTs_.size(); ++i) {
if (abs(kTs_[i] - kT) < K_BOLTZMANN*temperature_tolerance) {
if (abs(kTs_[i] - kT) < K_BOLTZMANN*settings::temperature_tolerance) {
break;
}
}

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -1,97 +1,64 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="1 -2 3 -4 5 -6" universe="1" />
<cell id="2" material="2" region="1 -2 3 -4 6 -7" universe="1" />
<cell id="3" material="3" region="1 -2 3 -4 7 -8" universe="1" />
<cell id="4" material="4" region="1 -2 3 -4 8 -9" universe="1" />
<cell id="5" material="5" region="1 -2 3 -4 9 -10" universe="1" />
<cell id="6" material="6" region="1 -2 3 -4 10 -11" universe="1" />
<cell id="7" material="7" region="1 -2 3 -4 11 -12" universe="1" />
<cell id="8" material="8" region="1 -2 3 -4 12 -13" universe="1" />
<cell id="9" material="9" region="1 -2 3 -4 13 -14" universe="1" />
<cell id="10" material="10" region="1 -2 3 -4 14 -15" universe="1" />
<cell id="11" material="11" region="1 -2 3 -4 15 -16" universe="1" />
<cell id="12" material="12" region="1 -2 3 -4 16 -17" universe="1" />
<cell id="1" material="1" region="1 -2" universe="0" />
<cell id="2" material="2" region="2 -3" universe="0" />
<cell id="3" material="3" region="3 -4" universe="0" />
<cell id="4" material="4" region="4 -5" universe="0" />
<cell id="5" material="5" region="5 -6" universe="0" />
<cell id="6" material="6" region="6 -7" universe="0" />
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane" />
<surface boundary="reflective" coeffs="10.0" id="2" type="x-plane" />
<surface boundary="reflective" coeffs="0.0" id="3" type="y-plane" />
<surface boundary="reflective" coeffs="10.0" id="4" type="y-plane" />
<surface boundary="reflective" coeffs="0.0" id="5" type="z-plane" />
<surface coeffs="0.4167" id="6" type="z-plane" />
<surface coeffs="0.8334" id="7" type="z-plane" />
<surface coeffs="1.2501" id="8" type="z-plane" />
<surface coeffs="1.6668" id="9" type="z-plane" />
<surface coeffs="2.0835" id="10" type="z-plane" />
<surface coeffs="2.5002" id="11" type="z-plane" />
<surface coeffs="2.9169" id="12" type="z-plane" />
<surface coeffs="3.3336" id="13" type="z-plane" />
<surface coeffs="3.7503" id="14" type="z-plane" />
<surface coeffs="4.167" id="15" type="z-plane" />
<surface coeffs="4.5837" id="16" type="z-plane" />
<surface boundary="reflective" coeffs="5.0" id="17" type="z-plane" />
<surface coeffs="154.90833333333333" id="2" type="x-plane" />
<surface coeffs="309.81666666666666" id="3" type="x-plane" />
<surface coeffs="464.725" id="4" type="x-plane" />
<surface coeffs="619.6333333333333" id="5" type="x-plane" />
<surface coeffs="774.5416666666666" id="6" type="x-plane" />
<surface boundary="vacuum" coeffs="929.45" id="7" type="x-plane" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<cross_sections>../../1d_mgxs.h5</cross_sections>
<material id="1" name="1">
<cross_sections>2g.h5</cross_sections>
<material id="1" name="base leg">
<density units="macro" value="1.0" />
<macroscopic name="uo2_ang" />
<macroscopic name="mat_1" />
</material>
<material id="2" name="2">
<material id="2" name="base tab">
<density units="macro" value="1.0" />
<macroscopic name="uo2_ang_mu" />
<macroscopic name="mat_2" />
</material>
<material id="3" name="3">
<material id="3" name="base hist">
<density units="macro" value="1.0" />
<macroscopic name="uo2_iso" />
<macroscopic name="mat_3" />
</material>
<material id="4" name="4">
<material id="4" name="base matrix">
<density units="macro" value="1.0" />
<macroscopic name="uo2_iso_mu" />
<macroscopic name="mat_4" />
</material>
<material id="5" name="5">
<material id="5" name="base ang">
<density units="macro" value="1.0" />
<macroscopic name="clad_ang" />
<macroscopic name="mat_5" />
</material>
<material id="6" name="6">
<density units="macro" value="1.0" />
<macroscopic name="clad_ang_mu" />
</material>
<material id="7" name="7">
<density units="macro" value="1.0" />
<macroscopic name="clad_iso" />
</material>
<material id="8" name="8">
<density units="macro" value="1.0" />
<macroscopic name="clad_iso_mu" />
</material>
<material id="9" name="9">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_ang" />
</material>
<material id="10" name="10">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_ang_mu" />
</material>
<material id="11" name="11">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_iso" />
</material>
<material id="12" name="12">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_iso_mu" />
<material id="6" name="micro">
<density units="sum" />
<nuclide ao="0.5" name="mat_1" />
<nuclide ao="0.5" name="mat_6" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>100</particles>
<particles>1000</particles>
<batches>10</batches>
<inactive>5</inactive>
<source strength="1.0">
<space type="box">
<parameters>0.0 0.0 0.0 10.0 10.0 5.0</parameters>
<parameters>0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0</parameters>
</space>
</source>
<output>
<summary>false</summary>
</output>
<energy_mode>multi-group</energy_mode>
<tabular_legendre>
<enable>false</enable>
</tabular_legendre>
</settings>

View file

@ -1,2 +1,2 @@
k-combined:
1.073147E+00 1.602384E-02
1.005345E+00 1.109180E-02

View file

@ -1,9 +1,97 @@
import os
import numpy as np
import openmc
from openmc.examples import slab_mg
from tests.testing_harness import PyAPITestHarness
def create_library():
# Instantiate the energy group data and file object
groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6])
mg_cross_sections_file = openmc.MGXSLibrary(groups)
# Make the base, isotropic data
nu = [2.50, 2.50]
fiss = np.array([0.002817, 0.097])
capture = [0.008708, 0.02518]
absorption = np.add(capture, fiss)
scatter = np.array(
[[[0.31980, 0.06694], [0.004555, -0.0003972]],
[[0.00000, 0.00000], [0.424100, 0.05439000]]])
total = [0.33588, 0.54628]
chi = [1., 0.]
mat_1 = openmc.XSdata('mat_1', groups)
mat_1.order = 1
mat_1.set_nu_fission(np.multiply(nu, fiss))
mat_1.set_absorption(absorption)
mat_1.set_scatter_matrix(scatter)
mat_1.set_total(total)
mat_1.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_1)
# Make a version of mat-1 which has a tabular representation of the
# scattering vice Legendre with 33 points
mat_2 = mat_1.convert_scatter_format('tabular', 33)
mat_2.name = 'mat_2'
mg_cross_sections_file.add_xsdata(mat_2)
# Make a version of mat-1 which has a histogram representation of the
# scattering vice Legendre with 33 bins
mat_3 = mat_1.convert_scatter_format('histogram', 33)
mat_3.name = 'mat_3'
mg_cross_sections_file.add_xsdata(mat_3)
# Make a version which uses a fission matrix vice chi & nu-fission
mat_4 = openmc.XSdata('mat_4', groups)
mat_4.order = 1
mat_4.set_nu_fission(np.outer(np.multiply(nu, fiss), chi))
mat_4.set_absorption(absorption)
mat_4.set_scatter_matrix(scatter)
mat_4.set_total(total)
mg_cross_sections_file.add_xsdata(mat_4)
# Make an angle-dependent version of mat_1 with 2 polar and 2 azim. angles
mat_5 = mat_1.convert_representation('angle', 2, 2)
mat_5.name = 'mat_5'
mg_cross_sections_file.add_xsdata(mat_5)
# Make a copy of mat_1 for testing microscopic cross sections
mat_6 = openmc.XSdata('mat_6', groups)
mat_6.order = 1
mat_6.set_nu_fission(np.multiply(nu, fiss))
mat_6.set_absorption(absorption)
mat_6.set_scatter_matrix(scatter)
mat_6.set_total(total)
mat_6.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_6)
# Write the file
mg_cross_sections_file.export_to_hdf5('2g.h5')
class MGXSTestHarness(PyAPITestHarness):
def _cleanup(self):
super()._cleanup()
f = '2g.h5'
if os.path.exists(f):
os.remove(f)
def test_mg_basic():
model = slab_mg()
create_library()
mat_names = ['base leg', 'base tab', 'base hist', 'base matrix',
'base ang', 'micro']
model = slab_mg(num_regions=6, mat_names=mat_names)
# Modify the last material to be a microscopic combination of nuclides
model.materials[-1] = openmc.Material(name='micro', material_id=6)
model.materials[-1].set_density("sum")
model.materials[-1].add_nuclide("mat_1", 0.5)
model.materials[-1].add_nuclide("mat_6", 0.5)
harness = PyAPITestHarness('statepoint.10.h5', model)
harness.main()

View file

@ -0,0 +1,63 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="1 -2" universe="0" />
<cell id="2" material="2" region="2 -3" universe="0" />
<cell id="3" material="3" region="3 -4" universe="0" />
<cell id="4" material="4" region="4 -5" universe="0" />
<cell id="5" material="5" region="5 -6" universe="0" />
<cell id="6" material="6" region="6 -7" universe="0" />
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane" />
<surface coeffs="154.90833333333333" id="2" type="x-plane" />
<surface coeffs="309.81666666666666" id="3" type="x-plane" />
<surface coeffs="464.725" id="4" type="x-plane" />
<surface coeffs="619.6333333333333" id="5" type="x-plane" />
<surface coeffs="774.5416666666666" id="6" type="x-plane" />
<surface boundary="vacuum" coeffs="929.45" id="7" type="x-plane" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<cross_sections>2g.h5</cross_sections>
<material id="1" name="vec beta">
<density units="macro" value="1.0" />
<macroscopic name="mat_1" />
</material>
<material id="2" name="vec no beta">
<density units="macro" value="1.0" />
<macroscopic name="mat_2" />
</material>
<material id="3" name="matrix beta">
<density units="macro" value="1.0" />
<macroscopic name="mat_3" />
</material>
<material id="4" name="matrix no beta">
<density units="macro" value="1.0" />
<macroscopic name="mat_4" />
</material>
<material id="5" name="vec group beta">
<density units="macro" value="1.0" />
<macroscopic name="mat_5" />
</material>
<material id="6" name="matrix group beta">
<density units="macro" value="1.0" />
<macroscopic name="mat_6" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>1000</particles>
<batches>10</batches>
<inactive>5</inactive>
<source strength="1.0">
<space type="box">
<parameters>0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0</parameters>
</space>
</source>
<output>
<summary>false</summary>
</output>
<energy_mode>multi-group</energy_mode>
<tabular_legendre>
<enable>false</enable>
</tabular_legendre>
</settings>

View file

@ -0,0 +1,2 @@
k-combined:
1.003463E+00 2.173155E-02

View file

@ -0,0 +1,131 @@
import os
import numpy as np
import openmc
from openmc.examples import slab_mg
from tests.testing_harness import PyAPITestHarness
def create_library():
# Instantiate the energy group data and file object
groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6])
n_dg = 2
mg_cross_sections_file = openmc.MGXSLibrary(groups)
mg_cross_sections_file.num_delayed_groups = n_dg
beta = np.array([0.003, 0.003])
one_m_beta = 1. - np.sum(beta)
nu = [2.50, 2.50]
fiss = np.array([0.002817, 0.097])
capture = [0.008708, 0.02518]
absorption = np.add(capture, fiss)
scatter = np.array(
[[[0.31980, 0.06694], [0.004555, -0.0003972]],
[[0.00000, 0.00000], [0.424100, 0.05439000]]])
total = [0.33588, 0.54628]
chi = [1., 0.]
# Make the base data that uses chi & nu-fission vectors with a beta
mat_1 = openmc.XSdata('mat_1', groups)
mat_1.order = 1
mat_1.num_delayed_groups = 2
mat_1.set_beta(beta)
mat_1.set_nu_fission(np.multiply(nu, fiss))
mat_1.set_absorption(absorption)
mat_1.set_scatter_matrix(scatter)
mat_1.set_total(total)
mat_1.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_1)
# Make a version that uses prompt and delayed version of nufiss and chi
mat_2 = openmc.XSdata('mat_2', groups)
mat_2.order = 1
mat_2.num_delayed_groups = 2
mat_2.set_prompt_nu_fission(one_m_beta * np.multiply(nu, fiss))
delay_nu_fiss = np.zeros((n_dg, groups.num_groups))
for dg in range(n_dg):
for g in range(groups.num_groups):
delay_nu_fiss[dg, g] = beta[dg] * nu[g] * fiss[g]
mat_2.set_delayed_nu_fission(delay_nu_fiss)
mat_2.set_absorption(absorption)
mat_2.set_scatter_matrix(scatter)
mat_2.set_total(total)
mat_2.set_chi_prompt(chi)
mat_2.set_chi_delayed(np.stack([chi] * n_dg))
mg_cross_sections_file.add_xsdata(mat_2)
# Make a version that uses a nu-fission matrix with a beta
mat_3 = openmc.XSdata('mat_3', groups)
mat_3.order = 1
mat_3.num_delayed_groups = 2
mat_3.set_beta(beta)
mat_3.set_nu_fission(np.outer(np.multiply(nu, fiss), chi))
mat_3.set_absorption(absorption)
mat_3.set_scatter_matrix(scatter)
mat_3.set_total(total)
mg_cross_sections_file.add_xsdata(mat_3)
# Make a version that uses prompt and delayed version of the nufiss matrix
mat_4 = openmc.XSdata('mat_4', groups)
mat_4.order = 1
mat_4.num_delayed_groups = 2
mat_4.set_prompt_nu_fission(one_m_beta *
np.outer(np.multiply(nu, fiss), chi))
delay_nu_fiss = np.zeros((n_dg, groups.num_groups, groups.num_groups))
for dg in range(n_dg):
for g in range(groups.num_groups):
for go in range(groups.num_groups):
delay_nu_fiss[dg, g, go] = beta[dg] * nu[g] * fiss[g] * chi[go]
mat_4.set_delayed_nu_fission(delay_nu_fiss)
mat_4.set_absorption(absorption)
mat_4.set_scatter_matrix(scatter)
mat_4.set_total(total)
mg_cross_sections_file.add_xsdata(mat_4)
# Make the base data that uses chi & nu-fiss vectors with a group-wise beta
mat_5 = openmc.XSdata('mat_5', groups)
mat_5.order = 1
mat_5.num_delayed_groups = 2
mat_5.set_beta(np.stack([beta] * groups.num_groups))
mat_5.set_nu_fission(np.multiply(nu, fiss))
mat_5.set_absorption(absorption)
mat_5.set_scatter_matrix(scatter)
mat_5.set_total(total)
mat_5.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_5)
# Make a version that uses a nu-fission matrix with a group-wise beta
mat_6 = openmc.XSdata('mat_6', groups)
mat_6.order = 1
mat_6.num_delayed_groups = 2
mat_6.set_beta(np.stack([beta] * groups.num_groups))
mat_6.set_nu_fission(np.outer(np.multiply(nu, fiss), chi))
mat_6.set_absorption(absorption)
mat_6.set_scatter_matrix(scatter)
mat_6.set_total(total)
mg_cross_sections_file.add_xsdata(mat_6)
# Write the file
mg_cross_sections_file.export_to_hdf5('2g.h5')
class MGXSTestHarness(PyAPITestHarness):
def _cleanup(self):
super()._cleanup()
f = '2g.h5'
if os.path.exists(f):
os.remove(f)
def test_mg_basic_delayed():
create_library()
model = slab_mg(num_regions=6, mat_names=['vec beta', 'vec no beta',
'matrix beta', 'matrix no beta',
'vec group beta',
'matrix group beta'])
harness = PyAPITestHarness('statepoint.10.h5', model)
harness.main()

View file

@ -17,7 +17,7 @@ def build_mgxs_library(convert):
# Instantiate the energy group data
groups = openmc.mgxs.EnergyGroups(group_edges=[1e-5, 0.625, 20.0e6])
# Instantiate the 7-group (C5G7) cross section data
# Instantiate the 2-group (C5G7) cross section data
uo2_xsdata = openmc.XSdata('UO2', groups)
uo2_xsdata.order = 2
uo2_xsdata.set_total([2., 2.])

View file

@ -1,44 +1,31 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="1 -2 3 -4 5 -6" universe="1" />
<cell id="2" material="2" region="1 -2 3 -4 6 -7" universe="1" />
<cell id="3" material="3" region="1 -2 3 -4 7 -8" universe="1" />
<cell id="1" material="1" region="1 -2" universe="0" />
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane" />
<surface boundary="reflective" coeffs="10.0" id="2" type="x-plane" />
<surface boundary="reflective" coeffs="0.0" id="3" type="y-plane" />
<surface boundary="reflective" coeffs="10.0" id="4" type="y-plane" />
<surface boundary="reflective" coeffs="0.0" id="5" type="z-plane" />
<surface coeffs="1.6667" id="6" type="z-plane" />
<surface coeffs="3.3334" id="7" type="z-plane" />
<surface boundary="reflective" coeffs="5.0" id="8" type="z-plane" />
<surface boundary="vacuum" coeffs="929.45" id="2" type="x-plane" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<cross_sections>../../1d_mgxs.h5</cross_sections>
<material id="1" name="1">
<cross_sections>2g.h5</cross_sections>
<material id="1" name="mat_1">
<density units="macro" value="1.0" />
<macroscopic name="uo2_iso" />
</material>
<material id="2" name="2">
<density units="macro" value="1.0" />
<macroscopic name="clad_iso" />
</material>
<material id="3" name="3">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_iso" />
<macroscopic name="mat_1" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>100</particles>
<particles>1000</particles>
<batches>10</batches>
<inactive>5</inactive>
<source strength="1.0">
<space type="box">
<parameters>0.0 0.0 0.0 10.0 10.0 5.0</parameters>
<parameters>0.0 -1000.0 -1000.0 929.45 1000.0 1000.0</parameters>
</space>
</source>
<output>
<summary>false</summary>
</output>
<energy_mode>multi-group</energy_mode>
<tabular_legendre>
<enable>false</enable>

View file

@ -1,2 +1,2 @@
k-combined:
1.110122E+00 2.549637E-02
9.934975E-01 2.679669E-02

View file

@ -1,10 +1,54 @@
import os
import numpy as np
import openmc
from openmc.examples import slab_mg
from tests.testing_harness import PyAPITestHarness
def create_library():
# Instantiate the energy group data and file object
groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6])
mg_cross_sections_file = openmc.MGXSLibrary(groups)
# Make the base, isotropic data
nu = [2.50, 2.50]
fiss = np.array([0.002817, 0.097])
capture = [0.008708, 0.02518]
absorption = np.add(capture, fiss)
scatter = np.array(
[[[0.31980, 0.06694], [0.004555, -0.0003972]],
[[0.00000, 0.00000], [0.424100, 0.05439000]]])
total = [0.33588, 0.54628]
chi = [1., 0.]
mat_1 = openmc.XSdata('mat_1', groups)
mat_1.order = 1
mat_1.set_nu_fission(np.multiply(nu, fiss))
mat_1.set_absorption(absorption)
mat_1.set_scatter_matrix(scatter)
mat_1.set_total(total)
mat_1.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_1)
# Write the file
mg_cross_sections_file.export_to_hdf5('2g.h5')
class MGXSTestHarness(PyAPITestHarness):
def _cleanup(self):
super()._cleanup()
f = '2g.h5'
if os.path.exists(f):
os.remove(f)
def test_mg_legendre():
model = slab_mg(reps=['iso'])
create_library()
model = slab_mg()
model.settings.tabular_legendre = {'enable': False}
harness = PyAPITestHarness('statepoint.10.h5', model)

View file

@ -1,44 +1,34 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="1 -2 3 -4 5 -6" universe="1" />
<cell id="2" material="2" region="1 -2 3 -4 6 -7" universe="1" />
<cell id="3" material="3" region="1 -2 3 -4 7 -8" universe="1" />
<cell id="1" material="1" region="1 -2" universe="0" />
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane" />
<surface boundary="reflective" coeffs="10.0" id="2" type="x-plane" />
<surface boundary="reflective" coeffs="0.0" id="3" type="y-plane" />
<surface boundary="reflective" coeffs="10.0" id="4" type="y-plane" />
<surface boundary="reflective" coeffs="0.0" id="5" type="z-plane" />
<surface coeffs="1.6667" id="6" type="z-plane" />
<surface coeffs="3.3334" id="7" type="z-plane" />
<surface boundary="reflective" coeffs="5.0" id="8" type="z-plane" />
<surface boundary="vacuum" coeffs="929.45" id="2" type="x-plane" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<cross_sections>../../1d_mgxs.h5</cross_sections>
<material id="1" name="1">
<cross_sections>2g.h5</cross_sections>
<material id="1" name="mat_1">
<density units="macro" value="1.0" />
<macroscopic name="uo2_iso" />
</material>
<material id="2" name="2">
<density units="macro" value="1.0" />
<macroscopic name="clad_iso" />
</material>
<material id="3" name="3">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_iso" />
<macroscopic name="mat_1" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>100</particles>
<particles>1000</particles>
<batches>10</batches>
<inactive>5</inactive>
<source strength="1.0">
<space type="box">
<parameters>0.0 0.0 0.0 10.0 10.0 5.0</parameters>
<parameters>0.0 -1000.0 -1000.0 929.45 1000.0 1000.0</parameters>
</space>
</source>
<output>
<summary>false</summary>
</output>
<energy_mode>multi-group</energy_mode>
<max_order>1</max_order>
<tabular_legendre>
<enable>false</enable>
</tabular_legendre>
</settings>

View file

@ -1,2 +1,2 @@
k-combined:
1.074551E+00 1.871525E-02
9.934975E-01 2.679669E-02

View file

@ -1,10 +1,55 @@
import os
import numpy as np
import openmc
from openmc.examples import slab_mg
from tests.testing_harness import PyAPITestHarness
def create_library():
# Instantiate the energy group data and file object
groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6])
mg_cross_sections_file = openmc.MGXSLibrary(groups)
# Make the base, isotropic data
nu = [2.50, 2.50]
fiss = np.array([0.002817, 0.097])
capture = [0.008708, 0.02518]
absorption = np.add(capture, fiss)
scatter = np.array(
[[[0.31980, 0.06694, 0.003], [0.004555, -0.0003972, 0.00002]],
[[0.00000, 0.00000, 0.000], [0.424100, 0.05439000, 0.0025]]])
total = [0.33588, 0.54628]
chi = [1., 0.]
mat_1 = openmc.XSdata('mat_1', groups)
mat_1.order = 2
mat_1.set_nu_fission(np.multiply(nu, fiss))
mat_1.set_absorption(absorption)
mat_1.set_scatter_matrix(scatter)
mat_1.set_total(total)
mat_1.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_1)
# Write the file
mg_cross_sections_file.export_to_hdf5('2g.h5')
class MGXSTestHarness(PyAPITestHarness):
def _cleanup(self):
super()._cleanup()
f = '2g.h5'
if os.path.exists(f):
os.remove(f)
def test_mg_max_order():
model = slab_mg(reps=['iso'])
create_library()
model = slab_mg()
model.settings.max_order = 1
harness = PyAPITestHarness('statepoint.10.h5', model)
harness.main()

View file

@ -1,97 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="1 -2 3 -4 5 -6" universe="1" />
<cell id="2" material="2" region="1 -2 3 -4 6 -7" universe="1" />
<cell id="3" material="3" region="1 -2 3 -4 7 -8" universe="1" />
<cell id="4" material="4" region="1 -2 3 -4 8 -9" universe="1" />
<cell id="5" material="5" region="1 -2 3 -4 9 -10" universe="1" />
<cell id="6" material="6" region="1 -2 3 -4 10 -11" universe="1" />
<cell id="7" material="7" region="1 -2 3 -4 11 -12" universe="1" />
<cell id="8" material="8" region="1 -2 3 -4 12 -13" universe="1" />
<cell id="9" material="9" region="1 -2 3 -4 13 -14" universe="1" />
<cell id="10" material="10" region="1 -2 3 -4 14 -15" universe="1" />
<cell id="11" material="11" region="1 -2 3 -4 15 -16" universe="1" />
<cell id="12" material="12" region="1 -2 3 -4 16 -17" universe="1" />
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane" />
<surface boundary="reflective" coeffs="10.0" id="2" type="x-plane" />
<surface boundary="reflective" coeffs="0.0" id="3" type="y-plane" />
<surface boundary="reflective" coeffs="10.0" id="4" type="y-plane" />
<surface boundary="reflective" coeffs="0.0" id="5" type="z-plane" />
<surface coeffs="0.4167" id="6" type="z-plane" />
<surface coeffs="0.8334" id="7" type="z-plane" />
<surface coeffs="1.2501" id="8" type="z-plane" />
<surface coeffs="1.6668" id="9" type="z-plane" />
<surface coeffs="2.0835" id="10" type="z-plane" />
<surface coeffs="2.5002" id="11" type="z-plane" />
<surface coeffs="2.9169" id="12" type="z-plane" />
<surface coeffs="3.3336" id="13" type="z-plane" />
<surface coeffs="3.7503" id="14" type="z-plane" />
<surface coeffs="4.167" id="15" type="z-plane" />
<surface coeffs="4.5837" id="16" type="z-plane" />
<surface boundary="reflective" coeffs="5.0" id="17" type="z-plane" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<cross_sections>../../1d_mgxs.h5</cross_sections>
<material id="1" name="1">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_ang" />
</material>
<material id="2" name="2">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_ang_mu" />
</material>
<material id="3" name="3">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_iso" />
</material>
<material id="4" name="4">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_iso_mu" />
</material>
<material id="5" name="5">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_ang" />
</material>
<material id="6" name="6">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_ang_mu" />
</material>
<material id="7" name="7">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_iso" />
</material>
<material id="8" name="8">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_iso_mu" />
</material>
<material id="9" name="9">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_ang" />
</material>
<material id="10" name="10">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_ang_mu" />
</material>
<material id="11" name="11">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_iso" />
</material>
<material id="12" name="12">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_iso_mu" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>100</particles>
<batches>10</batches>
<inactive>5</inactive>
<source strength="1.0">
<space type="box">
<parameters>0.0 0.0 0.0 10.0 10.0 5.0</parameters>
</space>
</source>
<energy_mode>multi-group</energy_mode>
</settings>

View file

@ -1,2 +0,0 @@
k-combined:
1.073147E+00 1.602384E-02

View file

@ -1,9 +0,0 @@
from openmc.examples import slab_mg
from tests.testing_harness import PyAPITestHarness
def test_mg_nuclide():
model = slab_mg(as_macro=False)
harness = PyAPITestHarness('statepoint.10.h5', model)
harness.main()

View file

@ -1,98 +1,34 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="1 -2 3 -4 5 -6" universe="1" />
<cell id="2" material="2" region="1 -2 3 -4 6 -7" universe="1" />
<cell id="3" material="3" region="1 -2 3 -4 7 -8" universe="1" />
<cell id="4" material="4" region="1 -2 3 -4 8 -9" universe="1" />
<cell id="5" material="5" region="1 -2 3 -4 9 -10" universe="1" />
<cell id="6" material="6" region="1 -2 3 -4 10 -11" universe="1" />
<cell id="7" material="7" region="1 -2 3 -4 11 -12" universe="1" />
<cell id="8" material="8" region="1 -2 3 -4 12 -13" universe="1" />
<cell id="9" material="9" region="1 -2 3 -4 13 -14" universe="1" />
<cell id="10" material="10" region="1 -2 3 -4 14 -15" universe="1" />
<cell id="11" material="11" region="1 -2 3 -4 15 -16" universe="1" />
<cell id="12" material="12" region="1 -2 3 -4 16 -17" universe="1" />
<cell id="1" material="1" region="1 -2" universe="0" />
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane" />
<surface boundary="reflective" coeffs="10.0" id="2" type="x-plane" />
<surface boundary="reflective" coeffs="0.0" id="3" type="y-plane" />
<surface boundary="reflective" coeffs="10.0" id="4" type="y-plane" />
<surface boundary="reflective" coeffs="0.0" id="5" type="z-plane" />
<surface coeffs="0.4167" id="6" type="z-plane" />
<surface coeffs="0.8334" id="7" type="z-plane" />
<surface coeffs="1.2501" id="8" type="z-plane" />
<surface coeffs="1.6668" id="9" type="z-plane" />
<surface coeffs="2.0835" id="10" type="z-plane" />
<surface coeffs="2.5002" id="11" type="z-plane" />
<surface coeffs="2.9169" id="12" type="z-plane" />
<surface coeffs="3.3336" id="13" type="z-plane" />
<surface coeffs="3.7503" id="14" type="z-plane" />
<surface coeffs="4.167" id="15" type="z-plane" />
<surface coeffs="4.5837" id="16" type="z-plane" />
<surface boundary="reflective" coeffs="5.0" id="17" type="z-plane" />
<surface boundary="vacuum" coeffs="929.45" id="2" type="x-plane" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<cross_sections>../../1d_mgxs.h5</cross_sections>
<material id="1" name="1">
<cross_sections>2g.h5</cross_sections>
<material id="1" name="mat_1">
<density units="macro" value="1.0" />
<macroscopic name="uo2_ang" />
</material>
<material id="2" name="2">
<density units="macro" value="1.0" />
<macroscopic name="uo2_ang_mu" />
</material>
<material id="3" name="3">
<density units="macro" value="1.0" />
<macroscopic name="uo2_iso" />
</material>
<material id="4" name="4">
<density units="macro" value="1.0" />
<macroscopic name="uo2_iso_mu" />
</material>
<material id="5" name="5">
<density units="macro" value="1.0" />
<macroscopic name="clad_ang" />
</material>
<material id="6" name="6">
<density units="macro" value="1.0" />
<macroscopic name="clad_ang_mu" />
</material>
<material id="7" name="7">
<density units="macro" value="1.0" />
<macroscopic name="clad_iso" />
</material>
<material id="8" name="8">
<density units="macro" value="1.0" />
<macroscopic name="clad_iso_mu" />
</material>
<material id="9" name="9">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_ang" />
</material>
<material id="10" name="10">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_ang_mu" />
</material>
<material id="11" name="11">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_iso" />
</material>
<material id="12" name="12">
<density units="macro" value="1.0" />
<macroscopic name="lwtr_iso_mu" />
<macroscopic name="mat_1" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>100</particles>
<particles>1000</particles>
<batches>10</batches>
<inactive>5</inactive>
<source strength="1.0">
<space type="box">
<parameters>0.0 0.0 0.0 10.0 10.0 5.0</parameters>
<parameters>0.0 -1000.0 -1000.0 929.45 1000.0 1000.0</parameters>
</space>
</source>
<output>
<summary>false</summary>
</output>
<energy_mode>multi-group</energy_mode>
<survival_biasing>true</survival_biasing>
<tabular_legendre>
<enable>false</enable>
</tabular_legendre>
</settings>

View file

@ -1,2 +1,2 @@
k-combined:
1.080832E+00 1.336780E-02
9.979905E-01 6.207495E-03

View file

@ -1,10 +1,55 @@
import os
import numpy as np
import openmc
from openmc.examples import slab_mg
from tests.testing_harness import PyAPITestHarness
def create_library():
# Instantiate the energy group data and file object
groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6])
mg_cross_sections_file = openmc.MGXSLibrary(groups)
# Make the base, isotropic data
nu = [2.50, 2.50]
fiss = np.array([0.002817, 0.097])
capture = [0.008708, 0.02518]
absorption = np.add(capture, fiss)
scatter = np.array(
[[[0.31980, 0.06694], [0.004555, -0.0003972]],
[[0.00000, 0.00000], [0.424100, 0.05439000]]])
total = [0.33588, 0.54628]
chi = [1., 0.]
mat_1 = openmc.XSdata('mat_1', groups)
mat_1.order = 1
mat_1.set_nu_fission(np.multiply(nu, fiss))
mat_1.set_absorption(absorption)
mat_1.set_scatter_matrix(scatter)
mat_1.set_total(total)
mat_1.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_1)
# Write the file
mg_cross_sections_file.export_to_hdf5('2g.h5')
class MGXSTestHarness(PyAPITestHarness):
def _cleanup(self):
super()._cleanup()
f = '2g.h5'
if os.path.exists(f):
os.remove(f)
def test_mg_survival_biasing():
create_library()
model = slab_mg()
model.settings.survival_biasing = True
harness = PyAPITestHarness('statepoint.10.h5', model)
harness.main()

View file

@ -1,112 +1,48 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="1 -2 3 -4 5 -6" universe="1" />
<cell id="2" material="2" region="1 -2 3 -4 6 -7" universe="1" />
<cell id="3" material="3" region="1 -2 3 -4 7 -8" universe="1" />
<cell id="4" material="4" region="1 -2 3 -4 8 -9" universe="1" />
<cell id="5" material="5" region="1 -2 3 -4 9 -10" universe="1" />
<cell id="6" material="6" region="1 -2 3 -4 10 -11" universe="1" />
<cell id="7" material="7" region="1 -2 3 -4 11 -12" universe="1" />
<cell id="8" material="8" region="1 -2 3 -4 12 -13" universe="1" />
<cell id="9" material="9" region="1 -2 3 -4 13 -14" universe="1" />
<cell id="10" material="10" region="1 -2 3 -4 14 -15" universe="1" />
<cell id="11" material="11" region="1 -2 3 -4 15 -16" universe="1" />
<cell id="12" material="12" region="1 -2 3 -4 16 -17" universe="1" />
<cell id="1" material="1" region="1 -2" universe="0" />
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane" />
<surface boundary="reflective" coeffs="10.0" id="2" type="x-plane" />
<surface boundary="reflective" coeffs="0.0" id="3" type="y-plane" />
<surface boundary="reflective" coeffs="10.0" id="4" type="y-plane" />
<surface boundary="reflective" coeffs="0.0" id="5" type="z-plane" />
<surface coeffs="0.4167" id="6" type="z-plane" />
<surface coeffs="0.8334" id="7" type="z-plane" />
<surface coeffs="1.2501" id="8" type="z-plane" />
<surface coeffs="1.6668" id="9" type="z-plane" />
<surface coeffs="2.0835" id="10" type="z-plane" />
<surface coeffs="2.5002" id="11" type="z-plane" />
<surface coeffs="2.9169" id="12" type="z-plane" />
<surface coeffs="3.3336" id="13" type="z-plane" />
<surface coeffs="3.7503" id="14" type="z-plane" />
<surface coeffs="4.167" id="15" type="z-plane" />
<surface coeffs="4.5837" id="16" type="z-plane" />
<surface boundary="reflective" coeffs="5.0" id="17" type="z-plane" />
<surface boundary="vacuum" coeffs="929.45" id="2" type="x-plane" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<cross_sections>../../1d_mgxs.h5</cross_sections>
<material id="1" name="1">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_ang" />
</material>
<material id="2" name="2">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_ang_mu" />
</material>
<material id="3" name="3">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_iso" />
</material>
<material id="4" name="4">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="uo2_iso_mu" />
</material>
<material id="5" name="5">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_ang" />
</material>
<material id="6" name="6">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_ang_mu" />
</material>
<material id="7" name="7">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_iso" />
</material>
<material id="8" name="8">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="clad_iso_mu" />
</material>
<material id="9" name="9">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_ang" />
</material>
<material id="10" name="10">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_ang_mu" />
</material>
<material id="11" name="11">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_iso" />
</material>
<material id="12" name="12">
<density units="atom/b-cm" value="1.0" />
<nuclide ao="1.0" name="lwtr_iso_mu" />
<cross_sections>2g.h5</cross_sections>
<material id="1" name="mat_1">
<density units="macro" value="1.0" />
<macroscopic name="mat_1" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>100</particles>
<particles>1000</particles>
<batches>10</batches>
<inactive>5</inactive>
<source strength="1.0">
<space type="box">
<parameters>0.0 0.0 0.0 10.0 10.0 5.0</parameters>
<parameters>0.0 -1000.0 -1000.0 929.45 1000.0 1000.0</parameters>
</space>
</source>
<output>
<summary>false</summary>
</output>
<energy_mode>multi-group</energy_mode>
<tabular_legendre>
<enable>false</enable>
</tabular_legendre>
</settings>
<?xml version='1.0' encoding='utf-8'?>
<tallies>
<mesh id="1" type="regular">
<dimension>1 1 10</dimension>
<dimension>10 1 1</dimension>
<lower_left>0.0 0.0 0.0</lower_left>
<upper_right>10 10 5</upper_right>
<upper_right>929.45 1000 1000</upper_right>
</mesh>
<filter id="5" type="mesh">
<bins>1</bins>
</filter>
<filter id="6" type="material">
<bins>1 2 3 4 5 6 7 8 9 10 11 12</bins>
<bins>1</bins>
</filter>
<filter id="1" type="energy">
<bins>0.0 20000000.0</bins>
@ -115,10 +51,10 @@
<bins>0.0 20000000.0</bins>
</filter>
<filter id="3" type="energy">
<bins>1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0</bins>
<bins>0.0 0.625 20000000.0</bins>
</filter>
<filter id="4" type="energyout">
<bins>1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0</bins>
<bins>0.0 0.625 20000000.0</bins>
</filter>
<tally id="1">
<filters>5</filters>
@ -170,60 +106,60 @@
</tally>
<tally id="11">
<filters>5</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission</scores>
<estimator>analog</estimator>
</tally>
<tally id="12">
<filters>5</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission</scores>
<estimator>tracklength</estimator>
</tally>
<tally id="13">
<filters>6 1</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission scatter nu-scatter</scores>
<estimator>analog</estimator>
</tally>
<tally id="14">
<filters>6 1</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission</scores>
<estimator>collision</estimator>
</tally>
<tally id="15">
<filters>6 1</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission</scores>
<estimator>tracklength</estimator>
</tally>
<tally id="16">
<filters>6 1 2</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>scatter nu-scatter nu-fission</scores>
</tally>
<tally id="17">
<filters>6 3</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission scatter nu-scatter</scores>
<estimator>analog</estimator>
</tally>
<tally id="18">
<filters>6 3</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission</scores>
<estimator>collision</estimator>
</tally>
<tally id="19">
<filters>6 3</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>total absorption fission nu-fission</scores>
<estimator>tracklength</estimator>
</tally>
<tally id="20">
<filters>6 3 4</filters>
<nuclides>uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu</nuclides>
<nuclides>mat_1</nuclides>
<scores>scatter nu-scatter nu-fission</scores>
</tally>
</tallies>

View file

@ -1 +1 @@
9183f8b191f2e62334f992acd865d29e3f4e3f871a6df498e280fc4e2d91f2d2d20c732fbd75fa88e2e8c576f86e744f7655af6bb9da66e9b28b1009c8742899
41ea1f6b17c58a8141921af2f1d044eda93f3a9bca9463ee023af2e9865da613ace90fc8a25b42edde128ed827182ea9df0fe09d9b7887282d0ec092692cf717

View file

@ -1,23 +1,66 @@
import os
import numpy as np
import openmc
from openmc.examples import slab_mg
from tests.testing_harness import HashedPyAPITestHarness
def create_library():
# Instantiate the energy group data and file object
groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6])
mg_cross_sections_file = openmc.MGXSLibrary(groups)
# Make the base, isotropic data
nu = [2.50, 2.50]
fiss = np.array([0.002817, 0.097])
capture = [0.008708, 0.02518]
absorption = np.add(capture, fiss)
scatter = np.array(
[[[0.31980, 0.06694], [0.004555, -0.0003972]],
[[0.00000, 0.00000], [0.424100, 0.05439000]]])
total = [0.33588, 0.54628]
chi = [1., 0.]
mat_1 = openmc.XSdata('mat_1', groups)
mat_1.order = 1
mat_1.set_nu_fission(np.multiply(nu, fiss))
mat_1.set_absorption(absorption)
mat_1.set_scatter_matrix(scatter)
mat_1.set_total(total)
mat_1.set_chi(chi)
mg_cross_sections_file.add_xsdata(mat_1)
# Write the file
mg_cross_sections_file.export_to_hdf5('2g.h5')
class MGXSTestHarness(HashedPyAPITestHarness):
def _cleanup(self):
super()._cleanup()
f = '2g.h5'
if os.path.exists(f):
os.remove(f)
def test_mg_tallies():
model = slab_mg(as_macro=False)
create_library()
model = slab_mg()
# Instantiate a tally mesh
mesh = openmc.Mesh(mesh_id=1)
mesh.type = 'regular'
mesh.dimension = [1, 1, 10]
mesh.dimension = [10, 1, 1]
mesh.lower_left = [0.0, 0.0, 0.0]
mesh.upper_right = [10, 10, 5]
mesh.upper_right = [929.45, 1000, 1000]
# Instantiate some tally filters
energy_filter = openmc.EnergyFilter([0.0, 20.0e6])
energyout_filter = openmc.EnergyoutFilter([0.0, 20.0e6])
energies = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6]
energies = [0.0, 0.625, 20.0e6]
matching_energy_filter = openmc.EnergyFilter(energies)
matching_eout_filter = openmc.EnergyoutFilter(energies)
mesh_filter = openmc.MeshFilter(mesh)

View file

@ -22,4 +22,4 @@ python tools/ci/travis-install.py
pip install -e .[test]
# For uploading to coveralls
pip install python-coveralls
pip install coveralls