mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Merge pull request #1623 from DanShort12/serialised_custom_source
Add support for serialized custom sources
This commit is contained in:
commit
726adfb46c
17 changed files with 488 additions and 56 deletions
|
|
@ -462,9 +462,23 @@ attributes/sub-elements:
|
|||
:library:
|
||||
If this attribute is given, it indicates that the source is to be
|
||||
instantiated from an externally compiled source function. This source can be
|
||||
as complex as is required to define the source for your problem. The only
|
||||
requirement is that there is a function called ``sample_source()``. More
|
||||
documentation on how to build sources can be found in :ref:`custom_source`.
|
||||
as complex as is required to define the source for your problem. The library
|
||||
has a few basic requirements:
|
||||
|
||||
* It must contain a class that inherits from ``openmc::CustomSource``;
|
||||
* The class must implement a function called ``sample()``;
|
||||
* There must be an ``openmc_create_source()`` function that creates the source
|
||||
as a unique pointer. This function can be used to pass parameters through to
|
||||
the source from the XML, if needed.
|
||||
|
||||
More documentation on how to build sources can be found in :ref:`custom_source`.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:parameters:
|
||||
If this attribute is given, it provides the parameters to pass through to the
|
||||
class generated using the ``library`` parameter . More documentation on how to
|
||||
build parametrized sources can be found in :ref:`parameterized_custom_source`.
|
||||
|
||||
*Default*: None
|
||||
|
||||
|
|
|
|||
|
|
@ -182,42 +182,56 @@ Custom Sources
|
|||
|
||||
It is often the case that one may wish to simulate a complex source distribution
|
||||
that is not possible to represent with the classes described above. For these
|
||||
situations, it is possible to define a complex source with an externally defined
|
||||
source function that is loaded at runtime. A simple example source is shown
|
||||
situations, it is possible to define a complex source class containing an externally
|
||||
defined source function that is loaded at runtime. A simple example source is shown
|
||||
below.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
// you must have external C linkage here
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed) {
|
||||
openmc::Particle::Bank particle;
|
||||
// weight
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
particle.wgt = 1.0;
|
||||
// position
|
||||
double angle = 2.0 * M_PI * openmc::prn(seed);
|
||||
double radius = 3.0;
|
||||
particle.r.x = radius * std::cos(angle);
|
||||
particle.r.y = radius * std::sin(angle);
|
||||
particle.r.z = 0.0;
|
||||
// angle
|
||||
particle.u = {1.0, 0.0, 0.0};
|
||||
particle.E = 14.08e6;
|
||||
particle.delayed_group = 0;
|
||||
return particle;
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
|
||||
class Source : public openmc::CustomSource
|
||||
{
|
||||
openmc::Particle::Bank sample(uint64_t* seed)
|
||||
{
|
||||
openmc::Particle::Bank particle;
|
||||
// weight
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
particle.wgt = 1.0;
|
||||
// position
|
||||
double angle = 2.0 * M_PI * openmc::prn(seed);
|
||||
double radius = 3.0;
|
||||
particle.r.x = radius * std::cos(angle);
|
||||
particle.r.y = radius * std::sin(angle);
|
||||
particle.r.z = 0.0;
|
||||
// angle
|
||||
particle.u = {1.0, 0.0, 0.0};
|
||||
particle.E = 14.08e6;
|
||||
particle.delayed_group = 0;
|
||||
return particle;
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" std::unique_ptr<Source> openmc_create_source(std::string parameters)
|
||||
{
|
||||
return std::make_unique<Source>();
|
||||
}
|
||||
|
||||
The above source creates monodirectional 14.08 MeV neutrons that are distributed
|
||||
in a ring with a 3 cm radius. This routine is not particularly complex, but
|
||||
should serve as an example upon which to build more complicated sources.
|
||||
|
||||
.. note:: The function signature must be declared ``extern "C"``.
|
||||
.. note:: The source class must inherit from ``openmc::CustomSource`` and
|
||||
implement a ``sample()`` function.
|
||||
|
||||
.. note:: You should only use the openmc::prn() random number generator
|
||||
.. note:: The ``openmc_create_source()`` function signature must be declared
|
||||
``extern "C"``.
|
||||
|
||||
.. note:: You should only use the ``openmc::prn()`` random number generator.
|
||||
|
||||
In order to build your external source, you will need to link it against the
|
||||
OpenMC shared library. This can be done by writing a CMakeLists.txt file:
|
||||
|
|
@ -235,6 +249,58 @@ file in your build directory. Setting the :attr:`openmc.Source.library`
|
|||
attribute to the path of this shared library will indicate that it should be
|
||||
used for sampling source particles at runtime.
|
||||
|
||||
.. _parameterized_custom_source:
|
||||
|
||||
Custom Parameterized Sources
|
||||
----------------------------
|
||||
|
||||
Some custom sources may have values (parameters) that can be changed between
|
||||
runs. This is supported by using the ``openmc_create_source()`` function to
|
||||
pass parameters defined in the :attr:`openmc.Source.parameters` attribute to
|
||||
the source class when it is created:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
|
||||
class Source : public openmc::CustomSource {
|
||||
public:
|
||||
Source(double energy) : energy_{energy} { }
|
||||
|
||||
// Samples from an instance of this class.
|
||||
openmc::Particle::Bank sample(uint64_t* seed)
|
||||
{
|
||||
openmc::Particle::Bank particle;
|
||||
// weight
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
particle.wgt = 1.0;
|
||||
// position
|
||||
particle.r.x = 0.0;
|
||||
particle.r.y = 0.0;
|
||||
particle.r.z = 0.0;
|
||||
// angle
|
||||
particle.u = {1.0, 0.0, 0.0};
|
||||
particle.E = this->energy_;
|
||||
particle.delayed_group = 0;
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
private:
|
||||
double energy_;
|
||||
};
|
||||
|
||||
extern "C" std::unique_ptr<Source> openmc_create_source(std::string parameter) {
|
||||
double energy = std::stod(parameter);
|
||||
return std::make_unique<Source>(energy);
|
||||
}
|
||||
|
||||
As with the basic custom source functionality, the custom source library
|
||||
location must be provided in the :attr:`openmc.Source.library` attribute.
|
||||
|
||||
---------------
|
||||
Shannon Entropy
|
||||
---------------
|
||||
|
|
|
|||
|
|
@ -1,26 +1,36 @@
|
|||
#include <cmath> // for M_PI
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
|
||||
// you must have external C linkage here otherwise
|
||||
// dlopen will not find the file
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed)
|
||||
class Source : public openmc::CustomSource
|
||||
{
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
particle.wgt = 1.0;
|
||||
// position
|
||||
double angle = 2. * M_PI * openmc::prn(seed);
|
||||
double radius = 3.0;
|
||||
particle.r.x = radius * std::cos(angle);
|
||||
particle.r.y = radius * std::sin(angle);
|
||||
particle.r.z = 0.0;
|
||||
// angle
|
||||
particle.u = {1.0, 0.0, 0.0};
|
||||
particle.E = 14.08e6;
|
||||
particle.delayed_group = 0;
|
||||
return particle;
|
||||
openmc::Particle::Bank sample(uint64_t* seed)
|
||||
{
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
particle.wgt = 1.0;
|
||||
// position
|
||||
double angle = 2.0 * M_PI * openmc::prn(seed);
|
||||
double radius = 3.0;
|
||||
particle.r.x = radius * std::cos(angle);
|
||||
particle.r.y = radius * std::sin(angle);
|
||||
particle.r.z = 0.0;
|
||||
// angle
|
||||
particle.u = {1.0, 0.0, 0.0};
|
||||
particle.E = 14.08e6;
|
||||
particle.delayed_group = 0;
|
||||
return particle;
|
||||
}
|
||||
};
|
||||
|
||||
// A function to create a unique pointer to an instance of this class when generated
|
||||
// via a plugin call using dlopen/dlsym.
|
||||
// You must have external C linkage here otherwise dlopen will not find the file
|
||||
extern "C" std::unique_ptr<Source> openmc_create_source(std::string parameters)
|
||||
{
|
||||
return std::make_unique<Source>();
|
||||
}
|
||||
|
|
|
|||
8
examples/parameterized_custom_source/CMakeLists.txt
Normal file
8
examples/parameterized_custom_source/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
|
||||
project(openmc_sources CXX)
|
||||
add_library(parameterized_source SHARED parameterized_source_ring.cpp)
|
||||
find_package(OpenMC REQUIRED)
|
||||
if (OpenMC_FOUND)
|
||||
message(STATUS "Found OpenMC: ${OpenMC_DIR}")
|
||||
endif()
|
||||
target_link_libraries(parameterized_source OpenMC::libopenmc)
|
||||
23
examples/parameterized_custom_source/README.md
Normal file
23
examples/parameterized_custom_source/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Building a Parameterized Custom Source
|
||||
|
||||
To run this example, you first need to compile the custom source library, which
|
||||
requires headers from OpenMC. A CMakeLists.txt file has been set up for you that
|
||||
will search for OpenMC and build the custom library. To build the source
|
||||
library, you can run:
|
||||
|
||||
mkdir build && cd build
|
||||
OPENMC_ROOT=<path_to_openmc_install> cmake ..
|
||||
make
|
||||
|
||||
After this, you can build the model by running `python build_xml.py`. In the XML
|
||||
files that are created, you should see a reference to build/libparameterized_source.so,
|
||||
the custom source library that was built by CMake, and values in the parameters
|
||||
attribute. The model is also set up with a mesh tally of the flux, so once you run
|
||||
`openmc`, you will get a statepoint file with the tally results in it. Running
|
||||
`python show_flux.py` will pull in the results from the statepoint file and display
|
||||
them. If all worked well, you should see a ring "imprint" as well as a higher flux to
|
||||
the right side (since the custom source has all particles moving in the positive x
|
||||
direction).
|
||||
|
||||
Once built, you can edit the parameters attribute on the source to change the radius of
|
||||
the sampled ring or the energy of the sampled particles.
|
||||
37
examples/parameterized_custom_source/build_xml.py
Normal file
37
examples/parameterized_custom_source/build_xml.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import openmc
|
||||
|
||||
# Create a single material
|
||||
iron = openmc.Material()
|
||||
iron.set_density('g/cm3', 5.0)
|
||||
iron.add_element('Fe', 1.0)
|
||||
mats = openmc.Materials([iron])
|
||||
mats.export_to_xml()
|
||||
|
||||
# Create a 5 cm x 5 cm box filled with iron
|
||||
box = openmc.model.rectangular_prism(10.0, 10.0, boundary_type='vacuum')
|
||||
cell = openmc.Cell(fill=iron, region=box)
|
||||
geometry = openmc.Geometry([cell])
|
||||
geometry.export_to_xml()
|
||||
|
||||
# Tell OpenMC we're going to use our custom source
|
||||
settings = openmc.Settings()
|
||||
settings.run_mode = 'fixed source'
|
||||
settings.batches = 10
|
||||
settings.particles = 1000
|
||||
source = openmc.Source()
|
||||
source.library = 'build/libparameterized_source.so'
|
||||
source.parameters = 'radius=3.0, energy=14.08e6'
|
||||
settings.source = source
|
||||
settings.export_to_xml()
|
||||
|
||||
# Finally, define a mesh tally so that we can see the resulting flux
|
||||
mesh = openmc.RegularMesh()
|
||||
mesh.lower_left = (-5.0, -5.0)
|
||||
mesh.upper_right = (5.0, 5.0)
|
||||
mesh.dimension = (50, 50)
|
||||
|
||||
tally = openmc.Tally()
|
||||
tally.filters = [openmc.MeshFilter(mesh)]
|
||||
tally.scores = ['flux']
|
||||
tallies = openmc.Tallies([tally])
|
||||
tallies.export_to_xml()
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#include <cmath> // for M_PI
|
||||
#include <memory> // for unique_ptr
|
||||
#include <unordered_map>
|
||||
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
|
||||
class Source : public openmc::CustomSource
|
||||
{
|
||||
public:
|
||||
Source(double radius, double energy) : radius_(radius), energy_(energy) { }
|
||||
|
||||
// Defines a function that can create a unique pointer to a new instance of this class
|
||||
// by extracting the parameters from the provided string.
|
||||
static std::unique_ptr<Source> from_string(std::string parameters)
|
||||
{
|
||||
std::unordered_map<std::string, std::string> parameter_mapping;
|
||||
|
||||
std::stringstream ss(parameters);
|
||||
std::string parameter;
|
||||
while (std::getline(ss, parameter, ',')) {
|
||||
parameter.erase(0, parameter.find_first_not_of(' '));
|
||||
std::string key = parameter.substr(0, parameter.find_first_of('='));
|
||||
std::string value = parameter.substr(parameter.find_first_of('=') + 1, parameter.length());
|
||||
parameter_mapping[key] = value;
|
||||
}
|
||||
|
||||
double radius = std::stod(parameter_mapping["radius"]);
|
||||
double energy = std::stod(parameter_mapping["energy"]);
|
||||
return std::make_unique<Source>(radius, energy);
|
||||
}
|
||||
|
||||
// Samples from an instance of this class.
|
||||
openmc::Particle::Bank sample(uint64_t* seed)
|
||||
{
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
particle.wgt = 1.0;
|
||||
// position
|
||||
double angle = 2.0 * M_PI * openmc::prn(seed);
|
||||
double radius = this->radius_;
|
||||
particle.r.x = radius * std::cos(angle);
|
||||
particle.r.y = radius * std::sin(angle);
|
||||
particle.r.z = 0.0;
|
||||
// angle
|
||||
particle.u = {1.0, 0.0, 0.0};
|
||||
particle.E = this->energy_;
|
||||
particle.delayed_group = 0;
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
private:
|
||||
double radius_;
|
||||
double energy_;
|
||||
};
|
||||
|
||||
// A function to create a unique pointer to an instance of this class when generated
|
||||
// via a plugin call using dlopen/dlsym.
|
||||
// You must have external C linkage here otherwise dlopen will not find the file
|
||||
extern "C" std::unique_ptr<Source> openmc_create_source(std::string parameters)
|
||||
{
|
||||
return Source::from_string(parameters);
|
||||
}
|
||||
14
examples/parameterized_custom_source/show_flux.py
Normal file
14
examples/parameterized_custom_source/show_flux.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import openmc
|
||||
|
||||
# Get the flux from the statepoint
|
||||
with openmc.StatePoint('statepoint.10.h5') as sp:
|
||||
flux = sp.tallies[1].mean
|
||||
flux.shape = (50, 50)
|
||||
|
||||
# Plot the flux
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(flux, origin='lower', extent=(-5.0, 5.0, -5.0, 5.0))
|
||||
ax.set_xlabel('x [cm]')
|
||||
ax.set_ylabel('y [cm]')
|
||||
plt.show()
|
||||
|
|
@ -59,6 +59,15 @@ private:
|
|||
UPtrDist energy_; //!< Energy distribution
|
||||
};
|
||||
|
||||
class CustomSource {
|
||||
public:
|
||||
virtual ~CustomSource() {}
|
||||
|
||||
virtual Particle::Bank sample(uint64_t* seed) = 0;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<CustomSource> create_custom_source_t(std::string parameters);
|
||||
|
||||
//==============================================================================
|
||||
// Functions
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ class Source:
|
|||
Source file from which sites should be sampled
|
||||
library : str
|
||||
Path to a custom source library
|
||||
parameters : str
|
||||
Parameters to be provided to the custom source library
|
||||
|
||||
.. versionadded:: 0.12
|
||||
strength : float
|
||||
|
|
@ -41,6 +43,8 @@ class Source:
|
|||
Source file from which sites should be sampled
|
||||
library : str or None
|
||||
Path to a custom source library
|
||||
parameters : str
|
||||
Parameters to be provided to the custom source library
|
||||
strength : float
|
||||
Strength of the source
|
||||
particle : {'neutron', 'photon'}
|
||||
|
|
@ -49,12 +53,13 @@ class Source:
|
|||
"""
|
||||
|
||||
def __init__(self, space=None, angle=None, energy=None, filename=None,
|
||||
library=None, strength=1.0, particle='neutron'):
|
||||
library=None, parameters=None, strength=1.0, particle='neutron'):
|
||||
self._space = None
|
||||
self._angle = None
|
||||
self._energy = None
|
||||
self._file = None
|
||||
self._library = None
|
||||
self._parameters = None
|
||||
|
||||
if space is not None:
|
||||
self.space = space
|
||||
|
|
@ -66,6 +71,8 @@ class Source:
|
|||
self.file = filename
|
||||
if library is not None:
|
||||
self.library = library
|
||||
if parameters is not None:
|
||||
self.parameters = parameters
|
||||
self.strength = strength
|
||||
self.particle = particle
|
||||
|
||||
|
|
@ -77,6 +84,10 @@ class Source:
|
|||
def library(self):
|
||||
return self._library
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
return self._parameters
|
||||
|
||||
@property
|
||||
def space(self):
|
||||
return self._space
|
||||
|
|
@ -107,6 +118,11 @@ class Source:
|
|||
cv.check_type('library', library_name, str)
|
||||
self._library = library_name
|
||||
|
||||
@parameters.setter
|
||||
def parameters(self, parameters_path):
|
||||
cv.check_type('parameters', parameters_path, str)
|
||||
self._parameters = parameters_path
|
||||
|
||||
@space.setter
|
||||
def space(self, space):
|
||||
cv.check_type('spatial distribution', space, Spatial)
|
||||
|
|
@ -150,6 +166,8 @@ class Source:
|
|||
element.set("file", self.file)
|
||||
if self.library is not None:
|
||||
element.set("library", self.library)
|
||||
if self.parameters is not None:
|
||||
element.set("parameters", self.parameters)
|
||||
if self.space is not None:
|
||||
element.append(self.space.to_xml_element())
|
||||
if self.angle is not None:
|
||||
|
|
@ -191,6 +209,10 @@ class Source:
|
|||
if library is not None:
|
||||
source.library = library
|
||||
|
||||
parameters = get_text(elem, 'parameters')
|
||||
if parameters is not None:
|
||||
source.parameters = parameters
|
||||
|
||||
space = elem.find('space')
|
||||
if space is not None:
|
||||
source.space = Spatial.from_xml_element(space)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#endif
|
||||
|
||||
#include <algorithm> // for move
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#ifdef HAS_DYNAMIC_LINKING
|
||||
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
|
||||
|
|
@ -44,9 +45,9 @@ std::vector<SourceDistribution> external_sources;
|
|||
|
||||
namespace {
|
||||
|
||||
using sample_t = Particle::Bank (*)(uint64_t* seed);
|
||||
sample_t custom_source_function;
|
||||
void* custom_source_library;
|
||||
std::string custom_source_parameters;
|
||||
std::unique_ptr<CustomSource> custom_source;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +95,10 @@ SourceDistribution::SourceDistribution(pugi::xml_node node)
|
|||
fatal_error(fmt::format("Source library '{}' does not exist.",
|
||||
settings::path_source_library));
|
||||
}
|
||||
|
||||
if (check_for_node(node, "parameters")) {
|
||||
custom_source_parameters = get_node_value(node, "parameters", false, true);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Spatial distribution for external source
|
||||
|
|
@ -366,17 +371,21 @@ void load_custom_source_library()
|
|||
// reset errors
|
||||
dlerror();
|
||||
|
||||
// get the function from the library
|
||||
using sample_t = Particle::Bank (*)(uint64_t* seed);
|
||||
custom_source_function = reinterpret_cast<sample_t>(dlsym(custom_source_library, "sample_source"));
|
||||
// get the function to create the CustomSource from the library
|
||||
auto create_custom_source = reinterpret_cast<create_custom_source_t*>(
|
||||
dlsym(custom_source_library, "openmc_create_source"));
|
||||
|
||||
// check for any dlsym errors
|
||||
auto dlsym_error = dlerror();
|
||||
if (dlsym_error) {
|
||||
std::string error_msg = fmt::format("Couldn't open the openmc_create_source symbol: {}", dlsym_error);
|
||||
dlclose(custom_source_library);
|
||||
fatal_error(fmt::format("Couldn't open the sample_source symbol: {}", dlsym_error));
|
||||
fatal_error(error_msg);
|
||||
}
|
||||
|
||||
// create a pointer to an instance of the CustomSource
|
||||
custom_source = create_custom_source(custom_source_parameters);
|
||||
|
||||
#else
|
||||
fatal_error("Custom source libraries have not yet been implemented for "
|
||||
"non-POSIX systems");
|
||||
|
|
@ -385,6 +394,11 @@ void load_custom_source_library()
|
|||
|
||||
void close_custom_source_library()
|
||||
{
|
||||
if (custom_source.get()) {
|
||||
// Make sure the custom source is destroyed before we close it's libary.
|
||||
custom_source.reset();
|
||||
}
|
||||
|
||||
#ifdef HAS_DYNAMIC_LINKING
|
||||
dlclose(custom_source_library);
|
||||
#else
|
||||
|
|
@ -395,7 +409,8 @@ void close_custom_source_library()
|
|||
|
||||
Particle::Bank sample_custom_source_library(uint64_t* seed)
|
||||
{
|
||||
return custom_source_function(seed);
|
||||
// sample from the instance of the CustomSource
|
||||
return custom_source->sample(seed);
|
||||
}
|
||||
|
||||
void fill_source_bank_custom_source()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
|
||||
// you must have external C linkage here otherwise
|
||||
// dlopen will not find the file
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t *seed) {
|
||||
class Source : openmc::CustomSource
|
||||
{
|
||||
openmc::Particle::Bank sample(uint64_t *seed)
|
||||
{
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
|
|
@ -20,4 +23,13 @@ extern "C" openmc::Particle::Bank sample_source(uint64_t *seed) {
|
|||
particle.E = 14.08e6;
|
||||
particle.delayed_group = 0;
|
||||
return particle;
|
||||
}
|
||||
};
|
||||
|
||||
// A function to create a unique pointer to an instance of this class when generated
|
||||
// via a plugin call using dlopen/dlsym.
|
||||
// You must have external C linkage here otherwise dlopen will not find the file
|
||||
extern "C" std::unique_ptr<Source> openmc_create_source(std::string parameters)
|
||||
{
|
||||
return std::make_unique<Source>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<geometry>
|
||||
<cell id="1" material="1" region="-1" universe="1" />
|
||||
<surface boundary="vacuum" coeffs="0.0 0.0 0.0 100" id="1" type="sphere" />
|
||||
</geometry>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<materials>
|
||||
<material id="1" name="natural_lead">
|
||||
<density units="g/cm3" value="11.34" />
|
||||
<nuclide ao="0.014" name="Pb204" />
|
||||
<nuclide ao="0.241" name="Pb206" />
|
||||
<nuclide ao="0.221" name="Pb207" />
|
||||
<nuclide ao="0.524" name="Pb208" />
|
||||
</material>
|
||||
</materials>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<settings>
|
||||
<run_mode>fixed source</run_mode>
|
||||
<particles>1000</particles>
|
||||
<batches>10</batches>
|
||||
<inactive>0</inactive>
|
||||
<source library="build/libsource.so" parameters="1e3" strength="1.0" />
|
||||
</settings>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
|
||||
class Source : public openmc::CustomSource {
|
||||
public:
|
||||
Source(double energy) : energy_(energy) { }
|
||||
|
||||
// Samples from an instance of this class.
|
||||
openmc::Particle::Bank sample(uint64_t* seed)
|
||||
{
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
particle.particle = openmc::Particle::Type::neutron;
|
||||
particle.wgt = 1.0;
|
||||
// position
|
||||
particle.r.x = 0.0;
|
||||
particle.r.y = 0.0;
|
||||
particle.r.z = 0.0;
|
||||
// angle
|
||||
particle.u = {1.0, 0.0, 0.0};
|
||||
particle.E = this->energy_;
|
||||
particle.delayed_group = 0;
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
private:
|
||||
double energy_;
|
||||
};
|
||||
|
||||
// A function to create a unique pointer to an instance of this class when generated
|
||||
// via a plugin call using dlopen/dlsym.
|
||||
// You must have external C linkage here otherwise dlopen will not find the file
|
||||
extern "C" std::unique_ptr<Source> openmc_create_source(std::string parameter)
|
||||
{
|
||||
double energy = std::stod(parameter);
|
||||
return std::make_unique<Source>(energy);
|
||||
}
|
||||
75
tests/regression_tests/source_parameterized_dlopen/test.py
Normal file
75
tests/regression_tests/source_parameterized_dlopen/test.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
from pathlib import Path
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import textwrap
|
||||
|
||||
import openmc
|
||||
import pytest
|
||||
|
||||
from tests.testing_harness import PyAPITestHarness
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def compile_source(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_sources CXX)
|
||||
add_library(source SHARED parameterized_source_sampling.cpp)
|
||||
find_package(OpenMC REQUIRED HINTS {})
|
||||
target_link_libraries(source OpenMC::libopenmc)
|
||||
""".format(openmc_dir)))
|
||||
|
||||
# Create temporary build directory and change to there
|
||||
local_builddir = Path('build')
|
||||
local_builddir.mkdir(exist_ok=True)
|
||||
os.chdir(str(local_builddir))
|
||||
|
||||
# Run cmake/make to build the shared libary
|
||||
subprocess.run(['cmake', os.path.pardir], check=True)
|
||||
subprocess.run(['make'], check=True)
|
||||
os.chdir(os.path.pardir)
|
||||
|
||||
yield
|
||||
|
||||
# Remove local build directory when test is complete
|
||||
shutil.rmtree('build')
|
||||
os.remove('CMakeLists.txt')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def model():
|
||||
model = openmc.model.Model()
|
||||
natural_lead = openmc.Material(name="natural_lead")
|
||||
natural_lead.add_element('Pb', 1.0)
|
||||
natural_lead.set_density('g/cm3', 11.34)
|
||||
model.materials.append(natural_lead)
|
||||
|
||||
# geometry
|
||||
surface_sph1 = openmc.Sphere(r=100, boundary_type='vacuum')
|
||||
cell_1 = openmc.Cell(fill=natural_lead, region=-surface_sph1)
|
||||
model.geometry = openmc.Geometry([cell_1])
|
||||
|
||||
# settings
|
||||
model.settings.batches = 10
|
||||
model.settings.inactive = 0
|
||||
model.settings.particles = 1000
|
||||
model.settings.run_mode = 'fixed source'
|
||||
|
||||
# custom source from shared library
|
||||
source = openmc.Source()
|
||||
source.library = 'build/libsource.so'
|
||||
source.parameters = '1e3'
|
||||
model.settings.source = source
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def test_dlopen_source(compile_source, model):
|
||||
harness = PyAPITestHarness('statepoint.10.h5', model)
|
||||
harness.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue