Only instantiate custom source once

In the existing custom_source implementation, the source will only be
created once. This is much more efficient than the custom serialized
source, where each sampling will create a new instance of the class.
As there could be many samples, this introduces an overhead,
particularly if the operations to instantiate the class are not trivial.
This implementation defines an abstract class, which is then used by the
custom classes to allow the custom serialized class to be created based
on the plugin, sampled from, and then destroyed.
Update documentation and test to reflect this.
This commit is contained in:
Dan Short 2020-08-03 17:38:12 +01:00
parent 8bb10563c1
commit 9c1b318e78
6 changed files with 181 additions and 87 deletions

View file

@ -472,9 +472,15 @@ attributes/sub-elements:
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
``sample_source()`` function must take as input an additional character
array containing the serialization that OpenMC will have read from this
attribute. If the library attribute is not provided then this attribute
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 array of integers as an
argument. The custom source library must also contain a ``create`` method,
which takes a serialized form of the source (as provided to the parameters
attribute) as an argument and returns a pointer to an instance of the custom
source, and a ``destroy`` method, which takes a pointer to an instance of
the custom source as an argument and deletes the memory allocated to the
custom source. If the library attribute is not provided then this attribute
will be ignored. More documentation on how to build serialized sources can
be found in :ref:`serialized_custom_source`.

View file

@ -243,47 +243,92 @@ Custom Serialized Sources
If the custom source may be used with parameters at a variety of values then it
may be necessary to serialize the source to an appropriate format in order to
avoid recompiling the source library for each run. This is supported by defining
the ``source_sampling`` function with an additional parameter that receives the
parameters used to build the source:
a class inheriting from ``openmc::CustomSource`` that implements a
``sample_source`` function. The class should also have logic to deserialise
the parameters provided via the settings.xml file in the
:attr:``openmc.Source.parameters`` attribute:
.. code-block:: c++
// you must have external C linkage here
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, const char* parameters) {
// function to deserialize the source
SerializedSource source = SerializedSource::from_string(parameters);
#include <unordered_map>
openmc::Particle::Bank particle;
// wgt
particle.particle = openmc::Particle::Type::neutron;
particle.wgt = 1.0;
// position
double angle = 2. * M_PI * openmc::prn(seed);
#include "openmc/random_lcg.h"
#include "openmc/source.h"
#include "openmc/particle.h"
// get the radius from the serialized form of the source
double radius = source.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};
class SerializedSource : public openmc::CustomSource {
protected:
double energy_;
// get the energy from the serialized form of the source
particle.E = source.energy();
particle.delayed_group = 0;
// Protect the constructor so that the class can only be created by serialisation.
SerializedSource(double energy) {
energy_ = energy;
}
return particle;
}
public:
// Getters for the values that we want to use in sampling.
double energy() { return energy_; }
The details of the serialization routine, in particular the schema of the source
are to be defined by the implementation of the serializable source class. The
location of the serialized representation of the source to be used must be
provided via the :attr:`openmc.Source.parameters` attribute, along with the
custom source library location in :attr:`openmc.Source.library`.
// Defines a function that can create a pointer to a new instance of this class
// by deserializing from the provided string.
static SerializedSource* from_string(const char* parameters) {
std::unordered_map<std::string, std::string> parameter_mapping;
When defining a class to be implemented via this deserialization approach, care
must be taken to ensure that unique symbols in the resulting binary are
discoverable when the ``sample_source`` function is loaded via ``dlsym``.
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;
}
return new SerializedSource(std::stod(parameter_mapping["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;
return particle;
}
};
The custom source library function in this case must also define a ``create``
method and a ``destroy`` method. The ``create`` method will be used to
generate an instance of the custom source, based on the value supplied in
the :attr:``openmc.Source.parameters`` attribute. The ``destroy`` method
will be used to destroy that instance once the sampling has completed. Both
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" SerializedSource* create(const char* serialized_source) {
return SerializedSource::from_string(serialized_source);
}
// you must have external C linkage here otherwise
// dlopen will not find the file
extern "C" void destroy(SerializedSource* source) {
delete source;
}
As with the basic custom source functionality, the custom source library
location must also be provided in the :attr:`openmc.Source.library`
attribute.
---------------
Shannon Entropy

View file

@ -5,7 +5,7 @@
#include "openmc/source.h"
#include "openmc/particle.h"
class SerializedSource {
class SerializedSource : public openmc::CustomSource {
protected:
double radius_;
double energy_;
@ -21,7 +21,9 @@ class SerializedSource {
double radius() { return radius_; }
double energy() { return energy_; }
static SerializedSource from_string(const char* parameters) {
// Defines a function that can create a pointer to a new instance of this class
// by deserializing from the provided string.
static SerializedSource* from_string(const char* parameters) {
std::unordered_map<std::string, std::string> parameter_mapping;
std::stringstream ss(parameters);
@ -33,29 +35,40 @@ class SerializedSource {
parameter_mapping[key] = value;
}
return SerializedSource(std::stod(parameter_mapping["radius"]), std::stod(parameter_mapping["energy"]));
return new SerializedSource(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 particle;
// wgt
particle.particle = openmc::Particle::Type::neutron;
particle.wgt = 1.0;
// position
double angle = 2. * 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;
}
};
// you must have external C linkage here otherwise
// dlopen will not find the file
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, const char* parameters) {
SerializedSource source = SerializedSource::from_string(parameters);
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 = source.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 = source.energy();
particle.delayed_group = 0;
return particle;
// A function to create a 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" SerializedSource* create(const char* parameters) {
return SerializedSource::from_string(parameters);
}
// A function to destroy a 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" void destroy(SerializedSource* source) {
delete source;
}

View file

@ -59,6 +59,16 @@ private:
UPtrDist energy_; //!< Energy distribution
};
class CustomSource {
public:
virtual ~CustomSource() {}
virtual Particle::Bank sample_source(uint64_t* seed) = 0;
};
typedef CustomSource* create_custom_source_t(const char* serialized_source);
typedef void destroy_custom_source_t(CustomSource*);
//==============================================================================
// Functions
//==============================================================================

View file

@ -44,12 +44,13 @@ 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;
using serialized_sample_t = Particle::Bank (*)(uint64_t* seed, const char* parameters);
serialized_sample_t custom_serialized_source_function;
void* custom_source_library;
CustomSource* custom_source;
destroy_custom_source_t* destroy_custom_source;
}
@ -379,9 +380,12 @@ void load_custom_source_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 from the library using the provided serialization
using sample_t = Particle::Bank (*)(uint64_t* seed, const char* parameters);
custom_serialized_source_function = reinterpret_cast<sample_t>(dlsym(custom_source_library, "sample_source"));
// get the functions to create and destroy the CustomSource from the library
create_custom_source_t* create_custom_source = (create_custom_source_t*) dlsym(custom_source_library, "create");
destroy_custom_source = (destroy_custom_source_t*) dlsym(custom_source_library, "destroy");
// create a pointer to an instance of the CustomSource
custom_source = create_custom_source(custom_source_parameters.c_str());
}
// check for any dlsym errors
@ -399,6 +403,11 @@ void load_custom_source_library()
void close_custom_source_library()
{
if (custom_source) {
// destroy the CustomSource if it exists
destroy_custom_source(custom_source);
}
#ifdef HAS_DYNAMIC_LINKING
dlclose(custom_source_library);
#else
@ -412,7 +421,8 @@ Particle::Bank sample_custom_source_library(uint64_t* seed)
if (custom_source_parameters.empty()) {
return custom_source_function(seed);
} else {
return custom_serialized_source_function(seed, custom_source_parameters.c_str());
// sample from the instance of the CustomSource
return custom_source->sample_source(seed);
}
}

View file

@ -3,9 +3,8 @@
#include "openmc/random_lcg.h"
#include "openmc/source.h"
#include "openmc/particle.h"
#include "pugixml.hpp"
class SerializedSource {
class SerializedSource : public openmc::CustomSource {
protected:
double energy_;
@ -18,7 +17,9 @@ class SerializedSource {
// Getters for the values that we want to use in sampling.
double energy() { return energy_; }
static SerializedSource from_string(const char* parameters) {
// Defines a function that can create a pointer to a new instance of this class
// by deserializing from the provided string.
static SerializedSource* from_string(const char* parameters) {
std::unordered_map<std::string, std::string> parameter_mapping;
std::stringstream ss(parameters);
@ -30,27 +31,36 @@ class SerializedSource {
parameter_mapping[key] = value;
}
return SerializedSource(std::stod(parameter_mapping["energy"]));
return new SerializedSource(std::stod(parameter_mapping["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;
return particle;
}
};
// you must have external C linkage here otherwise
// dlopen will not find the file
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, const char* parameters) {
SerializedSource source = SerializedSource::from_string(parameters);
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 = source.energy();
particle.delayed_group = 0;
return particle;
extern "C" SerializedSource* create(const char* serialized_source) {
return SerializedSource::from_string(serialized_source);
}
// you must have external C linkage here otherwise
// dlopen will not find the file
extern "C" void destroy(SerializedSource* source) {
delete source;
}