Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restricted file source #2916

Merged
merged 63 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
3df948b
add code to reject on domains also for file sources
ebknudsen Mar 11, 2024
9e850f6
also add the domain restriction to python layer
ebknudsen Mar 11, 2024
35e160c
move rejection to the sampling step
ebknudsen Mar 11, 2024
0fbc48d
improve commenting and formatting
ebknudsen Mar 11, 2024
471a4aa
fix flipped logic
ebknudsen Mar 11, 2024
972a281
add option to kill non-conforming particles
ebknudsen Mar 12, 2024
b1537a7
add option to reject by hypercube of particles
ebknudsen Mar 13, 2024
e12732b
allow hypercube description in python layer
ebknudsen Mar 14, 2024
03e122c
fix typos
ebknudsen Mar 14, 2024
1baf072
fix typos
ebknudsen Mar 14, 2024
916315e
yet another misprint
ebknudsen Mar 14, 2024
248353d
Merge branch 'restricted_file_source' of github.com:ebknudsen/openmc …
ebknudsen Mar 14, 2024
672f4e6
hypercube and rejection_strategy added to python
ebknudsen Mar 14, 2024
11da3c5
Merge branch 'openmc-dev:develop' into restricted_file_source
ebknudsen Mar 14, 2024
ed46945
adjust whitespace acc. to clang-format
ebknudsen Mar 14, 2024
183779f
Merge branch 'restricted_file_source' of github.com:ebknudsen/openmc …
ebknudsen Mar 14, 2024
5a892f8
remove unimplemented FILTER option
ebknudsen Mar 14, 2024
eed4aaf
fix var name
ebknudsen Mar 14, 2024
bb8e892
initialize attributes to None
ebknudsen Mar 14, 2024
23a29b4
initialize the rej. stragtegy and add doc string
ebknudsen Mar 14, 2024
20b27f8
correct wrong condition
church89 Mar 19, 2024
c188fab
Merge pull request #29 from openmsr/correct_iterator
church89 Mar 19, 2024
d93ac83
move from a single vector to discrete bounds
ebknudsen Mar 19, 2024
86ae064
source restriction is now an intermediate class
ebknudsen Apr 8, 2024
78a8716
apply clang-format
ebknudsen Apr 8, 2024
c0a2f31
use limits instead of cfloats
ebknudsen Apr 8, 2024
6dad9f8
check bounds for pair assignement
ebknudsen Apr 8, 2024
6831f7d
default to kill - not resample
ebknudsen Apr 8, 2024
b45f343
better style (dont use and...)
ebknudsen Apr 8, 2024
a0cd9ec
prefer ! to not
ebknudsen Apr 8, 2024
4671d90
better style
ebknudsen Apr 8, 2024
bd8978b
apply clang-format
ebknudsen Apr 8, 2024
e342243
fix typo
ebknudsen Apr 8, 2024
6ede6ef
remove restr. source class
ebknudsen Apr 8, 2024
3453353
remove useless code
ebknudsen Apr 11, 2024
aa86285
don't search for xs for already dead particles
ebknudsen Apr 11, 2024
ca03b4d
fix particle search logic to allow fissionable to work
ebknudsen Apr 16, 2024
306137d
revert to having resample as the default
ebknudsen Apr 16, 2024
45b2a08
fix domain search logic which accepted particles regardless
ebknudsen Apr 16, 2024
0152c2b
Merge branch 'develop' into restricted_file_source
ebknudsen Apr 22, 2024
5b36f80
Check for restriction nodes in MeshSource
paulromano Apr 30, 2024
96e89f8
Get rid of lower_left, upper_right. Use GeomState instead of Particle
paulromano Apr 30, 2024
44b4c20
Consolidate common logic in source.py
paulromano May 1, 2024
cf60499
Generalize source rejection on C++ side
paulromano May 1, 2024
0395150
Preserve per-element probabilities for MeshSource
paulromano May 1, 2024
71538d2
Add missing dummy method on MeshSource
paulromano May 1, 2024
989d4ae
Merge branch 'develop' into pr/ebknudsen/2916
paulromano May 7, 2024
4ca3996
Remove superfluous check in transport_history_based_single_particle
paulromano May 7, 2024
9755ac9
Use single constraints argument in source classes
paulromano May 7, 2024
99eb170
Check for constraints/strength in Source constructor
paulromano May 7, 2024
31ad069
Extend tests for source constraints
paulromano May 9, 2024
0e2f747
Respond to many @gridley comments
paulromano May 9, 2024
cd04ecb
Add fissionable source constraint
paulromano May 10, 2024
91cfdcc
Rename to sample / sample_with_constraints
paulromano May 10, 2024
a60ff90
Make sample_with_constraints non-virtual
paulromano May 10, 2024
e3f56e7
Add user's guide section on source constraints
paulromano May 13, 2024
e468dd4
Put source constraints into a <constraints> XML element
paulromano May 13, 2024
42d5cad
Add comment describing Source base/derived relationship
paulromano May 13, 2024
c9d130d
Update reference inputs to reflect contraints format
paulromano May 14, 2024
bad3a01
Fix one more reference input file
paulromano May 14, 2024
42898cc
Implement rejection fraction check in sample_with_constraints
paulromano May 14, 2024
ca3ba80
Fix rejection threshold check in sample_with_constraints
paulromano May 14, 2024
da08d77
Skip rejection limit test for MPI config
paulromano May 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions docs/source/io_formats/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,9 @@ attributes/sub-elements:
*Default*: 1.0

:type:
Indicator of source type. One of ``independent``, ``file``, ``compiled``, or ``mesh``.
The type of the source will be determined by this attribute if it is present.
Indicator of source type. One of ``independent``, ``file``, ``compiled``, or
``mesh``. The type of the source will be determined by this attribute if it
is present.

:particle:
The source particle type, either ``neutron`` or ``photon``.
Expand Down Expand Up @@ -716,6 +717,39 @@ attributes/sub-elements:
mesh element and follows the format for :ref:`source_element`. The number of
``<source>`` sub-elements should correspond to the number of mesh elements.

:constraints:
This sub-element indicates the presence of constraints on sampled source
sites (see :ref:`usersguide_source_constraints` for details). It may have
the following sub-elements:

:domain_ids:
The unique IDs of domains for which source sites must be within.

*Default*: None

:domain_type:
The type of each domain for source rejection ("cell", "material", or
"universe").

*Default*: None

:fissionable:
A boolean indicating whether source sites must be sampled within a
material that is fissionable in order to be accepted.

:time_bounds:
A pair of times in [s] indicating the lower and upper bound for a time
interval that source particles must be within.

:energy_bounds:
A pair of energies in [eV] indicating the lower and upper bound for an
energy interval that source particles must be within.

:rejection_strategy:
Either "resample", indicating that source sites should be resampled when
one is rejected, or "kill", indicating that a rejected source site is
assigned zero weight.

.. _univariate:

Univariate Probability Distributions
Expand Down
48 changes: 48 additions & 0 deletions docs/source/usersguide/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,54 @@ string, which gets passed down to the ``openmc_create_source()`` function::

settings.source = openmc.CompiledSource('libsource.so', '3.5e6')

.. _usersguide_source_constraints:

Source Constraints
------------------

All source classes in OpenMC have the ability to apply a set of "constraints"
that limit which sampled source sites are actually used for transport. The most
common use case is to sample source sites over some simple spatial distribution
(e.g., uniform over a box) and then only accept those that appear in a given
cell or material. This can be done with a domain constraint, which can be
specified as follows::

source_cell = openmc.Cell(...)
...

spatial_dist = openmc.stats.Box((-10., -10., -10.), (10., 10., 10.))
source = openmc.IndependentSource(
space=spatial_dist,
constraints={'domains': [source_cell]}
)

For k-eigenvalue problems, a convenient constraint is available that limits
source sites to those sampled in a fissionable material::

source = openmc.IndependentSource(
space=spatial_dist, constraints={'fissionable': True}
)

Constraints can also be placed on a range of energies or times::

# Only use source sites between 500 keV and 1 MeV and with times under 1 sec
source = openmc.FileSource(
'source.h5',
constraints={'energy_bounds': [500.0e3, 1.0e6], 'time_bounds': [0.0, 1.0]}
)

Normally, when a source site is rejected, a new one will be resampled until one
is found that meets the constraints. However, the rejection strategy can be
changed so that a rejected site will just not be simulated by specifying::

source = openmc.IndependentSource(
space=spatial_dist,
constraints={'domains': [cell], 'rejection_strategy': 'kill'}
)

In this case, the actual number of particles simulated may be less than what you
specified in :attr:`Settings.particles`.

.. _usersguide_entropy:

