mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-26 21:25:36 -04:00
Pass serialization as parameter attribute
Changes the implementation of the serialization to be on an attribute of the source XML element within settings.xml. This removes the need for a new file containing the serialization. Parameters are provided as a key-value string, separated by a comma and a space, although the implementation can change this is required. Change example values to align with existing custom_source to make comparisons easier. Update documentation to be consistent.
This commit is contained in:
parent
1de3d9ddf9
commit
8bb10563c1
11 changed files with 84 additions and 92 deletions
|
|
@ -468,14 +468,13 @@ attributes/sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
:serialization:
|
||||
: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 a serialized form of the source. The serialized source will be
|
||||
read from a file in the location provided by 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 the
|
||||
provided file. If the library attribute is not provided then this attribute
|
||||
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
|
||||
will be ignored. More documentation on how to build serialized sources can
|
||||
be found in :ref:`serialized_custom_source`.
|
||||
|
||||
|
|
|
|||
|
|
@ -241,17 +241,17 @@ 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 (XML, JSON,
|
||||
etc.) 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 serialized form of the source:
|
||||
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:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// you must have external C linkage here
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, char* serialized_source) {
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, const char* parameters) {
|
||||
// function to deserialize the source
|
||||
SerializedSource source = SerializedSource::from_xml(serialized_source);
|
||||
SerializedSource source = SerializedSource::from_string(parameters);
|
||||
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
|
|
@ -278,7 +278,7 @@ parameter that receives the serialized form of the source:
|
|||
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.serialization` attribute, along with the
|
||||
provided via the :attr:`openmc.Source.parameters` attribute, along with the
|
||||
custom source library location in :attr:`openmc.Source.library`.
|
||||
|
||||
When defining a class to be implemented via this deserialization approach, care
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ library, you can run:
|
|||
|
||||
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/libserialized_source.so,
|
||||
the custom source library that was built by CMake, and the path to the serialized
|
||||
representation of the source in serialized_source.xml. 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).
|
||||
the custom source library that was built by CMake, and the serialized representation
|
||||
of the source 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 serialized_source.xml file to change the radius of the
|
||||
sampled ring or the energy of the sampled particles.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,14 +1,5 @@
|
|||
import openmc
|
||||
|
||||
# Define the serialized source
|
||||
serialized_source = """<Source>
|
||||
<Radius>1.5</Radius>
|
||||
<Energy>1e3</Energy>
|
||||
</Source>
|
||||
"""
|
||||
with open('serialized_source.xml', 'w') as f:
|
||||
f.write(serialized_source)
|
||||
|
||||
# Create a single material
|
||||
iron = openmc.Material()
|
||||
iron.set_density('g/cm3', 5.0)
|
||||
|
|
@ -29,7 +20,7 @@ settings.batches = 10
|
|||
settings.particles = 1000
|
||||
source = openmc.Source()
|
||||
source.library = 'build/libserialized_source.so'
|
||||
source.serialization = 'serialized_source.xml'
|
||||
source.parameters = 'radius=3.0, energy=14.08e6'
|
||||
settings.source = source
|
||||
settings.export_to_xml()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#include <cmath> // for M_PI
|
||||
#include <unordered_map>
|
||||
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
#include "pugixml.hpp"
|
||||
|
||||
class SerializedSource {
|
||||
protected:
|
||||
|
|
@ -21,24 +21,26 @@ class SerializedSource {
|
|||
double radius() { return radius_; }
|
||||
double energy() { return energy_; }
|
||||
|
||||
// The deserialisation routine populates the constructor from well defined elements
|
||||
// in the input XML document.
|
||||
// Note that the source will have already been read from file, so what will be passed
|
||||
// in here is a string-like serialized value (not the path to the serialized value).
|
||||
static SerializedSource from_xml(char* serialized_source) {
|
||||
pugi::xml_document doc;
|
||||
doc.load_string(serialized_source);
|
||||
pugi::xml_node root_node = doc.root().child("Source");
|
||||
double radius = root_node.child("Radius").text().as_double();
|
||||
double energy = root_node.child("Energy").text().as_double();
|
||||
return SerializedSource(radius, energy);
|
||||
static SerializedSource from_string(const char* 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;
|
||||
}
|
||||
|
||||
return SerializedSource(std::stod(parameter_mapping["radius"]), std::stod(parameter_mapping["energy"]));
|
||||
}
|
||||
};
|
||||
|
||||
// you must have external C linkage here otherwise
|
||||
// dlopen will not find the file
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, char* serialized_source) {
|
||||
SerializedSource source = SerializedSource::from_xml(serialized_source);
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, const char* parameters) {
|
||||
SerializedSource source = SerializedSource::from_string(parameters);
|
||||
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ class Source:
|
|||
Source file from which sites should be sampled
|
||||
library : str
|
||||
Path to a custom source library
|
||||
serialization : str
|
||||
Path to the serialized representation of the custom source
|
||||
parameters : str
|
||||
Parameters to be provided to the custom source
|
||||
|
||||
.. versionadded:: 0.12
|
||||
strength : float
|
||||
|
|
@ -43,8 +43,8 @@ class Source:
|
|||
Source file from which sites should be sampled
|
||||
library : str or None
|
||||
Path to a custom source library
|
||||
serialization : str
|
||||
Path to the serialized representation of the custom source
|
||||
parameters : str
|
||||
Parameters to be provided to the custom source
|
||||
strength : float
|
||||
Strength of the source
|
||||
particle : {'neutron', 'photon'}
|
||||
|
|
@ -53,13 +53,13 @@ class Source:
|
|||
"""
|
||||
|
||||
def __init__(self, space=None, angle=None, energy=None, filename=None,
|
||||
library=None, serialization=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._serialization = None
|
||||
self._parameters = None
|
||||
|
||||
if space is not None:
|
||||
self.space = space
|
||||
|
|
@ -71,8 +71,8 @@ class Source:
|
|||
self.file = filename
|
||||
if library is not None:
|
||||
self.library = library
|
||||
if serialization is not None:
|
||||
self.serialization = serialization
|
||||
if parameters is not None:
|
||||
self.parameters = parameters
|
||||
self.strength = strength
|
||||
self.particle = particle
|
||||
|
||||
|
|
@ -85,8 +85,8 @@ class Source:
|
|||
return self._library
|
||||
|
||||
@property
|
||||
def serialization(self):
|
||||
return self._serialization
|
||||
def parameters(self):
|
||||
return self._parameters
|
||||
|
||||
@property
|
||||
def space(self):
|
||||
|
|
@ -118,10 +118,10 @@ class Source:
|
|||
cv.check_type('library', library_name, str)
|
||||
self._library = library_name
|
||||
|
||||
@serialization.setter
|
||||
def serialization(self, serialization_path):
|
||||
cv.check_type('serialization', serialization_path, str)
|
||||
self._serialization = serialization_path
|
||||
@parameters.setter
|
||||
def parameters(self, parameters_path):
|
||||
cv.check_type('parameters', parameters_path, str)
|
||||
self._parameters = parameters_path
|
||||
|
||||
@space.setter
|
||||
def space(self, space):
|
||||
|
|
@ -166,8 +166,8 @@ class Source:
|
|||
element.set("file", self.file)
|
||||
if self.library is not None:
|
||||
element.set("library", self.library)
|
||||
if self.serialization is not None:
|
||||
element.set("serialization", self.serialization)
|
||||
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:
|
||||
|
|
@ -209,9 +209,9 @@ class Source:
|
|||
if library is not None:
|
||||
source.library = library
|
||||
|
||||
serialization = get_text(elem, 'serialization')
|
||||
if serialization is not None:
|
||||
source.serialization = serialization
|
||||
parameters = get_text(elem, 'parameters')
|
||||
if parameters is not None:
|
||||
source.parameters = parameters
|
||||
|
||||
space = elem.find('space')
|
||||
if space is not None:
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ namespace {
|
|||
|
||||
using sample_t = Particle::Bank (*)(uint64_t* seed);
|
||||
sample_t custom_source_function;
|
||||
std::string serialization;
|
||||
using serialized_sample_t = Particle::Bank (*)(uint64_t* seed, const char* serialization);
|
||||
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;
|
||||
|
||||
|
|
@ -98,14 +98,8 @@ SourceDistribution::SourceDistribution(pugi::xml_node node)
|
|||
settings::path_source_library));
|
||||
}
|
||||
|
||||
if (check_for_node(node, "serialization")) {
|
||||
// If the source is serialized then make sure we only load it from file once, otherwise there will
|
||||
// be a significant I/O overhead.
|
||||
pugi::xml_document doc;
|
||||
doc.load_file(get_node_value(node, "serialization", false, true).c_str());
|
||||
std::stringstream ss;
|
||||
doc.print(ss);
|
||||
serialization = ss.str();
|
||||
if (check_for_node(node, "parameters")) {
|
||||
custom_source_parameters = get_node_value(node, "parameters", false, true);
|
||||
}
|
||||
} else {
|
||||
|
||||
|
|
@ -380,13 +374,13 @@ void load_custom_source_library()
|
|||
// reset errors
|
||||
dlerror();
|
||||
|
||||
if (serialization.empty()) {
|
||||
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 from the library using the provided serialization
|
||||
using sample_t = Particle::Bank (*)(uint64_t* seed, const char* 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"));
|
||||
}
|
||||
|
||||
|
|
@ -415,10 +409,10 @@ void close_custom_source_library()
|
|||
|
||||
Particle::Bank sample_custom_source_library(uint64_t* seed)
|
||||
{
|
||||
if (serialization.empty()) {
|
||||
if (custom_source_parameters.empty()) {
|
||||
return custom_source_function(seed);
|
||||
} else {
|
||||
return custom_serialized_source_function(seed, serialization.c_str());
|
||||
return custom_serialized_source_function(seed, custom_source_parameters.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,5 +19,5 @@
|
|||
<particles>1000</particles>
|
||||
<batches>10</batches>
|
||||
<inactive>0</inactive>
|
||||
<source library="build/libserialized_source.so" serialization="serialized_source.xml" strength="1.0" />
|
||||
<source library="build/libserialized_source.so" parameters="energy=1e3" strength="1.0" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
<Source>
|
||||
<Energy>1e3</Energy>
|
||||
</Source>
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/source.h"
|
||||
#include "openmc/particle.h"
|
||||
|
|
@ -16,19 +18,26 @@ class SerializedSource {
|
|||
// Getters for the values that we want to use in sampling.
|
||||
double energy() { return energy_; }
|
||||
|
||||
static SerializedSource from_xml(char* serialized_source) {
|
||||
pugi::xml_document doc;
|
||||
doc.load_string(serialized_source);
|
||||
pugi::xml_node root_node = doc.root().child("Source");
|
||||
double energy = root_node.child("Energy").text().as_double();
|
||||
return SerializedSource(energy);
|
||||
static SerializedSource from_string(const char* 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;
|
||||
}
|
||||
|
||||
return SerializedSource(std::stod(parameter_mapping["energy"]));
|
||||
}
|
||||
};
|
||||
|
||||
// you must have external C linkage here otherwise
|
||||
// dlopen will not find the file
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, char* serialized_source) {
|
||||
SerializedSource source = SerializedSource::from_xml(serialized_source);
|
||||
extern "C" openmc::Particle::Bank sample_source(uint64_t* seed, const char* parameters) {
|
||||
SerializedSource source = SerializedSource::from_string(parameters);
|
||||
|
||||
openmc::Particle::Bank particle;
|
||||
// wgt
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ def model():
|
|||
# custom source from shared library
|
||||
source = openmc.Source()
|
||||
source.library = 'build/libserialized_source.so'
|
||||
source.serialization = 'serialized_source.xml'
|
||||
source.parameters = 'energy=1e3'
|
||||
model.settings.source = source
|
||||
|
||||
return model
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue