Creating Processing System Files

SaltProc uses two different files to represent a processing system:

  • A .json file defining processing system components

  • A .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 and core_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')