Merge branch 'develop' of github.com:myerspat/openmc into otimize-photon-calcxs

This commit is contained in:
Pat Myers 2022-06-09 11:14:15 -05:00
commit 8eb21c0af2
78 changed files with 2191 additions and 459 deletions

View file

@ -455,9 +455,10 @@ target_compile_options(openmc PRIVATE ${cxxflags})
target_include_directories(openmc PRIVATE ${CMAKE_BINARY_DIR}/include)
target_link_libraries(openmc libopenmc)
# Ensure C++14 standard is used
# Ensure C++14 standard is used and turn off GNU extensions
target_compile_features(openmc PUBLIC cxx_std_14)
target_compile_features(libopenmc PUBLIC cxx_std_14)
set_target_properties(openmc libopenmc PROPERTIES CXX_EXTENSIONS OFF)
#===============================================================================
# Python package

View file

@ -490,9 +490,10 @@ attributes/sub-elements:
independent distributions of r-, phi-, and z-coordinates where phi is the
azimuthal angle and the origin for the cylindrical coordinate system is
specified by origin. A "spherical" spatial distribution specifies
independent distributions of r-, theta-, and phi-coordinates where theta
is the angle with respect to the z-axis, phi is the azimuthal angle, and
the sphere is centered on the coordinate (x0,y0,z0).
independent distributions of r-, cos_theta-, and phi-coordinates where
cos_theta is the cosine of the angle with respect to the z-axis, phi is
the azimuthal angle, and the sphere is centered on the coordinate
(x0,y0,z0).
*Default*: None

View file

