One of the main purposes of the bright framework in addition to providing a suite of fuel cycle components is to allow the user or developer to easily create their own custom components. The natural way to leverage the existing functionality is to subclass existing components. This may be done either on the C++ level or on the Python level.
The abstract base class for bright is the fuel cycle component FCComp. This class handles much of the logic for initialization, I/O, and contains member variables which are common to all components. To successfully subclass FCComp, the child class should override the following attributes and methods:
The exact semantics of subclassing depend on which language is used. For conciseness Python will be used here.
Suppose that a new type of cladding material which can only be produced in outer space has just been discovered! This rare substance has been dubbed Gundanium alloy and is comprised of silver and neodymium in equal parts (AgNd). A very expensive fabrication facility has been constructed. As it turns out, the amount of Gundanium that may be produced is a strong function of the number of g-forces. As a plucky young fuel cycle student, you have decided to model this facility.
You start out by subclassing FCComp:
from bright import bright_conf
from bright.fccomp import FCComp
from pyne.material import Material
class GundaniumFab(FCComp):
# Override constructor to provide a default g-force
# Also, set track_params for the class here
def __init__(self, g=1E-3, name=""):
track_params = set(['g', 'mass_AgNd'])
super(GundaniumFab, self).__init__(params=track_params, name=name)
self.g = g
# Override calc() method to create Gundanium mass
def calc(self, input=None):
"""Removes silver and neodymium from a material to make Gundanium alloy."""
# Check the input first
if input is None:
pass
elif isinstance(input, Material):
self.mat_feed = input
else:
self.mat_feed = Material(input)
feed = self.mat_feed
ag = feed['Ag':'Cd']
nd = feed['Nd':'Pm']
if ag.mass <= nd.mass:
nd.mass = ag.mass
else:
ag.mass = nd.mass
agnd = ag + nd
agnd.mass = agnd.mass * 10**(-self.g/9.8)
agnd.name = "Gundanium Alloy"
agnd.atoms_per_mol = 2
self.mat_prod = agnd
return agnd
# Override the calc_params() to set the appropriate parameter values
def calc_params(self):
"""Calculate fabrication parameters."""
self.params_prior_calc['g'] = self.g
self.params_after_calc['g'] = self.g
self.params_prior_calc['mass_AgNd'] = 0.0
self.params_after_calc['mass_AgNd'] = self.mat_prod.mass
if __name__ == '__main__':
# Init the nuclides
bright_conf.track_nucs = set(['Ag107', 'B10', 'ND144'])
# Create an instance of the sub-class and some material
gf = GundaniumFab(0.98, "Shangri-La")
mat = Material({'Ag107': 10.0, 'B10': 42.0, 'ND144': 65.0})
# Calculate the product produced
prod = gf.calc(mat)
print prod
# Set the parameters and display output
gf.calc_params()
gf.write()
Any of the other daughter classes of FCComp may be subclassed and their behavior altered. More sophisticated components may require additional methods or attributes to be specified. In all cases, the three attributes above must be implemented.
An example of subclassing that has become part of the bright suite is the OrigenReactorMG component. This class inherits from the standard ReactorMG class and swaps out the parent’s transmutation methods with the an ORIGEN 2.2 based approach. Please refer to the source code for more implementation details.
Another powerful feature of this subclassing approach is the ability to adapt the existing classes to new use cases. Suppose a pricing model (based on the mass of the output) is desired. Thus all components should have an associated price() method. Thin subclasses which mix an adapter and the base classes can easily be defined. For example:
from bright.api import *
# Adapter class
class PriceAdapter(object):
"""I am useless on my own."""
def price(self):
return self.mat_prod.mass * 42.0
# Adapted classes
class PricedFCComp(FCComp, PriceAdapter):
pass
class PricedEnrichment(Enrichment, PriceAdapter):
pass
class PricedStorage(Storage, PriceAdapter):
pass