mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
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:
parent
8bb10563c1
commit
9c1b318e78
6 changed files with 181 additions and 87 deletions
|
|
@ -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`.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue