Creating Processing System Files¶
SaltProc uses two different files to represent a processing system:
A
.json
file defining processing system componentsA
.dot
file linking the processing system components in a graph
Note
An API to write the file defining processing system components is in development, but for now it must be written by hand.
Defining Processing System Components¶
The processing system components are defined for each material that a user want to process.
Warning
The of the material in the processing system components file must match the name of the corresponding material in the transport code.
We start with an empty JSON file:
{
}
Then add the materials we want to process. For simpilicty, let’s only consider
one material, fuel
:
{
"fuel": {
"extraction_processes": {}
}
}
Inside the fuel
key, we define our processing system components via the
extraction_processes
parameter. There are two components that every model
must have: core_outlet
– which marks the beginning of the reprocessing
system – and core_inlet
– which marks the end of the reprocessing system.
Each process has several fields that must be filled by the user:
capacity
Maximum mass flow rate the process can handle in \(g/s\). This parameter is currently unused, but could be useful in the future for more physics-based implementations of reprocessing.
efficiency
Dictionary mapping element names to extraction efficiencies. These should be empty for the
core_outlet
andcore_inlet
processes since they represetnt the beginning and end of the processng system.mass_flowrate
The mass flowrate of the process in \(g/s\).
volume
The volume of the processing component in \(cm^3\).
Let’s create core_inlet
and core_outlet
first, assuming a capacity
of \(9.92 \cdot 10^6\) \(g/s\), and that the mass flowrate is equal to the
capacity:
{
"fuel": {
"extraction_processes": {
"core_inlet": {
"capacity": 9920000,
"efficiency": {},
"mass_flowrate": 9920000,
"volume": 0
},
"core_outlet":
"capacity": 9920000,
"efficiency": {},
"mass_flowrate": 9920000,
"volume": 0
}
}
}
Now, let’s create three processes that acutally do things: gas_separator
,
which removes Kr, Xe, and H from the fuel, nickel_filter
, which removes
dissolved metals from the fuel, and reductive_extractor
, which removes
fission procducts from the fuel:
{
"fuel": {
"extraction_processes": {
"core_inlet": {
"capacity": 9920000,
"efficiency": {},
"mass_flowrate": 9920000,
"volume": 0
},
"core_outlet":
"capacity": 9920000,
"efficiency": {},
"mass_flowrate": 9920000,
"volume": 0
}
"gas_separator": {
"capacity": 9920000,
"efficiency": {
"Kr": 1,
"Xe": 1,
"H": 1
},
"mass_flowrate": 9920000,
"volume": 10000000
},
"nickel_filter": {
"capacity": 9920000,
"efficiency": {
"Se": 1,
"Nb": 1,
"Mo": 1,
"Tc": 1,
"Ru": 1,
"Rh": 1,
"Pd": 1,
"Ag": 1,
"Sb": 1,
"Te": 1
},
"mass_flowrate": 9920000,
"volume": 11
},
"reductive_extractor": {
"capacity": 9920000,
"efficiency": {
"Pa": 1,
"Y": 0.8904,
"La": 0.8904,
"Ce": 0.8904,
"Pr": 0.8904,
"Nd": 0.8904,
"Pm": 0.8904,
"Sm": 0.8904,
"Gd": 0.8904,
"Eu": 0.2,
"Rb": 0.032,
"Sr": 0.032,
"Cs": 0.032,
"Ba": 0.032,
"Zr": 0.425,
"Cd": 0.425,
"In": 0.425,
"Sn": 0.425,
"Br": 0.842,
"I": 0.842
},
"mass_flowrate": 9920000,
"volume": 11
}
}
}
In addition to extraction_processes
, we can also define feeds
, which
are materials added to the reprocessed material after the processing is
performed. feeds
require the following parameters:
density
Density of the material in \(g/cm^3\)
volume
Volume of the material in \(cm^3\)
mass
Mass of the material in \(g\)
comp
A dictionary mapping nuclide names to weight-percent.
In this case, we can add a simple fuel_salt
feed:
{
"fuel": {
"extraction_processes": {
"core_inlet": {
"capacity": 9920000,
"efficiency": {},
"mass_flowrate": 9920000,
"volume": 0
},
"core_outlet":
"capacity": 9920000,
"efficiency": {},
"mass_flowrate": 9920000,
"volume": 0
}
"gas_separator": {
"capacity": 9920000,
"efficiency": {
"Kr": 1,
"Xe": 1,
"H": 1
},
"mass_flowrate": 9920000,
"volume": 10000000
},
"nickel_filter": {
"capacity": 9920000,
"efficiency": {
"Se": 1,
"Nb": 1,
"Mo": 1,
"Tc": 1,
"Ru": 1,
"Rh": 1,
"Pd": 1,
"Ag": 1,
"Sb": 1,
"Te": 1
},
"mass_flowrate": 9920000,
"volume": 11
},
"reductive_extractor": {
"capacity": 9920000,
"efficiency": {
"Pa": 1,
"Y": 0.8904,
"La": 0.8904,
"Ce": 0.8904,
"Pr": 0.8904,
"Nd": 0.8904,
"Pm": 0.8904,
"Sm": 0.8904,
"Gd": 0.8904,
"Eu": 0.2,
"Rb": 0.032,
"Sr": 0.032,
"Cs": 0.032,
"Ba": 0.032,
"Zr": 0.425,
"Cd": 0.425,
"In": 0.425,
"Sn": 0.425,
"Br": 0.842,
"I": 0.842
},
"mass_flowrate": 9920000,
"volume": 11
},
"feeds": {
"fuel_salt": {
"density": 3.35,
"volume": 1e8,
"mass": 3.35e
"comp": {
"Li-7": 0.07875361306856505,
"Be-9": 0.022558425114333525,
"Fl-19": 0.4540013117137259,
"Th-232": 0.4446866501033755
}
}
}
}
}
Defining the Processing System Graph¶
As mentioned earlier, processing system graphs must be acyclic directed graphs. Directed means each edge in the graphs connecting nodes has a specific direction associated with it, and acyclic means there shouldn’t be any “loops”, that is, there should be no way to traverse along an edge that takes you back to a node you already visited.
SaltProc uses the networkx package to process graphs, and this is also
how they should be constructed. We start by importing networkx
and creating
a DiGraph
:
import networkx as nx
system = nx.DiGraph
Next, we need to add nodes to our graph. Each processing system component we defined previously is a node:
Warning
The names of the nodes must match the names of the processing system components.
nodes = ['core_outlet', 'core_inlet', 'gas_separator', 'nickel_filter', 'reductive_extractor']
for node in nodes:
system.add_node(node)
Finally, we need to connect the nodes with edges. The graph strucure of acyclic directed graphs can get fairly complicated, but for this example our graph is going to be a single point-to-point path:
edges = [('core_outlet', 'gas_separator'),
('gas_separator', 'nickel_filter'),
('nickel_filter', 'reductive_extractor'),
('reductive_extractor', 'core_inlet')]
for edge in edges:
system.add_edge(*edge)
To ensure that our graph is acyclic, we can run
nx.algorithms.is_directed_acyclic_graph(system)
Finally, we need to export this graph to a .dot
file:
import networkx.drawing as nxd
nxd.nx_pydot.write_dot(system, 'graph.dot')