Remove old source_sampling custom source method

Custom sources are now created only through the new class-based method,
which supports parameterization.
New method also slightly adjusted to create the source as managed by a
unique_ptr.
Examples and tests updated to align with this approach.
Documentation updated.
This commit is contained in:
Dan Short 2020-08-21 12:05:43 +01:00
parent 475656e6e0
commit 821e7d9825
10 changed files with 177 additions and 151 deletions

View file

@ -462,25 +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_source()``;
* There must be a ``create_openmc_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 indicates that the source is to be
instantiated from an externally compiled source function, with parameters
defined by the string provided in this attribute. In this case, the
custom source library must define a class that inherits from the
``openmc::CustomSource`` abstract class. This class must implement a
``sample_source()`` function, which takes an unsigned integer pointer as an
argument. The custom source library must also contain an
``openmc_create_source`` method, which takes the value provided to the
parameters attribute as an argument and returns a pointer to an instance of
the custom source. If the library attribute is not provided then this
attribute will be ignored. More documentation on how to build parametrized
sources can be found in :ref:`parameterized_custom_source`.
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

View file

@ -182,42 +182,55 @@ 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_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;
}
};
extern "C" std::unique_ptr<Source> openmc_create_source() {
return std::unique_ptr<Source> (new 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_source()`` 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:
@ -240,61 +253,53 @@ used for sampling source particles at runtime.
Custom Parameterized Sources
----------------------------
If the custom source may be used with parameters at a variety of values then it
may be necessary to represent those parameters as a string in order to avoid
recompiling the source library for each run. This is supported by defining a
class inheriting from ``openmc::CustomSource`` that implements a
``sample_source`` function:
Some custom sources may have values (parameters) that can be changed between
runs. This is supported by using the ``create_custom_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 ParameterizedSource : public openmc::CustomSource {
public:
double energy;
ParameterizedSource(double energy) {
this->energy = energy;
}
class Source : public openmc::CustomSource
{
double energy;
// Samples from an instance of this class.
openmc::Particle::Bank sample_source(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;
Source(double energy)
{
this->energy = energy;
}
return particle;
}
// Samples from an instance of this class.
openmc::Particle::Bank sample_source(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;
}
};
The custom source library function in this case must also define an
``openmc_create_source`` method, which will be used to generate an instance of
the custom source, based on the value supplied in the
:attr:``openmc.Source.parameters`` attribute. The
``openmc_create_source`` method must be defined with ``extern "C"``:
.. code-block:: c++
// you must have external C linkage here otherwise
// dlopen will not find the file
extern "C" ParameterizedSource* openmc_create_source(const char* parameter) {
return new ParameterizedSource(atof(parameter));
extern "C" std::unique_ptr<Source> openmc_create_source(const char* parameter) {
return std::unique_ptr<Source> (new Source(atof(parameter)));
}
As with the basic custom source functionality, the custom source library
location must also be provided in the :attr:`openmc.Source.library`
attribute.
location must be provided in the :attr:`openmc.Source.library` attribute.
---------------
Shannon Entropy

View file

@ -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_source(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()
{
return std::unique_ptr<Source> (new Source());
}

View file

@ -1,17 +1,20 @@
#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 ParameterizedSource : public openmc::CustomSource {
class Source : public openmc::CustomSource
{
protected:
double radius_;
double energy_;
// Protect the constructor so that the class can only be created by serialisation.
ParameterizedSource(double radius, double energy) {
// Protect the constructor as we only want the class to be created by the from_string method.
Source(double radius, double energy)
{
radius_ = radius;
energy_ = energy;
}
@ -21,9 +24,10 @@ class ParameterizedSource : public openmc::CustomSource {
double radius() { return radius_; }
double energy() { return energy_; }
// Defines a function that can create a pointer to a new instance of this class
// by deserializing from the provided string.
static ParameterizedSource* from_string(const char* parameters) {
// 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(const char* parameters)
{
std::unordered_map<std::string, std::string> parameter_mapping;
std::stringstream ss(parameters);
@ -35,17 +39,20 @@ class ParameterizedSource : public openmc::CustomSource {
parameter_mapping[key] = value;
}
return new ParameterizedSource(std::stod(parameter_mapping["radius"]), std::stod(parameter_mapping["energy"]));
return std::unique_ptr<Source> (
new Source(std::stod(parameter_mapping["radius"]), std::stod(parameter_mapping["energy"]))
);
}
// Samples from an instance of this class.
openmc::Particle::Bank sample_source(uint64_t* seed) {
openmc::Particle::Bank sample_source(uint64_t* seed)
{
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 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);
@ -59,9 +66,10 @@ class ParameterizedSource : public openmc::CustomSource {
}
};
// A function to create a pointer to an instance of this class when generated
// 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" ParameterizedSource* openmc_create_source(const char* parameters) {
return ParameterizedSource::from_string(parameters);
extern "C" std::unique_ptr<Source> openmc_create_source(const char* parameters)
{
return Source::from_string(parameters);
}

View file

@ -66,7 +66,7 @@ class CustomSource {
virtual Particle::Bank sample_source(uint64_t* seed) = 0;
};
typedef CustomSource* create_custom_source_t(const char* parameters);
typedef std::unique_ptr<CustomSource> create_custom_source_t(const char* parameters);
//==============================================================================
// Functions

View file

@ -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
@ -45,11 +46,8 @@ std::vector<SourceDistribution> external_sources;
namespace {
void* custom_source_library;
using sample_t = Particle::Bank (*)(uint64_t* seed);
sample_t custom_source_function;
std::string custom_source_parameters;
CustomSource* custom_source;
std::string custom_source_parameters = "";
std::unique_ptr<CustomSource> custom_source;
}
@ -374,17 +372,11 @@ void load_custom_source_library()
// reset errors
dlerror();
if (custom_source_parameters.empty()) {
// 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"));
} else {
// get the function to create the CustomSource from the library
create_custom_source_t* create_custom_source = (create_custom_source_t*) dlsym(custom_source_library, "openmc_create_source");
// get the function to create the CustomSource from the library
create_custom_source_t* create_custom_source = (create_custom_source_t*) dlsym(custom_source_library, "openmc_create_source");
// create a pointer to an instance of the CustomSource
custom_source = create_custom_source(custom_source_parameters.c_str());
}
// create a pointer to an instance of the CustomSource
custom_source = create_custom_source(custom_source_parameters.c_str());
// check for any dlsym errors
auto dlsym_error = dlerror();
@ -401,9 +393,9 @@ void load_custom_source_library()
void close_custom_source_library()
{
if (custom_source) {
// delete the CustomSource if it exists
delete custom_source;
if (custom_source.get()) {
// Make sure the custom source is destroyed before we close it's libary.
custom_source.reset();
}
#ifdef HAS_DYNAMIC_LINKING
@ -416,12 +408,8 @@ void close_custom_source_library()
Particle::Bank sample_custom_source_library(uint64_t* seed)
{
if (custom_source_parameters.empty()) {
return custom_source_function(seed);
} else {
// sample from the instance of the CustomSource
return custom_source->sample_source(seed);
}
// sample from the instance of the CustomSource
return custom_source->sample_source(seed);
}
void fill_source_bank_custom_source()

View file

@ -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_source(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()
{
return std::unique_ptr<Source> (new Source());
}

View file

@ -19,5 +19,5 @@
<particles>1000</particles>
<batches>10</batches>
<inactive>0</inactive>
<source library="build/libparameterized_source.so" parameters="energy=1e3" strength="1.0" />
<source library="build/libsource.so" parameters="1e3" strength="1.0" />
</settings>

View file

@ -1,16 +1,19 @@
#include "openmc/source.h"
#include "openmc/particle.h"
class ParameterizedSource : public openmc::CustomSource {
class Source : public openmc::CustomSource
{
public:
double energy;
ParameterizedSource(double energy) {
Source(double energy)
{
this->energy = energy;
}
// Samples from an instance of this class.
openmc::Particle::Bank sample_source(uint64_t* seed) {
openmc::Particle::Bank sample_source(uint64_t* seed)
{
openmc::Particle::Bank particle;
// wgt
particle.particle = openmc::Particle::Type::neutron;
@ -28,8 +31,10 @@ class ParameterizedSource : public openmc::CustomSource {
}
};
// you must have external C linkage here otherwise
// dlopen will not find the file
extern "C" ParameterizedSource* openmc_create_source(const char* parameter) {
return new ParameterizedSource(atof(parameter));
// 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(const char* parameter)
{
return std::unique_ptr<Source> (new Source(atof(parameter)));
}

View file

@ -20,9 +20,9 @@ def compile_source(request):
f.write(textwrap.dedent("""
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
project(openmc_sources CXX)
add_library(parameterized_source SHARED parameterized_source_sampling.cpp)
add_library(source SHARED parameterized_source_sampling.cpp)
find_package(OpenMC REQUIRED HINTS {})
target_link_libraries(parameterized_source OpenMC::libopenmc)
target_link_libraries(source OpenMC::libopenmc)
""".format(openmc_dir)))
# Create temporary build directory and change to there
@ -63,8 +63,8 @@ def model():
# custom source from shared library
source = openmc.Source()
source.library = 'build/libparameterized_source.so'
source.parameters = 'energy=1e3'
source.library = 'build/libsource.so'
source.parameters = '1e3'
model.settings.source = source
return model