---------------
Expand Down
9 changes: 4 additions & 5 deletions examples/assembly/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,10 @@ def assembly_model():
model.settings.batches = 150
model.settings.inactive = 50
model.settings.particles = 1000
model.settings.source = openmc.IndependentSource(space=openmc.stats.Box(
(-pitch/2, -pitch/2, -1),
(pitch/2, pitch/2, 1),
only_fissionable=True
))
model.settings.source = openmc.IndependentSource(
space=openmc.stats.Box((-pitch/2, -pitch/2, -1), (pitch/2, pitch/2, 1)),
constraints={'fissionable': True}
)

# NOTE: We never actually created a Materials object. When you export/run
# using the Model object, if no materials were assigned it will look through
Expand Down
5 changes: 3 additions & 2 deletions examples/pincell_depletion/restart_depletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@

# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings.source = openmc.IndependentSource(space=uniform_dist)
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:])
settings.source = openmc.IndependentSource(
space=uniform_dist, constraints={'fissionable': True})

entropy_mesh = openmc.RegularMesh()
entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50]
Expand Down
5 changes: 3 additions & 2 deletions examples/pincell_depletion/run_depletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@

# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings.source = openmc.IndependentSource(space=uniform_dist)
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:])
settings.source = openmc.IndependentSource(
space=uniform_dist, constraints={'fissionable': True})

entropy_mesh = openmc.RegularMesh()
entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50]
Expand Down
5 changes: 5 additions & 0 deletions include/openmc/distribution_spatial.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ class MeshSpatial : public SpatialDistribution {
//! \return Sampled element index and position within that element
std::pair<int32_t, Position> sample_mesh(uint64_t* seed) const;

//! Sample a mesh element
//! \param seed Pseudorandom number seed pointer
//! \return Sampled element index
int32_t sample_element_index(uint64_t* seed) const;

//! For unstructured meshes, ensure that elements are all linear tetrahedra
void check_element_types() const;

Expand Down
90 changes: 74 additions & 16 deletions include/openmc/source.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#ifndef OPENMC_SOURCE_H
#define OPENMC_SOURCE_H

#include <limits>
#include <unordered_set>

#include "pugixml.hpp"
Expand Down Expand Up @@ -39,19 +40,72 @@ extern vector<unique_ptr<Source>> external_sources;

//==============================================================================
//! Abstract source interface
//
//! The Source class provides the interface that must be implemented by derived
//! classes, namely the sample() method that returns a sampled source site. From
//! this base class, source rejection is handled within the
//! sample_with_constraints() method. However, note that some classes directly
//! check for constraints for efficiency reasons (like IndependentSource), in
//! which case the constraints_applied() method indicates that constraints
//! should not be checked a second time from the base class.
//==============================================================================

class Source {
public:
// Constructors, destructors
Source() = default;
explicit Source(pugi::xml_node node);
virtual ~Source() = default;

// Methods that must be implemented
virtual SourceSite sample(uint64_t* seed) const = 0;

// Methods that can be overridden
virtual double strength() const { return 1.0; }
virtual double strength() const { return strength_; }

//! Sample a source site and apply constraints
//
//! \param[inout] seed Pseudorandom seed pointer
//! \return Sampled site
SourceSite sample_with_constraints(uint64_t* seed) const;

//! Sample a source site (without applying constraints)
//
//! Sample from the external source distribution
//! \param[inout] seed Pseudorandom seed pointer
//! \return Sampled site
virtual SourceSite sample(uint64_t* seed) const { return {}; }
gridley marked this conversation as resolved.
Show resolved Hide resolved

static unique_ptr<Source> create(pugi::xml_node node);

protected:
// Domain types
gridley marked this conversation as resolved.
Show resolved Hide resolved
enum class DomainType { UNIVERSE, MATERIAL, CELL };

// Strategy used for rejecting sites when constraints are applied. KILL means
// that sites are always accepted but if they don't satisfy constraints, they
// are given weight 0. RESAMPLE means that a new source site will be sampled
// until constraints are met.
enum class RejectionStrategy { KILL, RESAMPLE };
gridley marked this conversation as resolved.
Show resolved Hide resolved

// Indicates whether derived class already handles constraints
virtual bool constraints_applied() const { return false; }

// Methods for constraints
void read_constraints(pugi::xml_node node);
bool satisfies_spatial_constraints(Position r) const;
bool satisfies_energy_constraints(double E) const;
bool satisfies_time_constraints(double time) const;

// Data members
double strength_ {1.0}; //!< Source strength
std::unordered_set<int32_t> domain_ids_; //!< Domains to reject from
DomainType domain_type_; //!< Domain type for rejection
std::pair<double, double> time_bounds_ {-std::numeric_limits<double>::max(),
std::numeric_limits<double>::max()}; //!< time limits
std::pair<double, double> energy_bounds_ {
0, std::numeric_limits<double>::max()}; //!< energy limits
bool only_fissionable_ {
false}; //!< Whether site must be in fissionable material
RejectionStrategy rejection_strategy_ {
RejectionStrategy::RESAMPLE}; //!< Procedure for rejecting
};

//==============================================================================
Expand All @@ -73,27 +127,24 @@ class IndependentSource : public Source {

// Properties
ParticleType particle_type() const { return particle_; }
double strength() const override { return strength_; }

// Make observing pointers available
SpatialDistribution* space() const { return space_.get(); }
UnitSphereDistribution* angle() const { return angle_.get(); }
Distribution* energy() const { return energy_.get(); }
Distribution* time() const { return time_.get(); }

private:
// Domain types
enum class DomainType { UNIVERSE, MATERIAL, CELL };
protected:
// Indicates whether derived class already handles constraints
bool constraints_applied() const override { return true; }

private:
// Data members
ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted
double strength_ {1.0}; //!< Source strength
UPtrSpace space_; //!< Spatial distribution
UPtrAngle angle_; //!< Angular distribution
UPtrDist energy_; //!< Energy distribution
UPtrDist time_; //!< Time distribution
DomainType domain_type_; //!< Domain type for rejection
std::unordered_set<int32_t> domain_ids_; //!< Domains to reject from
};

//==============================================================================
Expand All @@ -107,9 +158,12 @@ class FileSource : public Source {
explicit FileSource(const std::string& path);

// Methods
SourceSite sample(uint64_t* seed) const override;
void load_sites_from_file(
const std::string& path); //!< Load source sites from file

protected:
SourceSite sample(uint64_t* seed) const override;

private:
vector<SourceSite> sites_; //!< Source sites from a file
};
Expand All @@ -124,16 +178,17 @@ class CompiledSourceWrapper : public Source {
CompiledSourceWrapper(pugi::xml_node node);
~CompiledSourceWrapper();

double strength() const override { return compiled_source_->strength(); }

void setup(const std::string& path, const std::string& parameters);

protected:
// Defer implementation to custom source library
SourceSite sample(uint64_t* seed) const override
{
return compiled_source_->sample(seed);
}

double strength() const override { return compiled_source_->strength(); }

void setup(const std::string& path, const std::string& parameters);

private:
void* shared_library_; //!< library from dlopen
unique_ptr<Source> compiled_source_;
Expand Down Expand Up @@ -164,6 +219,9 @@ class MeshSource : public Source {
return sources_.size() == 1 ? sources_[0] : sources_[i];
}

protected:
bool constraints_applied() const override { return true; }

private:
// Data members
unique_ptr<MeshSpatial> space_; //!< Mesh spatial
Expand Down
12 changes: 8 additions & 4 deletions openmc/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ def pwr_pin_cell():
model.settings.batches = 10
model.settings.inactive = 5
model.settings.particles = 100
model.settings.source = openmc.IndependentSource(space=openmc.stats.Box(
[-pitch/2, -pitch/2, -1], [pitch/2, pitch/2, 1], only_fissionable=True))
model.settings.source = openmc.IndependentSource(
space=openmc.stats.Box([-pitch/2, -pitch/2, -1], [pitch/2, pitch/2, 1]),
constraints={'fissionable': True}
)

plot = openmc.Plot.from_geometry(model.geometry)
plot.pixels = (300, 300)
Expand Down Expand Up @@ -527,8 +529,10 @@ def pwr_assembly():
model.settings.batches = 10
model.settings.inactive = 5
model.settings.particles = 100
model.settings.source = openmc.IndependentSource(space=openmc.stats.Box(
[-pitch/2, -pitch/2, -1], [pitch/2, pitch/2, 1], only_fissionable=True))
model.settings.source = openmc.IndependentSource(
space=openmc.stats.Box([-pitch/2, -pitch/2, -1], [pitch/2, pitch/2, 1]),
constraints={'fissionable': True}
)

plot = openmc.Plot()
plot.origin = (0.0, 0.0, 0)
Expand Down
Loading
Loading