@ -4,18 +4,34 @@
Track File Format
=================
The current revision of the particle track file format is 2.0.
The current revision of the particle track file format is 3.0.
**/**
:Attributes: - **filetype** (*char[]*) -- String indicating the type of file.
- **version** (*int[2]*) -- Major and minor version of the track
file format.
- **n_particles** (*int*) -- Number of particles for which tracks
are recorded.
- **n_coords** (*int[]*) -- Number of coordinates for each
particle.
:Datasets:
- **coordinates_<i>** (*double[][3]*) -- (x,y,z) coordinates for the
*i*-th particle.
- **track_<b>_<g>_<p>** (Compound type) -- Particle track information
for source particle in batch *b*, generation *g*, and particle
number *p*. particle. The compound type has fields ``r``, ``u``,
``E``, ``time``, ``wgt``, ``cell_id``, ``cell_instance``, and
``material_id``, which represent the position (each coordinate in
[cm]), direction, energy in [eV], time in [s], weight, cell ID,
cell instance, and material ID, respectively. When the particle is
present in a cell with no material assigned, the material ID is
given as -1. Note that this array contains information for one or
more primary/secondary particles originating. The starting index
for each primary/secondary particle is given by the ``offsets``
attribute.
:Attributes: - **n_particles** (*int*) -- Number of
primary/secondary particles for the source history.
- **offsets** (*int[]*) Offset (starting index) into
the array for each primary/secondary particle. The
last offset should match the total size of the
array.
- **particles** (*int[]*) -- Particle type for each
primary/secondary particle (0=neutron, 1=photon,
2=electron, 3=positron).

View file

@ -185,8 +185,11 @@ Post-processing
:template: myclass.rst
openmc.Particle
openmc.ParticleTrack
openmc.StatePoint
openmc.Summary
openmc.Track
openmc.Tracks
The following classes and functions are used for functional expansion reconstruction.

View file

@ -8,39 +8,56 @@ This quick install guide outlines the basic steps needed to install OpenMC on
your computer. For more detailed instructions on configuring and installing
OpenMC, see :ref:`usersguide_install` in the User's Manual.
----------------------------------------
Installing on Linux/Mac with conda-forge
----------------------------------------
--------------------------------------------------
Installing on Linux/Mac with Mamba and conda-forge
--------------------------------------------------
`Conda <https://conda.io/en/latest/>`_ is an open source package management
system and environment management system for installing multiple versions of
software packages and their dependencies and switching easily between them. If
you have `conda` installed on your system, OpenMC can be installed via the
`conda-forge` channel. First, add the `conda-forge` channel with:
system and environments management system for installing multiple versions of
software packages and their dependencies and switching easily between them.
`Mamba <https://mamba.readthedocs.io/en/latest/>`_ is a cross-platform package
manager and is compatible with `conda` packages.
OpenMC can be installed in a `conda` environment with `mamba`.
First, `conda` should be installed with one of the following installers:
`Miniconda <https://docs.conda.io/en/latest/miniconda.html>`_,
`Anaconda <https://www.anaconda.com/>`_, or `Miniforge <https://github.com/conda-forge/miniforge>`_.
Once you have `conda` installed on your system, OpenMC can be installed via the
`conda-forge` channel with `mamba`.
First, add the `conda-forge` channel with:
.. code-block:: sh
conda config --add channels conda-forge
Then create and activate a new conda enviroment called `openmc-env` in
which to install OpenMC.
.. code-block:: sh
conda create -n openmc-env
conda activate openmc-env
Then install `mamba`, which will be used to install OpenMC.
.. code-block:: sh
conda install mamba
To list the versions of OpenMC that are available on the `conda-forge` channel,
in your terminal window or an Anaconda Prompt run:
.. code-block:: sh
conda search openmc
mamba search openmc
OpenMC can then be installed with:
.. code-block:: sh
conda create -n openmc-env openmc
mamba install openmc
This will install OpenMC in a conda environment called `openmc-env`. To activate
the environment, run:
.. code-block:: sh
conda activate openmc-env
You are now in a conda environment called `openmc-env` that has OpenMC installed.
-------------------------------------------
Installing on Linux/Mac/Windows with Docker

View file

@ -8,39 +8,56 @@ Installation and Configuration
.. _install_conda:
----------------------------------------
Installing on Linux/Mac with conda-forge
----------------------------------------
--------------------------------------------------
Installing on Linux/Mac with Mamba and conda-forge
--------------------------------------------------
Conda_ is an open source package management system and environment management
system for installing multiple versions of software packages and their
dependencies and switching easily between them. If you have `conda` installed on
your system, OpenMC can be installed via the `conda-forge` channel. First, add
the `conda-forge` channel with:
`Conda <https://conda.io/en/latest/>`_ is an open source package management
systems and environments management system for installing multiple versions of
software packages and their dependencies and switching easily between them.
`Mamba <https://mamba.readthedocs.io/en/latest/>`_ is a cross-platform package
manager and is compatible with `conda` packages.
OpenMC can be installed in a `conda` environment with `mamba`.
First, `conda` should be installed with one of the following installers:
`Miniconda <https://docs.conda.io/en/latest/miniconda.html>`_,
`Anaconda <https://www.anaconda.com/>`_, or `Miniforge <https://github.com/conda-forge/miniforge>`_.
Once you have `conda` installed on your system, OpenMC can be installed via the
`conda-forge` channel with `mamba`.
First, add the `conda-forge` channel with:
.. code-block:: sh
conda config --add channels conda-forge
Then create and activate a new conda enviroment called `openmc-env` in
which to install OpenMC.
.. code-block:: sh
conda create -n openmc-env
conda activate openmc-env
Then install `mamba`, which will be used to install OpenMC.
.. code-block:: sh
conda install mamba
To list the versions of OpenMC that are available on the `conda-forge` channel,
in your terminal window or an Anaconda Prompt run:
.. code-block:: sh
conda search openmc
mamba search openmc
OpenMC can then be installed with:
.. code-block:: sh
conda create -n openmc-env openmc
mamba install openmc
This will install OpenMC in a conda environment called `openmc-env`. To activate
the environment, run:
.. code-block:: sh
conda activate openmc-env
You are now in a conda environment called `openmc-env` that has OpenMC installed.
-------------------------------------------
Installing on Linux/Mac/Windows with Docker

View file

@ -47,7 +47,7 @@ flags:
-r, --restart file Restart a previous run from a state point or a particle
restart file
-s, --threads N Run with *N* OpenMP threads
-t, --track Write tracks for all particles
-t, --track Write tracks for all particles (up to max_tracks)
-v, --version Show version information
-h, --help Show help message
@ -112,6 +112,19 @@ otherwise.
tallies. The path to the statepoint file can be provided as an optional arugment
(if omitted, a file dialog will be presented).
.. _scripts_track_combine:
------------------------
``openmc-track-combine``
------------------------
This script combines multiple HDF5 :ref:`particle track files
<usersguide_track>` into a single HDF5 particle track file. The filenames of the
particle track files should be given as posititional arguments. The output
filename can also be changed with the ``-o`` flag:
-o OUT, --out OUT Output HDF5 particle track file
.. _scripts_track:
-----------------------

View file

@ -514,4 +514,108 @@ As an example, to write a statepoint file every five batches::
settings.batches = n
settings.statepoint = {'batches': range(5, n + 5, 5)}
.. _NIST ESTAR database: https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html
Particle Track Files
--------------------
OpenMC can generate a particle track file that contains track information
(position, direction, energy, time, weight, cell ID, and material ID) for each
state along a particle's history. There are two ways to indicate which particles
and/or how many particles should have their tracks written. First, you can
identify specific source particles by their batch, generation, and particle ID
numbers::
settings.tracks = [
(1, 1, 50),
(2, 1, 30),
(5, 1, 75)
]
In this example, track information would be written for the 50th particle in the
1st generation of batch 1, the 30th particle in the first generation of batch 2,
and the 75th particle in the 1st generation of batch 5. Unless you are using
more than one generation per batch (see :ref:`usersguide_particles`), the
generation number should be 1. Alternatively, you can run OpenMC in a mode where
track information is written for *all* particles, up to a user-specified limit::
openmc.run(tracks=True)
In this case, you can control the maximum number of source particles for which
tracks will be written as follows::
settings.max_tracks = 1000
Particle track information is written to the ``tracks.h5`` file, which can be
analyzed using the :class:`~openmc.Tracks` class::
>>> tracks = openmc.Tracks('tracks.h5')
>>> tracks
[<Track (1, 1, 50): 151 particles>,
<Track (2, 1, 30): 191 particles>,
<Track (5, 1, 75): 81 particles>]
Each :class:`~openmc.Track` object stores a list of track information for every
primary/secondary particle. In the above example, the first source particle
produced 150 secondary particles for a total of 151 particles. Information for
each primary/secondary particle can be accessed using the
:attr:`~openmc.Track.particle_tracks` attribute::
>>> first_track = tracks[0]
>>> first_track.particle_tracks
[<ParticleTrack: neutron, 120 states>,
<ParticleTrack: photon, 6 states>,
<ParticleTrack: electron, 2 states>,
<ParticleTrack: electron, 2 states>,
<ParticleTrack: electron, 2 states>,
...
<ParticleTrack: electron, 2 states>,
<ParticleTrack: electron, 2 states>]
>>> photon = first_track.particle_tracks[1]
The :class:`~openmc.ParticleTrack` class is a named tuple indicating the
particle type and then a NumPy array of the "states". The states array is a
compound type with a field for each physical quantity (position, direction,
energy, time, weight, cell ID, and material ID). For example, to get the
position for the above particle track::
>>> photon.states['r']
array([(-11.92987939, -12.28467295, 0.67837495),
(-11.95213726, -12.2682 , 0.68783964),
(-12.2682 , -12.03428339, 0.82223855),
(-12.5913778 , -11.79510096, 0.95966298),
(-12.6622572 , -11.74264344, 0.98980293),
(-12.6907775 , -11.7215357 , 1.00193058)],
dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
The full list of fields is as follows:
:r: Position (each direction in [cm])
:u: Direction
:E: Energy in [eV]
:time: Time in [s]
:wgt: Weight
:cell_id: Cell ID
:cell_instance: Cell instance
:material_id: Material ID
Both the :class:`~openmc.Tracks` and :class:`~openmc.Track` classes have a
``filter`` method that allows you to get a subset of tracks that meet a given
criteria. For example, to get all tracks that involved a photon::
>>> tracks.filter(particle='photon')
[<Track (1, 1, 50): 151 particles>,
<Track (2, 1, 30): 191 particles>,
<Track (5, 1, 75): 81 particles>]
The :meth:`openmc.Tracks.filter` method returns a new :class:`~openmc.Tracks`
instance, whereas the :meth:`openmc.Track.filter` method returns a new
:class:`~openmc.Track` instance.
.. note:: If you are using an MPI-enabled install of OpenMC and run a simulation
with more than one process, a separate track file will be written for
each MPI process with the filename ``tracks_p#.h5`` where # is the
rank of the corresponding process. Multiple track files can be
combined with the :ref:`scripts_track_combine` script:
.. code-block:: sh
openmc-track-combine tracks_p*.h5 --out tracks.h5

View file

@ -300,5 +300,8 @@ struct CellInstanceHash {
void read_cells(pugi::xml_node node);
//! Add cells to universes
void populate_universes();
} // namespace openmc
#endif // OPENMC_CELL_H

View file

@ -26,7 +26,7 @@ constexpr int HDF5_VERSION[] {3, 0};
// Version numbers for binary files
constexpr array<int, 2> VERSION_STATEPOINT {17, 0};
constexpr array<int, 2> VERSION_PARTICLE_RESTART {2, 0};
constexpr array<int, 2> VERSION_TRACK {2, 0};
constexpr array<int, 2> VERSION_TRACK {3, 0};
constexpr array<int, 2> VERSION_SUMMARY {6, 0};
constexpr array<int, 2> VERSION_VOLUME {1, 0};
constexpr array<int, 2> VERSION_VOXEL {2, 0};

View file

@ -22,6 +22,7 @@ void check_dagmc_root_univ();
#ifdef DAGMC
#include "DagMC.hpp"
#include "dagmcmetadata.hpp"
#include "openmc/cell.h"
#include "openmc/particle.h"
@ -87,6 +88,11 @@ public:
explicit DAGUniverse(const std::string& filename, bool auto_geom_ids = false,
bool auto_mat_ids = false);
//! Alternative DAGMC universe constructor for external DAGMC instance
explicit DAGUniverse(std::shared_ptr<moab::DagMC> external_dagmc_ptr,
const std::string& filename = "", bool auto_geom_ids = false,
bool auto_mat_ids = false);
//! Initialize the DAGMC accel. data structures, indices, material
//! assignments, etc.
void initialize();
@ -139,10 +145,16 @@ public:
bool has_graveyard() const { return has_graveyard_; }
private:
void set_id(); //!< Deduce the universe id from model::universes
void init_dagmc(); //!< Create and initialise DAGMC pointer
void init_metadata(); //!< Create and initialise dagmcMetaData pointer
void init_geometry(); //!< Create cells and surfaces from DAGMC entities
std::string
filename_; //!< Name of the DAGMC file used to create this universe
std::shared_ptr<UWUW>
uwuw_; //!< Pointer to the UWUW instance for this universe
uwuw_; //!< Pointer to the UWUW instance for this universe
std::unique_ptr<dagmcMetaData> dmd_ptr; //! Pointer to DAGMC metadata object
bool adjust_geometry_ids_; //!< Indicates whether or not to automatically
//!< generate new cell and surface IDs for the
//!< universe

View file

@ -70,7 +70,7 @@ private:
};
//==============================================================================
//! Distribution of points specified by spherical coordinates r,theta,phi
//! Distribution of points specified by spherical coordinates r,cos_theta,phi
//==============================================================================
class SphericalIndependent : public SpatialDistribution {
@ -83,15 +83,15 @@ public:
Position sample(uint64_t* seed) const;
Distribution* r() const { return r_.get(); }
Distribution* theta() const { return theta_.get(); }
Distribution* cos_theta() const { return cos_theta_.get(); }
Distribution* phi() const { return phi_.get(); }
Position origin() const { return origin_; }
private:
UPtrDist r_; //!< Distribution of r coordinates
UPtrDist theta_; //!< Distribution of theta coordinates
UPtrDist phi_; //!< Distribution of phi coordinates
Position origin_; //!< Cartesian coordinates of the sphere center
UPtrDist r_; //!< Distribution of r coordinates
UPtrDist cos_theta_; //!< Distribution of cos_theta coordinates
UPtrDist phi_; //!< Distribution of phi coordinates
Position origin_; //!< Cartesian coordinates of the sphere center
};
//==============================================================================

View file

@ -113,6 +113,9 @@ public:
//! \param[in] units Units of density
void set_density(double density, gsl::cstring_span units);
//! Set temperature of the material
void set_temperature(double temperature) { temperature_ = temperature; };
//! Get nuclides in material
//! \return Indices into the global nuclides vector
gsl::span<const int> nuclides() const

View file

@ -56,6 +56,24 @@ struct SourceSite {
int64_t progeny_id;
};
//! State of a particle used for particle track files
struct TrackState {
Position r; //!< Position in [cm]
Direction u; //!< Direction
double E; //!< Energy in [eV]
double time {0.0}; //!< Time in [s]
double wgt {1.0}; //!< Weight
int cell_id; //!< Cell ID
int cell_instance; //!< Cell instance
int material_id {-1}; //!< Material ID (default value indicates void)
};
//! Full history of a single particle's track states
struct TrackStateHistory {
ParticleType particle;
std::vector<TrackState> states;
};
//! Saved ("banked") state of a particle, for nu-fission tallying
struct NuBank {
double E; //!< particle energy
@ -290,7 +308,7 @@ private:
vector<FilterMatch> filter_matches_; // tally filter matches
vector<vector<Position>> tracks_; // tracks for outputting to file
vector<TrackStateHistory> tracks_; // tracks for outputting to file
vector<NuBank> nu_bank_; // bank of most recently fissioned particles
@ -342,6 +360,9 @@ public:
const LocalCoord& coord(int i) const { return coord_[i]; }
const vector<LocalCoord>& coord() const { return coord_; }
LocalCoord& lowest_coord() { return coord_[n_coord_ - 1]; }
const LocalCoord& lowest_coord() const { return coord_[n_coord_ - 1]; }
int& n_coord_last() { return n_coord_last_; }
const int& n_coord_last() const { return n_coord_last_; }
int& cell_last(int i) { return cell_last_[i]; }
@ -357,6 +378,7 @@ public:
const int& g_last() const { return g_last_; }
double& wgt() { return wgt_; }
double wgt() const { return wgt_; }
double& mu() { return mu_; }
const double& mu() const { return mu_; }
double& time() { return time_; }
@ -479,6 +501,9 @@ public:
n_coord_ = 1;
}
//! Get track information based on particle's current state
TrackState get_track_state() const;
void zero_delayed_bank()
{
for (int& n : n_delayed_bank_) {

View file

@ -88,6 +88,7 @@ extern int max_order; //!< Maximum Legendre order for multigroup data
extern int n_log_bins; //!< number of bins for logarithmic energy grid
extern int n_batches; //!< number of (inactive+active) batches
extern int n_max_batches; //!< Maximum number of batches
extern int max_tracks; //!< Maximum number of particle tracks written to file
extern ResScatMethod res_scat_method; //!< resonance upscattering method
extern double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering
extern double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering

View file

@ -9,8 +9,31 @@ namespace openmc {
// Non-member functions
//==============================================================================
//! Open HDF5 track file for writing and create track datatype
void open_track_file();
//! Close HDF5 resources for track file
void close_track_file();
//! Determine whether a given particle should collect/write track information
//
//! \param[in] p Current particle
//! \return Whether to collect/write track information
bool check_track_criteria(const Particle& p);
//! Create a new track state history for a primary/secondary particle
//
//! \param[in] p Current particle
void add_particle_track(Particle& p);
//! Store particle's current state
//
//! \param[in] p Current particle
void write_particle_track(Particle& p);
//! Write full particle state history to HDF5 track file
//
//! \param[in] p Current particle
void finalize_particle_track(Particle& p);
} // namespace openmc

View file

@ -34,7 +34,7 @@ Restart a previous run from a state point or a particle restart file named
Use \fIN\fP OpenMP threads.
.TP
.B "\-t\fR, \fP\-\-track"
Write tracks for all particles.
Write tracks for all particles (up to max_tracks).
.TP
.B "\-v\fR, \fP\-\-version"
Show version information.

View file

@ -30,6 +30,7 @@ from openmc.mixin import *
from openmc.plotter import *
from openmc.search import *
from openmc.polynomial import *
from openmc.tracks import *
from . import examples
# Import a few names from the model module

View file

@ -690,7 +690,10 @@ class Cell(IDManagerMixin):
for key in ('temperature', 'rotation', 'translation'):
value = get_text(elem, key)
if value is not None:
setattr(c, key, [float(x) for x in value.split()])
values = [float(x) for x in value.split()]
if key == 'rotation' and len(values) == 9:
values = np.array(values).reshape(3, 3)
setattr(c, key, values)
# Add this cell to appropriate universe
univ_id = int(get_text(elem, 'universe', 0))

View file

@ -29,9 +29,9 @@ class Material(IDManagerMixin):
To create a material, one should create an instance of this class, add
nuclides or elements with :meth:`Material.add_nuclide` or
`Material.add_element`, respectively, and set the total material density
with `Material.set_density()`. The material can then be assigned to a cell
using the :attr:`Cell.fill` attribute.
:meth:`Material.add_element`, respectively, and set the total material
density with :meth:`Material.set_density()`. The material can then be
assigned to a cell using the :attr:`Cell.fill` attribute.
Parameters
----------

View file

@ -1,4 +1,4 @@
from abc import ABC
from abc import ABC, abstractmethod
from collections.abc import Iterable
from math import pi
from numbers import Real, Integral
@ -60,7 +60,7 @@ class MeshBase(IDManagerMixin, ABC):
return string
def _volume_dim_check(self):
if len(self.dimension) != 3 or \
if self.n_dimension != 3 or \
any([d == 0 for d in self.dimension]):
raise RuntimeError(f'Mesh {self.id} is not 3D. '
'Volumes cannot be provided.')
@ -126,7 +126,75 @@ class MeshBase(IDManagerMixin, ABC):
raise ValueError(f'Unrecognized mesh type "{mesh_type}" found.')
class RegularMesh(MeshBase):
class StructuredMesh(MeshBase):
"""A base class for structured mesh functionality
Parameters
----------
mesh_id : int
Unique identifier for the mesh
name : str
Name of the mesh
Attributes
----------
id : int
Unique identifier for the mesh
name : str
Name of the mesh
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@property
@abstractmethod
def dimension(self):
pass
@property
@abstractmethod
def n_dimension(self):
pass
@property
@abstractmethod
def _grids(self):
pass
@property
def vertices(self):
"""Return coordinates of mesh vertices.
Returns
-------
vertices : numpy.ndarray
Returns a numpy.ndarray representing the coordinates of the mesh
vertices with a shape equal to (dim1 + 1, ..., dimn + 1, ndim).
"""
return np.stack(np.meshgrid(*self._grids, indexing='ij'), axis=-1)
@property
def centroids(self):
"""Return coordinates of mesh element centroids.
Returns
-------
centroids : numpy.ndarray
Returns a numpy.ndarray representing the mesh element centroid
coordinates with a shape equal to (dim1, ..., dimn, ndim).
"""
ndim = self.n_dimension
vertices = self.vertices
s0 = (slice(0, -1),)*ndim + (slice(None),)
s1 = (slice(1, None),)*ndim + (slice(None),)
return (vertices[s0] + vertices[s1]) / 2
class RegularMesh(StructuredMesh):
"""A regular Cartesian mesh in one, two, or three dimensions
Parameters
@ -244,6 +312,30 @@ class RegularMesh(MeshBase):
nx, = self.dimension
return ((x,) for x in range(1, nx + 1))
@property
def _grids(self):
ndim = len(self._dimension)
if ndim == 3:
x0, y0, z0 = self.lower_left
x1, y1, z1 = self.upper_right
nx, ny, nz = self.dimension
xarr = np.linspace(x0, x1, nx + 1)
yarr = np.linspace(y0, y1, ny + 1)
zarr = np.linspace(z0, z1, nz + 1)
return (xarr, yarr, zarr)
elif ndim == 2:
x0, y0 = self.lower_left
x1, y1 = self.upper_right
nx, ny = self.dimension
xarr = np.linspace(x0, x1, nx + 1)
yarr = np.linspace(y0, y1, ny + 1)
return (xarr, yarr)
else:
nx, = self.dimension
x0, = self.lower_left
x1, = self.upper_right
return (np.linspace(x0, x1, nx + 1),)
@dimension.setter
def dimension(self, dimension):
cv.check_type('mesh dimension', dimension, Iterable, Integral)
@ -440,7 +532,7 @@ class RegularMesh(MeshBase):
for entry in bc:
cv.check_value('bc', entry, _BOUNDARY_TYPES)
n_dim = len(self.dimension)
n_dim = self.n_dimension
# Build the cell which will contain the lattice
xplanes = [openmc.XPlane(self.lower_left[0], boundary_type=bc[0]),
@ -537,7 +629,7 @@ def Mesh(*args, **kwargs):
return RegularMesh(*args, **kwargs)
class RectilinearMesh(MeshBase):
class RectilinearMesh(StructuredMesh):
"""A 3D rectilinear Cartesian mesh
Parameters
@ -598,6 +690,10 @@ class RectilinearMesh(MeshBase):
def z_grid(self):
return self._z_grid
@property
def _grids(self):
return (self.x_grid, self.y_grid, self.z_grid)
@property
def volumes(self):
"""Return Volumes for every mesh cell
@ -726,7 +822,7 @@ class RectilinearMesh(MeshBase):
return element
class CylindricalMesh(MeshBase):
class CylindricalMesh(StructuredMesh):
"""A 3D cylindrical mesh
Parameters
@ -789,6 +885,10 @@ class CylindricalMesh(MeshBase):
def z_grid(self):
return self._z_grid
@property
def _grids(self):
return (self.r_grid, self.phi_grid, self.z_grid)
@property
def indices(self):
nr, np, nz = self.dimension
@ -913,7 +1013,7 @@ class CylindricalMesh(MeshBase):
return np.multiply.outer(np.outer(V_r, V_p), V_z)
class SphericalMesh(MeshBase):
class SphericalMesh(StructuredMesh):
"""A 3D spherical mesh
Parameters
@ -977,6 +1077,10 @@ class SphericalMesh(MeshBase):
def phi_grid(self):
return self._phi_grid
@property
def _grids(self):
return (self.r_grid, self.theta_grid, self.phi_grid)
@property
def indices(self):
nr, nt, np = self.dimension
@ -1146,7 +1250,7 @@ class UnstructuredMesh(MeshBase):
(1.0, 1.0, 1.0), ...]
"""
def __init__(self, filename, library, mesh_id=None, name='',
length_multiplier=1.0):
length_multiplier=1.0):
super().__init__(mesh_id, name)
self.filename = filename
self._volumes = None
@ -1241,6 +1345,19 @@ class UnstructuredMesh(MeshBase):
Real)
self._length_multiplier = length_multiplier
@property
def dimension(self):
return self.n_elements
@property
def n_dimension(self):
return 3
@property
def vertices(self):
raise NotImplementedError("Vertices for UnstructuredMesh objects are "
"not yet available")
def __repr__(self):
string = super().__repr__()
string += '{: <16}=\t{}\n'.format('\tFilename', self.filename)
@ -1371,7 +1488,7 @@ class UnstructuredMesh(MeshBase):
subelement.text = self.filename
if self._length_multiplier != 1.0:
element.set("length_multiplier", str(self.length_multiplier))
element.set("length_multiplier", str(self.length_multiplier))
return element

View file

@ -212,7 +212,7 @@ class CylinderSector(CompositeSurface):
class IsogonalOctagon(CompositeSurface):
"""Infinite isogonal octagon composite surface
r"""Infinite isogonal octagon composite surface
An isogonal octagon is composed of eight planar surfaces. The prism is
parallel to the x, y, or z axis. The remaining two axes (y and z, z and x,

View file

@ -213,8 +213,8 @@ class Plot(IDManagerMixin):
The basis directions for the plot
background : Iterable of int or str
Color of the background
mask_components : Iterable of openmc.Cell or openmc.Material
The cells or materials to plot
mask_components : Iterable of openmc.Cell or openmc.Material or int
The cells or materials (or corresponding IDs) to mask
mask_background : Iterable of int or str
Color to apply to all cells/materials not listed in mask_components
show_overlaps : bool
@ -222,8 +222,10 @@ class Plot(IDManagerMixin):
overlap_color : Iterable of int or str
Color to apply to overlapping regions
colors : dict
Dictionary indicating that certain cells/materials (keys) should be
displayed with a particular color.
Dictionary indicating that certain cells/materials should be
displayed with a particular color. The keys can be of type
:class:`~openmc.Cell`, :class:`~openmc.Material`, or int (ID for a
cell/material).
level : int
Universe depth to plot at
meshlines : dict
@ -373,14 +375,15 @@ class Plot(IDManagerMixin):
def colors(self, colors):
cv.check_type('plot colors', colors, Mapping)
for key, value in colors.items():
cv.check_type('plot color key', key, (openmc.Cell, openmc.Material))
cv.check_type('plot color key', key,
(openmc.Cell, openmc.Material, Integral))
self._check_color('plot color value', value)
self._colors = colors
@mask_components.setter
def mask_components(self, mask_components):
cv.check_type('plot mask components', mask_components, Iterable,
(openmc.Cell, openmc.Material))
(openmc.Cell, openmc.Material, Integral))
self._mask_components = mask_components
@mask_background.setter
@ -634,11 +637,16 @@ class Plot(IDManagerMixin):
color = _SVG_COLORS[color.lower()]
subelement.text = ' '.join(str(x) for x in color)
# Helper function that returns the domain ID given either a
# Cell/Material object or the domain ID itself
def get_id(domain):
return domain if isinstance(domain, Integral) else domain.id
if self._colors:
for domain, color in sorted(self._colors.items(),
key=lambda x: x[0].id):
key=lambda x: get_id(x[0])):
subelement = ET.SubElement(element, "color")
subelement.set("id", str(domain.id))
subelement.set("id", str(get_id(domain)))
if isinstance(color, str):
color = _SVG_COLORS[color.lower()]
subelement.set("rgb", ' '.join(str(x) for x in color))
@ -646,7 +654,7 @@ class Plot(IDManagerMixin):
if self._mask_components is not None:
subelement = ET.SubElement(element, "mask")
subelement.set("components", ' '.join(
str(d.id) for d in self._mask_components))
str(get_id(d)) for d in self._mask_components))
color = self._mask_background
if color is not None:
if isinstance(color, str):
@ -720,15 +728,14 @@ class Plot(IDManagerMixin):
# Set plot colors
colors = {}
for color_elem in elem.findall("color"):
uid = color_elem.get("id")
uid = int(color_elem.get("id"))
colors[uid] = tuple([int(x) for x in color_elem.get("rgb").split()])
# TODO: set colors (needs geometry information)
plot.colors = colors
# Set masking information
mask_elem = elem.find("mask")
if mask_elem is not None:
mask_components = [int(x) for x in mask_elem.get("components").split()]
# TODO: set mask components (needs geometry information)
plot.mask_components = [int(x) for x in mask_elem.get("components").split()]
background = mask_elem.get("background")
if background is not None:
plot.mask_background = tuple([int(x) for x in background.split()])

View file

@ -2,6 +2,7 @@ import os
import typing # imported separately as py3.8 requires typing.Iterable
from collections.abc import Iterable, Mapping, MutableSequence
from enum import Enum
import itertools
from math import ceil
from numbers import Integral, Real
from pathlib import Path
@ -104,6 +105,10 @@ class Settings:
Maximum number of times a particle can split during a history
.. versionadded:: 0.13
max_tracks : int
Maximum number of tracks written to a track file (per MPI process).
.. versionadded:: 0.13.1
no_reduce : bool
Indicate that all user-defined and global tallies should not be reduced
across processes in a parallel calculation.
@ -187,7 +192,7 @@ class Settings:
integers: the batch number, generation number, and particle number
track : tuple or list
Specify particles for which track files should be written. Each particle
is identified by a triplet with the batch number, generation number, and
is identified by a tuple with the batch number, generation number, and
particle number.
trigger_active : bool
Indicate whether tally triggers are used
@ -290,6 +295,7 @@ class Settings:
self._weight_windows = cv.CheckedList(WeightWindows, 'weight windows')
self._weight_windows_on = None
self._max_splits = None
self._max_tracks = None
@property
def run_mode(self):
@ -475,6 +481,10 @@ class Settings:
def max_splits(self):
return self._max_splits
@property
def max_tracks(self):
return self._max_tracks
@run_mode.setter
def run_mode(self, run_mode: str):
cv.check_value('run mode', run_mode, {x.value for x in RunMode})
@ -776,16 +786,18 @@ class Settings:
self._trace = trace
@track.setter
def track(self, track: typing.Iterable[int]):
cv.check_type('track', track, Iterable, Integral)
if len(track) % 3 != 0:
msg = f'Unable to set the track to "{track}" since its length is ' \
'not a multiple of 3'
raise ValueError(msg)
for t in zip(track[::3], track[1::3], track[2::3]):
def track(self, track: typing.Iterable[typing.Iterable[int]]):
cv.check_type('track', track, Iterable)
for t in track:
if len(t) != 3:
msg = f'Unable to set the track to "{t}" since its length is not 3'
raise ValueError(msg)
cv.check_greater_than('track batch', t[0], 0)
cv.check_greater_than('track generation', t[0], 0)
cv.check_greater_than('track particle', t[0], 0)
cv.check_greater_than('track generation', t[1], 0)
cv.check_greater_than('track particle', t[2], 0)
cv.check_type('track batch', t[0], Integral)
cv.check_type('track generation', t[1], Integral)
cv.check_type('track particle', t[2], Integral)
self._track = track
@ufs_mesh.setter
@ -881,9 +893,15 @@ class Settings:
@max_splits.setter
def max_splits(self, value: int):
cv.check_type('maximum particle splits', value, Integral)
cv.check_greater_than('max particles in flight', value, 0)
cv.check_greater_than('max particle splits', value, 0)
self._max_splits = value
@max_tracks.setter
def max_tracks(self, value: int):
cv.check_type('maximum particle tracks', value, Integral)
cv.check_greater_than('maximum particle tracks', value, 0, True)
self._max_tracks = value
def _create_run_mode_subelement(self, root):
elem = ET.SubElement(root, "run_mode")
elem.text = self._run_mode.value
@ -1110,7 +1128,7 @@ class Settings:
def _create_track_subelement(self, root):
if self._track is not None:
element = ET.SubElement(root, "track")
element.text = ' '.join(map(str, self._track))
element.text = ' '.join(map(str, itertools.chain(*self._track)))
def _create_ufs_mesh_subelement(self, root):
if self.ufs_mesh is not None:
@ -1196,6 +1214,11 @@ class Settings:
elem = ET.SubElement(root, "max_splits")
elem.text = str(self._max_splits)
def _create_max_tracks_subelement(self, root):
if self._max_tracks is not None:
elem = ET.SubElement(root, "max_tracks")
elem.text = str(self._max_tracks)
def _eigenvalue_from_xml_element(self, root):
elem = root.find('eigenvalue')
if elem is not None:
@ -1424,7 +1447,8 @@ class Settings:
def _track_from_xml_element(self, root):
text = get_text(root, 'track')
if text is not None:
self.track = [int(x) for x in text.split()]
values = [int(x) for x in text.split()]
self.track = list(zip(values[::3], values[1::3], values[2::3]))
def _ufs_mesh_from_xml_element(self, root):
text = get_text(root, 'ufs_mesh')
@ -1498,6 +1522,11 @@ class Settings:
if text is not None:
self.max_splits = int(text)
def _max_tracks_from_xml_element(self, root):
text = get_text(root, 'max_tracks')
if text is not None:
self.max_tracks = int(text)
def export_to_xml(self, path: Union[str, os.PathLike] = 'settings.xml'):
"""Export simulation settings to an XML file.
@ -1554,6 +1583,7 @@ class Settings:
self._create_write_initial_source_subelement(root_element)
self._create_weight_windows_subelement(root_element)
self._create_max_splits_subelement(root_element)
self._create_max_tracks_subelement(root_element)
# Clean the indentation in the file to be user-readable
clean_indentation(root_element)
@ -1633,6 +1663,7 @@ class Settings:
settings._write_initial_source_from_xml_element(root)
settings._weight_windows_from_xml_element(root)
settings._max_splits_from_xml_element(root)
settings._max_tracks_from_xml_element(root)
# TODO: Get volume calculations

View file

@ -298,6 +298,10 @@ class SourceParticle:
self.surf_id = surf_id
self.particle = particle
def __repr__(self):
name = self.particle.name.lower()
return f'<SourceParticle: {name} at E={self.E:.6e} eV>'
def to_tuple(self):
"""Return source particle attributes as a tuple

View file

@ -1,6 +1,6 @@
from abc import ABC, abstractmethod
from collections.abc import Iterable
from math import pi
from math import pi, cos
from numbers import Real
from xml.etree import ElementTree as ET
@ -8,7 +8,7 @@ import numpy as np
import openmc.checkvalue as cv
from .._xml import get_text
from .univariate import Univariate, Uniform
from .univariate import Univariate, Uniform, PowerLaw
class UnitSphere(ABC):
@ -208,7 +208,6 @@ class Monodirectional(UnitSphere):
"""
def __init__(self, reference_uvw=[1., 0., 0.]):
super().__init__(reference_uvw)
@ -375,8 +374,8 @@ class SphericalIndependent(Spatial):
r"""Spatial distribution represented in spherical coordinates.
This distribution allows one to specify coordinates whose :math:`r`,
:math:`\theta`, and :math:`\phi` components are sampled independently from
one another and centered on the coordinates (x0, y0, z0).
:math:`\theta`, and :math:`\phi` components are sampled independently
from one another and centered on the coordinates (x0, y0, z0).
.. versionadded: 0.12
@ -385,9 +384,9 @@ class SphericalIndependent(Spatial):
r : openmc.stats.Univariate
Distribution of r-coordinates in a reference frame specified by
the origin parameter
theta : openmc.stats.Univariate
Distribution of theta-coordinates (angle relative to the z-axis) in a
reference frame specified by the origin parameter
cos_theta : openmc.stats.Univariate
Distribution of the cosine of the theta-coordinates (angle relative to
the z-axis) in a reference frame specified by the origin parameter
phi : openmc.stats.Univariate
Distribution of phi-coordinates (azimuthal angle) in a reference frame
specified by the origin parameter
@ -399,9 +398,9 @@ class SphericalIndependent(Spatial):
----------
r : openmc.stats.Univariate
Distribution of r-coordinates in the local reference frame
theta : openmc.stats.Univariate
Distribution of theta-coordinates (angle relative to the z-axis) in the
local reference frame
cos_theta : openmc.stats.Univariate
Distribution of the cosine of the theta-coordinates (angle relative to
the z-axis) in the local reference frame
phi : openmc.stats.Univariate
Distribution of phi-coordinates (azimuthal angle) in the local
reference frame
@ -411,9 +410,9 @@ class SphericalIndependent(Spatial):
"""
def __init__(self, r, theta, phi, origin=(0.0, 0.0, 0.0)):
def __init__(self, r, cos_theta, phi, origin=(0.0, 0.0, 0.0)):
self.r = r
self.theta = theta
self.cos_theta = cos_theta
self.phi = phi
self.origin = origin
@ -422,8 +421,8 @@ class SphericalIndependent(Spatial):
return self._r
@property
def theta(self):
return self._theta
def cos_theta(self):
return self._cos_theta
@property
def phi(self):
@ -438,10 +437,10 @@ class SphericalIndependent(Spatial):
cv.check_type('r coordinate', r, Univariate)
self._r = r
@theta.setter
def theta(self, theta):
cv.check_type('theta coordinate', theta, Univariate)
self._theta = theta
@cos_theta.setter
def cos_theta(self, cos_theta):
cv.check_type('cos_theta coordinate', cos_theta, Univariate)
self._cos_theta = cos_theta
@phi.setter
def phi(self, phi):
@ -466,7 +465,7 @@ class SphericalIndependent(Spatial):
element = ET.Element('space')
element.set('type', 'spherical')
element.append(self.r.to_xml_element('r'))
element.append(self.theta.to_xml_element('theta'))
element.append(self.cos_theta.to_xml_element('cos_theta'))
element.append(self.phi.to_xml_element('phi'))
element.set("origin", ' '.join(map(str, self.origin)))
return element
@ -487,10 +486,10 @@ class SphericalIndependent(Spatial):
"""
r = Univariate.from_xml_element(elem.find('r'))
theta = Univariate.from_xml_element(elem.find('theta'))
cos_theta = Univariate.from_xml_element(elem.find('cos_theta'))
phi = Univariate.from_xml_element(elem.find('phi'))
origin = [float(x) for x in elem.get('origin').split()]
return cls(r, theta, phi, origin=origin)
return cls(r, cos_theta, phi, origin=origin)
class CylindricalIndependent(Spatial):
@ -640,7 +639,6 @@ class Box(Spatial):
"""
def __init__(self, lower_left, upper_right, only_fissionable=False):
self.lower_left = lower_left
self.upper_right = upper_right
@ -779,3 +777,42 @@ class Point(Spatial):
"""
xyz = [float(x) for x in get_text(elem, 'parameters').split()]
return cls(xyz)
def spherical_uniform(r_outer, r_inner=0.0, thetas=(0., pi), phis=(0., 2*pi),
origin=(0., 0., 0.)):
"""Return a uniform spatial distribution over a spherical shell.
This function provides a uniform spatial distribution over a spherical
shell between `r_inner` and `r_outer`. Optionally, the range of angles
can be restricted by the `thetas` and `phis` arguments.
.. versionadded: 0.13.1
Parameters
----------
r_outer : float
Outer radius of the spherical shell in [cm]
r_inner : float, optional
Inner radius of the spherical shell in [cm]
thetas : iterable of float, optional
Starting and ending theta coordinates (angle relative to
the z-axis) in radius in a reference frame centered at `origin`
phis : iterable of float, optional
Starting and ending phi coordinates (azimuthal angle) in
radians in a reference frame centered at `origin`
origin: iterable of float, optional
Coordinates (x0, y0, z0) of the center of the spherical
reference frame for the distribution.
Returns
-------
openmc.stats.SphericalIndependent
Uniform distribution over the spherical shell
"""
r_dist = PowerLaw(r_inner, r_outer, 2)
cos_thetas_dist = Uniform(cos(thetas[1]), cos(thetas[0]))
phis_dist = Uniform(phis[0], phis[1])
return SphericalIndependent(r_dist, cos_thetas_dist, phis_dist, origin)

View file

@ -1,4 +1,5 @@
from abc import ABC, abstractmethod
from collections import defaultdict
from collections.abc import Iterable
from numbers import Real
from xml.etree import ElementTree as ET
@ -156,6 +157,38 @@ class Discrete(Univariate):
p = params[len(params)//2:]
return cls(x, p)
@classmethod
def merge(cls, dists, probs):
"""Merge multiple discrete distributions into a single distribution
Parameters
----------
dists : iterable of openmc.stats.Discrete
Discrete distributions to combine
probs : iterable of float
Probability of each distribution
Returns
-------
openmc.stats.Discrete
Combined discrete distribution
"""
if len(dists) != len(probs):
raise ValueError("Number of distributions and probabilities must match.")
# Combine distributions accounting for duplicate x values
x_merged = set()
p_merged = defaultdict(float)
for dist, p_dist in zip(dists, probs):
for x, p in zip(dist.x, dist.p):
x_merged.add(x)
p_merged[x] += p*p_dist
# Create values and probabilities as arrays
x_arr = np.array(sorted(x_merged))
p_arr = np.array([p_merged[x] for x in x_arr])
return cls(x_arr, p_arr)
class Uniform(Univariate):
"""Distribution with constant probability over a finite interval [a,b]

View file

@ -760,7 +760,7 @@ class XPlane(PlaneMixin, Surface):
Parameters
----------
x0 : float, optional
Location of the plane. Defaults to 0.
Location of the plane in [cm]. Defaults to 0.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
@ -775,7 +775,7 @@ class XPlane(PlaneMixin, Surface):
Attributes
----------
x0 : float
Location of the plane
Location of the plane in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -819,7 +819,7 @@ class YPlane(PlaneMixin, Surface):
Parameters
----------
y0 : float, optional
Location of the plane
Location of the plane in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
@ -834,7 +834,7 @@ class YPlane(PlaneMixin, Surface):
Attributes
----------
y0 : float
Location of the plane
Location of the plane in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -878,7 +878,7 @@ class ZPlane(PlaneMixin, Surface):
Parameters
----------
z0 : float, optional
Location of the plane. Defaults to 0.
Location of the plane in [cm]. Defaults to 0.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
@ -893,7 +893,7 @@ class ZPlane(PlaneMixin, Surface):
Attributes
----------
z0 : float
Location of the plane
Location of the plane in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -991,8 +991,8 @@ class QuadricMixin:
Parameters
----------
point : 3-tuple of float
The Cartesian coordinates, :math:`(x',y',z')`, at which the surface
equation should be evaluated.
The Cartesian coordinates, :math:`(x',y',z')`, in [cm] at which the
surface equation should be evaluated.
Returns
-------
@ -1106,13 +1106,13 @@ class Cylinder(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate for the origin of the Cylinder. Defaults to 0
x-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
y0 : float, optional
y-coordinate for the origin of the Cylinder. Defaults to 0
y-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
z0 : float, optional
z-coordinate for the origin of the Cylinder. Defaults to 0
z-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
r : float, optional
Radius of the cylinder. Defaults to 1.
Radius of the cylinder in [cm]. Defaults to 1.
dx : float, optional
x-component of the vector representing the axis of the cylinder.
Defaults to 0.
@ -1136,13 +1136,13 @@ class Cylinder(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate for the origin of the Cylinder
x-coordinate for the origin of the Cylinder in [cm]
y0 : float
y-coordinate for the origin of the Cylinder
y-coordinate for the origin of the Cylinder in [cm]
z0 : float
z-coordinate for the origin of the Cylinder
z-coordinate for the origin of the Cylinder in [cm]
r : float
Radius of the cylinder
Radius of the cylinder in [cm]
dx : float
x-component of the vector representing the axis of the cylinder
dy : float
@ -1243,7 +1243,7 @@ class Cylinder(QuadricMixin, Surface):
p1, p2 : 3-tuples
Points that pass through the plane, p1 will be used as (x0, y0, z0)
r : float, optional
Radius of the cylinder. Defaults to 1.
Radius of the cylinder in [cm]. Defaults to 1.
kwargs : dict
Keyword arguments passed to the :class:`Cylinder` constructor
@ -1288,11 +1288,11 @@ class XCylinder(QuadricMixin, Surface):
Parameters
----------
y0 : float, optional
y-coordinate for the origin of the Cylinder. Defaults to 0
y-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
z0 : float, optional
z-coordinate for the origin of the Cylinder. Defaults to 0
z-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
r : float, optional
Radius of the cylinder. Defaults to 1.
Radius of the cylinder in [cm]. Defaults to 1.
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
@ -1307,11 +1307,11 @@ class XCylinder(QuadricMixin, Surface):
Attributes
----------
y0 : float
y-coordinate for the origin of the Cylinder
y-coordinate for the origin of the Cylinder in [cm]
z0 : float
z-coordinate for the origin of the Cylinder
z-coordinate for the origin of the Cylinder in [cm]
r : float
Radius of the cylinder
Radius of the cylinder in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -1379,11 +1379,11 @@ class YCylinder(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate for the origin of the Cylinder. Defaults to 0
x-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
z0 : float, optional
z-coordinate for the origin of the Cylinder. Defaults to 0
z-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
r : float, optional
Radius of the cylinder. Defaults to 1.
Radius of the cylinder in [cm]. Defaults to 1.
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
@ -1398,11 +1398,11 @@ class YCylinder(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate for the origin of the Cylinder
x-coordinate for the origin of the Cylinder in [cm]
z0 : float
z-coordinate for the origin of the Cylinder
z-coordinate for the origin of the Cylinder in [cm]
r : float
Radius of the cylinder
Radius of the cylinder in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -1470,11 +1470,11 @@ class ZCylinder(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate for the origin of the Cylinder. Defaults to 0
x-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
y0 : float, optional
y-coordinate for the origin of the Cylinder. Defaults to 0
y-coordinate for the origin of the Cylinder in [cm]. Defaults to 0
r : float, optional
Radius of the cylinder. Defaults to 1.
Radius of the cylinder in [cm]. Defaults to 1.
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
@ -1489,11 +1489,11 @@ class ZCylinder(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate for the origin of the Cylinder
x-coordinate for the origin of the Cylinder in [cm]
y0 : float
y-coordinate for the origin of the Cylinder
y-coordinate for the origin of the Cylinder in [cm]
r : float
Radius of the cylinder
Radius of the cylinder in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -1560,13 +1560,13 @@ class Sphere(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate of the center of the sphere. Defaults to 0.
x-coordinate of the center of the sphere in [cm]. Defaults to 0.
y0 : float, optional
y-coordinate of the center of the sphere. Defaults to 0.
y-coordinate of the center of the sphere in [cm]. Defaults to 0.
z0 : float, optional
z-coordinate of the center of the sphere. Defaults to 0.
z-coordinate of the center of the sphere in [cm]. Defaults to 0.
r : float, optional
Radius of the sphere. Defaults to 1.
Radius of the sphere in [cm]. Defaults to 1.
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
@ -1580,13 +1580,13 @@ class Sphere(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate of the center of the sphere
x-coordinate of the center of the sphere in [cm]
y0 : float
y-coordinate of the center of the sphere
y-coordinate of the center of the sphere in [cm]
z0 : float
z-coordinate of the center of the sphere
z-coordinate of the center of the sphere in [cm]
r : float
Radius of the sphere
Radius of the sphere in [cm]
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -1653,11 +1653,11 @@ class Cone(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate of the apex. Defaults to 0.
x-coordinate of the apex in [cm]. Defaults to 0.
y0 : float, optional
y-coordinate of the apex. Defaults to 0.
y-coordinate of the apex in [cm]. Defaults to 0.
z0 : float, optional
z-coordinate of the apex. Defaults to 0.
z-coordinate of the apex in [cm]. Defaults to 0.
r2 : float, optional
Parameter related to the aperature. Defaults to 1.
dx : float, optional
@ -1682,11 +1682,11 @@ class Cone(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate of the apex
x-coordinate of the apex in [cm]
y0 : float
y-coordinate of the apex
y-coordinate of the apex in [cm]
z0 : float
z-coordinate of the apex
z-coordinate of the apex in [cm]
r2 : float
Parameter related to the aperature
dx : float
@ -1796,11 +1796,11 @@ class XCone(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate of the apex. Defaults to 0.
x-coordinate of the apex in [cm]. Defaults to 0.
y0 : float, optional
y-coordinate of the apex. Defaults to 0.
y-coordinate of the apex in [cm]. Defaults to 0.
z0 : float, optional
z-coordinate of the apex. Defaults to 0.
z-coordinate of the apex in [cm]. Defaults to 0.
r2 : float, optional
Parameter related to the aperature. Defaults to 1.
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional
@ -1816,11 +1816,11 @@ class XCone(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate of the apex
x-coordinate of the apex in [cm]
y0 : float
y-coordinate of the apex
y-coordinate of the apex in [cm]
z0 : float
z-coordinate of the apex
z-coordinate of the apex in [cm]
r2 : float
Parameter related to the aperature
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
@ -1885,11 +1885,11 @@ class YCone(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate of the apex. Defaults to 0.
x-coordinate of the apex in [cm]. Defaults to 0.
y0 : float, optional
y-coordinate of the apex. Defaults to 0.
y-coordinate of the apex in [cm]. Defaults to 0.
z0 : float, optional
z-coordinate of the apex. Defaults to 0.
z-coordinate of the apex in [cm]. Defaults to 0.
r2 : float, optional
Parameter related to the aperature. Defaults to 1.
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional
@ -1905,11 +1905,11 @@ class YCone(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate of the apex
x-coordinate of the apex in [cm]
y0 : float
y-coordinate of the apex
y-coordinate of the apex in [cm]
z0 : float
z-coordinate of the apex
z-coordinate of the apex in [cm]
r2 : float
Parameter related to the aperature
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
@ -1974,11 +1974,11 @@ class ZCone(QuadricMixin, Surface):
Parameters
----------
x0 : float, optional
x-coordinate of the apex. Defaults to 0.
x-coordinate of the apex in [cm]. Defaults to 0.
y0 : float, optional
y-coordinate of the apex. Defaults to 0.
y-coordinate of the apex in [cm]. Defaults to 0.
z0 : float, optional
z-coordinate of the apex. Defaults to 0.
z-coordinate of the apex in [cm]. Defaults to 0.
r2 : float, optional
Parameter related to the aperature. Defaults to 1.
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional
@ -1994,11 +1994,11 @@ class ZCone(QuadricMixin, Surface):
Attributes
----------
x0 : float
x-coordinate of the apex
x-coordinate of the apex in [cm]
y0 : float
y-coordinate of the apex
y-coordinate of the apex in [cm]
z0 : float
z-coordinate of the apex
z-coordinate of the apex in [cm]
r2 : float
Parameter related to the aperature
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
@ -2198,34 +2198,34 @@ class XTorus(TorusMixin, Surface):
Parameters
----------
x0 : float
x-coordinate of the center of the axis of revolution
x-coordinate of the center of the axis of revolution in [cm]
y0 : float
y-coordinate of the center of the axis of revolution
y-coordinate of the center of the axis of revolution in [cm]
z0 : float
z-coordinate of the center of the axis of revolution
z-coordinate of the center of the axis of revolution in [cm]
a : float
Major radius of the torus
Major radius of the torus in [cm]
b : float
Minor radius of the torus (parallel to axis of revolution)
Minor radius of the torus in [cm] (parallel to axis of revolution)
c : float
Minor radius of the torus (perpendicular to axis of revolution)
Minor radius of the torus in [cm] (perpendicular to axis of revolution)
kwargs : dict
Keyword arguments passed to the :class:`Surface` constructor
Attributes
----------
x0 : float
x-coordinate of the center of the axis of revolution
x-coordinate of the center of the axis of revolution in [cm]
y0 : float
y-coordinate of the center of the axis of revolution
y-coordinate of the center of the axis of revolution in [cm]
z0 : float
z-coordinate of the center of the axis of revolution
z-coordinate of the center of the axis of revolution in [cm]
a : float
Major radius of the torus
Major radius of the torus in [cm]
b : float
Minor radius of the torus (parallel to axis of revolution)
Minor radius of the torus in [cm] (parallel to axis of revolution)
c : float
Minor radius of the torus (perpendicular to axis of revolution)
Minor radius of the torus in [cm] (perpendicular to axis of revolution)
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.
@ -2269,32 +2269,32 @@ class YTorus(TorusMixin, Surface):
Parameters
----------
x0 : float
x-coordinate of the center of the axis of revolution
x-coordinate of the center of the axis of revolution in [cm]
y0 : float
y-coordinate of the center of the axis of revolution
y-coordinate of the center of the axis of revolution in [cm]
z0 : float
z-coordinate of the center of the axis of revolution
z-coordinate of the center of the axis of revolution in [cm]
a : float
Major radius of the torus
Major radius of the torus in [cm]
b : float
Minor radius of the torus (parallel to axis of revolution)
Minor radius of the torus in [cm] (parallel to axis of revolution)
c : float
Minor radius of the torus (perpendicular to axis of revolution)
Minor radius of the torus in [cm] (perpendicular to axis of revolution)
kwargs : dict
Keyword arguments passed to the :class:`Surface` constructor
Attributes
----------
x0 : float
x-coordinate of the center of the axis of revolution
x-coordinate of the center of the axis of revolution in [cm]
y0 : float
y-coordinate of the center of the axis of revolution
y-coordinate of the center of the axis of revolution in [cm]
z0 : float
z-coordinate of the center of the axis of revolution
z-coordinate of the center of the axis of revolution in [cm]
a : float
Major radius of the torus
Major radius of the torus in [cm]
b : float
Minor radius of the torus (parallel to axis of revolution)
Minor radius of the torus in [cm] (parallel to axis of revolution)
c : float
Minor radius of the torus (perpendicular to axis of revolution)
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
@ -2340,34 +2340,34 @@ class ZTorus(TorusMixin, Surface):
Parameters
----------
x0 : float
x-coordinate of the center of the axis of revolution
x-coordinate of the center of the axis of revolution in [cm]
y0 : float
y-coordinate of the center of the axis of revolution
y-coordinate of the center of the axis of revolution in [cm]
z0 : float
z-coordinate of the center of the axis of revolution
z-coordinate of the center of the axis of revolution in [cm]
a : float
Major radius of the torus
Major radius of the torus in [cm]
b : float
Minor radius of the torus (parallel to axis of revolution)
Minor radius of the torus in [cm] (parallel to axis of revolution)
c : float
Minor radius of the torus (perpendicular to axis of revolution)
Minor radius of the torus in [cm] (perpendicular to axis of revolution)
kwargs : dict
Keyword arguments passed to the :class:`Surface` constructor
Attributes
----------
x0 : float
x-coordinate of the center of the axis of revolution
x-coordinate of the center of the axis of revolution in [cm]
y0 : float
y-coordinate of the center of the axis of revolution
y-coordinate of the center of the axis of revolution in [cm]
z0 : float
z-coordinate of the center of the axis of revolution
z-coordinate of the center of the axis of revolution in [cm]
a : float
Major radius of the torus
Major radius of the torus in [cm]
b : float
Minor radius of the torus (parallel to axis of revolution)
Minor radius of the torus in [cm] (parallel to axis of revolution)
c : float
Minor radius of the torus (perpendicular to axis of revolution)
Minor radius of the torus in [cm] (perpendicular to axis of revolution)
boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}
Boundary condition that defines the behavior for particles hitting the
surface.

291
openmc/tracks.py Normal file
View file

@ -0,0 +1,291 @@
from collections import namedtuple
from collections.abc import Sequence
import h5py
from .checkvalue import check_filetype_version
from .source import SourceParticle, ParticleType
ParticleTrack = namedtuple('ParticleTrack', ['particle', 'states'])
ParticleTrack.__doc__ = """\
Particle track information
Parameters
----------
particle : openmc.ParticleType
Type of the particle
states : numpy.ndarray
Structured array containing each state of the particle. The structured array
contains the following fields: ``r`` (position; each direction in [cm]),
``u`` (direction), ``E`` (energy in [eV]), ``time`` (time in [s]), ``wgt``
(weight), ``cell_id`` (cell ID) , ``cell_instance`` (cell instance), and
``material_id`` (material ID).
"""
def _particle_track_repr(self):
name = self.particle.name.lower()
return f"<ParticleTrack: {name}, {len(self.states)} states>"
ParticleTrack.__repr__ = _particle_track_repr
_VERSION_TRACK = 3
def _identifier(dset_name):
"""Return (batch, gen, particle) tuple given dataset name"""
_, batch, gen, particle = dset_name.split('_')
return (int(batch), int(gen), int(particle))
class Track(Sequence):
"""Tracks resulting from a single source particle
This class stores information for all tracks resulting from a primary source
particle and any secondary particles that it created. The track for each
primary/secondary particle is stored in the :attr:`particle_tracks`
attribute.
Parameters
----------
dset : h5py.Dataset
Dataset to read track data from
Attributes
----------
identifier : tuple
Tuple of (batch, generation, particle number)
particle_tracks : list
List of tuples containing (particle type, array of track states)
sources : list
List of :class:`SourceParticle` representing each primary/secondary
particle
"""
def __init__(self, dset):
tracks = dset[()]
offsets = dset.attrs['offsets']
particles = dset.attrs['particles']
self.identifier = _identifier(dset.name)
# Construct list of track histories
tracks_list = []
for particle, start, end in zip(particles, offsets[:-1], offsets[1:]):
ptype = ParticleType(particle)
tracks_list.append(ParticleTrack(ptype, tracks[start:end]))
self.particle_tracks = tracks_list
def __repr__(self):
return f'<Track {self.identifier}: {len(self.particle_tracks)} particles>'
def __getitem__(self, index):
return self.particle_tracks[index]
def __len__(self):
return len(self.particle_tracks)
def filter(self, particle=None, state_filter=None):
"""Filter particle tracks by given criteria
Parameters
----------
particle : {'neutron', 'photon', 'electron', 'positron'}
Matching particle type
state_filter : function
Function that takes a state (structured datatype) and returns a bool
depending on some criteria.
Returns
-------
Track
New instance with only matching :class:`openmc.ParticleTrack` objects
Examples
--------
Get all particle tracks for photons:
>>> track.filter(particle='photon')
Get all particle tracks that entered cell with ID=15:
>>> track.filter(state_filter=lambda s: s['cell_id'] == 15)
Get all particle tracks in entered material with ID=2:
>>> track.filter(state_filter=lambda s: s['material_id'] == 2)
See Also
--------
openmc.ParticleTrack
"""
matching = []
for t in self:
# Check for matching particle
if particle is not None:
if t.particle.name.lower() != particle:
continue
# Apply arbitrary state filter
match = True
if state_filter is not None:
for state in t.states:
if state_filter(state):
break
else:
match = False
if match:
matching.append(t)
# Return new Track instance with only matching particle tracks
track = type(self).__new__(type(self))
track.identifier = self.identifier
track.particle_tracks = matching
return track
def plot(self, axes=None):
"""Produce a 3D plot of particle tracks
Parameters
----------
axes : matplotlib.axes.Axes, optional
Axes for plot
Returns
-------
axes : matplotlib.axes.Axes
Axes for plot
"""
import matplotlib.pyplot as plt
# Setup axes is one wasn't passed
if axes is None:
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlabel('x [cm]')
ax.set_ylabel('y [cm]')
ax.set_zlabel('z [cm]')
else:
ax = axes
# Plot each particle track
for _, states in self:
r = states['r']
ax.plot3D(r['x'], r['y'], r['z'])
return ax
@property
def sources(self):
sources = []
for particle_track in self:
particle_type = ParticleType(particle_track.particle)
state = particle_track.states[0]
sources.append(
SourceParticle(
r=state['r'], u=state['u'], E=state['E'],
time=state['time'], wgt=state['wgt'],
particle=particle_type
)
)
return sources
class Tracks(list):
"""Collection of particle tracks
This class behaves like a list and can be indexed using the normal subscript
notation. Each element in the list is a :class:`openmc.Track` object.
Parameters
----------
filepath : str or pathlib.Path
Path of file to load
"""
def __init__(self, filepath='tracks.h5'):
# Read data from track file
with h5py.File(filepath, 'r') as fh:
# Check filetype and version
check_filetype_version(fh, 'track', _VERSION_TRACK)
for dset_name in sorted(fh, key=_identifier):
dset = fh[dset_name]
self.append(Track(dset))
def filter(self, particle=None, state_filter=None):
"""Filter tracks by given criteria
Parameters
----------
particle : {'neutron', 'photon', 'electron', 'positron'}
Matching particle type
state_filter : function
Function that takes a state (structured datatype) and returns a bool
depending on some criteria.
Returns
-------
Tracks
List of :class:`openmc.Track` objects
See Also
--------
openmc.Track.filter
"""
# Create a new Tracks instance but avoid call to __init__
matching = type(self).__new__(type(self))
# Append matching Track objects
for track in self:
if track.filter(particle, state_filter):
matching.append(track)
return matching
def plot(self):
"""Produce a 3D plot of particle tracks
Returns
-------
matplotlib.axes.Axes
Axes for plot
"""
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlabel('x [cm]')
ax.set_ylabel('y [cm]')
ax.set_zlabel('z [cm]')
for track in self:
track.plot(ax)
return ax
@staticmethod
def combine(track_files, path='tracks.h5'):
"""Combine multiple track files into a single track file
Parameters
----------
track_files : list of path-like
Paths to track files to combine
path : path-like
Path of combined track file to create
"""
with h5py.File(path, 'w') as h5_out:
for i, fname in enumerate(track_files):
with h5py.File(fname, 'r') as h5_in:
# Copy file attributes for first file
if i == 0:
h5_out.attrs['filetype'] = h5_in.attrs['filetype']
h5_out.attrs['version'] = h5_in.attrs['version']
# Copy each 'track_*' dataset from input file
for dset in h5_in:
h5_in.copy(dset, h5_out)

24
scripts/openmc-track-combine Executable file
View file

@ -0,0 +1,24 @@
#!/usr/bin/env python3
"""Combine multiple HDF5 particle track files."""
import argparse
import openmc
def main():
# Parse command-line arguments
parser = argparse.ArgumentParser(
description='Combine particle track files into a single .h5 file.')
parser.add_argument('input', metavar='IN', nargs='+',
help='Input HDF5 particle track filename(s).')
parser.add_argument('-o', '--out', metavar='OUT', default='tracks.h5',
help='Output HDF5 particle track file.')
args = parser.parse_args()
openmc.Tracks.combine(args.input, args.out)
if __name__ == '__main__':
main()

View file

@ -4,18 +4,16 @@
"""
import os
import argparse
import struct
import h5py
import openmc
import vtk
def _parse_args():
# Create argument parser.
parser = argparse.ArgumentParser(
description='Convert particle track file to a .pvtp file.')
description='Convert particle track file(s) to a .pvtp file.')
parser.add_argument('input', metavar='IN', type=str, nargs='+',
help='Input particle track data filename(s).')
parser.add_argument('-o', '--out', metavar='OUT', type=str, dest='out',
@ -41,25 +39,22 @@ def main():
point_offset = 0
for fname in args.input:
# Write coordinate values to points array.
track = h5py.File(fname)
n_particles = track.attrs['n_particles']
n_coords = track.attrs['n_coords']
coords = []
for i in range(n_particles):
coords.append(track['coordinates_' + str(i + 1)][()])
for j in range(n_coords[i]):
points.InsertNextPoint(coords[i][j,:])
track_file = openmc.Tracks(fname)
for track in track_file:
for particle in track:
for state in particle.states:
points.InsertNextPoint(state['r'])
for i in range(n_particles):
# Create VTK line and assign points to line.
line = vtk.vtkPolyLine()
line.GetPointIds().SetNumberOfIds(n_coords[i])
for j in range(n_coords[i]):
line.GetPointIds().SetId(j, point_offset + j)
# Create VTK line and assign points to line.
n = particle.states.size
line = vtk.vtkPolyLine()
line.GetPointIds().SetNumberOfIds(n)
for i in range(n):
line.GetPointIds().SetId(i, point_offset + i)
point_offset += n
# Add line to cell array
cells.InsertNextCell(line)
point_offset += n_coords[i]
# Add line to cell array
cells.InsertNextCell(line)
data = vtk.vtkPolyData()
data.SetPoints(points)

View file

@ -846,6 +846,20 @@ void read_cells(pugi::xml_node node)
read_dagmc_universes(node);
populate_universes();
// Allocate the cell overlap count if necessary.
if (settings::check_overlaps) {
model::overlap_check_count.resize(model::cells.size(), 0);
}
if (model::cells.size() == 0) {
fatal_error("No cells were found in the geometry.xml file");
}
}
void populate_universes()
{
// Populate the Universe vector and map.
for (int i = 0; i < model::cells.size(); i++) {
int32_t uid = model::cells[i]->universe_;
@ -860,15 +874,6 @@ void read_cells(pugi::xml_node node)
}
}
model::universes.shrink_to_fit();
// Allocate the cell overlap count if necessary.
if (settings::check_overlaps) {
model::overlap_check_count.resize(model::cells.size(), 0);
}
if (model::cells.size() == 0) {
fatal_error("No cells were found in the geometry.xml file");
}
}
//==============================================================================

View file

@ -12,7 +12,6 @@
#include "openmc/string_utils.h"
#ifdef DAGMC
#include "dagmcmetadata.hpp"
#include "uwuw.hpp"
#endif
#include <fmt/core.h>
@ -50,6 +49,9 @@ DAGUniverse::DAGUniverse(pugi::xml_node node)
if (check_for_node(node, "filename")) {
filename_ = get_node_value(node, "filename");
if (!file_exists(filename_)) {
fatal_error(fmt::format("DAGMC file '{}' could not be found", filename_));
}
} else {
fatal_error("Must specify a file for the DAGMC universe");
}
@ -71,6 +73,23 @@ DAGUniverse::DAGUniverse(
const std::string& filename, bool auto_geom_ids, bool auto_mat_ids)
: filename_(filename), adjust_geometry_ids_(auto_geom_ids),
adjust_material_ids_(auto_mat_ids)
{
set_id();
initialize();
}
DAGUniverse::DAGUniverse(std::shared_ptr<moab::DagMC> dagmc_ptr,
const std::string& filename, bool auto_geom_ids, bool auto_mat_ids)
: dagmc_instance_(dagmc_ptr), filename_(filename),
adjust_geometry_ids_(auto_geom_ids), adjust_material_ids_(auto_mat_ids)
{
set_id();
init_metadata();
read_uwuw_materials();
init_geometry();
}
void DAGUniverse::set_id()
{
// determine the next universe id
int32_t next_univ_id = 0;
@ -82,48 +101,27 @@ DAGUniverse::DAGUniverse(
// set the universe id
id_ = next_univ_id;
initialize();
}
void DAGUniverse::initialize()
{
geom_type() = GeometryType::DAG;
// determine the next cell id
int32_t next_cell_id = 0;
for (const auto& c : model::cells) {
if (c->id_ > next_cell_id)
next_cell_id = c->id_;
}
cell_idx_offset_ = model::cells.size();
next_cell_id++;
init_dagmc();
// determine the next surface id
int32_t next_surf_id = 0;
for (const auto& s : model::surfaces) {
if (s->id_ > next_surf_id)
next_surf_id = s->id_;
}
surf_idx_offset_ = model::surfaces.size();
next_surf_id++;
init_metadata();
read_uwuw_materials();
init_geometry();
}
void DAGUniverse::init_dagmc()
{
// create a new DAGMC instance
dagmc_instance_ = std::make_shared<moab::DagMC>();
// --- Materials ---
// read any UWUW materials from the file
read_uwuw_materials();
// check for uwuw material definitions
bool using_uwuw = uses_uwuw();
// notify user if UWUW materials are going to be used
if (using_uwuw) {
write_message("Found UWUW Materials in the DAGMC geometry file.", 6);
}
// load the DAGMC geometry
filename_ = settings::path_input + filename_;
if (!file_exists(filename_)) {
@ -135,18 +133,35 @@ void DAGUniverse::initialize()
// initialize acceleration data structures
rval = dagmc_instance_->init_OBBTree();
MB_CHK_ERR_CONT(rval);
}
void DAGUniverse::init_metadata()
{
// parse model metadata
dagmcMetaData DMD(dagmc_instance_.get(), false, false);
DMD.load_property_data();
dmd_ptr =
std::make_unique<dagmcMetaData>(dagmc_instance_.get(), false, false);
dmd_ptr->load_property_data();
std::vector<std::string> keywords {"temp"};
std::map<std::string, std::string> dum;
std::string delimiters = ":/";
moab::ErrorCode rval;
rval = dagmc_instance_->parse_properties(keywords, dum, delimiters.c_str());
MB_CHK_ERR_CONT(rval);
}
// --- Cells (Volumes) ---
void DAGUniverse::init_geometry()
{
moab::ErrorCode rval;
// determine the next cell id
int32_t next_cell_id = 0;
for (const auto& c : model::cells) {
if (c->id_ > next_cell_id)
next_cell_id = c->id_;
}
cell_idx_offset_ = model::cells.size();
next_cell_id++;
// initialize cell objects
int n_cells = dagmc_instance_->num_entities(3);
@ -175,7 +190,7 @@ void DAGUniverse::initialize()
// --- Materials ---
// determine volume material assignment
std::string mat_str = DMD.get_volume_property("material", vol_handle);
std::string mat_str = dmd_ptr->get_volume_property("material", vol_handle);
if (mat_str.empty()) {
fatal_error(fmt::format("Volume {} has no material assignment.", c->id_));
@ -191,9 +206,10 @@ void DAGUniverse::initialize()
if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") {
c->material_.push_back(MATERIAL_VOID);
} else {
if (using_uwuw) {
if (uses_uwuw()) {
// lookup material in uwuw if present
std::string uwuw_mat = DMD.volume_material_property_data_eh[vol_handle];
std::string uwuw_mat =
dmd_ptr->volume_material_property_data_eh[vol_handle];
if (uwuw_->material_library.count(uwuw_mat) != 0) {
// Note: material numbers are set by UWUW
int mat_number = uwuw_->material_library.get_material(uwuw_mat)
@ -243,7 +259,14 @@ void DAGUniverse::initialize()
has_graveyard_ = graveyard;
// --- Surfaces ---
// determine the next surface id
int32_t next_surf_id = 0;
for (const auto& s : model::surfaces) {
if (s->id_ > next_surf_id)
next_surf_id = s->id_;
}
surf_idx_offset_ = model::surfaces.size();
next_surf_id++;
// initialize surface objects
int n_surfaces = dagmc_instance_->num_entities(2);
@ -256,7 +279,8 @@ void DAGUniverse::initialize()
: dagmc_instance_->id_by_index(2, i + 1);
// set BCs
std::string bc_value = DMD.get_surface_property("boundary", surf_handle);
std::string bc_value =
dmd_ptr->get_surface_property("boundary", surf_handle);
to_lower(bc_value);
if (bc_value.empty() || bc_value == "transmit" ||
bc_value == "transmission") {
@ -395,7 +419,7 @@ void DAGUniverse::to_hdf5(hid_t universes_group) const
bool DAGUniverse::uses_uwuw() const
{
return !uwuw_->material_library.empty();
return uwuw_ && !uwuw_->material_library.empty();
}
std::string DAGUniverse::get_uwuw_materials_xml() const
@ -484,33 +508,32 @@ void DAGUniverse::legacy_assign_material(
void DAGUniverse::read_uwuw_materials()
{
int32_t next_material_id = 0;
for (const auto& m : model::materials) {
next_material_id = std::max(m->id_, next_material_id);
}
next_material_id++;
// If no filename was provided, don't read UWUW materials
if (filename_ == "")
return;
uwuw_ = std::make_shared<UWUW>(filename_.c_str());
const auto& mat_lib = uwuw_->material_library;
if (mat_lib.size() == 0)
if (!uses_uwuw())
return;
// Notify user if UWUW materials are going to be used
write_message("Found UWUW Materials in the DAGMC geometry file.", 6);
// if we're using automatic IDs, update the UWUW material metadata
if (adjust_material_ids_) {
int32_t next_material_id = 0;
for (const auto& m : model::materials) {
next_material_id = std::max(m->id_, next_material_id);
}
next_material_id++;
for (auto& mat : uwuw_->material_library) {
mat.second->metadata["mat_number"] = next_material_id++;
}
}
std::stringstream ss;
ss << "<?xml version=\"1.0\"?>\n";
ss << "<materials>\n";
for (auto mat : mat_lib) {
ss << mat.second->openmc("atom");
}
ss << "</materials>";
std::string mat_xml_string = ss.str();
std::string mat_xml_string = get_uwuw_materials_xml();
// create a pugi XML document from this string
pugi::xml_document doc;

View file

@ -132,15 +132,15 @@ SphericalIndependent::SphericalIndependent(pugi::xml_node node)
r_ = make_unique<Discrete>(x, p, 1);
}
// Read distribution for theta-coordinate
if (check_for_node(node, "theta")) {
pugi::xml_node node_dist = node.child("theta");
theta_ = distribution_from_xml(node_dist);
// Read distribution for cos_theta-coordinate
if (check_for_node(node, "cos_theta")) {
pugi::xml_node node_dist = node.child("cos_theta");
cos_theta_ = distribution_from_xml(node_dist);
} else {
// If no distribution was specified, default to a single point at theta=0
// If no distribution was specified, default to a single point at cos_theta=0
double x[] {0.0};
double p[] {1.0};
theta_ = make_unique<Discrete>(x, p, 1);
cos_theta_ = make_unique<Discrete>(x, p, 1);
}
// Read distribution for phi-coordinate
@ -171,11 +171,12 @@ SphericalIndependent::SphericalIndependent(pugi::xml_node node)
Position SphericalIndependent::sample(uint64_t* seed) const
{
double r = r_->sample(seed);
double theta = theta_->sample(seed);
double cos_theta = cos_theta_->sample(seed);
double phi = phi_->sample(seed);
double x = r * sin(theta) * cos(phi) + origin_.x;
double y = r * sin(theta) * sin(phi) + origin_.y;
double z = r * cos(theta) + origin_.z;
// sin(theta) by sin**2 + cos**2 = 1
double x = r * std::sqrt(1 - cos_theta * cos_theta) * cos(phi) + origin_.x;
double y = r * std::sqrt(1 - cos_theta * cos_theta) * sin(phi) + origin_.y;
double z = r * cos_theta + origin_.z;
return {x, y, z};
}

View file

@ -85,6 +85,7 @@ int openmc_finalize()
settings::material_cell_offsets = true;
settings::max_particles_in_flight = 100000;
settings::max_splits = 1000;
settings::max_tracks = 1000;
settings::n_inactive = 0;
settings::n_particles = -1;
settings::output_summary = true;

View file

@ -318,7 +318,8 @@ void print_usage()
" -r, --restart Restart a previous run from a state point\n"
" or a particle restart file\n"
" -s, --threads Number of OpenMP threads\n"
" -t, --track Write tracks for all particles\n"
" -t, --track Write tracks for all particles (up to "
"max_tracks)\n"
" -e, --event Run using event-based parallelism\n"
" -v, --version Show version information\n"
" -h, --help Show this message\n");

View file

@ -337,6 +337,11 @@ void Particle::event_revive_from_secondary()
// Check for secondary particles if this particle is dead
if (!alive()) {
// Write final position for this particle
if (write_track()) {
write_particle_track(*this);
}
// If no secondary particles, break out of event loop
if (secondary_bank().empty())
return;
@ -359,7 +364,6 @@ void Particle::event_death()
// Finish particle track output.
if (write_track()) {
write_particle_track(*this);
finalize_particle_track(*this);
}
@ -454,8 +458,7 @@ void Particle::cross_surface()
// ==========================================================================
// COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS
// Remove lower coordinate levels and assignment of surface
surface() = 0;
// Remove lower coordinate levels
n_coord() = 1;
bool found = exhaustive_find_cell(*this);
@ -465,6 +468,7 @@ void Particle::cross_surface()
// the particle is really traveling tangent to a surface, if we move it
// forward a tiny bit it should fix the problem.
surface() = 0;
n_coord() = 1;
r() += TINY_BIT * u();

View file

@ -1,6 +1,8 @@
#include "openmc/particle_data.h"
#include "openmc/cell.h"
#include "openmc/geometry.h"
#include "openmc/material.h"
#include "openmc/nuclide.h"
#include "openmc/photon.h"
#include "openmc/settings.h"
@ -53,4 +55,20 @@ ParticleData::ParticleData()
photon_xs_.resize(data::elements.size());
}
TrackState ParticleData::get_track_state() const
{
TrackState state;
state.r = this->r();
state.u = this->u();
state.E = this->E();
state.time = this->time();
state.wgt = this->wgt();
state.cell_id = model::cells[this->lowest_coord().cell]->id_;
state.cell_instance = this->cell_instance();
if (this->material() != MATERIAL_VOID) {
state.material_id = model::materials[material()]->id_;
}
return state;
}
} // namespace openmc

View file

@ -95,6 +95,7 @@ int n_log_bins {8000};
int n_batches;
int n_max_batches;
int max_splits {1000};
int max_tracks {1000};
ResScatMethod res_scat_method {ResScatMethod::rvs};
double res_scat_energy_min {0.01};
double res_scat_energy_max {1000.0};
@ -802,6 +803,12 @@ void read_settings_xml()
}
if (check_for_node(root, "temperature_multipole")) {
temperature_multipole = get_node_value_bool(root, "temperature_multipole");
// Multipole currently doesn't work with photon transport
if (temperature_multipole && photon_transport) {
fatal_error("Multipole data cannot currently be used in conjunction with "
"photon transport.");
}
}
if (check_for_node(root, "temperature_range")) {
auto range = get_node_array<double>(root, "temperature_range");
@ -871,6 +878,10 @@ void read_settings_xml()
if (check_for_node(root, "max_splits")) {
settings::max_splits = std::stoi(get_node_value(root, "max_splits"));
}
if (check_for_node(root, "max_tracks")) {
settings::max_tracks = std::stoi(get_node_value(root, "max_tracks"));
}
}
void free_memory_settings()

View file

@ -82,6 +82,11 @@ int openmc_simulation_init()
// Allocate source, fission and surface source banks.
allocate_banks();
// Create track file if needed
if (!settings::track_identifiers.empty() || settings::write_all_tracks) {
open_track_file();
}
// If doing an event-based simulation, intialize the particle buffer
// and event queues
if (settings::event_based) {
@ -152,6 +157,11 @@ int openmc_simulation_finalize()
mat->mat_nuclide_index_.clear();
}
// Close track file if open
if (!settings::track_identifiers.empty() || settings::write_all_tracks) {
close_track_file();
}
// Increment total number of generations
simulation::total_gen += simulation::current_batch * settings::gen_per_batch;
@ -507,18 +517,7 @@ void initialize_history(Particle& p, int64_t index_source)
p.trace() = true;
// Set particle track.
p.write_track() = false;
if (settings::write_all_tracks) {
p.write_track() = true;
} else if (settings::track_identifiers.size() > 0) {
for (const auto& t : settings::track_identifiers) {
if (simulation::current_batch == t[0] &&
simulation::current_gen == t[1] && p.id() == t[2]) {
p.write_track() = true;
break;
}
}
}
p.write_track() = check_track_criteria(p);
// Display message if high verbosity or trace is on
if (settings::verbosity >= 9 || p.trace()) {

View file

@ -963,18 +963,23 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index,
score = score_neutron_heating(
p, tally, flux, HEATING, i_nuclide, atom_density);
} else {
// The energy deposited is the difference between the pre-collision and
// post-collision energy...
score = E - p.E();
if (i_nuclide == -1 || i_nuclide == p.event_nuclide()) {
// The energy deposited is the difference between the pre-collision
// and post-collision energy...
score = E - p.E();
// ...less the energy of any secondary particles since they will be
// transported individually later
const auto& bank = p.secondary_bank();
for (auto it = bank.end() - p.n_bank_second(); it < bank.end(); ++it) {
score -= it->E;
// ...less the energy of any secondary particles since they will be
// transported individually later
const auto& bank = p.secondary_bank();
for (auto it = bank.end() - p.n_bank_second(); it < bank.end();
++it) {
score -= it->E;
}
score *= p.wgt_last();
} else {
score = 0.0;
}
score *= p.wgt_last();
}
break;

View file

@ -2,6 +2,7 @@
#include "openmc/constants.h"
#include "openmc/hdf5_interface.h"
#include "openmc/message_passing.h"
#include "openmc/position.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
@ -9,6 +10,7 @@
#include "xtensor/xtensor.hpp"
#include <fmt/core.h>
#include <hdf5.h>
#include <cstddef> // for size_t
#include <string>
@ -19,6 +21,10 @@ namespace openmc {
// Global variables
//==============================================================================
hid_t track_file; //! HDF5 identifier for track file
hid_t track_dtype; //! HDF5 identifier for track datatype
int n_tracks_written; //! Number of tracks written
//==============================================================================
// Non-member functions
//==============================================================================
@ -26,45 +32,123 @@ namespace openmc {
void add_particle_track(Particle& p)
{
p.tracks().emplace_back();
p.tracks().back().particle = p.type();
}
void write_particle_track(Particle& p)
{
p.tracks().back().push_back(p.r());
p.tracks().back().states.push_back(p.get_track_state());
}
void open_track_file()
{
// Open file and write filetype/version -- when MPI is enabled and there is
// more than one rank, each rank writes its own file
#ifdef OPENMC_MPI
std::string filename;
if (mpi::n_procs > 1) {
filename = fmt::format("{}tracks_p{}.h5", settings::path_output, mpi::rank);
} else {
filename = fmt::format("{}tracks.h5", settings::path_output);
}
#else
std::string filename = fmt::format("{}tracks.h5", settings::path_output);
#endif
track_file = file_open(filename, 'w');
write_attribute(track_file, "filetype", "track");
write_attribute(track_file, "version", VERSION_TRACK);
// Create compound type for Position
hid_t postype = H5Tcreate(H5T_COMPOUND, sizeof(struct Position));
H5Tinsert(postype, "x", HOFFSET(Position, x), H5T_NATIVE_DOUBLE);
H5Tinsert(postype, "y", HOFFSET(Position, y), H5T_NATIVE_DOUBLE);
H5Tinsert(postype, "z", HOFFSET(Position, z), H5T_NATIVE_DOUBLE);
// Create compound type for TrackState
track_dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct TrackState));
H5Tinsert(track_dtype, "r", HOFFSET(TrackState, r), postype);
H5Tinsert(track_dtype, "u", HOFFSET(TrackState, u), postype);
H5Tinsert(track_dtype, "E", HOFFSET(TrackState, E), H5T_NATIVE_DOUBLE);
H5Tinsert(track_dtype, "time", HOFFSET(TrackState, time), H5T_NATIVE_DOUBLE);
H5Tinsert(track_dtype, "wgt", HOFFSET(TrackState, wgt), H5T_NATIVE_DOUBLE);
H5Tinsert(
track_dtype, "cell_id", HOFFSET(TrackState, cell_id), H5T_NATIVE_INT);
H5Tinsert(track_dtype, "cell_instance", HOFFSET(TrackState, cell_instance),
H5T_NATIVE_INT);
H5Tinsert(track_dtype, "material_id", HOFFSET(TrackState, material_id),
H5T_NATIVE_INT);
H5Tclose(postype);
}
void close_track_file()
{
H5Tclose(track_dtype);
file_close(track_file);
// Reset number of tracks written
n_tracks_written = 0;
}
bool check_track_criteria(const Particle& p)
{
if (settings::write_all_tracks) {
// Increment number of tracks written and get previous value
int n;
#pragma omp atomic capture
n = n_tracks_written++;
// Indicate that track should be written for this particle
return n < settings::max_tracks;
}
// Check for match from explicit track identifiers
if (settings::track_identifiers.size() > 0) {
for (const auto& t : settings::track_identifiers) {
if (simulation::current_batch == t[0] &&
simulation::current_gen == t[1] && p.id() == t[2]) {
return true;
}
}
}
return false;
}
void finalize_particle_track(Particle& p)
{
std::string filename =
fmt::format("{}track_{}_{}_{}.h5", settings::path_output,
simulation::current_batch, simulation::current_gen, p.id());
// Determine number of coordinates for each particle
vector<int> n_coords;
for (auto& coords : p.tracks()) {
n_coords.push_back(coords.size());
vector<int> offsets;
vector<int> particles;
vector<TrackState> tracks;
int offset = 0;
for (auto& track_i : p.tracks()) {
offsets.push_back(offset);
particles.push_back(static_cast<int>(track_i.particle));
offset += track_i.states.size();
tracks.insert(tracks.end(), track_i.states.begin(), track_i.states.end());
}
offsets.push_back(offset);
#pragma omp critical(FinalizeParticleTrack)
{
hid_t file_id = file_open(filename, 'w');
write_attribute(file_id, "filetype", "track");
write_attribute(file_id, "version", VERSION_TRACK);
write_attribute(file_id, "n_particles", p.tracks().size());
write_attribute(file_id, "n_coords", n_coords);
for (auto i = 1; i <= p.tracks().size(); ++i) {
const auto& t {p.tracks()[i - 1]};
size_t n = t.size();
xt::xtensor<double, 2> data({n, 3});
for (int j = 0; j < n; ++j) {
data(j, 0) = t[j].x;
data(j, 1) = t[j].y;
data(j, 2) = t[j].z;
}
std::string name = fmt::format("coordinates_{}", i);
write_dataset(file_id, name.c_str(), data);
}
file_close(file_id);
// Create name for dataset
std::string dset_name = fmt::format("track_{}_{}_{}",
simulation::current_batch, simulation::current_gen, p.id());
// Write array of TrackState to file
hsize_t dims[] {static_cast<hsize_t>(tracks.size())};
hid_t dspace = H5Screate_simple(1, dims, nullptr);
hid_t dset = H5Dcreate(track_file, dset_name.c_str(), track_dtype, dspace,
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
H5Dwrite(dset, track_dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, tracks.data());
// Write attributes
write_attribute(dset, "n_particles", p.tracks().size());
write_attribute(dset, "offsets", offsets);
write_attribute(dset, "particles", particles);
// Free resources
H5Dclose(dset);
H5Sclose(dspace);
}
// Clear particle tracks

View file

View file

@ -0,0 +1 @@
../legacy/dagmc.h5m

View file

@ -0,0 +1,40 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<dagmc_universe filename="dagmc.h5m" id="1" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<material depletable="true" id="40" name="no-void fuel">
<density units="g/cc" value="11" />
<nuclide ao="1.0" name="U235" />
</material>
<material id="41" name="water">
<density units="g/cc" value="1.0" />
<nuclide ao="2.0" name="H1" />
<nuclide ao="1.0" name="O16" />
<sab name="c_H_in_H2O" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>eigenvalue</run_mode>
<particles>100</particles>
<batches>5</batches>
<inactive>0</inactive>
<source strength="1.0">
<space type="box">
<parameters>-4 -4 -4 4 4 4</parameters>
</space>
</source>
<temperature_default>293</temperature_default>
</settings>
<?xml version='1.0' encoding='utf-8'?>
<tallies>
<filter id="1" type="cell">
<bins>1</bins>
</filter>
<tally id="1">
<filters>1</filters>
<scores>total</scores>
</tally>
</tallies>

View file

@ -0,0 +1,94 @@
#include "openmc/capi.h"
#include "openmc/cross_sections.h"
#include "openmc/dagmc.h"
#include "openmc/error.h"
#include "openmc/geometry.h"
#include "openmc/geometry_aux.h"
#include "openmc/material.h"
#include "openmc/nuclide.h"
#include <iostream>
int main(int argc, char* argv[])
{
using namespace openmc;
int openmc_err;
// Initialise OpenMC
openmc_err = openmc_init(argc, argv, nullptr);
if (openmc_err == -1) {
// This happens for the -h and -v flags
return EXIT_SUCCESS;
} else if (openmc_err) {
fatal_error(openmc_err_msg);
}
// Create DAGMC ptr
std::string filename = "dagmc.h5m";
std::shared_ptr<moab::DagMC> dag_ptr = std::make_shared<moab::DagMC>();
moab::ErrorCode rval = dag_ptr->load_file(filename.c_str());
if (rval != moab::MB_SUCCESS) {
fatal_error("Failed to load file");
}
// Initialize acceleration data structures
rval = dag_ptr->init_OBBTree();
if (rval != moab::MB_SUCCESS) {
fatal_error("Failed to initialise OBB tree");
}
// Get rid of existing geometry
std::unordered_map<std::string, int> nuclide_map_copy =
openmc::data::nuclide_map;
openmc::data::nuclides.clear();
openmc::data::nuclide_map = nuclide_map_copy;
openmc::model::surfaces.clear();
openmc::model::surface_map.clear();
openmc::model::cells.clear();
openmc::model::cell_map.clear();
openmc::model::universes.clear();
openmc::model::universe_map.clear();
// Update materials (emulate an external program)
for (auto& mat_ptr : openmc::model::materials) {
mat_ptr->set_temperature(300);
}
// Create new DAGMC universe
openmc::model::universes.push_back(
std::make_unique<openmc::DAGUniverse>(dag_ptr));
model::universe_map[model::universes.back()->id_] =
model::universes.size() - 1;
// Add cells to universes
openmc::populate_universes();
// Set root universe
openmc::model::root_universe = openmc::find_root_universe();
openmc::check_dagmc_root_univ();
// Final geometry setup and assign temperatures
openmc::finalize_geometry();
// Finalize cross sections having assigned temperatures
openmc::finalize_cross_sections();
// Check that we correctly assigned cell temperatures with non-void fill
for (auto& cell_ptr : openmc::model::cells) {
if (cell_ptr->material_.front() != openmc::C_NONE &&
cell_ptr->temperature() != 300) {
fatal_error("Failed to set cell temperature");
}
}
// Run OpenMC
openmc_err = openmc_run();
if (openmc_err)
fatal_error(openmc_err_msg);
// Deallocate memory
openmc_err = openmc_finalize();
if (openmc_err)
fatal_error(openmc_err_msg);
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,5 @@
k-combined:
8.426936E-01 5.715847E-02
tally 1:
8.093843E+00
1.328829E+01

View file

@ -0,0 +1,128 @@
from pathlib import Path
import os
import shutil
import subprocess
import textwrap
import openmc
import openmc.lib
import pytest
from tests.regression_tests import config
from tests.testing_harness import PyAPITestHarness
pytestmark = pytest.mark.skipif(
not openmc.lib._dagmc_enabled(),
reason="DAGMC is not enabled.")
# Test that an external DAGMC instance can be passed in through the C API
@pytest.fixture
def cpp_driver(request):
"""Compile the external source"""
# Get build directory and write CMakeLists.txt file
openmc_dir = Path(str(request.config.rootdir)) / 'build'
with open('CMakeLists.txt', 'w') as f:
f.write(textwrap.dedent("""
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
project(openmc_cpp_driver CXX)
add_executable(main main.cpp)
find_package(OpenMC REQUIRED HINTS {})
target_link_libraries(main OpenMC::libopenmc)
target_compile_features(main PUBLIC cxx_std_14)
set(CMAKE_CXX_FLAGS "-pedantic-errors")
add_compile_definitions(DAGMC=1)
""".format(openmc_dir)))
# Create temporary build directory and change to there
local_builddir = Path('build')
local_builddir.mkdir(exist_ok=True)
os.chdir(local_builddir)
mpi_arg = "On" if config['mpi'] else "Off"
try:
# Run cmake/make to build the shared libary
subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True)
subprocess.run(['make'], check=True)
os.chdir(os.path.pardir)
yield "./build/main"
finally:
# Remove local build directory when test is complete
shutil.rmtree('build')
os.remove('CMakeLists.txt')
@pytest.fixture
def model():
model = openmc.model.Model()
# Settings
model.settings.batches = 5
model.settings.inactive = 0
model.settings.particles = 100
source_box = openmc.stats.Box([-4, -4, -4],
[ 4, 4, 4])
source = openmc.Source(space=source_box)
model.settings.source = source
model.settings.temperature['default'] = 293
# Geometry
dag_univ = openmc.DAGMCUniverse("dagmc.h5m")
model.geometry = openmc.Geometry(dag_univ)
# Tallies
tally = openmc.Tally()
tally.scores = ['total']
tally.filters = [openmc.CellFilter(1)]
model.tallies = [tally]
# Materials
u235 = openmc.Material(name="no-void fuel")
u235.add_nuclide('U235', 1.0, 'ao')
u235.set_density('g/cc', 11)
u235.id = 40
water = openmc.Material(name="water")
water.add_nuclide('H1', 2.0, 'ao')
water.add_nuclide('O16', 1.0, 'ao')
water.set_density('g/cc', 1.0)
water.add_s_alpha_beta('c_H_in_H2O')
water.id = 41
mats = openmc.Materials([u235, water])
model.materials = mats
return model
class ExternalDAGMCTest(PyAPITestHarness):
def __init__(self, executable, statepoint_name, model):
super().__init__(statepoint_name, model)
self.executable = executable
def _run_openmc(self):
"""
Just test if results generated with the external C++ API are
self-consistent with the internal python API.
We generate the "truth" results with the python API but
the main test produces the results file by running the
executable compiled from main.cpp. This future-proofs
the test - we only care that the two routes are equivalent.
"""
if config['update']:
# Generate the results file with internal python API
openmc.run(openmc_exec=config['exe'], event_based=config['event'])
elif config['mpi']:
mpi_args = [config['mpiexec'], '-n', config['mpi_np']]
# Run main cpp executable with MPI
openmc.run(openmc_exec=self.executable,
mpi_args=mpi_args,
event_based=config['event'])
else:
# Run main cpp executable
openmc.run(openmc_exec=self.executable,
event_based=config['event'])
def test_external_dagmc(cpp_driver, model):
harness = ExternalDAGMCTest(cpp_driver, 'statepoint.5.h5', model)
harness.main()

View file

@ -67,8 +67,8 @@ tally 3:
0.000000E+00
0.000000E+00
0.000000E+00
1.764573E+05
3.113718E+10
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
@ -79,8 +79,8 @@ tally 3:
0.000000E+00
0.000000E+00
0.000000E+00
7.691658E+03
5.916160E+07
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00

View file

@ -53,9 +53,9 @@
<source strength="0.1">
<space origin="1.0 1.0 0.0" type="spherical">
<r parameters="2.0 3.0" type="uniform" />
<theta type="discrete">
<parameters>0.7853981633974483 1.5707963267948966 2.356194490192345 0.3 0.4 0.3</parameters>
</theta>
<cos_theta type="discrete">
<parameters>0.7071067811865476 0.0 -0.7071067811865475 0.3 0.4 0.3</parameters>
</cos_theta>
<phi parameters="0.0 6.283185307179586" type="uniform" />
</space>
<angle type="isotropic" />
@ -102,9 +102,9 @@
<source strength="0.1">
<space origin="1.0 1.0 0.0" type="spherical">
<r parameters="2.0 3.0 2.0" type="powerlaw" />
<theta type="discrete">
<parameters>0.7853981633974483 1.5707963267948966 2.356194490192345 0.3 0.4 0.3</parameters>
</theta>
<cos_theta type="discrete">
<parameters>0.7071067811865476 0.0 -0.7071067811865475 0.3 0.4 0.3</parameters>
</cos_theta>
<phi parameters="0.0 6.283185307179586" type="uniform" />
</space>
<angle type="isotropic" />

View file

@ -1,4 +1,4 @@
from math import pi
from math import pi, cos
import numpy as np
import openmc
@ -32,19 +32,19 @@ class SourceTestHarness(PyAPITestHarness):
r_dist = openmc.stats.Uniform(2., 3.)
r_dist1 = openmc.stats.PowerLaw(2., 3., 1.)
r_dist2 = openmc.stats.PowerLaw(2., 3., 2.)
theta_dist = openmc.stats.Discrete([pi/4, pi/2, 3*pi/4],
[0.3, 0.4, 0.3])
cos_theta_dist = openmc.stats.Discrete([cos(pi/4), 0.0, cos(3*pi/4)],
[0.3, 0.4, 0.3])
phi_dist = openmc.stats.Uniform(0.0, 2*pi)
spatial1 = openmc.stats.CartesianIndependent(x_dist, y_dist, z_dist)
spatial2 = openmc.stats.Box([-4., -4., -4.], [4., 4., 4.])
spatial3 = openmc.stats.Point([1.2, -2.3, 0.781])
spatial4 = openmc.stats.SphericalIndependent(r_dist, theta_dist,
spatial4 = openmc.stats.SphericalIndependent(r_dist, cos_theta_dist,
phi_dist,
origin=(1., 1., 0.))
spatial5 = openmc.stats.CylindricalIndependent(r_dist, phi_dist,
z_dist,
origin=(1., 1., 0.))
spatial6 = openmc.stats.SphericalIndependent(r_dist2, theta_dist,
spatial6 = openmc.stats.SphericalIndependent(r_dist2, cos_theta_dist,
phi_dist,
origin=(1., 1., 0.))
spatial7 = openmc.stats.CylindricalIndependent(r_dist1, phi_dist,

View file

@ -1,9 +1,266 @@
<?xml version="1.0"?>
<VTKFile type="PPolyData" version="0.1" byte_order="LittleEndian" header_type="UInt32" compressor="vtkZLibDataCompressor">
<PPolyData GhostLevel="0">
<PPoints>
<PDataArray type="Float32" Name="Points" NumberOfComponents="3"/>
</PPoints>
<Piece Source="poly_0.vtp"/>
</PPolyData>
</VTKFile>
ParticleType.NEUTRON [((-9.663085e-01, -6.616522e-01, -9.863690e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 0.000000e+00, 1.000000e+00, 23, 2403, 1)
((-1.281491e+00, -4.879529e-01, -7.708709e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 1.323629e-10, 1.000000e+00, 22, 2403, 3)
((-1.368452e+00, -4.400285e-01, -7.114141e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 1.688824e-10, 1.000000e+00, 21, 2403, 2)
((-2.150539e+00, -9.015151e-03, -1.766823e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 4.973246e-10, 1.000000e+00, 22, 2403, 3)
((-2.237499e+00, 3.890922e-02, -1.172255e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 5.338441e-10, 1.000000e+00, 23, 2403, 1)
((-2.453640e+00, 1.580257e-01, 3.055504e-02), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 6.246136e-10, 1.000000e+00, 23, 2402, 1)
((-2.767485e+00, 3.309877e-01, 2.451383e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 7.564146e-10, 1.000000e+00, 22, 2402, 3)
((-2.825099e+00, 3.627392e-01, 2.845305e-01), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 7.806100e-10, 1.000000e+00, 22, 2402, 3)
((-3.274427e+00, 6.029890e-01, 6.987901e-01), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 9.878481e-10, 1.000000e+00, 23, 2402, 1)
((-3.676327e+00, 8.178800e-01, 1.069324e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.173212e-09, 1.000000e+00, 23, 2416, 1)
((-4.089400e+00, 1.038745e+00, 1.450158e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.363728e-09, 1.000000e+00, 23, 2415, 1)
((-4.456639e+00, 1.235102e+00, 1.788735e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.533105e-09, 1.000000e+00, 22, 2415, 3)
((-4.536974e+00, 1.278056e+00, 1.862800e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.570157e-09, 1.000000e+00, 21, 2415, 2)
((-5.410401e+00, 1.745067e+00, 2.668060e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.972998e-09, 1.000000e+00, 22, 2415, 3)
((-5.490736e+00, 1.788021e+00, 2.742125e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 2.010050e-09, 1.000000e+00, 23, 2415, 1)
((-5.576301e+00, 1.833771e+00, 2.821012e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 2.049514e-09, 1.000000e+00, 23, 2415, 1)
((-5.725160e+00, 1.896661e+00, 2.330311e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 2.227030e-09, 1.000000e+00, 23, 2414, 1)
((-6.116514e+00, 2.061999e+00, 1.040248e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 2.693724e-09, 1.000000e+00, 22, 2414, 3)
((-6.534762e+00, 2.238699e+00, -3.384686e-01), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 3.192489e-09, 1.000000e+00, 23, 2414, 1)
((-7.043525e+00, 2.453640e+00, -2.015560e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 3.799195e-09, 1.000000e+00, 23, 2428, 1)
((-7.360920e+00, 2.587732e+00, -3.061825e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 4.177692e-09, 1.000000e+00, 11, 1757, 1)
((-8.996680e+00, 3.278803e+00, -8.453960e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 6.128354e-09, 1.000000e+00, 23, 2427, 1)
((-9.220205e+00, 3.373238e+00, -9.190791e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 6.394910e-09, 1.000000e+00, 22, 2427, 3)
((-9.320244e+00, 3.415502e+00, -9.520561e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 6.514208e-09, 1.000000e+00, 21, 2427, 2)
((-1.005591e+01, 3.726304e+00, -1.194562e+01), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 7.391498e-09, 1.000000e+00, 22, 2427, 3)
((-1.015595e+01, 3.768569e+00, -1.227539e+01), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 7.510796e-09, 1.000000e+00, 23, 2427, 1)
((-1.062054e+01, 3.964848e+00, -1.380687e+01), (-5.395885e-01, 2.109921e-01, -8.150623e-01), 4.068754e+06, 8.064826e-09, 1.000000e+00, 23, 2427, 1)
((-1.063244e+01, 3.969501e+00, -1.382484e+01), (-5.395885e-01, 2.109921e-01, -8.150623e-01), 4.068754e+06, 8.072756e-09, 1.000000e+00, 23, 2426, 1)
((-1.093907e+01, 4.089400e+00, -1.428802e+01), (-5.395885e-01, 2.109921e-01, -8.150623e-01), 4.068754e+06, 8.277097e-09, 1.000000e+00, 23, 2437, 1)
((-1.108410e+01, 4.146112e+00, -1.450710e+01), (4.855193e-01, 3.316053e-01, -8.088937e-01), 8.856868e+05, 8.373750e-09, 1.000000e+00, 23, 2437, 1)
((-1.080173e+01, 4.338973e+00, -1.497755e+01), (2.818553e-01, 5.419536e-02, -9.579251e-01), 7.653670e+05, 8.820862e-09, 1.000000e+00, 23, 2437, 1)
((-1.063244e+01, 4.371524e+00, -1.555290e+01), (2.818553e-01, 5.419536e-02, -9.579251e-01), 7.653670e+05, 9.317522e-09, 1.000000e+00, 23, 2438, 1)
((-1.041266e+01, 4.413783e+00, -1.629984e+01), (2.686183e-01, -3.269476e-01, -9.060626e-01), 6.562001e+05, 9.962308e-09, 1.000000e+00, 23, 2438, 1)
((-1.016754e+01, 4.115428e+00, -1.712667e+01), (-6.340850e-01, -7.142137e-01, -2.963697e-01), 7.155058e+04, 1.077718e-08, 1.000000e+00, 23, 2438, 1)
((-1.019064e+01, 4.089400e+00, -1.713747e+01), (-6.340850e-01, -7.142137e-01, -2.963697e-01), 7.155058e+04, 1.087569e-08, 1.000000e+00, 23, 2427, 1)
((-1.024915e+01, 4.023496e+00, -1.716481e+01), (1.827568e-01, -7.452255e-01, -6.412791e-01), 2.628504e+04, 1.112511e-08, 1.000000e+00, 23, 2427, 1)
((-1.018199e+01, 3.749638e+00, -1.740047e+01), (1.827568e-01, -7.452255e-01, -6.412791e-01), 2.628504e+04, 1.276389e-08, 1.000000e+00, 22, 2427, 3)
((-1.015866e+01, 3.654498e+00, -1.748234e+01), (1.827568e-01, -7.452255e-01, -6.412791e-01), 2.628504e+04, 1.333321e-08, 1.000000e+00, 21, 2427, 2)
((-1.009978e+01, 3.414404e+00, -1.768895e+01), (1.735861e-01, -9.678731e-01, 1.819053e-01), 2.620416e+04, 1.476994e-08, 1.000000e+00, 21, 2427, 2)
((-9.994809e+00, 2.829099e+00, -1.757894e+01), (4.635724e-01, 5.558106e-01, 6.900545e-01), 2.214649e+04, 1.747088e-08, 1.000000e+00, 21, 2427, 2)
((-9.553365e+00, 3.358379e+00, -1.692183e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 2.209726e-08, 1.000000e+00, 21, 2427, 2)
((-1.011854e+01, 3.687059e+00, -1.760205e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 2.716243e-08, 1.000000e+00, 22, 2427, 3)
((-1.020058e+01, 3.734765e+00, -1.770077e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 2.789760e-08, 1.000000e+00, 23, 2427, 1)
((-1.063244e+01, 3.985917e+00, -1.822054e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 3.176800e-08, 1.000000e+00, 23, 2426, 1)
((-1.081038e+01, 4.089400e+00, -1.843470e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 3.336273e-08, 1.000000e+00, 23, 2437, 1)
((-1.081564e+01, 4.092455e+00, -1.844103e+01), (-6.690877e-01, 6.718700e-01, -3.176671e-01), 1.355236e+04, 3.340982e-08, 1.000000e+00, 23, 2437, 1)
((-1.096190e+01, 4.239327e+00, -1.851047e+01), (-5.904492e-01, 8.058629e-01, 4.421237e-02), 1.153343e+04, 3.476743e-08, 1.000000e+00, 23, 2437, 1)
((-1.109457e+01, 4.420404e+00, -1.850054e+01), (-5.904492e-01, 8.058629e-01, 4.421237e-02), 1.153343e+04, 3.628014e-08, 1.000000e+00, 22, 2437, 3)
((-1.113484e+01, 4.475368e+00, -1.849752e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 3.673931e-08, 1.000000e+00, 22, 2437, 3)
((-1.117187e+01, 4.372425e+00, -1.850142e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 3.748914e-08, 1.000000e+00, 23, 2437, 1)
((-1.127367e+01, 4.089400e+00, -1.851213e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 3.955064e-08, 1.000000e+00, 23, 2426, 1)
((-1.135375e+01, 3.866733e+00, -1.852056e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.117251e-08, 1.000000e+00, 22, 2426, 3)
((-1.138419e+01, 3.782113e+00, -1.852377e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.178887e-08, 1.000000e+00, 21, 2426, 2)
((-1.172456e+01, 2.835777e+00, -1.855959e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.868184e-08, 1.000000e+00, 22, 2426, 3)
((-1.175499e+01, 2.751157e+00, -1.856280e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.929820e-08, 1.000000e+00, 23, 2426, 1)
((-1.179525e+01, 2.639224e+00, -1.856703e+01), (-4.738733e-01, -7.934600e-01, 3.819231e-01), 8.873828e+03, 5.011351e-08, 1.000000e+00, 23, 2426, 1)
((-1.190609e+01, 2.453640e+00, -1.847770e+01), (-4.738733e-01, -7.934600e-01, 3.819231e-01), 8.873828e+03, 5.190861e-08, 1.000000e+00, 23, 2411, 1)
((-1.222017e+01, 1.927741e+00, -1.822457e+01), (-5.716267e-01, 2.823908e-01, 7.703884e-01), 1.028345e+03, 5.699551e-08, 1.000000e+00, 23, 2411, 1)
((-1.226820e+01, 1.951470e+00, -1.815983e+01), (-5.716267e-01, 2.823908e-01, 7.703884e-01), 1.028345e+03, 5.888995e-08, 1.000000e+00, 23, 2100, 1)
((-1.236181e+01, 1.997712e+00, -1.803368e+01), (1.748787e-01, 6.613493e-01, 7.294070e-01), 4.250133e+02, 6.258186e-08, 1.000000e+00, 23, 2100, 1)
((-1.234968e+01, 2.043587e+00, -1.798308e+01), (-6.043263e-01, 1.493281e-01, 7.826180e-01), 4.022525e+02, 6.501444e-08, 1.000000e+00, 23, 2100, 1)
((-1.276513e+01, 2.146245e+00, -1.744506e+01), (-6.043263e-01, 1.493281e-01, 7.826180e-01), 4.022525e+02, 8.979603e-08, 1.000000e+00, 22, 2100, 3)
((-1.313233e+01, 2.236980e+00, -1.696953e+01), (-6.043263e-01, 1.493281e-01, 7.826180e-01), 4.022525e+02, 1.116994e-07, 1.000000e+00, 23, 2100, 1)
((-1.318468e+01, 2.249916e+00, -1.690173e+01), (-8.862976e-01, -6.402442e-02, 4.586692e-01), 3.113244e+02, 1.148223e-07, 1.000000e+00, 23, 2100, 1)
((-1.363685e+01, 2.217253e+00, -1.666773e+01), (5.149966e-01, 4.184610e-01, -7.481102e-01), 2.471907e+02, 1.357269e-07, 1.000000e+00, 23, 2100, 1)
((-1.334593e+01, 2.453640e+00, -1.709033e+01), (5.149966e-01, 4.184610e-01, -7.481102e-01), 2.471907e+02, 1.617034e-07, 1.000000e+00, 23, 2101, 1)
((-1.308145e+01, 2.668542e+00, -1.747452e+01), (5.149966e-01, 4.184610e-01, -7.481102e-01), 2.471907e+02, 1.853189e-07, 1.000000e+00, 22, 2101, 3)
((-1.304150e+01, 2.701005e+00, -1.753256e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 1.888862e-07, 1.000000e+00, 22, 2101, 3)
((-1.304665e+01, 2.669815e+00, -1.753058e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 1.903682e-07, 1.000000e+00, 23, 2101, 1)
((-1.308231e+01, 2.453640e+00, -1.751686e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 2.006392e-07, 1.000000e+00, 23, 2100, 1)
((-1.311790e+01, 2.237916e+00, -1.750317e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 2.108888e-07, 1.000000e+00, 22, 2100, 3)
((-1.313266e+01, 2.148507e+00, -1.749750e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 2.151369e-07, 1.000000e+00, 21, 2100, 2)
((-1.320190e+01, 1.728791e+00, -1.747087e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 2.350787e-07, 1.000000e+00, 21, 2100, 2)
((-1.309582e+01, 2.150526e+00, -1.834143e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 2.808996e-07, 1.000000e+00, 22, 2100, 3)
((-1.307365e+01, 2.238628e+00, -1.852329e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 2.904717e-07, 1.000000e+00, 23, 2100, 1)
((-1.301957e+01, 2.453640e+00, -1.896713e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 3.138324e-07, 1.000000e+00, 23, 2101, 1)
((-1.297913e+01, 2.614390e+00, -1.929896e+01), (6.012134e-01, 7.985735e-01, -2.868317e-02), 4.041011e+01, 3.312976e-07, 1.000000e+00, 23, 2101, 1)
((-1.295975e+01, 2.640133e+00, -1.929988e+01), (6.762805e-01, -5.875192e-01, 4.443713e-01), 3.527833e+01, 3.349640e-07, 1.000000e+00, 23, 2101, 1)
((-1.282907e+01, 2.526604e+00, -1.921402e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 3.584852e-07, 1.000000e+00, 23, 2101, 1)
((-1.284217e+01, 2.453640e+00, -1.921346e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 3.799102e-07, 1.000000e+00, 23, 2100, 1)
((-1.288683e+01, 2.204886e+00, -1.921158e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 4.529534e-07, 1.000000e+00, 22, 2100, 3)
((-1.290264e+01, 2.116831e+00, -1.921091e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 4.788097e-07, 1.000000e+00, 21, 2100, 2)
((-1.308145e+01, 1.120923e+00, -1.920338e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 7.712448e-07, 1.000000e+00, 22, 2100, 3)
((-1.309726e+01, 1.032868e+00, -1.920271e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 7.971010e-07, 1.000000e+00, 23, 2100, 1)
((-1.312690e+01, 8.677798e-01, -1.920146e+01), (-6.009065e-01, -7.617185e-01, 2.422732e-01), 3.971935e+00, 8.455768e-07, 1.000000e+00, 23, 2100, 1)
((-1.316626e+01, 8.178800e-01, -1.918559e+01), (-6.009065e-01, -7.617185e-01, 2.422732e-01), 3.971935e+00, 8.693415e-07, 1.000000e+00, 23, 2099, 1)
((-1.324019e+01, 7.241633e-01, -1.915578e+01), (-9.367321e-01, -1.471894e-01, -3.175976e-01), 2.365289e+00, 9.139738e-07, 1.000000e+00, 23, 2099, 1)
((-1.348497e+01, 6.857011e-01, -1.923877e+01), (-9.722929e-01, 2.325825e-01, -2.349230e-02), 1.799413e+00, 1.036815e-06, 1.000000e+00, 23, 2099, 1)
((-1.390396e+01, 7.859275e-01, -1.924890e+01), (-9.722929e-01, 2.325825e-01, -2.349230e-02), 1.799413e+00, 1.269071e-06, 1.000000e+00, 23, 2114, 1)
((-1.403754e+01, 8.178800e-01, -1.925212e+01), (-9.722929e-01, 2.325825e-01, -2.349230e-02), 1.799413e+00, 1.343115e-06, 1.000000e+00, 23, 2115, 1)
((-1.504223e+01, 1.058212e+00, -1.927640e+01), (-4.085804e-01, -2.719942e-01, -8.712526e-01), 1.628266e+00, 1.900041e-06, 1.000000e+00, 23, 2115, 1)
((-1.523744e+01, 9.282558e-01, -1.969268e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 2.170751e-06, 1.000000e+00, 23, 2115, 1)
((-1.553972e+01, 1.106680e+00, -1.997974e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 2.636878e-06, 1.000000e+00, 23, 2129, 1)
((-1.585973e+01, 1.295571e+00, -2.028364e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 3.130348e-06, 1.000000e+00, 22, 2129, 3)
((-1.593583e+01, 1.340489e+00, -2.035590e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 3.247693e-06, 1.000000e+00, 21, 2129, 2)
((-1.647184e+01, 1.656882e+00, -2.086494e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 4.074258e-06, 1.000000e+00, 21, 2129, 2)
((-1.585080e+01, 1.545037e+00, -2.159072e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 5.061748e-06, 1.000000e+00, 22, 2129, 3)
((-1.576406e+01, 1.529415e+00, -2.169209e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 5.199673e-06, 1.000000e+00, 23, 2129, 1)
((-1.553972e+01, 1.489015e+00, -2.195425e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 5.556377e-06, 1.000000e+00, 23, 2115, 1)
((-1.546081e+01, 1.474803e+00, -2.204648e+01), (3.257038e-01, -9.420596e-01, -8.025439e-02), 3.076319e-01, 5.681852e-06, 1.000000e+00, 23, 2115, 1)
((-1.543563e+01, 1.401988e+00, -2.205268e+01), (-3.677195e-01, -8.784218e-01, -3.052172e-01), 8.422973e-02, 5.782605e-06, 1.000000e+00, 23, 2115, 1)
((-1.553972e+01, 1.153338e+00, -2.213907e+01), (-3.677195e-01, -8.784218e-01, -3.052172e-01), 8.422973e-02, 6.487752e-06, 1.000000e+00, 23, 2129, 1)
((-1.565955e+01, 8.670807e-01, -2.223854e+01), (1.611814e-01, 2.692337e-01, -9.494913e-01), 8.559448e-02, 7.299552e-06, 1.000000e+00, 23, 2129, 1)
((-1.563263e+01, 9.120446e-01, -2.239711e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 7.712256e-06, 1.000000e+00, 23, 2129, 1)
((-1.592604e+01, 1.214620e+00, -2.249695e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 9.406031e-06, 1.000000e+00, 22, 2129, 3)
((-1.598742e+01, 1.277922e+00, -2.251784e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 9.760391e-06, 1.000000e+00, 21, 2129, 2)
((-1.670388e+01, 2.016770e+00, -2.276163e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 1.389636e-05, 1.000000e+00, 22, 2129, 3)
((-1.676526e+01, 2.080073e+00, -2.278252e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 1.425072e-05, 1.000000e+00, 23, 2129, 1)
((-1.681575e+01, 2.132139e+00, -2.279970e+01), (4.286076e-02, -9.409799e-01, -3.357375e-01), 1.813618e-02, 1.454218e-05, 1.000000e+00, 23, 2129, 1)
((-1.681374e+01, 2.087933e+00, -2.281547e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.479439e-05, 1.000000e+00, 23, 2129, 1)
((-1.682083e+01, 2.021795e+00, -2.287841e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.532970e-05, 1.000000e+00, 22, 2129, 3)
((-1.684417e+01, 1.804084e+00, -2.308558e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.709182e-05, 1.000000e+00, 21, 2129, 2)
((-1.686879e+01, 1.574384e+00, -2.330417e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.895097e-05, 1.000000e+00, 22, 2129, 3)
((-1.689212e+01, 1.356673e+00, -2.351134e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 2.071309e-05, 1.000000e+00, 23, 2129, 1)
((-1.689233e+01, 1.354754e+00, -2.351317e+01), (6.002458e-01, 4.967948e-01, 6.268172e-01), 8.602342e-03, 2.072862e-05, 1.000000e+00, 23, 2129, 1)
((-1.689148e+01, 1.355453e+00, -2.351229e+01), (6.002458e-01, 4.967948e-01, 6.268172e-01), 8.602342e-03, 2.073958e-05, 1.000000e+00, 22, 2129, 3)
((-1.682182e+01, 1.413107e+00, -2.343954e+01), (6.002458e-01, 4.967948e-01, 6.268172e-01), 8.602342e-03, 2.164421e-05, 1.000000e+00, 21, 2129, 2)
((-1.636364e+01, 1.792326e+00, -2.296107e+01), (9.641593e-01, -1.162836e-01, 2.384846e-01), 7.837617e-03, 2.759442e-05, 1.000000e+00, 21, 2129, 2)
((-1.598564e+01, 1.746738e+00, -2.286758e+01), (9.641593e-01, -1.162836e-01, 2.384846e-01), 7.837617e-03, 3.079609e-05, 0.000000e+00, 21, 2129, 2)]
ParticleType.NEUTRON [((-9.469716e-01, -2.580266e-01, 1.414357e-01), (1.438873e-01, 2.365819e-01, 9.608983e-01), 9.724461e+05, 0.000000e+00, 1.000000e+00, 23, 2403, 1)
((-9.064818e-01, -1.914524e-01, 4.118326e-01), (9.231638e-01, 3.100833e-01, 2.271937e-01), 1.746594e+05, 2.064696e-10, 1.000000e+00, 23, 2403, 1)
((-8.178800e-01, -1.616918e-01, 4.336377e-01), (9.231638e-01, 3.100833e-01, 2.271937e-01), 1.746594e+05, 3.725262e-10, 1.000000e+00, 11, 1756, 1)
((-3.834875e-01, -1.578285e-02, 5.405432e-01), (-5.547498e-01, -6.358598e-02, 8.295839e-01), 1.474011e+05, 1.186660e-09, 1.000000e+00, 11, 1756, 1)
((-7.999329e-01, -6.351625e-02, 1.163304e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 2.600465e-09, 1.000000e+00, 11, 1756, 1)
((-8.178800e-01, -6.341842e-02, 1.190721e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 2.662321e-09, 1.000000e+00, 23, 2403, 1)
((-1.035984e+00, -6.222961e-02, 1.523906e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 3.414026e-09, 1.000000e+00, 22, 2403, 3)
((-1.124618e+00, -6.174649e-02, 1.659308e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 3.719509e-09, 1.000000e+00, 21, 2403, 2)
((-1.705404e+00, -5.858082e-02, 2.546543e+00), (-3.196694e-01, -8.974451e-01, 3.039799e-01), 1.460188e+05, 5.721217e-09, 1.000000e+00, 21, 2403, 2)
((-1.851116e+00, -4.676544e-01, 2.685103e+00), (-3.196694e-01, -8.974451e-01, 3.039799e-01), 1.460188e+05, 6.583734e-09, 1.000000e+00, 22, 2403, 3)
((-1.880791e+00, -5.509662e-01, 2.713322e+00), (-3.196694e-01, -8.974451e-01, 3.039799e-01), 1.460188e+05, 6.759394e-09, 1.000000e+00, 23, 2403, 1)
((-1.948026e+00, -7.397227e-01, 2.777257e+00), (-8.068708e-01, -5.218555e-01, -2.768147e-01), 6.012975e+04, 7.157380e-09, 1.000000e+00, 23, 2403, 1)
((-2.068870e+00, -8.178800e-01, 2.735799e+00), (-8.068708e-01, -5.218555e-01, -2.768147e-01), 6.012975e+04, 7.598974e-09, 1.000000e+00, 23, 2388, 1)
((-2.111345e+00, -8.453511e-01, 2.721228e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 7.754188e-09, 1.000000e+00, 23, 2388, 1)
((-2.453640e+00, -1.161216e+00, 3.335542e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 1.421339e-08, 1.000000e+00, 23, 2387, 1)
((-2.715369e+00, -1.402736e+00, 3.805265e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 1.915229e-08, 1.000000e+00, 22, 2387, 3)
((-2.785083e+00, -1.467067e+00, 3.930380e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 2.046780e-08, 1.000000e+00, 21, 2387, 2)
((-2.940196e+00, -1.610203e+00, 4.208760e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 2.339483e-08, 1.000000e+00, 21, 2387, 2)
((-3.600602e+00, -1.239800e+00, 3.812386e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 3.112139e-08, 1.000000e+00, 22, 2387, 3)
((-3.682067e+00, -1.194109e+00, 3.763490e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 3.207450e-08, 1.000000e+00, 23, 2387, 1)
((-4.089400e+00, -9.656478e-01, 3.519009e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 3.684019e-08, 1.000000e+00, 23, 2386, 1)
((-4.185927e+00, -9.115084e-01, 3.461074e+00), (4.334152e-01, -1.576729e-02, -9.010564e-01), 2.532713e+01, 3.796953e-08, 1.000000e+00, 23, 2386, 1)
((-4.089400e+00, -9.150200e-01, 3.260397e+00), (4.334152e-01, -1.576729e-02, -9.010564e-01), 2.532713e+01, 6.996444e-08, 1.000000e+00, 23, 2387, 1)
((-3.920090e+00, -9.211794e-01, 2.908406e+00), (9.094673e-01, -3.659267e-01, -1.974002e-01), 9.547085e+00, 1.260840e-07, 1.000000e+00, 23, 2387, 1)
((-3.729948e+00, -9.976834e-01, 2.867135e+00), (-2.916959e-01, -6.494657e-01, -7.022164e-01), 8.590591e+00, 1.750036e-07, 1.000000e+00, 23, 2387, 1)
((-3.744933e+00, -1.031047e+00, 2.831062e+00), (-2.916959e-01, -6.494657e-01, -7.022164e-01), 8.590591e+00, 1.876753e-07, 0.000000e+00, 23, 2387, 1)]
ParticleType.NEUTRON [((6.474155e+00, -5.192870e+00, 5.413003e+00), (-3.042035e-02, -9.914979e-01, 1.265170e-01), 2.052226e+06, 4.450859e-05, 1.000000e+00, 21, 2367, 2)
((6.467293e+00, -5.416535e+00, 5.441544e+00), (-3.042035e-02, -9.914979e-01, 1.265170e-01), 2.052226e+06, 4.450870e-05, 1.000000e+00, 22, 2367, 3)
((6.464574e+00, -5.505149e+00, 5.452851e+00), (-3.042035e-02, -9.914979e-01, 1.265170e-01), 2.052226e+06, 4.450874e-05, 1.000000e+00, 23, 2367, 1)
((6.461877e+00, -5.593034e+00, 5.464065e+00), (6.364108e-01, -7.713322e-01, 5.284745e-03), 1.141417e+06, 4.450879e-05, 1.000000e+00, 23, 2367, 1)
((6.570892e+00, -5.725160e+00, 5.464970e+00), (6.364108e-01, -7.713322e-01, 5.284745e-03), 1.141417e+06, 4.450891e-05, 1.000000e+00, 32, 7, 1)
((6.821003e+00, -6.028296e+00, 5.467047e+00), (6.364108e-01, -7.713322e-01, 5.284745e-03), 1.141417e+06, 4.450917e-05, 1.000000e+00, 31, 7, 4)
((7.101210e+00, -6.367908e+00, 5.469374e+00), (6.364108e-01, -7.713322e-01, 5.284745e-03), 1.141417e+06, 4.450947e-05, 1.000000e+00, 32, 7, 1)
((7.360920e+00, -6.682677e+00, 5.471531e+00), (6.364108e-01, -7.713322e-01, 5.284745e-03), 1.141417e+06, 4.450975e-05, 1.000000e+00, 23, 2353, 1)
((7.833926e+00, -7.255962e+00, 5.475459e+00), (9.404210e-01, 3.383106e-01, -3.397550e-02), 1.289848e+05, 4.451025e-05, 1.000000e+00, 23, 2353, 1)
((8.142528e+00, -7.144944e+00, 5.464309e+00), (9.404210e-01, 3.383106e-01, -3.397550e-02), 1.289848e+05, 4.451091e-05, 1.000000e+00, 22, 2353, 3)
((8.590199e+00, -6.983897e+00, 5.448136e+00), (9.404210e-01, 3.383106e-01, -3.397550e-02), 1.289848e+05, 4.451187e-05, 1.000000e+00, 23, 2353, 1)
((8.682515e+00, -6.950687e+00, 5.444801e+00), (6.997233e-01, 7.142820e-01, 1.373092e-02), 1.043159e+05, 4.451207e-05, 1.000000e+00, 23, 2353, 1)
((8.996680e+00, -6.629985e+00, 5.450966e+00), (6.997233e-01, 7.142820e-01, 1.373092e-02), 1.043159e+05, 4.451307e-05, 1.000000e+00, 23, 2354, 1)
((9.231138e+00, -6.390649e+00, 5.455567e+00), (6.997233e-01, 7.142820e-01, 1.373092e-02), 1.043159e+05, 4.451382e-05, 1.000000e+00, 22, 2354, 3)
((9.650189e+00, -5.962879e+00, 5.463790e+00), (6.997233e-01, 7.142820e-01, 1.373092e-02), 1.043159e+05, 4.451516e-05, 1.000000e+00, 23, 2354, 1)
((9.721007e+00, -5.890588e+00, 5.465180e+00), (6.565855e-01, -1.880518e-01, -7.304327e-01), 1.036285e+04, 4.451539e-05, 1.000000e+00, 23, 2354, 1)
((9.939012e+00, -5.953026e+00, 5.222655e+00), (6.565855e-01, -1.880518e-01, -7.304327e-01), 1.036285e+04, 4.451775e-05, 1.000000e+00, 22, 2354, 3)
((1.002133e+01, -5.976602e+00, 5.131083e+00), (6.565855e-01, -1.880518e-01, -7.304327e-01), 1.036285e+04, 4.451864e-05, 1.000000e+00, 23, 2354, 1)
((1.020258e+01, -6.028514e+00, 4.929446e+00), (8.848761e-01, -4.005580e-02, -4.641012e-01), 8.908873e+03, 4.452060e-05, 1.000000e+00, 23, 2354, 1)
((1.063244e+01, -6.047973e+00, 4.703991e+00), (8.848761e-01, -4.005580e-02, -4.641012e-01), 8.908873e+03, 4.452432e-05, 1.000000e+00, 23, 2355, 1)
((1.084241e+01, -6.057477e+00, 4.593868e+00), (9.066652e-01, -6.565865e-02, 4.167099e-01), 3.324374e+03, 4.452614e-05, 1.000000e+00, 23, 2355, 1)
((1.107130e+01, -6.074053e+00, 4.699070e+00), (9.066652e-01, -6.565865e-02, 4.167099e-01), 3.324374e+03, 4.452930e-05, 1.000000e+00, 22, 2355, 3)
((1.121611e+01, -6.084540e+00, 4.765623e+00), (9.066652e-01, -6.565865e-02, 4.167099e-01), 3.324374e+03, 4.453130e-05, 1.000000e+00, 21, 2355, 2)
((1.174815e+01, -6.123069e+00, 5.010155e+00), (9.066652e-01, -6.565865e-02, 4.167099e-01), 3.324374e+03, 4.453866e-05, 1.000000e+00, 22, 2355, 3)
((1.189296e+01, -6.133556e+00, 5.076709e+00), (9.066652e-01, -6.565865e-02, 4.167099e-01), 3.324374e+03, 4.454067e-05, 1.000000e+00, 23, 2355, 1)
((1.221698e+01, -6.157021e+00, 5.225634e+00), (5.263177e-01, -7.048209e-01, -4.756229e-01), 3.449618e+02, 4.454515e-05, 1.000000e+00, 23, 2355, 1)
((1.226820e+01, -6.225607e+00, 5.179351e+00), (5.263177e-01, -7.048209e-01, -4.756229e-01), 3.449618e+02, 4.454893e-05, 1.000000e+00, 23, 2519, 1)
((1.230378e+01, -6.273253e+00, 5.147199e+00), (9.203567e-01, -2.667811e-01, -2.859569e-01), 2.269067e+02, 4.455157e-05, 1.000000e+00, 23, 2519, 1)
((1.237909e+01, -6.295083e+00, 5.123800e+00), (8.444192e-01, -5.115818e-01, 1.588717e-01), 1.686474e+02, 4.455549e-05, 1.000000e+00, 23, 2519, 1)
((1.250758e+01, -6.372926e+00, 5.147974e+00), (8.444192e-01, -5.115818e-01, 1.588717e-01), 1.686474e+02, 4.456396e-05, 1.000000e+00, 22, 2519, 3)
((1.258603e+01, -6.420455e+00, 5.162734e+00), (8.444192e-01, -5.115818e-01, 1.588717e-01), 1.686474e+02, 4.456914e-05, 1.000000e+00, 21, 2519, 2)
((1.264082e+01, -6.453648e+00, 5.173042e+00), (3.837750e-01, -8.931658e-01, -2.344604e-01), 1.632815e+02, 4.457275e-05, 1.000000e+00, 21, 2519, 2)
((1.288240e+01, -7.015898e+00, 5.025449e+00), (3.837750e-01, -8.931658e-01, -2.344604e-01), 1.632815e+02, 4.460837e-05, 1.000000e+00, 22, 2519, 3)
((1.292943e+01, -7.125332e+00, 4.996722e+00), (3.837750e-01, -8.931658e-01, -2.344604e-01), 1.632815e+02, 4.461530e-05, 1.000000e+00, 23, 2519, 1)
((1.303065e+01, -7.360920e+00, 4.934879e+00), (3.837750e-01, -8.931658e-01, -2.344604e-01), 1.632815e+02, 4.463022e-05, 1.000000e+00, 23, 2520, 1)
((1.309295e+01, -7.505897e+00, 4.896822e+00), (3.189366e-01, -9.220512e-01, 2.193195e-01), 1.303968e+02, 4.463941e-05, 1.000000e+00, 23, 2520, 1)
((1.311741e+01, -7.576618e+00, 4.913644e+00), (3.189366e-01, -9.220512e-01, 2.193195e-01), 1.303968e+02, 4.464426e-05, 1.000000e+00, 22, 2520, 3)
((1.314895e+01, -7.667795e+00, 4.935331e+00), (3.189366e-01, -9.220512e-01, 2.193195e-01), 1.303968e+02, 4.465052e-05, 1.000000e+00, 21, 2520, 2)
((1.345125e+01, -8.541749e+00, 5.143210e+00), (3.189366e-01, -9.220512e-01, 2.193195e-01), 1.303968e+02, 4.471053e-05, 1.000000e+00, 22, 2520, 3)
((1.348278e+01, -8.632925e+00, 5.164897e+00), (3.189366e-01, -9.220512e-01, 2.193195e-01), 1.303968e+02, 4.471679e-05, 1.000000e+00, 23, 2520, 1)
((1.356121e+01, -8.859646e+00, 5.218825e+00), (3.469373e-01, -8.669828e-01, 3.577365e-01), 1.276458e+02, 4.473236e-05, 1.000000e+00, 23, 2520, 1)
((1.361604e+01, -8.996680e+00, 5.275368e+00), (3.469373e-01, -8.669828e-01, 3.577365e-01), 1.276458e+02, 4.474248e-05, 1.000000e+00, 23, 2521, 1)
((1.390396e+01, -9.716172e+00, 5.572247e+00), (3.469373e-01, -8.669828e-01, 3.577365e-01), 1.276458e+02, 4.479558e-05, 1.000000e+00, 23, 2536, 1)
((1.399742e+01, -9.949713e+00, 5.668611e+00), (-5.897476e-01, -6.217559e-01, 5.153807e-01), 3.643244e+01, 4.481282e-05, 1.000000e+00, 23, 2536, 1)
((1.390396e+01, -1.004824e+01, 5.750282e+00), (-5.897476e-01, -6.217559e-01, 5.153807e-01), 3.643244e+01, 4.483180e-05, 1.000000e+00, 23, 2521, 1)
((1.334984e+01, -1.063244e+01, 6.234531e+00), (-5.897476e-01, -6.217559e-01, 5.153807e-01), 3.643244e+01, 4.494435e-05, 1.000000e+00, 23, 2522, 1)
((1.317711e+01, -1.081455e+01, 6.385480e+00), (-4.211158e-01, -4.515418e-01, 7.866203e-01), 3.302717e+01, 4.497943e-05, 1.000000e+00, 23, 2522, 1)
((1.316679e+01, -1.082561e+01, 6.404749e+00), (-5.182259e-01, 3.860612e-01, 7.631505e-01), 1.437084e+01, 4.498251e-05, 1.000000e+00, 23, 2522, 1)
((1.292206e+01, -1.064329e+01, 6.765147e+00), (-8.169098e-01, 1.482852e-01, 5.573777e-01), 1.219816e+01, 4.507257e-05, 1.000000e+00, 23, 2522, 1)
((1.286229e+01, -1.063244e+01, 6.805923e+00), (-8.169098e-01, 1.482852e-01, 5.573777e-01), 1.219816e+01, 4.508772e-05, 1.000000e+00, 23, 2521, 1)
((1.284917e+01, -1.063006e+01, 6.814880e+00), (-5.213758e-01, -3.360250e-01, 7.843816e-01), 8.052778e+00, 4.509105e-05, 1.000000e+00, 23, 2521, 1)
((1.284547e+01, -1.063244e+01, 6.820442e+00), (-5.213758e-01, -3.360250e-01, 7.843816e-01), 8.052778e+00, 4.509285e-05, 1.000000e+00, 23, 2522, 1)
((1.249929e+01, -1.085555e+01, 7.341246e+00), (-1.520079e-01, -3.513652e-01, 9.238161e-01), 7.069024e+00, 4.526201e-05, 1.000000e+00, 23, 2522, 1)
((1.240103e+01, -1.108268e+01, 7.938428e+00), (-7.405967e-01, -3.230926e-01, 5.891754e-01), 4.460475e+00, 4.543779e-05, 1.000000e+00, 23, 2522, 1)
((1.229354e+01, -1.112958e+01, 8.023945e+00), (-6.892797e-01, 5.508903e-01, 4.705458e-01), 2.006774e+00, 4.548748e-05, 1.000000e+00, 23, 2522, 1)
((1.226820e+01, -1.110933e+01, 8.041241e+00), (-6.892797e-01, 5.508903e-01, 4.705458e-01), 2.006774e+00, 4.550624e-05, 1.000000e+00, 23, 2314, 1)
((1.198501e+01, -1.088299e+01, 8.234567e+00), (-7.677533e-01, 4.569444e-01, 4.491733e-01), 1.935517e+00, 4.571593e-05, 1.000000e+00, 23, 2314, 1)
((1.156403e+01, -1.063244e+01, 8.480859e+00), (-7.677533e-01, 4.569444e-01, 4.491733e-01), 1.935517e+00, 4.600087e-05, 1.000000e+00, 23, 2329, 1)
((1.150264e+01, -1.059590e+01, 8.516776e+00), (-7.780064e-01, -9.937785e-02, 6.203467e-01), 1.893413e+00, 4.604243e-05, 1.000000e+00, 23, 2329, 1)
((1.121659e+01, -1.063244e+01, 8.744860e+00), (-7.780064e-01, -9.937785e-02, 6.203467e-01), 1.893413e+00, 4.623561e-05, 1.000000e+00, 23, 2314, 1)
((1.063244e+01, -1.070706e+01, 9.210632e+00), (-7.780064e-01, -9.937785e-02, 6.203467e-01), 1.893413e+00, 4.663011e-05, 1.000000e+00, 23, 2313, 1)
((1.035391e+01, -1.074263e+01, 9.432717e+00), (-4.847677e-01, -8.685325e-01, -1.032060e-01), 5.647686e-01, 4.681821e-05, 1.000000e+00, 23, 2313, 1)
((1.031409e+01, -1.081397e+01, 9.424239e+00), (-4.227592e-01, -9.022601e-01, -8.486053e-02), 5.651902e-01, 4.689723e-05, 1.000000e+00, 23, 2313, 1)
((1.028497e+01, -1.087614e+01, 9.418392e+00), (1.549579e-01, -4.270887e-01, 8.908329e-01), 5.395517e-01, 4.696349e-05, 1.000000e+00, 23, 2313, 1)
((1.034128e+01, -1.103135e+01, 9.742126e+00), (5.631544e-01, 4.925564e-01, 6.635098e-01), 2.637257e-01, 4.732118e-05, 1.000000e+00, 23, 2313, 1)
((1.044415e+01, -1.094137e+01, 9.863334e+00), (-1.034377e-01, 7.912271e-01, 6.027108e-01), 1.474934e-01, 4.757836e-05, 1.000000e+00, 23, 2313, 1)
((1.043959e+01, -1.090643e+01, 9.889950e+00), (1.163182e-01, 9.160889e-01, -3.837331e-01), 1.522434e-01, 4.766149e-05, 1.000000e+00, 23, 2313, 1)
((1.045733e+01, -1.076664e+01, 9.831395e+00), (-8.136862e-01, 9.805444e-02, -5.729748e-01), 3.341488e-02, 4.794423e-05, 1.000000e+00, 23, 2313, 1)
((1.044231e+01, -1.076483e+01, 9.820815e+00), (-8.437241e-01, 5.222731e-01, -1.239373e-01), 3.874838e-02, 4.801727e-05, 1.000000e+00, 23, 2313, 1)
((1.028725e+01, -1.066884e+01, 9.798037e+00), (-8.066297e-01, 3.960430e-01, -4.387465e-01), 3.989754e-02, 4.869227e-05, 1.000000e+00, 23, 2313, 1)
((1.021310e+01, -1.063244e+01, 9.757708e+00), (-8.066297e-01, 3.960430e-01, -4.387465e-01), 3.989754e-02, 4.902498e-05, 1.000000e+00, 23, 2328, 1)
((1.012717e+01, -1.059025e+01, 9.710967e+00), (-4.762033e-01, 8.377852e-01, -2.671075e-01), 3.271721e-02, 4.941058e-05, 1.000000e+00, 23, 2328, 1)
((1.001043e+01, -1.038486e+01, 9.645483e+00), (-4.762033e-01, 8.377852e-01, -2.671075e-01), 3.271721e-02, 5.039049e-05, 1.000000e+00, 22, 2328, 3)
((9.971285e+00, -1.031600e+01, 9.623530e+00), (5.437395e-01, -6.104117e-01, -5.759730e-01), 2.765456e-02, 5.071901e-05, 1.000000e+00, 22, 2328, 3)
((1.002723e+01, -1.037881e+01, 9.564267e+00), (5.437395e-01, -6.104117e-01, -5.759730e-01), 2.765456e-02, 5.116633e-05, 1.000000e+00, 23, 2328, 1)
((1.025152e+01, -1.063060e+01, 9.326683e+00), (-8.159392e-01, -4.792697e-03, 5.781179e-01), 2.920905e-02, 5.295966e-05, 1.000000e+00, 23, 2328, 1)
((1.000779e+01, -1.063203e+01, 9.499373e+00), (-6.486477e-01, -4.621598e-01, -6.047020e-01), 2.383638e-02, 5.422329e-05, 1.000000e+00, 23, 2328, 1)
((1.000721e+01, -1.063244e+01, 9.498835e+00), (-6.486477e-01, -4.621598e-01, -6.047020e-01), 2.383638e-02, 5.422746e-05, 1.000000e+00, 23, 2313, 1)
((9.852478e+00, -1.074269e+01, 9.354584e+00), (-6.686452e-01, -2.988396e-01, 6.808880e-01), 2.993224e-02, 5.534454e-05, 1.000000e+00, 23, 2313, 1)
((9.765042e+00, -1.078177e+01, 9.443621e+00), (7.712484e-01, -5.215773e-01, 3.648739e-01), 3.590785e-02, 5.589099e-05, 1.000000e+00, 23, 2313, 1)
((9.798992e+00, -1.080472e+01, 9.459682e+00), (-4.563170e-01, 8.081811e-01, -3.723144e-01), 2.674168e-02, 5.605894e-05, 1.000000e+00, 23, 2313, 1)
((9.792857e+00, -1.079386e+01, 9.454677e+00), (-9.080336e-01, 4.172659e-01, 3.693418e-02), 9.753404e-03, 5.611838e-05, 1.000000e+00, 23, 2313, 1)
((9.653805e+00, -1.072996e+01, 9.460332e+00), (-3.727950e-01, 2.889342e-01, -8.817828e-01), 1.781716e-02, 5.723944e-05, 1.000000e+00, 23, 2313, 1)
((9.628677e+00, -1.071048e+01, 9.400895e+00), (-6.406523e-01, 7.657269e-01, -5.680598e-02), 1.764425e-02, 5.760453e-05, 1.000000e+00, 23, 2313, 1)
((9.563380e+00, -1.063244e+01, 9.395105e+00), (-6.406523e-01, 7.657269e-01, -5.680598e-02), 1.764425e-02, 5.815928e-05, 1.000000e+00, 23, 2328, 1)
((9.411305e+00, -1.045068e+01, 9.381621e+00), (3.205568e-01, -1.311309e-02, -9.471385e-01), 2.153422e-02, 5.945128e-05, 1.000000e+00, 23, 2328, 1)
((9.860026e+00, -1.046903e+01, 8.055798e+00), (6.278124e-01, 1.023095e-01, -7.716115e-01), 2.013742e-02, 6.634788e-05, 1.000000e+00, 23, 2328, 1)
((1.007604e+01, -1.043383e+01, 7.790303e+00), (6.790346e-01, 5.167838e-01, 5.213891e-01), 1.340865e-02, 6.810089e-05, 1.000000e+00, 23, 2328, 1)
((1.008869e+01, -1.042421e+01, 7.800011e+00), (2.792456e-01, 1.367953e-01, 9.504257e-01), 8.576635e-03, 6.821715e-05, 1.000000e+00, 23, 2328, 1)
((1.017046e+01, -1.038415e+01, 8.078330e+00), (-9.462370e-01, 3.185163e-01, 5.641780e-02), 1.119274e-01, 7.050324e-05, 1.000000e+00, 23, 2328, 1)
((1.008276e+01, -1.035463e+01, 8.083559e+00), (-9.462370e-01, 3.185163e-01, 5.641780e-02), 1.119274e-01, 7.070353e-05, 1.000000e+00, 22, 2328, 3)
((9.952201e+00, -1.031068e+01, 8.091343e+00), (-9.462370e-01, 3.185163e-01, 5.641780e-02), 1.119274e-01, 7.100170e-05, 1.000000e+00, 21, 2328, 2)
((9.404929e+00, -1.012646e+01, 8.123973e+00), (-9.462370e-01, 3.185163e-01, 5.641780e-02), 1.119274e-01, 7.225157e-05, 1.000000e+00, 22, 2328, 3)
((9.274369e+00, -1.008251e+01, 8.131758e+00), (-9.462370e-01, 3.185163e-01, 5.641780e-02), 1.119274e-01, 7.254974e-05, 1.000000e+00, 23, 2328, 1)
((9.126730e+00, -1.003281e+01, 8.140560e+00), (-8.389978e-01, -2.248732e-01, 4.954945e-01), 1.164832e-01, 7.288692e-05, 1.000000e+00, 23, 2328, 1)
((8.996680e+00, -1.006767e+01, 8.217365e+00), (-8.389978e-01, -2.248732e-01, 4.954945e-01), 1.164832e-01, 7.321528e-05, 1.000000e+00, 23, 2327, 1)
((8.694758e+00, -1.014859e+01, 8.395674e+00), (5.156148e-01, 7.316558e-01, 4.458937e-01), 1.391972e-01, 7.397758e-05, 1.000000e+00, 23, 2327, 1)
((8.996680e+00, -9.720167e+00, 8.656770e+00), (5.156148e-01, 7.316558e-01, 4.458937e-01), 1.391972e-01, 7.511228e-05, 1.000000e+00, 23, 2328, 1)
((9.263031e+00, -9.342216e+00, 8.887105e+00), (5.734531e-01, -5.017666e-01, 6.475970e-01), 9.798082e-02, 7.611330e-05, 1.000000e+00, 23, 2328, 1)
((9.334809e+00, -9.405021e+00, 8.968164e+00), (5.419546e-03, -7.721039e-01, -6.354732e-01), 1.234091e-01, 7.640240e-05, 1.000000e+00, 23, 2328, 1)
((9.335117e+00, -9.448857e+00, 8.932084e+00), (5.419546e-03, -7.721039e-01, -6.354732e-01), 1.234091e-01, 7.651925e-05, 1.000000e+00, 22, 2328, 3)
((9.336345e+00, -9.623801e+00, 8.788098e+00), (5.419546e-03, -7.721039e-01, -6.354732e-01), 1.234091e-01, 7.698556e-05, 1.000000e+00, 21, 2328, 2)
((9.339070e+00, -1.001201e+01, 8.468584e+00), (5.419546e-03, -7.721039e-01, -6.354732e-01), 1.234091e-01, 7.802034e-05, 1.000000e+00, 22, 2328, 3)
((9.340298e+00, -1.018696e+01, 8.324598e+00), (5.419546e-03, -7.721039e-01, -6.354732e-01), 1.234091e-01, 7.848665e-05, 1.000000e+00, 23, 2328, 1)
((9.343193e+00, -1.059945e+01, 7.985097e+00), (1.798796e-01, 3.522536e-02, -9.830577e-01), 1.283272e-01, 7.958616e-05, 1.000000e+00, 23, 2328, 1)
((9.397753e+00, -1.058877e+01, 7.686922e+00), (6.459563e-01, 7.279465e-03, -7.633397e-01), 1.319449e-01, 8.019832e-05, 1.000000e+00, 23, 2328, 1)
((9.462343e+00, -1.058804e+01, 7.610595e+00), (-5.748716e-01, 5.760301e-01, -5.811299e-01), 8.880732e-02, 8.039733e-05, 1.000000e+00, 23, 2328, 1)
((9.287877e+00, -1.041322e+01, 7.434230e+00), (-4.932135e-03, 1.668926e-01, 9.859627e-01), 3.503028e-02, 8.113361e-05, 1.000000e+00, 23, 2328, 1)
((9.286930e+00, -1.038117e+01, 7.623600e+00), (-9.325544e-02, -9.933000e-01, -6.825369e-02), 3.173764e-02, 8.187553e-05, 1.000000e+00, 23, 2328, 1)
((9.283901e+00, -1.041342e+01, 7.621384e+00), (-4.235208e-01, -5.129112e-01, 7.466942e-01), 4.258127e-02, 8.200731e-05, 1.000000e+00, 23, 2328, 1)
((9.264517e+00, -1.043690e+01, 7.655560e+00), (-7.165846e-01, 3.926265e-01, 5.764988e-01), 4.485063e-02, 8.216767e-05, 1.000000e+00, 23, 2328, 1)
((9.256348e+00, -1.043242e+01, 7.662132e+00), (1.703748e-01, -9.175599e-01, -3.592441e-01), 3.521226e-02, 8.220659e-05, 1.000000e+00, 23, 2328, 1)
((9.284015e+00, -1.058143e+01, 7.603793e+00), (3.963415e-01, -8.051509e-01, 4.411865e-01), 2.970280e-02, 8.283227e-05, 1.000000e+00, 23, 2328, 1)
((9.309126e+00, -1.063244e+01, 7.631745e+00), (3.963415e-01, -8.051509e-01, 4.411865e-01), 2.970280e-02, 8.309804e-05, 1.000000e+00, 23, 2313, 1)
((9.317824e+00, -1.065011e+01, 7.641427e+00), (7.190682e-01, 8.976507e-02, -6.891177e-01), 2.867417e-02, 8.319011e-05, 1.000000e+00, 23, 2313, 1)
((9.459378e+00, -1.063244e+01, 7.505770e+00), (7.190682e-01, 8.976507e-02, -6.891177e-01), 2.867417e-02, 8.403060e-05, 1.000000e+00, 23, 2328, 1)
((9.492454e+00, -1.062831e+01, 7.474071e+00), (4.092619e-03, -9.482095e-01, -3.176193e-01), 4.656141e-02, 8.422700e-05, 1.000000e+00, 23, 2328, 1)
((9.492472e+00, -1.063244e+01, 7.472688e+00), (4.092619e-03, -9.482095e-01, -3.176193e-01), 4.656141e-02, 8.424159e-05, 1.000000e+00, 23, 2313, 1)
((9.492761e+00, -1.069927e+01, 7.450302e+00), (9.457151e-01, -1.928731e-01, 2.615777e-01), 4.542312e-02, 8.447773e-05, 1.000000e+00, 23, 2313, 1)
((9.890524e+00, -1.078039e+01, 7.560320e+00), (7.849118e-01, -1.237812e-01, -6.071175e-01), 5.609444e-02, 8.590450e-05, 1.000000e+00, 23, 2313, 1)
((1.004645e+01, -1.080498e+01, 7.439717e+00), (7.849118e-01, -1.237812e-01, -6.071175e-01), 5.609444e-02, 8.651090e-05, 0.000000e+00, 23, 2313, 1)]

View file

@ -16,7 +16,8 @@
<track>
1 1 1
1 1 2
1 1 30
2 1 60
</track>
</settings>

View file

@ -1,11 +1,13 @@
import glob
import os
from pathlib import Path
from subprocess import call
import shutil
import numpy as np
import openmc
import pytest
from tests.testing_harness import TestHarness
from tests.testing_harness import TestHarness, config
class TrackTestHarness(TestHarness):
@ -13,26 +15,33 @@ class TrackTestHarness(TestHarness):
"""Make sure statepoint.* and track* have been created."""
TestHarness._test_output_created(self)
outputs = glob.glob('track_1_1_*.h5')
assert len(outputs) == 2, 'Expected two track files.'
if config['mpi'] and int(config['mpi_np']) > 1:
outputs = Path.cwd().glob('tracks_p*.h5')
assert len(list(outputs)) == int(config['mpi_np'])
else:
assert Path('tracks.h5').is_file()
def _get_results(self):
"""Digest info in the statepoint and return as a string."""
# Run the track-to-vtk conversion script.
call(['../../../scripts/openmc-track-to-vtk', '-o', 'poly'] +
glob.glob('track_1_1_*.h5'))
"""Get data from track file and return as a string."""
# Make sure the vtk file was created then return it's contents.
assert os.path.isfile('poly.pvtp'), 'poly.pvtp file not found.'
# For MPI mode, combine track files
if config['mpi']:
call(['../../../scripts/openmc-track-combine', '-o', 'tracks.h5'] +
glob.glob('tracks_p*.h5'))
with open('poly.pvtp', 'r') as fin:
outstr = fin.read()
# Get string of track file information
outstr = ''
tracks = openmc.Tracks('tracks.h5')
for track in tracks:
with np.printoptions(formatter={'float_kind': '{:.6e}'.format}):
for ptrack in track:
outstr += f"{ptrack.particle} {ptrack.states}\n"
return outstr
def _cleanup(self):
TestHarness._cleanup(self)
output = glob.glob('track*') + glob.glob('poly*')
output = glob.glob('tracks*') + glob.glob('poly*')
for f in output:
if os.path.exists(f):
os.remove(f)

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -50,9 +50,9 @@
<source strength="1.0">
<space origin="0.0 0.0 0.0" type="spherical">
<r parameters="0.0 0.0" type="uniform" />
<theta type="discrete">
<parameters>0.0 1.0</parameters>
</theta>
<cos_theta type="discrete">
<parameters>1.0 1.0</parameters>
</cos_theta>
<phi type="discrete">
<parameters>0.0 1.0</parameters>
</phi>

View file

@ -228,10 +228,10 @@ def test_unstructured_mesh(test_opts):
# source setup
r = openmc.stats.Uniform(a=0.0, b=0.0)
theta = openmc.stats.Discrete(x=[0.0], p=[1.0])
cos_theta = openmc.stats.Discrete(x=[1.0], p=[1.0])
phi = openmc.stats.Discrete(x=[0.0], p=[1.0])
space = openmc.stats.SphericalIndependent(r, theta, phi)
space = openmc.stats.SphericalIndependent(r, cos_theta, phi)
energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0])
source = openmc.Source(space=space, energy=energy)
settings.source = source

View file

@ -297,3 +297,20 @@ def test_to_xml_element(cell_with_lattice):
elem = c.create_xml_subelement(root)
assert elem.get('region') == str(c.region)
assert elem.get('temperature') == str(c.temperature)
@pytest.mark.parametrize("rotation", [
(90, 45, 0),
[[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]]
])
def test_rotation_from_xml(rotation):
# Make sure rotation attribute (matrix) round trips through XML correctly
s = openmc.ZCylinder(r=10.0)
cell = openmc.Cell(region=-s)
cell.rotation = rotation
root = ET.Element('geometry')
elem = cell.create_xml_subelement(root)
new_cell = openmc.Cell.from_xml_element(
elem, {s.id: s}, {'void': None}, openmc.Universe
)
np.testing.assert_allclose(new_cell.rotation, cell.rotation)

View file

@ -0,0 +1,57 @@
import openmc
import pytest
from tests.testing_harness import config
@pytest.fixture
def model():
# Create simple sphere model
model = openmc.Model()
mat = openmc.Material()
mat.add_nuclide('U235', 1.0)
mat.add_nuclide('H1', 1.0)
mat.set_density('g/cm3', 5.0)
sph = openmc.Sphere(r=10.0, boundary_type='vacuum')
cell = openmc.Cell(fill=mat, region=-sph)
model.geometry = openmc.Geometry([cell])
model.settings.particles = 1000
model.settings.inactive = 0
model.settings.batches = 5
model.settings.photon_transport = True
# Add two tallies, one with heating by nuclide and one with total heating
particle_filter = openmc.ParticleFilter(['neutron', 'photon'])
heating_by_nuclide = openmc.Tally()
heating_by_nuclide.filters = [particle_filter]
heating_by_nuclide.nuclides = ['U235', 'H1']
heating_by_nuclide.scores = ['heating']
heating_total = openmc.Tally()
heating_total.filters = [particle_filter]
heating_total.scores = ['heating']
model.tallies.extend([heating_by_nuclide, heating_total])
return model
def test_heating_by_nuclide(model, run_in_tmpdir):
# If running in MPI mode, setup proper keyword arguments for run()
kwargs = {'openmc_exec': config['exe']}
if config['mpi']:
kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']]
sp_path = model.run(**kwargs)
# Get tallies from resulting statepoint
with openmc.StatePoint(sp_path) as sp:
heating_by_nuclide = sp.tallies[model.tallies[0].id]
heating_total = sp.tallies[model.tallies[1].id]
for particle in heating_by_nuclide.filters[0].bins:
# Get slice of each tally corresponding to a single particle
kwargs = {'filters': [openmc.ParticleFilter], 'filter_bins': [(particle,)]}
particle_slice_by_nuclide = heating_by_nuclide.get_values(**kwargs)
particle_slice_total = heating_total.get_values(**kwargs)
# Summing over nuclides should equal total
assert particle_slice_by_nuclide.sum() == pytest.approx(particle_slice_total.sum())

View file

@ -92,6 +92,8 @@ def test_xml_element(myplot):
def test_plots(run_in_tmpdir):
p1 = openmc.Plot(name='plot1')
p1.origin = (5., 5., 5.)
p1.colors = {10: (255, 100, 0)}
p1.mask_components = [2, 4, 6]
p2 = openmc.Plot(name='plot2')
p2.origin = (-3., -3., -3.)
plots = openmc.Plots([p1, p2])
@ -107,4 +109,6 @@ def test_plots(run_in_tmpdir):
new_plots = openmc.Plots.from_xml()
assert len(plots)
assert plots[0].origin == p1.origin
assert plots[0].colors == p1.colors
assert plots[0].mask_components == p1.mask_components
assert plots[1].origin == p2.origin

View file

@ -14,6 +14,7 @@ def test_export_to_xml(run_in_tmpdir):
s.keff_trigger = {'type': 'std_dev', 'threshold': 0.001}
s.energy_mode = 'continuous-energy'
s.max_order = 5
s.max_tracks = 1234
s.source = openmc.Source(space=openmc.stats.Point())
s.output = {'summary': True, 'tallies': False, 'path': 'here'}
s.verbosity = 7
@ -42,7 +43,7 @@ def test_export_to_xml(run_in_tmpdir):
s.temperature = {'default': 293.6, 'method': 'interpolation',
'multipole': True, 'range': (200., 1000.)}
s.trace = (10, 1, 20)
s.track = [1, 1, 1, 2, 1, 1]
s.track = [(1, 1, 1), (2, 1, 1)]
s.ufs_mesh = mesh
s.resonance_scattering = {'enable': True, 'method': 'rvs',
'energy_min': 1.0, 'energy_max': 1000.0,
@ -71,6 +72,7 @@ def test_export_to_xml(run_in_tmpdir):
assert s.keff_trigger == {'type': 'std_dev', 'threshold': 0.001}
assert s.energy_mode == 'continuous-energy'
assert s.max_order == 5
assert s.max_tracks == 1234
assert isinstance(s.source[0], openmc.Source)
assert isinstance(s.source[0].space, openmc.stats.Point)
assert s.output == {'summary': True, 'tallies': False, 'path': 'here'}
@ -99,7 +101,7 @@ def test_export_to_xml(run_in_tmpdir):
assert s.temperature == {'default': 293.6, 'method': 'interpolation',
'multipole': True, 'range': [200., 1000.]}
assert s.trace == [10, 1, 20]
assert s.track == [1, 1, 1, 2, 1, 1]
assert s.track == [(1, 1, 1), (2, 1, 1)]
assert isinstance(s.ufs_mesh, openmc.RegularMesh)
assert s.ufs_mesh.lower_left == [-10., -10., -10.]
assert s.ufs_mesh.upper_right == [10., 10., 10.]

View file

@ -1,6 +1,6 @@
import openmc
import openmc.stats
from math import pi
def test_source():
space = openmc.stats.Point()
@ -26,6 +26,22 @@ def test_source():
assert src.strength == 1.0
def test_spherical_uniform():
r_outer = 2.0
r_inner = 1.0
thetas = (0.0, pi/2)
phis = (0.0, pi)
origin = (0.0, 1.0, 2.0)
sph_indep_function = openmc.stats.spherical_uniform(r_outer,
r_inner,
thetas,
phis,
origin)
assert isinstance(sph_indep_function, openmc.stats.SphericalIndependent)
def test_source_file():
filename = 'source.h5'
src = openmc.Source(filename=filename)
@ -35,6 +51,7 @@ def test_source_file():
assert 'strength' in elem.attrib
assert 'file' in elem.attrib
def test_source_dlopen():
library = './libsource.so'
src = openmc.Source(library=library)

View file

@ -27,6 +27,29 @@ def test_discrete():
assert len(d2) == 1
def test_merge_discrete():
x1 = [0.0, 1.0, 10.0]
p1 = [0.3, 0.2, 0.5]
d1 = openmc.stats.Discrete(x1, p1)
x2 = [0.5, 1.0, 5.0]
p2 = [0.4, 0.5, 0.1]
d2 = openmc.stats.Discrete(x2, p2)
# Merged distribution should have x values sorted and probabilities
# appropriately combined. Duplicate x values should appear once.
merged = openmc.stats.Discrete.merge([d1, d2], [0.6, 0.4])
assert merged.x == pytest.approx([0.0, 0.5, 1.0, 5.0, 10.0])
assert merged.p == pytest.approx(
[0.6*0.3, 0.4*0.4, 0.6*0.2 + 0.4*0.5, 0.4*0.1, 0.6*0.5])
# Probabilities add up but are not normalized
d1 = openmc.stats.Discrete([3.0], [1.0])
triple = openmc.stats.Discrete.merge([d1, d1, d1], [1.0, 2.0, 3.0])
assert triple.x == pytest.approx([3.0])
assert triple.p == pytest.approx([6.0])
def test_uniform():
a, b = 10.0, 20.0
d = openmc.stats.Uniform(a, b)

View file

@ -0,0 +1,143 @@
from pathlib import Path
import numpy as np
import openmc
import pytest
from tests.testing_harness import config
@pytest.fixture
def sphere_model():
openmc.reset_auto_ids()
mat = openmc.Material()
mat.add_nuclide('Zr90', 1.0)
mat.set_density('g/cm3', 1.0)
model = openmc.Model()
sph = openmc.Sphere(r=25.0, boundary_type='vacuum')
cell = openmc.Cell(fill=mat, region=-sph)
model.geometry = openmc.Geometry([cell])
model.settings.run_mode = 'fixed source'
model.settings.batches = 2
model.settings.particles = 50
return model
def generate_track_file(model, **kwargs):
# If running in MPI mode, setup proper keyword arguments for run()
kwargs.setdefault('openmc_exec', config['exe'])
if config['mpi']:
kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']]
model.run(**kwargs)
if config['mpi'] and int(config['mpi_np']) > 1:
# With MPI, we need to combine track files
track_files = Path.cwd().glob('tracks_p*.h5')
openmc.Tracks.combine(track_files, 'tracks.h5')
else:
track_file = Path('tracks.h5')
assert track_file.is_file()
@pytest.mark.parametrize("particle", ["neutron", "photon"])
def test_tracks(sphere_model, particle, run_in_tmpdir):
# Set track identifiers
sphere_model.settings.track = [(1, 1, 1), (1, 1, 10), (2, 1, 15)]
# Set source particle
sphere_model.settings.source = openmc.Source(particle=particle)
# Run OpenMC to generate tracks.h5 file
generate_track_file(sphere_model)
# Open track file and make sure we have correct number of tracks
tracks = openmc.Tracks('tracks.h5')
assert len(tracks) == len(sphere_model.settings.track)
for track, identifier in zip(tracks, sphere_model.settings.track):
# Check attributes on Track object
assert isinstance(track, openmc.Track)
assert track.identifier == identifier
assert isinstance(track.particle_tracks, list)
if particle == 'neutron':
assert len(track.particle_tracks) == 1
# Check attributes on ParticleTrack object
particle_track = track.particle_tracks[0]
assert isinstance(particle_track, openmc.ParticleTrack)
assert particle_track.particle.name.lower() == particle
assert isinstance(particle_track.states, np.ndarray)
# Sanity checks on actual data
for state in particle_track.states:
assert np.linalg.norm([*state['r']]) <= 25.0001
assert np.linalg.norm([*state['u']]) == pytest.approx(1.0)
assert 0.0 <= state['E'] <= 20.0e6
assert state['time'] >= 0.0
assert 0.0 <= state['wgt'] <= 1.0
assert state['cell_id'] == 1
assert state['material_id'] == 1
# Checks on 'sources' property
sources = track.sources
assert len(sources) == len(track.particle_tracks)
x = sources[0]
state = particle_track.states[0]
assert x.r == (*state['r'],)
assert x.u == (*state['u'],)
assert x.E == state['E']
assert x.time == state['time']
assert x.wgt == state['wgt']
assert x.particle == particle_track.particle
def test_max_tracks(sphere_model, run_in_tmpdir):
# Set maximum number of tracks per process to write
sphere_model.settings.max_tracks = expected_num_tracks = 10
if config['mpi']:
expected_num_tracks *= int(config['mpi_np'])
# Run OpenMC to generate tracks.h5 file
generate_track_file(sphere_model, tracks=True)
# Open track file and make sure we have correct number of tracks
tracks = openmc.Tracks('tracks.h5')
assert len(tracks) == expected_num_tracks
def test_filter(sphere_model, run_in_tmpdir):
# Set maximum number of tracks per process to write
sphere_model.settings.max_tracks = 25
sphere_model.settings.photon_transport = True
# Run OpenMC to generate tracks.h5 file
generate_track_file(sphere_model, tracks=True)
tracks = openmc.Tracks('tracks.h5')
for track in tracks:
# Test filtering by particle
matches = track.filter(particle='photon')
for x in matches:
assert x.particle == openmc.ParticleType.PHOTON
# Test general state filter
matches = track.filter(state_filter=lambda s: s['cell_id'] == 1)
assert isinstance(matches, openmc.Track)
assert matches.particle_tracks == track.particle_tracks
matches = track.filter(state_filter=lambda s: s['cell_id'] == 2)
assert matches.particle_tracks == []
matches = track.filter(state_filter=lambda s: s['E'] < 0.0)
assert matches.particle_tracks == []
# Test filter method on Tracks
matches = tracks.filter(particle='neutron')
assert isinstance(matches, openmc.Tracks)
assert matches == tracks
matches = tracks.filter(state_filter=lambda s: s['E'] > 0.0)
assert matches == tracks
matches = tracks.filter(particle='bunnytron')
assert matches == []