2. Software Design and Implementation Document (SDID)

2.1. Purpose and Scope

This document is the Software Design and Implementation Document (SDID) for ARMI.

The purpose of this document is to define how the ARMI requirements are implemented. These are important user stories for anyone wanting to use ARMI or develop their own ARMI-based application. The implementation of the ARMI requirements is described in detail in an Implementation Traceability Matrix (ITM).

2.1.1. Procedural Compliance

This document includes information on four topics: the (1) software environment, (2) measures to mitigate possible failures, (2) implementation of the computational sequence, and (4) technical adequacy.

2.1.2. Software Environment

ARMI is built using the Python programming language and runs on Windows and Linux operating systems.

2.1.3. Failure Mitigation

ARMI provides a suite of unit tests which provide indication of the proper usage of the program. These tests are described in the software test report and are directly traceable to the requirements in the software requirements specification document. The purpose of these tests is to provide a way for downstream users to test and measure the utility of the ARMI framework for their own purposes, in their own environment. This allows users and developers to perform failure analysis. These tests allow for a push-button way to measure and mitigate consequences and problems including external and internal abnormal conditions and events that can affect the software.

2.1.4. Implementation of Computational Sequence

The computational sequence and relevant portions of the technical adequacy are specific to the implementation and are described for each implementation in the Implementation Traceability Matrix.

2.1.5. Technical Adequacy

The internal completeness for each implementation is shown by providing traceability to the requirements as showing in the Implementation Traceability Matrix. The consistency of the implementation is provided by a best practice used by the development team including, revision control, ensuring that code content is reviewed by non-code originating team members, and ensuring training for developers. Clarity is provided by the descriptions of the implementations in the Implementation Traceability Matrix. Figures are added as needed in the implementation in the Implementation Traceability Matrix.

2.2. Design and Implementation

To automate the process of tracking the implementation of all requirements in ARMI, we are using the Implementation Traceability Matrix below. This will connect high-quality, in-code documentation with each requirement in a complete way. However, before giving a complete overview of the requirement implementations, this document will describe the design of two main features in the ARMI codebase: the plugin system and the reactor data model. These are the two major features which you need to understand to understand what ARMI is, and why it is useful. So, at the risk of duplicating documentation, the design of these two features will be discussed in some detail.

2.2.1. Implementation of Plugin System

The first important design idea to understand in ARMI is that ARMI is a framework for nuclear reactor modeling. What this means is that the science or engineering calculations for nuclear reactor modeling do not happen in ARMI. The point of ARMI is to tie together disparate nuclear modeling softwares that already exist. Thus, ARMI must be able to wrap external codes, and orchestrate running them at each time step we want to model.

The second design idea is that at each time step, there is an ordered list of conceptual reactor modeling steps to be executed. ARMI calls these steps Interfaces and runs the code in each, in order, at each time step. While ARMI does have a default list of modeling steps, and a default order, none of the steps are mandatory, and their order is modifiable. An example interface stack would be:

  • preprocessing

  • fuel management

  • depletion

  • fuel performance

  • cross sections

  • critical control

  • flux

  • thermal hydraulics

  • reactivity coefficients

  • transient

  • bookkeeping

  • postprocessing

So, how do we add Interfaces to the simulation? The third major design idea is that developers can create an ARMI Plugin, which can add one or more Interfaces to the simulation.

Lastly, at the highest level of the design, a developer can create an ARMI Application. This is a flexible container that allows developers to register multiple Plugins, which register multiple Interfaces, which fully define all the code that will be run at each time step of the simulation.

Below is a diagram from an example ARMI Application. Following this design, in the real world you would expect an ARMI Application to be made by various teams of scientists and engineers that define one Plugin and a small number of Interfaces. Then a simulation of the reactor would be carried out over some number of cycles / time nodes, where each of the Interfaces would be run in a specified order at each time node.

../_images/armi_application_structure.png

An example ARMI Application.

If this high-level design seems abstract, that is by design. ARMI is not concerned with implementing scientific codes, or enforcing nuclear modelers do things a certain way. ARMI is a tool that aims to support a wide audience of nuclear reactor modelers.

2.2.2. Implementation of Reactor Data Model

In the previous section, we described how an ARMI Application is put together. But that Application is only useful if it can pass information about the reactor between all the external codes that are being wrapped by each Interface. Thus, an important part of the ARMI design is that is has a robust and detailed software data model to represent the current state of the reactor. This data model can be queried and manipulated by each Interface to get data that is needed to run the external reactor modeling codes.

The structure of the ARMI reactor data model is designed to be quite flexible, and heavily modifiable in code. But most of the practical work done with ARMI so far has been on pin-type reactor cores, so we will focus on such an example.

At the largest scale, the Reactor contains a Core and a Spent Fuel Pool. The Core is made primarily of a collection of Assemblies, which are vertical collections of Blocks. Each Block, and every other physical piece of the Reactor is a Composite. Composites have a defined shape, material(s), location in space, and parent. Composites have parents because ARMI defines all Reactors as a hierarchical model, where outer objects contain inner children, and the Reactor is the outermost object. The important thing about this model is that it is in code, so developers of ARMI Interfaces can query and modify the reactor data model in any way they need.

../_images/armi_reactor_objects.png

Structure of the ARMI reactor data model.

2.2.3. Hardware/OS Compatibility

ARMI is a Python-based framework, designed to help tie together various nuclear models, all written in a variety of languages. ARMI officially supports Python versions 3.9 and higher. ARMI is also designed to work on modern versions of both Windows and Linux.

The memory, CPU, and hardware needs of an ARMI simulation depend on the Reactor. Simulations run with lumped fission products will require more memory than those run without. Simulations with much larger, more detailed reactor core blueprints, or containing more components, will take up more memory than simpler blueprints. ARMI can also be run with only one process, but most users choose to run ARMI in parallel on a computing cluster of some kind. In practice, users tend to find that dozens or hundreds of parallel processes are helpful for speeding up ARMI runs, and each process will ideally have 1 or 2 GB of RAM.

2.2.4. Error/Input Handling

ARMI’s internal error-handling library is the runLog. This tool handles the warnings and errors for internal ARMI code and all the plugins. The runLog system will handle both print-to-screen and log file messages. At the end of the run, all log messages from every plugin and from all parallel processes are tabulated into centralized log files.

The runLog system will also tabulate a list of all warnings that occurred doing a simulation. And it should be noted that most full “errors” will cause the ARMI simulation to fail and stop hard, ending the run early. This is the ideal solution, so people know the run results are invalid. To that affect, ARMI makes use of Python’s robust Exception system.

2.2.5. Implementation Traceability Matrix

The requirements and associated tests which demonstrate acceptance of the codebase with the requirements are in the Software Requirements Specification Document (SRSD). This section contains a list of all requirement implementations.

Here are some quick metrics for the requirement implementations in ARMI:

  • 140 Accepted Requirements in ARMI

    • 138 Accepted Requirements with implementations

    • 190 implementations linked to Requirements

And here is a full listing of all the requirement implementations in ARMI, that are tied to requirements:

Implementation: Settings are used to define an ARMI run. I_ARMI_SETTING1
signature: init
requirements: R_ARMI_SETTING

This method initializes an ARMI run, and if successful returns an Operator. That operator is designed to drive the reactor simulation through time steps to simulate its operation. This method takes in a settings file or object to initialize the operator. Whether a settings file or object is supplied, the operator will be built based on the those settings. Because the total collection of settings can be modified by developers of ARMI applications, providing these settings allow ARMI end-users to define their simulation as granularly as they need.

Implementation: An App has a plugin manager. I_ARMI_APP_PLUGINS
signature: App
requirements: R_ARMI_APP_PLUGINS

The App class is intended to be subclassed in order to customize the functionality and look-and-feel of the ARMI Framework for a specific use case. An App contains a plugin manager, which should be populated in __init__() with a collection of plugins that are deemed suitable for a given application, as well as other methods which provide further customization.

The base App class is also a good place to expose some more convenient ways to get data out of the Plugin API; calling the pluggy hooks directly can sometimes be a pain, as the results returned by the individual plugins may need to be merged and/or checked for errors. Adding that logic here reduces boilerplate throughout the rest of the code.

Implementation: The database files are H5, and thus language agnostic. I_ARMI_DB_H51
signature: Database
requirements: R_ARMI_DB_H5

This class implements a light wrapper around H5 files, so they can be used to store ARMI outputs. H5 files are commonly used in scientific applications in Fortran and C++. As such, they are entirely language agnostic binary files. The implementation here is that ARMI wraps the h5py library, and uses its extensive tooling, instead of re-inventing the wheel.

Implementation: Add system attributes to the database. I_ARMI_DB_QA
signature: writeSystemAttributes
requirements: R_ARMI_DB_QA

This method writes some basic system information to the H5 file. This is designed as a starting point, so users can see information about the system their simulations were run on. As ARMI is used on Windows and Linux, the tooling here has to be platform independent. The two major sources of information are the ARMI context module and the Python standard library platform.

Implementation: The run settings are saved the settings file. I_ARMI_DB_CS
signature: writeInputsToDB
requirements: R_ARMI_DB_CS

A Settings object is passed into this method, and then the settings are converted into a YAML string stream. That stream is then written to the H5 file. Optionally, this method can take a pre-build settings string to be written directly to the file.

Implementation: The reactor blueprints are saved the settings file. I_ARMI_DB_BP
signature: writeInputsToDB
requirements: R_ARMI_DB_BP

A Blueprints string is optionally passed into this method, and then written to the H5 file. If it is not passed in, this method will attempt to find the blueprints input file in the settings, and read the contents of that file into a stream to be written to the H5 file.

Implementation: Users can load a reactor from a DB. I_ARMI_DB_TIME1
signature: load
requirements: R_ARMI_DB_TIME

This method creates a Reactor object by reading the reactor state out of an ARMI database file. This is done by passing in mandatory arguements that specify the exact place in time you want to load the reactor from. (That is, the cycle and node numbers.) Users can either pass the settings and blueprints directly into this method, or it will attempt to read them from the database file. The primary work done here is to read the hierarchy of reactor objects from the data file, then reconstruct them in the correct order.

Implementation: Runs at a particular timenode can be re-instantiated for a snapshot. I_ARMI_SNAPSHOT_RESTART
signature: prepRestartRun

This method loads the state of a reactor from a particular point in time from a standard ARMI Database. This is a major use-case for having ARMI databases in the first case. And restarting from such a database is easy, you just need to set a few settings:

* reloadDBName - Path to existing H5 file to reload from.
* startCycle - Operational cycle to restart from.
* startNode - Time node to start from.
Implementation: Write data to the DB for a given time step. I_ARMI_DB_TIME0
signature: writeToDB
requirements: R_ARMI_DB_TIME

This method writes a snapshot of the current state of the reactor to the database. It takes a pointer to an existing HDF5 file as input, and it writes the reactor data model to the file in depth-first search order. Other than this search order, there are no guarantees as to what order the objects are written to the file. Though, this turns out to still be very powerful. For instance, the data for all HexBlock children of a given parent are stored contiguously within the HexBlock group, and will not be interleaved with data from the HexBlock children of any of the parent's siblings.

Implementation: This interface allows users to retrieve run data from somewhere other than the database. I_ARMI_HIST_TRACK
signature: HistoryTrackerInterface
requirements: R_ARMI_HIST_TRACK

This is a special Interface that is designed to store assembly and cross section data throughout time. This is done directly, with time-based lists of assembly data, and dictionaries of cross- section data. Users turn this feature on or off using the "detailAllAssems" setting.

Implementation: Save extra data to be saved from a run, at specified time nodes. I_ARMI_SNAPSHOT0
signature: SnapshotInterface
requirements: R_ARMI_SNAPSHOT

This is a special Interface that is designed to run along all the other Interfaces during a simulation, to save off important or helpful data. By default, this is designed to be used with the "defaultSnapshots" and ""dumpSnapshot"" settings. These settings were added so users can control if snapshot data will be recorded during their run. Broadly, this class is implemented to run the Operator method o.snapshotRequest.

Implementation: The case class allows for a generic ARMI simulation. I_ARMI_CASE
signature: run
requirements: R_ARMI_CASE

This method is responsible for "running" the ARMI simulation instigated by the inputted settings. This initializes an Operator, a Reactor and invokes Operator.operate. It also activates supervisory things like code coverage checking, profiling, or tracing, if requested by users during debugging.

Implementation: Perform validity checks on case inputs. I_ARMI_CASE_CHECK
signature: checkInputs
requirements: R_ARMI_CASE_CHECK

This method checks the validity of the current settings. It relies on an Inspector object from the Operator to generate a list of Query objects that represent potential issues in the settings. After gathering the queries, this method prints a table of query "statements" and "questions" to the console. If running in an interactive mode, the user then has the opportunity to address the questions posed by the queries by either addressing the potential issue or ignoring it.

Implementation: A generic tool to modify user inputs on multiple cases. I_ARMI_CASE_MOD1
signature: InputModifier
requirements: R_ARMI_CASE_MOD

This class serves as an abstract base class for modifying the inputs of a case, typically case settings. Child classes must implement a __call__ method accepting a Settings, Blueprints, and SystemLayoutInput and return the appropriately modified version of these objects. The class attribute FAIL_IF_AFTER should be a tuple defining what, if any, modifications this should fail if performed after. For example, one should not adjust the smear density (a function of Cladding ID) before adjusting the Cladding ID. Some generic child classes are provided in this module, but it is expected that design-specific modifiers are built individually.

Implementation: CaseSuite allows for one case to start after another completes. I_ARMI_CASE_SUITE
signature: CaseSuite
requirements: R_ARMI_CASE_SUITE

The CaseSuite object allows multiple, often related, Case objects to be run sequentially. A CaseSuite is intended to be both a pre-processing or a post-processing tool to facilitate case generation and analysis. Under most circumstances one may wish to subclass a CaseSuite to meet the needs of a specific calculation. A CaseSuite is a collection that is keyed off Case titles.

Implementation: A generic tool to modify user inputs on multiple cases. I_ARMI_CASE_MOD0
signature: SuiteBuilder
requirements: R_ARMI_CASE_MOD

This class provides the capability to create a CaseSuite based on programmatic perturbations/modifications to case settings. It works by being constructed with a base or nominal Case object. Children classes then append the self.modifierSets member. Each entry in self.modifierSets is a InputModifier representing a case to add to the suite by specifying modifications to the settings of the base case. SuiteBuilder.buildSuite() is then invoked, returning an instance of the CaseSuite containing all the cases with modified settings.

Implementation: The basic ARMI CLI, for running a simulation. I_ARMI_CLI_CS
signature: ArmiCLI
requirements: R_ARMI_CLI_CS

Provides a basic command-line interface (CLI) for running an ARMI simulation. Available commands can be listed with -l. Information on individual commands can be obtained by running with <command> --help.

Implementation: Generic CLI base class for developers to use. I_ARMI_CLI_GEN
signature: EntryPoint
requirements: R_ARMI_CLI_GEN

Provides a base class for plugin developers to use in creating application-specific CLIs. Valid subclasses must at least provide a name class attribute.

Optional class attributes that a subclass may provide include description, a string describing the command's actions, splash, a boolean specifying whether to display a splash screen upon execution, and settingsArgument. If settingsArgument is specified as required, then a settings files is a required positional argument. If settingsArgument is set to optional, then a settings file is an optional positional argument. If None is specified for the settingsArgument, then no settings file argument is added.

Implementation: Define an ordered list of interfaces. I_ARMI_OPERATOR_INTERFACES0
signature: STACK_ORDER

At each time node during a simulation, an ordered colletion of Interfaces are run (referred to as the interface stack). But ARMI does not force the order upon the analyst. Instead, each Interface registers where in that ordered list it belongs by giving itself an order number (which can be an integer or a decimal). This class defines a set of constants which can be imported and used by Interface developers to define that Interface's position in the stack.

The constants defined are given names, based on common stack orderings in the ARMI ecosystem. But in the end, these are just constant values, and the names they are given are merely suggestions.

Implementation: The TightCoupler defines the convergence criteria for physics coupling. I_ARMI_OPERATOR_PHYSICS0
signature: TightCoupler

During a simulation, the developers of an ARMI application frequently want to iterate on some physical calculation until that calculation has converged to within some small tolerance. This is typically done to solve the nonlinear dependence of different physical properties of the reactor, like fuel performance. However, what parameter is being tightly coupled is configurable by the developer.

This class provides a way to calculate if a single parameter has converged based on some convergence tolerance. The user provides the parameter, tolerance, and a maximum number of iterations to define a basic convergence calculation. If in the isConverged method the parameter has not converged, the number of iterations is incremented, and this class will wait, presuming another iteration is forthcoming.

Implementation: The interface shall allow code execution at important operational points in time. I_ARMI_INTERFACE
signature: Interface
requirements: R_ARMI_INTERFACE

The Interface class defines a number methods with names like interact***. These methods are called in order at each time node. This allows for an individual Plugin defining multiple interfaces to insert code at the start or end of a particular time node or cycle during reactor simulation. In this fashion, the Plugins and thus the Operator control when their code is run.

The end goal of all this work is to allow the Plugins to carefully tune when and how they interact with the reactor data model.

Interface instances are gathered into an interface stack in armi.operators.operator.Operator.createInterfaces().

Implementation: Material collections are defined with an order of precedence in the case of duplicates. I_ARMI_MAT_ORDER
signature: setMaterialNamespaceOrder
requirements: R_ARMI_MAT_ORDER

An ARMI application will need materials. Materials can be imported from any code the application has access to, like plugin packages. This leads to the situation where one ARMI application will want to import multiple collections of materials. To handle this, ARMI keeps a list of material namespaces. This is an ordered list of importable packages that ARMI can search for a particular material by name.

This automatic exploration of an importable package saves the user the tedium have having to import or include hundreds of materials manually somehow. But it comes with a caveat; the list is ordered. If two different namespaces in the list include a material with the same name, the first one found in the list is chosen, i.e. earlier namespaces in the list have precedence.

Implementation: Materials can be searched across packages in a defined namespace. I_ARMI_MAT_NAMESPACE
signature: resolveMaterialClassByName
requirements: R_ARMI_MAT_NAMESPACE

During the runtime of an ARMI application, but particularly during the construction of the reactor in memory, materials will be requested by name. At that point, this code is called to search for that material name. The search goes through the ordered list of Python namespaces provided. The first time an instance of that material is found, it is returned. In this way, the first items in the material namespace list take precedence.

When a material name is passed to this function, it may be either a simple name like the string "UO2" or it may be much more specific, like armi.materials.uraniumOxide:UO2.

Implementation: The abstract material class. I_ARMI_MAT_PROPERTIES
signature: Material
requirements: R_ARMI_MAT_PROPERTIES

The ARMI Materials library is based on the Object-Oriented Programming design approach, and uses this generic Material base class. In this class we define a large number of material properties like density, heat capacity, or linear expansion coefficient. Specific materials then subclass this base class to assign particular values to those properties.

Implementation: Materials generate nuclide mass fractions at instantiation. I_ARMI_MAT_FRACS
signature: Material
requirements: R_ARMI_MAT_FRACS

An ARMI material is meant to be able to represent real world materials that might be used in the construction of a nuclear reactor. As such, they are not just individual nuclides, but practical materials like a particular concrete, steel, or water. One of the main things that will be needed to describe such a material is the exact nuclide fractions. As such, the constructor of every Material subclass attempts to set these mass fractions.

Implementation: Fluid materials are not thermally expandable. I_ARMI_MAT_FLUID
signature: linearExpansion
requirements: R_ARMI_MAT_FLUID

ARMI does not model thermal expansion of fluids. The Fluid superclass therefore sets the thermal expansion coefficient to zero. All fluids subclassing the Fluid material will inherit this method which sets the linear expansion coefficient to zero at all temperatures.

Implementation: A tool for querying basic data for elements of the periodic table. I_ARMI_ND_ELEMENTS0
requirements: R_ARMI_ND_ELEMENTS

The elements module defines the Element class which acts as a data structure for organizing information about an individual element, including number of protons, name, chemical symbol, phase (at STP), periodic table group, standard weight, and a list of isotope nuclideBase instances. The module includes a factory that generates the Element instances by reading from the elements.dat file stored in the ARMI resources folder. When an Element instance is initialized, it is added to a set of global dictionaries that are keyed by number of protons, element name, and element symbol. The module includes several helper functions for querying these global dictionaries.

Implementation: An element of the periodic table. I_ARMI_ND_ELEMENTS1
signature: Element
requirements: R_ARMI_ND_ELEMENTS

The Element class acts as a data structure for organizing information about an individual element, including number of protons, name, chemical symbol, phase (at STP), periodic table group, standard weight, and a list of isotope nuclideBase instances.

The Element class has a few methods for appending additional isotopes, checking whether an isotope is naturally occurring, retrieving the natural isotopic abundance, or whether the element is a heavy metal.

Implementation: Isotopes and isomers can be queried by name, label, MC2-3 ID, MCNP ID, and AAAZZZS ID. I_ARMI_ND_ISOTOPES0
requirements: R_ARMI_ND_ISOTOPES

The nuclideBases module defines the NuclideBase class which is used to organize and store metadata about each nuclide. The metadata is read from nuclides.dat file in the ARMI resources folder, which contains metadata for 4,614 isotopes. The module also contains classes for special types of nuclides, including DummyNuclideBase for dummy nuclides, LumpNuclideBase, for lumped fission product nuclides, and NaturalNuclideBase for when data is given collectively for an element at natural abundance rather than for individual isotopes.

The NuclideBase provides a data structure for information about a single nuclide, including the atom number, atomic weight, element, isomeric state, half-life, and name.

The nuclideBases module provides a factory and associated functions for instantiating the NuclideBase objects and building the global nuclide dictionaries, including:

  • instances (list of nuclides)

  • byName (keyed by name, e.g., U235)

  • byDBName (keyed by database name, e.g., nU235)

  • byLabel (keyed by label, e.g., U235)

  • byMcc2Id (keyed by MC2-2 ID, e.g., U-2355)

  • byMcc3Id (keyed by MC2-3 ID, e.g., U235_7)

  • byMcc3IdEndfbVII0 (keyed by MC2-3 ID, e.g., U235_7)

  • byMcc3IdEndfbVII1 (keyed by MC2-3 ID, e.g., U235_7)

  • byMcnpId (keyed by MCNP ID, e.g., 92235)

  • byAAAZZZSId (keyed by AAAZZZS, e.g., 2350920)

Implementation: Isotopes and isomers can be queried by name and label. I_ARMI_ND_ISOTOPES1
signature: NuclideBase
requirements: R_ARMI_ND_ISOTOPES

The NuclideBase class provides a data structure for information about a single nuclide, including the atom number, atomic weight, element, isomeric state, half-life, and name. The class contains static methods for creating an internal ARMI name or label for a nuclide. There are instance methods for generating the nuclide ID for external codes, e.g. MCNP or Serpent, and retrieving the nuclide ID for MC2-2 or MC2-3. There are also instance methods for generating an AAAZZZS ID and an ENDF MAT number.

Implementation: Isotopes and isomers can be queried by MC2-2 ID. I_ARMI_ND_ISOTOPES2
signature: getMcc2Id
requirements: R_ARMI_ND_ISOTOPES

This method returns the mcc2id attribute of a NuclideBase instance. This attribute is initially populated by reading from the mcc-nuclides.yaml file in the ARMI resources folder.

Implementation: Isotopes and isomers can be queried by MC2-3 ENDF/B-VII.0 ID. I_ARMI_ND_ISOTOPES3
signature: getMcc3IdEndfbVII0
requirements: R_ARMI_ND_ISOTOPES

This method returns the mcc3idEndfbVII0 attribute of a NuclideBase instance. This attribute is initially populated by reading from the mcc-nuclides.yaml file in the ARMI resources folder.

Implementation: Isotopes and isomers can be queried by MC2-3 ENDF/B-VII.1 ID. I_ARMI_ND_ISOTOPES7
signature: getMcc3IdEndfbVII1
requirements: R_ARMI_ND_ISOTOPES

This method returns the mcc3idEndfbVII1 attribute of a NuclideBase instance. This attribute is initially populated by reading from the mcc-nuclides.yaml file in the ARMI resources folder.

Implementation: Isotopes and isomers can be queried by MCNP ID. I_ARMI_ND_ISOTOPES4
signature: getMcnpId
requirements: R_ARMI_ND_ISOTOPES

This method generates the MCNP ID for an isotope using the standard MCNP format based on the atomic number A, number of protons Z, and excited state. The implementation includes the special rule for Am-242m, which is 95242. 95642 is used for the less common ground state Am-242.

Implementation: Isotopes and isomers can be queried by AAAZZZS ID. I_ARMI_ND_ISOTOPES5
signature: getAAAZZZSId
requirements: R_ARMI_ND_ISOTOPES

This method generates the AAAZZZS format ID for an isotope. Where AAA is the mass number, ZZZ is the atomic number, and S is the isomeric state. This is a general format independent of any code that precisely defines an isotope or isomer.

Implementation: Separating natural abundance data from code. I_ARMI_ND_DATA0
signature: addNuclideBases
requirements: R_ARMI_ND_DATA

This function reads the nuclides.dat file from the ARMI resources folder. This file contains metadata for 4,614 nuclides, including number of protons, number of neutrons, atomic number, excited state, element symbol, atomic mass, natural abundance, half-life, and spontaneous fission yield. The data in nuclides.dat have been collected from multiple different sources; the references are given in comments at the top of that file.

Implementation: Separating MCC data from code. I_ARMI_ND_DATA1
signature: readMCCNuclideData
requirements: R_ARMI_ND_DATA

This function reads the mcc-nuclides.yaml file from the ARMI resources folder. This file contains the MC2-2 ID (from ENDF/B-V.2) and MC2-3 ID (from ENDF/B-VII.0) for all nuclides in MC2. The mcc2id, mcc3idEndfVII0, and mcc3idEndfVII1 attributes of each NuclideBase instance are updated as the data is read, and the global dictionaries byMcc2Id byMcc3IdEndfVII0 and byMcc3IdEndfVII1 are populated with the nuclide bases keyed by their corresponding ID for each code.

Implementation: The special case name Am242g is supported. I_ARMI_ND_ISOTOPES6
signature: updateNuclideBasesForSpecialCases
requirements: R_ARMI_ND_ISOTOPES

This function updates the keys for the NuclideBase instances for Am-242m and Am-242 in the byName and byDBName global dictionaries. This function associates the more common isomer Am-242m with the name "AM242", and uses "AM242G" to denote the ground state.

Implementation: Generic tool for reading and writing Committee on Computer Code Coordination (CCCC) format files for reactor physics codes I_ARMI_NUCDATA

This module provides a number of base classes that implement general capabilities for binary and ASCII file I/O. The IORecord serves as an abstract base class that instantiates a number of methods that the binary and ASCII children classes are meant to implement. These methods, prefixed with rw, are meant to convert literal data types, e.g. float or int, to either binary or ASCII. This base class does its own conversion for container data types, e.g. list or matrix, relying on the child implementation of the literal types that the container possesses. The binary conversion is implemented in BinaryRecordReader and BinaryRecordWriter. The ASCII conversion is implemented in AsciiRecordReader and AsciiRecordWriter.

These IORecord classes are used within Stream objects for the data conversion. Stream is a context manager that opens a file for reading or writing on the __enter__ and closes that file upon __exit__. Stream is an abstract base class that is subclassed for each CCCC file. It is subclassed directly for the CCCC files that contain cross-section data:

For the CCCC file types that are outputs from a flux solver such as DIF3D (e.g., GEODST, DIF3D, NHFLUX) the streams are subclassed from StreamWithDataContainer, which is a special abstract subclass of Stream that implements a common pattern used for these file types. In a StreamWithDataContainer, the data is directly read to or written from a specialized data container.

The data container structure for each type of CCCC file is implemented in the module for that file, as a subclass of DataContainer. The subclasses for each CCCC file type define standard attribute names for the data that will be read from or written to the CCCC file. CCCC file types that follow this pattern include:

The logic to parse or write each specific file format is contained within the Stream.readWrite() implementations of the respective subclasses.

Implementation: Tool to read and write DIF3D files. I_ARMI_NUCDATA_DIF3D
signature: readWrite
requirements: R_ARMI_NUCDATA_DIF3D

The reading and writing of the DIF3D binary file is performed using StreamWithDataContainer from the cccc package. This class allows for the reading and writing of CCCC binary files, processing one record at a time using subclasses of the IORecord. Each record in a CCCC binary file consists of words that represent integers (short or long), floating-point numbers (single or double precision), or strings of data. One or more of these words are parsed one at a time by the reader. Multiple words processed together have meaning, such as such as groupwise overrelaxation factors. While reading, the data is stored in a Python dictionary as an attribute on the object, one for each record. The keys in each dictionary represent the parsed grouping of words in the records; for example, for the 4D record (stored as the attribute fourD), each groupwise overrelaxation factor is stored as the key OMEGA{i}, where i is the group number. See I_ARMI_NUCDATA for more details on the general implementation.

Each record is also embedded with the record size at the beginning and end of the record (always assumed to be present), which is used for error checking at the end of processing each record.

The DIF3D reader processes the file identification record (stored as the attribute _metadata) and the five data records for the DIF3D file, as defined in the specification for the file distributed with the DIF3D software.

This class can also read and write an ASCII version of the DIF3D file. While this format is not used by the DIF3D software, it can be a useful representation for users to access the file in a human-readable format.

Implementation: Tool to read and write DLAYXS files. I_ARMI_NUCDATA_DLAYXS
signature: readWrite
requirements: R_ARMI_NUCDATA_DLAYXS

Reading and writing DLAYXS delayed neutron data files is performed using the general nuclear data I/O functionalities described in I_ARMI_NUCDATA. Reading/writing a DLAYXS file is performed through the following steps:

  1. Read/write the data label for identification.

    Note

    MC2-3 file does not use the expected number of characters for the label, so its length needs to be stored in the IORecord.

  2. Read/write file control information, i.e. the 1D record, which includes:

    • Number of energy groups

    • Number of nuclides

    • Number of precursor families

  3. Read/write spectral data, including:

    • Nuclide IDs

    • Decay constants

    • Emission spectra

    • Energy group bounds

    • Number of families to which fission in a given nuclide contributes delayed neutron precursors

  4. Read/write 3D delayed neutron yield matrix on the 3D record, indexed by nuclide, precursor family, and outgoing neutron energy group.

Implementation: Tool to read and write GAMISO files. I_ARMI_NUCDATA_GAMISO
requirements: R_ARMI_NUCDATA_GAMISO

The majority of the functionality in this module is inherited from the isotxs module. See IsotxsIO and its associated implementation I_ARMI_NUCDATA_ISOTXS for more information. The only difference from ISOTXS neutron data is a special treatment for gamma velocities, which is done by overriding _rwLibraryEnergies.

Implementation: Tool to read and write GEODST files. I_ARMI_NUCDATA_GEODST
signature: readWrite
requirements: R_ARMI_NUCDATA_GEODST

Reading and writing GEODST files is performed using the general nuclear data I/O functionalities described in I_ARMI_NUCDATA. Reading/writing a GEODST file is performed through the following steps:

  1. Read/write file ID record

  2. Read/write file specifications on 1D record.

  3. Based on the geometry type (IGOM), one of following records are read/written:

    • Slab (1), cylinder (3), or sphere (3): Read/write 1-D coarse mesh boundaries and fine mesh intervals.

    • X-Y (6), R-Z (7), Theta-R (8), uniform triangular (9), hexagonal (10), or R-Theta (11): Read/write 2-D coarse mesh boundaries and fine mesh intervals.

    • R-Theta-Z (12, 15), R-Theta-Alpha (13, 16), X-Y-Z (14), uniform triangular-Z (17), hexagonal-Z(18): Read/write 3-D coarse mesh boundaries and fine mesh intervals.

  4. If the geometry is not zero-dimensional (IGOM > 0) and buckling values are specified (NBS > 0): Read/write geometry data from 5D record.

  5. If the geometry is not zero-dimensional (IGOM > 0) and region assignments are coarse-mesh-based (NRASS = 0): Read/write region assignments to coarse mesh interval.

  6. If the geometry is not zero-dimensional (IGOM > 0) and region assignments are fine-mesh-based (NRASS = 1): Read/write region assignments to fine mesh interval.

Implementation: Tool to read and write ISOTXS files. I_ARMI_NUCDATA_ISOTXS
signature: readWrite
requirements: R_ARMI_NUCDATA_ISOTXS

Reading and writing ISOTXS files is performed using the general nuclear data I/O functionalities described in I_ARMI_NUCDATA. Reading/writing a ISOTXS file is performed through the following steps:

  1. Read/write file ID record

  2. Read/write file 1D record, which includes:

    • Number of energy groups (NGROUP)

    • Maximum number of up-scatter groups (MAXUP)

    • Maximum number of down-scatter groups (MAXDN)

    • Maximum scattering order (MAXORD)

    • File-wide specification on fission spectrum type, i.e. vector or matrix (ICHIST)

    • Maximum number of blocks of scattering data (MSCMAX)

    • Subblocking control for scatter matrices (NSBLOK)

  3. Read/write file 2D record, which includes:

    • Library IDs for each isotope (HSETID(I))

    • Isotope names (HISONM(I))

    • Global fission spectrum (CHI(J)) if file-wide spectrum is specified (ICHIST = 1)

    • Energy group structure (EMAX(J) and EMIN)

    • Locations of each nuclide record in the file (LOCA(I))

      Note

      The offset data is not read from the binary file because the ISOTXS reader can dynamically calculate the offset itself. Therefore, during a read operation, this data is ignored.

  4. Read/write file 4D record for each nuclide, which includes isotope-dependent, group-independent data.

  5. Read/write file 5D record for each nuclide, which includes principal cross sections.

  6. Read/write file 6D record for each nuclide, which includes fission spectrum if it is flagged as a matrix (ICHI > 1).

  7. Read/write file 7D record for each nuclide, which includes the scattering matrices.

Implementation: Tool to read and write PMATRX files. I_ARMI_NUCDATA_PMATRX
signature: readWrite
requirements: R_ARMI_NUCDATA_PMATRX

Reading and writing PMATRX files is performed using the general nuclear data I/O functionalities described in I_ARMI_NUCDATA. Reading/writing a PMATRX file is performed through the following steps:

  1. Read/write global information including:

    • Number of gamma energy groups

    • Number of neutron energy groups

    • Maximum scattering order

    • Maximum number of compositions

    • Maximum number of materials

    • Maximum number of regions

  2. Read/write energy group structure for neutrons and gammas

  3. Read/write dose conversion factors

  4. Read/write gamma production matrices for each nuclide, as well as other reaction constants related to neutron-gamma production.

Implementation: Compute macroscopic cross sections from microscopic cross sections and number densities. I_ARMI_NUCDATA_MACRO
signature: computeMacroscopicGroupConstants
requirements: R_ARMI_NUCDATA_MACRO

This function computes the macroscopic cross sections of a specified reaction type from inputted microscopic cross sections and number densities. The constantName parameter specifies what type of reaction is requested. The numberDensities parameter is a dictionary mapping the nuclide to its number density. The lib parameter is a library object like IsotxsLibrary or CompxsLibrary that holds the microscopic cross-section data. The microSuffix parameter specifies from which part of the library the microscopic cross sections are gathered; this is typically gathered from a components getMicroSuffix method like Block.getMicroSuffix. libType is an optional parameter specifying whether the reaction is for neutrons or gammas. This function also has the optional parameters multConstant and multLib, which allows another constant from the library, such as neutrons per fission (nu) or energy per fission (kappa), to be multiplied to the primary one. The macroscopic cross sections are then computed as:

\[\Sigma_{g} = \sum_{n} N_n \sigma_{n,g}\nu_n \quad g=1,...,G\]

where \(n\) is the isotope index, \(g\) is the energy group index, \(\sigma\) is the microscopic cross section, and \(\nu\) is the scalar multiplier. If the library (lib) with suffix microSuffix is missing a cross section for the constantName reaction for one or more of the nuclides in numberDensities an error is raised; but if multConstant is missing that cross section, then those nuclides are printed as a warning.

Implementation: An operator will have a reactor object to communicate between plugins. I_ARMI_OPERATOR_COMM
signature: Operator
requirements: R_ARMI_OPERATOR_COMM

A major design feature of ARMI is that the Operator orchestrates the simulation, and as part of that, the Operator has access to the Reactor data model. In code, this just means the reactor object is a mandatory attribute of an instance of the Operator. But conceptually, this means that while the Operator drives the simulation of the reactor, all code has access to the same copy of the reactor data model. This is a crucial idea that allows disparate external nuclear models to interact; they interact with the ARMI reactor data model.

Implementation: An operator is built from user settings. I_ARMI_OPERATOR_SETTINGS
signature: Operator

A major design feature of ARMI is that a run is built from user settings. In code, this means that a case Settings object is passed into this class to intialize an Operator. Conceptually, this means that the Operator that controls a reactor simulation is defined by user settings. Because developers can create their own settings, the user can control an ARMI simulation with arbitrary granularity in this way. In practice, settings common control things like: how many cycles a reactor is being modeled for, how many timesteps are to be modeled per time node, the verbosity of the logging during the run, and which modeling steps (such as economics) will be run.

Implementation: Calculate step lengths from cycles and burn steps. I_ARMI_FW_HISTORY
signature: stepLengths
requirements: R_ARMI_FW_HISTORY

In all computational modeling of physical systems, it is necessary to break time into discrete chunks. In reactor modeling, it is common to first break the time a reactor is simulated for into the practical cycles the reactor runs. And then those cycles are broken down into smaller chunks called burn steps. The final step lengths this method returns is a two-tiered list, where primary indices correspond to the cycle and secondary indices correspond to the length of each intra-cycle step (in days).

Implementation: Physics coupling is driven from Operator. I_ARMI_OPERATOR_PHYSICS1
signature: interactAllCoupled

This method runs all the interfaces that are defined as part of the tight physics coupling of the reactor. Then it returns if the coupling has converged or not.

Tight coupling implies the operator has split iterations between two or more physics solvers at the same solution point in simulated time. For example, a flux solution might be computed, then a temperature solution, and then another flux solution based on updated temperatures (which updates densities, dimensions, and Doppler).

This is distinct from loose coupling, which simply uses the temperature values from the previous timestep in the current flux solution. It's also distinct from full coupling where all fields are solved simultaneously. ARMI supports tight and loose coupling.

Implementation: An operator will expose an ordered list of interfaces. I_ARMI_OPERATOR_INTERFACES
signature: getInterfaces

This method returns an ordered list of instances of the Interface class. This list is useful because at any time node in the reactor simulation, these interfaces will be called in sequence to perform various types of calculations. It is important to note that this Operator instance has a list of Plugins, and each of those Plugins potentially defines multiple Interfaces. And these Interfaces define their own order, separate from the ordering of the Plugins.

Implementation: There is an MPI-aware variant of the ARMI Operator. I_ARMI_OPERATOR_MPI
requirements: R_ARMI_OPERATOR_MPI

This sets up the main Operator on the primary MPI node and initializes worker processes on all other MPI nodes. At certain points in the run, particular interfaces might call into action all the workers. For example, a depletion or subchannel T/H module may ask the MPI pool to perform a few hundred independent physics calculations in parallel. In many cases, this can speed up the overall execution of an analysis, if a big enough computer or computing cluster is available.

See Operator for the parent class.

Implementation: Options for executing external calculations. I_ARMI_EX0
signature: ExecutionOptions
requirements: R_ARMI_EX

Implements a basic container to hold and report options to be used in the execution of an external code (see I_ARMI_EX1). Options are stored as instance attibutes and can be dumped as a string using describe(), which will include the name and value of all public attributes of the instance.

Also facilitates the ability to execute parallel instances of a code by providing the ability to resolve a runDir that is aware of the executing MPI rank. This is done via setRunDirFromCaseTitle(), where the user passes in a caseTitle string, which is hashed and combined with the MPI rank to provide a unique directory name to be used by each parallel instance.

Implementation: Default tool for executing external calculations. I_ARMI_EX1
signature: DefaultExecuter
requirements: R_ARMI_EX

Facilitates the execution of external calculations by accepting options (an ExecutionOptions object) and providing methods that build run directories and execute a code based on the values in options.

The run() method will first resolve any derived options in the options object and check if the specified executablePath option is valid, raising an error if not. If it is, preparation work for executing the code is performed, such as performing any geometry transformations specified in subclasses or building the directories needed to save input and output files. Once the temporary working directory is created, the executer moves into it and runs the external code, applying any results from the run as specified in subclasses.

Finally, any geometry perturbations that were performed are undone.

Implementation: ARMI provides a shuffle logic interface. I_ARMI_SHUFFLE
signature: FuelHandlerInterface
requirements: R_ARMI_SHUFFLE

This interface allows for a user to define custom shuffle logic that modifies to the core model. Being based on the Interface class, it has direct access to the current core model.

User logic is able to be executed from within the manageFuel() method, which will use the fuelHandlerFactory() to search for a Python file specified by the case setting shuffleLogic. If it exists, the fuel handler with name specified by the user via the fuelHandlerName case setting will be imported, and any actions in its outage method will be executed at the interactBOC() hook.

If no class with the name specified by the fuelHandlerName setting is found in the file with path shuffleLogic, an error is returned.

See the user manual for how the custom shuffle logic file should be constructed.

Implementation: User-specified blocks can be left in place during within-core swaps. I_ARMI_SHUFFLE_STATIONARY0
signature: swapAssemblies

Before assemblies are moved, the _transferStationaryBlocks class method is called to check if there are any block types specified by the user as stationary via the stationaryBlockFlags case setting. Using these flags, blocks are gathered from each assembly which should remain stationary and checked to make sure that both assemblies have the same number and same height of stationary blocks. If not, return an error.

If all checks pass, the remove() and insert() methods are used to swap the stationary blocks between the two assemblies.

Once this process is complete, the actual assembly movement can take place. Through this process, the stationary blocks remain in the same core location.

Implementation: User-specified blocks can be left in place for the discharge swap. I_ARMI_SHUFFLE_STATIONARY1
signature: dischargeSwap

Before assemblies are moved, the _transferStationaryBlocks class method is called to check if there are any block types specified by the user as stationary via the stationaryBlockFlags case setting. Using these flags, blocks are gathered from each assembly which should remain stationary and checked to make sure that both assemblies have the same number and same height of stationary blocks. If not, return an error.

If all checks pass, the remove() and insert() methods are used to swap the stationary blocks between the two assemblies.

Once this process is complete, the actual assembly movement can take place. Through this process, the stationary blocks from the outgoing assembly remain in the original core position, while the stationary blocks from the incoming assembly are discharged with the outgoing assembly.

Implementation: Provide an algorithm for rotating hexagonal assemblies to equalize burnup I_ARMI_ROTATE_HEX_BURNUP
signature: getOptimalAssemblyOrientation
Implementation: Create representative blocks using volume-weighted averaging. I_ARMI_XSGM_CREATE_REPR_BLOCKS0
signature: AverageBlockCollection

This class constructs new blocks from an existing block list based on a volume-weighted average. Inheriting functionality from the abstract Reactor object, this class will construct representative blocks using averaged parameters of all blocks in the given collection. Number density averages can be computed at a component level or at a block level by default. Average nuclide temperatures and burnup are also included when constructing a representative block.

Implementation: Create representative blocks using custom cylindrical averaging. I_ARMI_XSGM_CREATE_REPR_BLOCKS1
signature: CylindricalComponentsAverageBlockCollection

This class constructs representative blocks based on a volume-weighted average using cylindrical blocks from an existing block list. Inheriting functionality from the abstract Reactor object, this class will construct representative blocks using averaged parameters of all blocks in the given collection. Number density averages are computed at a component level. Nuclide temperatures from a median block-average temperature are used and the average burnup is evaluated across all blocks in the block list.

Implementation: Create partially heterogeneous representative blocks. I_ARMI_XSGM_CREATE_REPR_BLOCKS2
signature: CylindricalComponentsDuctHetAverageBlockCollection

This class constructs representative blocks based on a volume-weighted average using cylindrical blocks from an existing block list. Inheriting functionality from the abstract Reactor object, this class will construct representative blocks using averaged parameters of all blocks in the given collection. Number density averages are computed at a component level. Nuclide temperatures from a median block-average temperature are used and the average burnup is evaluated across all blocks in the block list.

The average nuclide temperatures are calculated only for the homogenized region inside of the duct. For the non-homogenized regions, the MC2 writer uses the component temperatures.

Implementation: The lattice physics interface and cross-section group manager are connected at BOL. I_ARMI_XSGM_FREQ0
signature: interactBOL
requirements: R_ARMI_XSGM_FREQ

This method sets the cross-section block averaging method and and logic for whether all blocks in a cross section group should be used when generating a representative block. Furthermore, if the control logic for lattice physics frequency updates is set at beginning-of-life (BOL) through the LatticePhysicsInterface, the cross-section group manager will construct representative blocks for each cross-section IDs at the beginning of the reactor state.

Implementation: The lattice physics interface and cross-section group manager are connected at BOC. I_ARMI_XSGM_FREQ1
signature: interactBOC
requirements: R_ARMI_XSGM_FREQ

This method updates representative blocks and block burnups at the beginning-of-cycle for each cross-section ID if the control logic for lattice physics frequency updates is set at beginning-of-cycle (BOC) through the LatticePhysicsInterface. At the beginning-of-cycle, the cross-section group manager will construct representative blocks for each cross-section IDs for the current reactor state.

Implementation: The lattice physics interface and cross-section group manager are connected at every time node. I_ARMI_XSGM_FREQ2
signature: interactEveryNode
requirements: R_ARMI_XSGM_FREQ

This method updates representative blocks and block burnups at every node for each cross-section ID if the control logic for lattices physics frequency updates is set for every node (everyNode) through the LatticePhysicsInterface. At every node, the cross-section group manager will construct representative blocks for each cross-section ID in the current reactor state.

Implementation: The lattice physics interface and cross-section group manager are connected during coupling. I_ARMI_XSGM_FREQ3
signature: interactCoupled
requirements: R_ARMI_XSGM_FREQ

This method updates representative blocks and block burnups at every node and the first coupled iteration for each cross-section ID if the control logic for lattices physics frequency updates is set for the first coupled iteration (firstCoupledIteration) through the LatticePhysicsInterface. The cross-section group manager will construct representative blocks for each cross-section ID at the first iteration of every time node.

Implementation: Create collections of blocks based on cross-section type and burn-up group. I_ARMI_XSGM_CREATE_XS_GROUPS
signature: createRepresentativeBlocks

This method constructs the representative blocks and block burnups for each cross-section ID in the reactor model. Starting with the making of cross-section groups, it will find candidate blocks and create representative blocks from that selection.

Implementation: Return the energy group index which contains a given energy threshold. I_ARMI_EG_FE
signature: getFastFluxGroupCutoff
requirements: R_ARMI_EG_FE

This function returns the energy group within a given group structure that contains the fast flux threshold energy. The threshold energy is imported from the constants in the neutronics module, where it is defined as 100 keV. This is a standard definition for fast flux. This function also calculates and returns the fraction of the threshold energy group that is above the 100 keV threshold.

Implementation: Provide the neutron energy group bounds for a given group structure. I_ARMI_EG_NE
signature: getGroupStructure
requirements: R_ARMI_EG_NE

There are several built-in group structures that are defined in this module, which are stored in a dictionary. This function takes a group structure name as an input parameter, which it uses as a key for the group structure dictionary. If the group structure name is valid, it returns a copy of the energy group structure resulting from the dictionary lookup. Otherwise, it throws an error.

Implementation: Validate the energy generation matches user specifications. I_ARMI_FLUX_CHECK_POWER
signature: checkEnergyBalance

This method checks that the global power computed from flux evaluation matches the global power specified from the user within a tolerance; if it does not, a ValueError is raised. The global power from the flux solve is computed by summing the block-wise power in the core. This value is then compared to the user-specified power and raises an error if relative difference is above \(10^{-5}\).

Implementation: Options for neutronics solvers. I_ARMI_FLUX_OPTIONS
signature: GlobalFluxOptions
requirements: R_ARMI_FLUX_OPTIONS

This class functions as a data structure for setting and retrieving execution options for performing flux evaluations, these options involve:

  • What sort of problem is to be solved, i.e. real/adjoint, eigenvalue/fixed-source, neutron/gamma, boundary conditions

  • Convergence criteria for iterative algorithms

  • Geometry type and mesh conversion details

  • Specific parameters to be calculated after flux has been evaluated

These options can be retrieved by directly accessing class members. The options are set by specifying a Settings object and optionally specifying a Reactor object.

Implementation: Ensure the mesh in the reactor model is appropriate for neutronics solver execution. I_ARMI_FLUX_GEOM_TRANSFORM
signature: GlobalFluxExecuter

The primary purpose of this class is perform geometric and mesh transformations on the reactor model to ensure a flux evaluation can properly perform. This includes:

  • Applying a uniform axial mesh for the 3D flux solve

  • Expanding symmetrical geometries to full-core if necessary

  • Adding/removing edge assemblies if necessary

  • Undoing any transformations that might affect downstream calculations

Implementation: Compute DPA rates. I_ARMI_FLUX_DPA
signature: computeDpaRate
requirements: R_ARMI_FLUX_DPA

This method calculates DPA rates using the inputted multigroup flux and DPA cross sections. Displacements calculated by displacement cross-section:

\begin{aligned} \text{Displacement rate} &= \phi N_{\text{HT9}} \sigma \\ &= (\#/\text{cm}^2/s) \cdot (1/cm^3) \cdot (\text{barn})\\ &= (\#/\text{cm}^5/s) \cdot \text{(barn)} * 10^{-24} \text{cm}^2/\text{barn} \\ &= \#/\text{cm}^3/s \end{aligned}
DPA rate = displacement density rate / (number of atoms/cc)
        = dr [#/cm^3/s] / (nHT9)  [1/cm^3]
        = flux * barn * 1e-24
\[\frac{\text{dpa}}{s} = \frac{\phi N \sigma}{N} = \phi * \sigma\]

the number density of the structural material cancels out. It's in the macroscopic cross-section and in the original number of atoms.

Implementation: Return the reaction rates for a given ArmiObject I_ARMI_FLUX_RX_RATES
signature: calcReactionRates
requirements: R_ARMI_FLUX_RX_RATES

This method computes 1-group reaction rates for the inputted ArmiObject These reaction rates include:

  • fission

  • nufission

  • n2n

  • absorption

Scatter could be added as well. This function is quite slow so it is skipped for now as it is uncommonly needed.

Reaction rates are:

\[\Sigma \phi = \sum_{\text{nuclides}} \sum_{\text{energy}} \Sigma \phi\]

The units of \(N \sigma \phi\) are:

[#/bn-cm] * [bn] * [#/cm^2/s] = [#/cm^3/s]

The group-averaged microscopic cross section is:

\[\sigma_g = \frac{\int_{E g}^{E_{g+1}} \phi(E) \sigma(E) dE}{\int_{E_g}^{E_{g+1}} \phi(E) dE}\]
Implementation: Build macroscopic cross sections for blocks. I_ARMI_MACRO_XS
signature: buildMacros
requirements: R_ARMI_MACRO_XS

This method builds macroscopic cross sections for a user-specified set of blocks using a specified microscopic neutron or gamma cross section library. If no blocks are specified, cross sections are calculated for all blocks in the core. If no library is specified, the existing r.core.lib is used. The basic arithmetic involved in generating macroscopic cross sections consists of multiplying isotopic number densities by isotopic microscopic cross sections and summing over all isotopes in a composition. The calculation is implemented in computeMacroscopicGroupConstants. This method uses an mpiAction to distribute the work of calculating macroscopic cross sections across the worker processes.

Implementation: Plugins add code to the application through interfaces. I_ARMI_PLUGIN
signature: ArmiPlugin
requirements: R_ARMI_PLUGIN

Each plugin has the option of implementing the exposeInterfaces method, and this will be used as a plugin hook to add one or more Interfaces to the ARMI Application. Interfaces can wrap external executables with nuclear modeling codes in them, or directly implement their logic in Python. But because Interfaces are Python code, they have direct access to read and write from ARMI's reactor data model. This Plugin to multiple Interfaces to reactor data model connection is the primary way that developers add code to an ARMI application and simulation.

Implementation: Plugins can add interfaces to the operator. I_ARMI_PLUGIN_INTERFACES
signature: exposeInterfaces

This method takes in a Settings object and returns a list of Interfaces, the position of each Interface in the Interface stack, and a list of arguments to pass to the Interface when initializing it later. These Interfaces can then be used to add code to a simulation.

Implementation: Plugins can add parameters to the reactor data model. I_ARMI_PLUGIN_PARAMS
signature: defineParameters
requirements: R_ARMI_PLUGIN_PARAMS

Through this method, plugin developers can create new Parameters. A parameter can represent any physical property an analyst might want to track. And they can be added at any level of the reactor data model. Through this, the developers can extend ARMI and what physical properties of the reactor they want to calculate, track, and store to the database.

Implementation: Define an arbitrary physical parameter. I_ARMI_PARAM0
signature: defineParameters
requirements: R_ARMI_PARAM

Through this method, plugin developers can create new Parameters. A parameter can represent any physical property an analyst might want to track. For example, through this method, a plugin developer can add a new thermodynamic property that adds a thermodynamic parameter to every block in the reactor. Or they could add a neutronics parameter to every fuel assembly. A parameter is quite generic. But these parameters will be tracked in the reactor data model, extend what developers can do with ARMI, and will be saved to the output database.

Implementation: Plugins can define new, unique flags to the system. I_ARMI_FLAG_EXTEND1
signature: defineFlags
requirements: R_ARMI_FLAG_EXTEND

This method allows a plugin developers to provide novel values for the Flags system. This method returns a dictionary mapping flag names to their desired numerical values. In most cases, no specific value is needed, one can be automatically generated using armi.utils.flags.auto. (For more information, see armi.reactor.flags.)

Implementation: Plugins can add settings to the run. I_ARMI_PLUGIN_SETTINGS
signature: defineSettings

This hook allows plugin developers to provide their own configuration settings, which can participate in the armi.settings.caseSettings.Settings. Plugins may provide entirely new settings to what are already provided by ARMI, as well as new options or default values for existing settings. For instance, the framework provides a neutronicsKernel setting for selecting which global physics solver to use. Since we wish to enforce that the user specify a valid kernel, the settings validator will check to make sure that the user's requested kernel is among the available options. If a plugin were to provide a new neutronics kernel (let's say MCNP), it should also define a new option to tell the settings system that "MCNP" is a valid option.

Implementation: Assemblies are made up of type Block. I_ARMI_ASSEM_BLOCKS
signature: add
requirements: R_ARMI_ASSEM_BLOCKS

Adds a unique Block to the top of the Assembly. If the Block already exists in the Assembly, an error is raised in armi.reactor.composites.Composite.add(). The spatialLocator of the Assembly is updated to account for the new Block. In reestablishBlockOrder, the Assembly spatialGrid is reinitialized and Block-wise spatialLocator and name objects are updated. The axial mesh and other Block geometry parameters are updated in calculateZCoords.

Implementation: Assembly location is retrievable. I_ARMI_ASSEM_POSI0
signature: getLocation
requirements: R_ARMI_ASSEM_POSI

This method returns a string label indicating the location of an Assembly. There are three options: 1) the Assembly is not within a Core object and is interpreted as in the "load queue"; 2) the Assembly is within the spent fuel pool; 3) the Assembly is within a Core object, so it has a physical location within the Core.

Implementation: Assembly coordinates are retrievable. I_ARMI_ASSEM_POSI1
signature: coords
requirements: R_ARMI_ASSEM_POSI

In this method, the spatialLocator of an Assembly is leveraged to return its physical (x,y) coordinates in cm.

Implementation: A hexagonal assembly shall support rotating around the z-axis in 60 degree increments. I_ARMI_ROTATE_HEX_ASSEM
signature: rotate
requirements: R_ARMI_ROTATE_HEX

This method loops through every Block in this HexAssembly and rotates it by a given angle (in radians). The rotation angle is positive in the counter-clockwise direction. To perform the Block rotation, the armi.reactor.blocks.HexBlock.rotate() method is called.

Implementation: Location of a block is retrievable. I_ARMI_BLOCK_POSI0
signature: getLocation
requirements: R_ARMI_BLOCK_POSI

If the block does not have its core attribute set, if the block's parent does not have a spatialGrid attribute, or if the block does not have its location defined by its spatialLocator attribute, return a string indicating that it is outside of the core.

Otherwise, use the getLabel static method to convert the block's indices into a string like "XXX-YYY-ZZZ". For hexagonal geometry, "XXX" is the zero-padded hexagonal core ring, "YYY" is the zero-padded position in that ring, and "ZZZ" is the zero-padded block axial index from the bottom of the core.

Implementation: Coordinates of a block are queryable. I_ARMI_BLOCK_POSI1
signature: coords
requirements: R_ARMI_BLOCK_POSI

Calls to the getGlobalCoordinates() method of the block's spatialLocator attribute, which recursively calls itself on all parents of the block to get the coordinates of the block's centroid in 3D cartesian space.

Implementation: Get the number of pins in a block. I_ARMI_BLOCK_NPINS
signature: getNumPins
requirements: R_ARMI_BLOCK_NPINS

Uses some simple criteria to infer the number of pins in the block.

For every flag in the module list PIN_COMPONENTS, loop over all components of that type in the block. If the component is an instance of Circle, add its multiplicity to a list, and sum that list over all components with each given flag.

After looping over all possibilities, return the maximum value returned from the process above, or if no compatible components were found, return zero.

Implementation: Set the target axial expansion components on a given block. I_ARMI_MANUAL_TARG_COMP
signature: setAxialExpTargetComp

Sets the axialExpTargetComponent parameter on the block to the name of the Component which is passed in. This is then used by the AxialExpansionChanger class during axial expansion.

This method is typically called from within construct() during the process of building a Block from the blueprints.

Implementation: ARMI has the ability to create hex shaped blocks. I_ARMI_BLOCK_HEX
signature: HexBlock
requirements: R_ARMI_BLOCK_HEX

This class defines hexagonal-shaped Blocks. It inherits functionality from the parent class, Block, and defines hexagonal-specific methods including, but not limited to, querying pin pitch, pin linear power densities, hydraulic diameter, and retrieving inner and outer pitch.

Implementation: Coordinates of a block are queryable. I_ARMI_BLOCK_POSI2
signature: coords
requirements: R_ARMI_BLOCK_POSI

Calls to the getGlobalCoordinates() method of the block's spatialLocator attribute, which recursively calls itself on all parents of the block to get the coordinates of the block's centroid in 3D cartesian space.

Will additionally adjust the x and y coordinates based on the block parameters displacementX and displacementY.

Implementation: Block compositions can be homogenized. I_ARMI_BLOCK_HOMOG
signature: createHomogenizedCopy
requirements: R_ARMI_BLOCK_HOMOG

This method creates and returns a homogenized representation of itself in the form of a new Block. The homogenization occurs in the following manner. A single Hexagon Component is created and added to the new Block. This Hexagon Component is given the armi.materials.mixture._Mixture material and a volume averaged temperature (getAverageTempInC). The number densities of the original Block are also stored on this new Component (I_ARMI_CMP_GET_NDENS). Several parameters from the original block are copied onto the homogenized block (e.g., macros, lumped fission products, burnup group, number of pins, and spatial grid).

Implementation: Rotating a hex block updates parameters on the boundary, the orientation parameter, and the spatial coordinates on contained objects. I_ARMI_ROTATE_HEX_BLOCK
signature: rotate
requirements: R_ARMI_ROTATE_HEX
Implementation: User-impact on material definitions. I_ARMI_MAT_USER_INPUT0
signature: MaterialModifications
requirements: R_ARMI_MAT_USER_INPUT

Defines a yaml map attribute for the assembly portion of the blueprints (see AssemblyBlueprint) that allows users to specify material attributes as lists corresponding to each axial block in the assembly. Two types of specifications can be made:

1. Key-value pairs can be specified directly, where the key is the name of the modification and the value is the list of block values.

2. The "by component" attribute can be used, in which case the user can specify material attributes that are specific to individual components in each block. This is enabled through the ByComponentModifications class, which basically just allows for one additional layer of attributes corresponding to the component names.

These material attributes can be used during the resolution of material classes during core instantiation (see construct() and construct()).

Implementation: Create assembly from blueprint file. I_ARMI_BP_ASSEM
signature: AssemblyBlueprint
requirements: R_ARMI_BP_ASSEM

Defines a yaml construct that allows the user to specify attributes of an assembly from within their blueprints file, including a name, flags, specifier for use in defining a core map, a list of blocks, a list of block heights, a list of axial mesh points in each block, a list of cross section identifiers for each block, and material options (see I_ARMI_MAT_USER_INPUT0).

Relies on the underlying infrastructure from the yamlize package for reading from text files, serialization, and internal storage of the data.

Is implemented as part of a blueprints file by being imported and used as an attribute within the larger Blueprints class.

Includes a construct method, which instantiates an instance of Assembly with the characteristics as specified in the blueprints.

Implementation: Create a Block from blueprint file. I_ARMI_BP_BLOCK
signature: BlockBlueprint
requirements: R_ARMI_BP_BLOCK

Defines a yaml construct that allows the user to specify attributes of a block from within their blueprints file, including a name, flags, a radial grid to specify locations of pins, and the name of a component which drives the axial expansion of the block (see axialExpansionChanger).

In addition, the user may specify key-value pairs to specify the components contained within the block, where the keys are component names and the values are component blueprints (see ComponentBlueprint).

Relies on the underlying infrastructure from the yamlize package for reading from text files, serialization, and internal storage of the data.

Is implemented into a blueprints file by being imported and used as an attribute within the larger Blueprints class.

Includes a construct method, which instantiates an instance of Block with the characteristics as specified in the blueprints.

Implementation: Construct component from blueprint file. I_ARMI_BP_COMP
signature: ComponentBlueprint
requirements: R_ARMI_BP_COMP

Defines a yaml construct that allows the user to specify attributes of a component from within their blueprints file, including a name, flags, shape, material and/or isotopic vector, input temperature, corresponding component dimensions, and ID for placement in a block lattice (see BlockBlueprint). Component dimensions that can be defined for a given component are dependent on the component's shape attribute, and the dimensions defining each shape can be found in the components module.

Limited validation on the inputs is performed to ensure that the component shape corresponds to a valid shape defined by the ARMI application.

Relies on the underlying infrastructure from the yamlize package for reading from text files, serialization, and internal storage of the data.

Is implemented as part of a blueprints file by being imported and used as an attribute within the larger Blueprints class. Can also be used within the BlockBlueprint class to enable specification of components directly within the "blocks" portion of the blueprint file.

Includes a construct method, which instantiates an instance of Component with the characteristics specified in the blueprints (see I_ARMI_MAT_USER_INPUT1).

Implementation: User-defined on material alterations are applied here. I_ARMI_MAT_USER_INPUT1
signature: construct
requirements: R_ARMI_MAT_USER_INPUT

Allows for user input to impact a component's materials by applying the "material modifications" section of a blueprints file (see I_ARMI_MAT_USER_INPUT0) to the material during construction. This takes place during lower calls to _conformKwargs() and subsequently _constructMaterial(), which operate using the component blueprint and associated material modifications from the component's block.

Within _constructMaterial(), the material class is resolved into a material object by calling resolveMaterialClassByName(). The applyInputParams() method of that material class is then called, passing in the associated material modifications data, which the material class can then use to modify the isotopics as necessary.

Implementation: Insert any depletable blueprint flags onto this component. I_ARMI_BP_NUC_FLAGS0
signature: insertDepletableNuclideKeys
requirements: R_ARMI_BP_NUC_FLAGS

This is called during the component construction process for each component from within construct().

For a given initialized component, check its flags to determine if it has been marked as depletable. If it is, use initReachableActiveNuclidesThroughBurnChain() to apply the user-specifications in the "nuclide flags" section of the blueprints to the Component such that all active isotopes and derivatives of those isotopes in the burn chain are initialized to have an entry in the component's numberDensities dictionary.

Note that certain case settings, including fpModel and fpModelLibrary, may trigger modifications to the active nuclides specified by the user in the "nuclide flags" section of the blueprints.

Implementation: Define a lattice map in reactor core. I_ARMI_BP_GRID
signature: GridBlueprint
requirements: R_ARMI_BP_GRID

Defines a yaml construct that allows the user to specify a grid from within their blueprints file, including a name, geometry, dimensions, symmetry, and a map with the relative locations of components within that grid.

Relies on the underlying infrastructure from the yamlize package for reading from text files, serialization, and internal storage of the data.

Is implemented as part of a blueprints file by being used in key-value pairs within the Grid class, which is imported and used as an attribute within the larger Blueprints class.

Includes a construct method, which instantiates an instance of one of the subclasses of StructuredGrid. This is typically called from within construct(), which then also associates the individual components in the block with locations specifed in the grid.

Implementation: Write a blueprint file from a blueprint object. I_ARMI_BP_TO_DB
signature: saveToStream
requirements: R_ARMI_BP_TO_DB

First makes a copy of the blueprints that are passed in. Then modifies any grids specified in the blueprints into a canonical lattice map style, if needed. Then uses the dump method that is inherent to all yamlize subclasses to write the blueprints to the given stream object.

If called with the full argument, the entire blueprints is dumped. If not, only the grids portion is dumped.

Implementation: The blueprint object that represents a nuclide flag. I_ARMI_BP_NUC_FLAGS1
signature: NuclideFlag
requirements: R_ARMI_BP_NUC_FLAGS

This class creates a yaml interface for the user to specify in their blueprints which isotopes should be depleted. It is incorporated into the "nuclide flags" section of a blueprints file by being included as key-value pairs within the NuclideFlags class, which is in turn included into the overall blueprints within Blueprints.

This class includes a boolean burn attribute which can be specified for any nuclide. This attribute is examined by the fileAsActiveOrInert() method to sort the nuclides into sets of depletable or not, which is typically called during construction of assemblies in constructAssem().

Note that while the burn attribute can be set by the user in the blueprints, other methods may also set it based on case settings (see, for instance, genDefaultNucFlags(), autoUpdateNuclideFlags(), and getAllNuclideBasesByLibrary()).

Implementation: Certain material modifications will be applied using this code. I_ARMI_MAT_USER_INPUT2
signature: CustomIsotopic
requirements: R_ARMI_MAT_USER_INPUT

Defines a yaml construct that allows the user to define a custom isotopic vector from within their blueprints file, including a name and key-value pairs corresponding to nuclide names and their concentrations.

Relies on the underlying infrastructure from the yamlize package for reading from text files, serialization, and internal storage of the data.

Is implemented as part of a blueprints file by being used in key-value pairs within the CustomIsotopics class, which is imported and used as an attribute within the larger Blueprints class.

These isotopics are linked to a component during calls to construct(), where the name specified in the isotopics attribute of the component blueprint is searched against the available CustomIsotopics defined in the "custom isotopics" section of the blueprints. Once linked, the apply() method is called, which adjusts the massFrac attribute of the component's material class.

Implementation: Build core and spent fuel pool from blueprints I_ARMI_BP_SYSTEMS
signature: SystemBlueprint

This class creates a yaml interface for the user to define systems with grids, such as cores or spent fuel pools, each having their own name, type, grid, and position in space. It is incorporated into the "systems" section of a blueprints file by being included as key-value pairs within the Systems class, which is in turn included into the overall blueprints within Blueprints.

This class includes a construct() method, which is typically called from within factory() during the initialization of the reactor object to instantiate the core and/or spent fuel pool objects. During that process, a spatial grid is constructed based on the grid blueprints specified in the "grids" section of the blueprints (see I_ARMI_BP_GRID) and the assemblies needed to fill the lattice are built from blueprints using constructAssem().

Implementation: The volume of a DerivedShape depends on the solid shapes surrounding them. I_ARMI_COMP_FLUID0
signature: computeVolume
requirements: R_ARMI_COMP_FLUID

Computing the volume of a DerivedShape means looking at the solid materials around it, and finding what shaped space is left over in between them. This method calls the method _deriveVolumeAndArea, which makes use of the fact that the ARMI reactor data model is hierarchical. It starts by finding the parent of this object, and then finding the volume of all the other objects at this level. Whatever is left over, is the volume of this object. Obviously, you can only have one DerivedShape child of any parent for this logic to work.

Implementation: Circle shaped Component I_ARMI_COMP_SHAPES0
signature: Circle
requirements: R_ARMI_COMP_SHAPES

This class provides the implementation of a Circle Component. This includes setting key parameters such as its material, temperature, and dimensions. It also includes a method to retrieve the area of a Circle Component via the getComponentArea method.

Implementation: Hexagon shaped Component I_ARMI_COMP_SHAPES1
signature: Hexagon
requirements: R_ARMI_COMP_SHAPES

This class provides the implementation of a hexagonal Component. This includes setting key parameters such as its material, temperature, and dimensions. It also includes methods for retrieving geometric dimension information unique to hexagons such as the getPerimeter and getPitchData methods.

Implementation: Rectangle shaped Component I_ARMI_COMP_SHAPES2
signature: Rectangle
requirements: R_ARMI_COMP_SHAPES

This class provides the implementation for a rectangular Component. This includes setting key parameters such as its material, temperature, and dimensions. It also includes methods for computing geometric information related to rectangles, such as the getBoundingCircleOuterDiameter and getPitchData methods.

Implementation: Square shaped Component I_ARMI_COMP_SHAPES3
signature: Square
requirements: R_ARMI_COMP_SHAPES

This class provides the implementation for a square Component. This class subclasses the Rectangle class because a square is a type of rectangle. This includes setting key parameters such as its material, temperature, and dimensions.

Implementation: Triangle shaped Component I_ARMI_COMP_SHAPES4
signature: Triangle
requirements: R_ARMI_COMP_SHAPES

This class provides the implementation for defining a triangular Component. This includes setting key parameters such as its material, temperature, and dimensions. It also includes providing a method for retrieving the area of a Triangle Component via the getComponentArea method.

Implementation: Holed hexagon shaped Component I_ARMI_COMP_SHAPES5
signature: HoledHexagon
requirements: R_ARMI_COMP_SHAPES

This class provides an implementation for a holed hexagonal Component. This includes setting key parameters such as its material, temperature, and dimensions. It also provides the capability to retrieve the diameter of the inner hole via the getCircleInnerDiameter method.

Implementation: Holed square shaped Component I_ARMI_COMP_SHAPES6
signature: HoledSquare
requirements: R_ARMI_COMP_SHAPES

This class provides an implementation for a holed square Component. This includes setting key parameters such as its material, temperature, and dimensions. It also includes methods to retrieve geometric dimension information unique to holed squares via the getComponentArea and getCircleInnerDiameter methods.

Implementation: Helix shaped Component I_ARMI_COMP_SHAPES7
signature: Helix
requirements: R_ARMI_COMP_SHAPES

This class provides the implementation for a helical Component. This includes setting key parameters such as its material, temperature, and dimensions. It also includes the getComponentArea method to retrieve the area of a helix. Helixes can be used for wire wrapping around fuel pins in fast reactor designs.

Implementation: Define a physical piece of a reactor. I_ARMI_COMP_DEF
signature: Component
requirements: R_ARMI_COMP_DEF

The primitive object in an ARMI reactor is a Component. A Component is comprised of a shape and composition. This class serves as a base class which all Component types within ARMI are built upon. All primitive shapes (such as a square, circle, holed hexagon, helix etc.) are derived from this base class.

Fundamental capabilities of this class include the ability to store parameters and attributes which describe the physical state of each Component within the ARMI data model.

Implementation: Order Components by their outermost diameter (using the < operator). I_ARMI_COMP_ORDER
signature: Component
requirements: R_ARMI_COMP_ORDER

Determining Component order by outermost diameters is implemented via the __lt__() method, which is used to control sort() as the standard approach in Python. However, __lt__() does not show up in the API.

Implementation: The volume of some defined shapes depend on the solid components surrounding them. I_ARMI_COMP_FLUID1
signature: resolveLinkedDims
requirements: R_ARMI_COMP_FLUID

Some Components are fluids and are thus defined by the shapes surrounding them. This method cycles through each dimension defining the border of this Component and converts the name of that Component to a link to the object itself. This series of links is then used downstream to resolve dimensional information.

Implementation: Material properties are retrievable. I_ARMI_COMP_MAT0
signature: getProperties
requirements: R_ARMI_COMP_MAT

This method returns the material object that is assigned to the Component.

Implementation: Components have one-and-only-one material. I_ARMI_COMP_1MAT
signature: getProperties
requirements: R_ARMI_COMP_1MAT

This method returns the material object that is assigned to the Component.

Implementation: Get a dimension of a Component. I_ARMI_COMP_VOL0
signature: getArea
requirements: R_ARMI_COMP_VOL

This method returns the area of a Component.

Implementation: Get a dimension of a Component. I_ARMI_COMP_VOL1
signature: getVolume
requirements: R_ARMI_COMP_VOL

This method returns the volume of a Component.

Implementation: Setting nuclide fractions. I_ARMI_COMP_NUCLIDE_FRACS0
signature: setNumberDensity

The method allows a user or plugin to set the number density of a Component. It also indicates to other processes that may depend on a Component's status about this change via the assigned attribute.

Implementation: Setting nuclide fractions. I_ARMI_COMP_NUCLIDE_FRACS1
signature: setNumberDensities

The method allows a user or plugin to set the number densities of a Component. In contrast to the setNumberDensity method, it sets all densities within a Component.

Implementation: Set a Component dimension, considering thermal expansion. I_ARMI_COMP_EXPANSION1
signature: setDimension
requirements: R_ARMI_COMP_EXPANSION

Dimensions should be set considering the impact of thermal expansion. This method allows for a user or plugin to set a dimension and indicate if the dimension is for a cold configuration or not. If it is not for a cold configuration, the thermal expansion factor is considered when setting the dimension.

If the retainLink argument is True, any Components linked to this one will also have its dimensions changed consistently. After a dimension is updated, the clearLinkedCache method is called which sets the volume of this Component to None. This ensures that when the volume is next accessed it is recomputed using the updated dimensions.

Implementation: Retrieve a dimension at a specified temperature. I_ARMI_COMP_DIMS
signature: getDimension
requirements: R_ARMI_COMP_DIMS

Due to thermal expansion, Component dimensions depend on their temperature. This method retrieves a dimension from the Component at a particular temperature, if provided. If the Component is a LinkedComponent then the dimensions are resolved to ensure that any thermal expansion that has occurred to the Components that the LinkedComponent depends on is reflected in the returned dimension.

Implementation: Calculates radial thermal expansion factor. I_ARMI_COMP_EXPANSION0
signature: getThermalExpansionFactor
requirements: R_ARMI_COMP_EXPANSION

This method enables the calculation of the thermal expansion factor for a given material. If the material is solid, the difference between T0 and Tc is used to calculate the thermal expansion factor. If a solid material does not have a linear expansion factor defined and the temperature difference is greater than a predetermined tolerance, an error is raised. Thermal expansion of fluids or custom materials is neglected, currently.

Implementation: Parameters are accessible throughout the armi tree. I_ARMI_PARAM1
signature: ArmiObject
requirements: R_ARMI_PARAM

An ARMI reactor model is composed of collections of ARMIObject objects. These objects are combined in a hierarchical manner. Each level of the composite tree is able to be assigned parameters which define it, such as temperature, flux, or keff values. This class defines an attribute of type ParameterCollection, which contains all the functionality of an ARMI Parameter object. Because the entire model is composed of ARMIObjects at the most basic level, each level of the Composite tree contains this parameter attribute and can thus be queried.

Implementation: Composites (and all ARMI objects) have parameter collections. I_ARMI_CMP_PARAMS
signature: getParameterCollection
requirements: R_ARMI_CMP_PARAMS

This class method allows a user to obtain the paramCollection object, which is the object containing the interface for all parameters of an ARMI object.

Implementation: Composites have queryable flags. I_ARMI_CMP_FLAG0
signature: hasFlags
requirements: R_ARMI_CMP_FLAG

This method queries the flags (i.e. the typeID) of the Composite for a given type, returning a boolean representing whether or not the candidate flag is present in this ArmiObject. Candidate flags cannot be passed as a string type and must be of a type Flag. If no flags exist in the object then False is returned.

If a list of flags is provided, then all input flags will be checked against the flags of the object. If exact is False, then the object must have at least one of candidates exactly. If it is True then the object flags and candidates must match exactly.

Implementation: Composites have modifiable flags. I_ARMI_CMP_FLAG1
signature: setType
requirements: R_ARMI_CMP_FLAG

This method allows for the setting of flags parameter of the Composite.

Implementation: Return mass of composite. I_ARMI_CMP_GET_MASS
signature: getMass
requirements: R_ARMI_CMP_GET_MASS

This method allows for the querying of the mass of a Composite. If the nuclideNames argument is included, it will filter for the mass of those nuclide names and provide the sum of the mass of those nuclides.

Implementation: Get number density for a specific nuclide I_ARMI_CMP_NUC0
signature: getNumberDensity
requirements: R_ARMI_CMP_NUC

This method queries the number density of a specific nuclide within the Composite. It invokes the getNuclideNumberDensities method for just the requested nuclide.

Implementation: Get number densities for specific nuclides. I_ARMI_CMP_NUC1
signature: getNuclideNumberDensities
requirements: R_ARMI_CMP_NUC

This method provides the capability to query the volume weighted number densities for a list of nuclides within a given Composite. It provides the result in units of atoms/barn-cm. The volume weighting is accomplished by multiplying the number densities within each child Composite by the volume of the child Composite and dividing by the total volume of the Composite.

Implementation: Number density of composite is retrievable. I_ARMI_CMP_GET_NDENS
signature: getNumberDensities
requirements: R_ARMI_CMP_GET_NDENS

This method provides a way for retrieving the number densities of all nuclides within the Composite. It does this by leveraging the _getNdensHelper method, which invokes the getNuclideNumberDensities method. This method considers the nuclides within each child Composite of this composite (if they exist). If the expandFissionProducts flag is True, then the lumped fission products are expanded to include their constituent elements via the _expandLFPs method.

Implementation: Composites are a physical part of the reactor in a hierarchical data model. I_ARMI_CMP0
signature: Composite
requirements: R_ARMI_CMP

An ARMI reactor model is composed of collections of ARMIObject objects. This class is a child-class of the ARMIObject class and provides a structure allowing a reactor model to be composed of Composites.

This class provides various methods to query and modify the hierarchical ARMI reactor model, including but not limited to, iterating, sorting, and adding or removing child Composites.

Implementation: Composites have children in the hierarchical data model. I_ARMI_CMP1
signature: getChildren
requirements: R_ARMI_CMP

This method retrieves all children within a given Composite object. Children of any generation can be retrieved. This is achieved by visiting all children and calling this method recursively for each generation requested.

If the method is called with includeMaterials, it will additionally include information about the material for each child. If a function is supplied as the predicate argument, then this method will be used to evaluate all children as a filter to include or not. For example, if the caller of this method only desires children with a certain flag, or children which only contain a certain material, then the predicate function can be used to perform this filtering.

Implementation: Composites can be synchronized across MPI threads. I_ARMI_CMP_MPI
signature: syncMpiState
requirements: R_ARMI_CMP_MPI

Parameters need to be handled properly during parallel code execution.This method synchronizes all parameters of the composite object across all processes by cycling through all the children of the Composite and ensuring that their parameters are properly synchronized. If it fails to synchronize, an error message is displayed which alerts the user to which Composite has inconsistent data across the processes.

Implementation: Perform expansion during core construction based on block heights at a specified temperature. I_ARMI_INP_COLD_HEIGHT
signature: expandColdDimsToHot

This method is designed to be used during core construction to axially thermally expand the assemblies to their "hot" temperatures (as determined by Thot values in blueprints). First, The Assembly is prepared for axial expansion via setAssembly. In applyColdHeightMassIncrease, the number densities on each Component is adjusted to reflect that Assembly inputs are at cold (i.e., Tinput) temperatures. To expand to the requested hot temperatures, thermal expansion factors are then computed in computeThermalExpansionFactors. Finally, the Assembly is axially thermally expanded in axiallyExpandAssembly.

If the setting detailedAxialExpansion is False, then each Assembly gets its Block mesh set to match that of the "reference" Assembly (see getDefaultReferenceAssem and setBlockMesh).

Once the Assemblies are axially expanded, the Block BOL heights are updated. To account for the change in Block volume from axial expansion, completeInitialLoading is called to update any volume-dependent Block information.

Implementation: Perform expansion/contraction, given a list of components and expansion coefficients. I_ARMI_AXIAL_EXP_PRESC
signature: performPrescribedAxialExpansion

This method performs component-wise axial expansion for an Assembly given expansion coefficients and a corresponding list of Components. In setAssembly, the Assembly is prepared for axial expansion by determining Component-wise axial linkage and checking to see if a dummy Block is in place (necessary for ensuring conservation properties). The provided expansion factors are then assigned to their corresponding Components in setExpansionFactors. Finally, the axial expansion is performed in axiallyExpandAssembly

Implementation: Perform thermal expansion/contraction, given an axial temperature distribution over an assembly. I_ARMI_AXIAL_EXP_THERM
signature: performThermalAxialExpansion

This method performs component-wise thermal expansion for an assembly given a discrete temperature distribution over the axial length of the Assembly. In setAssembly, the Assembly is prepared for axial expansion by determining Component-wise axial linkage and checking to see if a dummy Block is in place (necessary for ensuring conservation properties). The discrete temperature distribution is then leveraged to update Component temperatures and compute thermal expansion factors (via updateComponentTempsBy1DTempField and computeThermalExpansionFactors, respectively). Finally, the axial expansion is performed in axiallyExpandAssembly.

Implementation: Preserve the total height of an ARMI assembly, during expansion. I_ARMI_ASSEM_HEIGHT_PRES
signature: axiallyExpandAssembly

The total height of an Assembly is preserved by not changing the ztop position of the top-most Block in an Assembly. The zbottom of the top-most Block is adjusted to match the Block immediately below it. The height of the top-most Block is is then updated to reflect any expansion/contraction.

Implementation: Homogenize one component into another. I_ARMI_BLOCKCONV0
signature: ComponentMerger
requirements: R_ARMI_BLOCKCONV

This subclass of BlockConverter is meant as a one-time-use tool, to convert a Block into one Component. A Block is a Composite that may probably has multiple Components somewhere in it. This means averaging the material properties in the original Block, and ensuring that the final Component has the same shape and volume as the original Block. This subclass essentially just uses the base class method dissolveComponentIntoComponent() given prescribed solute and solvent materials, to define the merger.

Implementation: Homogenize multiple components into one. I_ARMI_BLOCKCONV1
signature: MultipleComponentMerger
requirements: R_ARMI_BLOCKCONV

This subclass of BlockConverter is meant as a one-time-use tool, to convert a multiple Components into one. This means averaging the material properties in the original Components, and ensuring that the final Component has the same shape and volume as all of the originals. This subclass essentially just uses the base class method dissolveComponentIntoComponent() given prescribed solute and solvent materials, to define the merger. Though care is taken here to ensure the merger isn't verified until it is completely finished.

Implementation: Convert hex blocks to cylindrical blocks. I_ARMI_BLOCKCONV_HEX_TO_CYL
signature: convert

This method converts a HexBlock to a cylindrical Block. Obviously, this is not a physically meaningful transition; it is a helpful approximation tool for analysts. This is a subclass of BlockAvgToCylConverter which is a subclass of BlockConverter. This converter expects the sourceBlock and driverFuelBlock to defined and for the sourceBlock to have a spatial grid defined. Additionally, both the sourceBlock and driverFuelBlock must be instances of HexBlocks.

Implementation: Tool to convert a hex core to an RZTheta core. I_ARMI_CONV_3DHEX_TO_2DRZ
signature: convert

This method converts the hex-z mesh to r-theta-z mesh. It first verifies that the geometry type of the input reactor r has the expected HEX geometry. Upon conversion, it determines the inner and outer diameters of each ring in the r-theta-z mesh and calls _createRadialThetaZone to create a radial theta zone with a homogenized mixture. The axial dimension of the r-theta-z mesh is then updated by updateAxialMesh.

Implementation: Convert a one-third-core geometry to a full-core geometry. I_ARMI_THIRD_TO_FULL_CORE0
signature: convert

This method first checks if the input reactor is already full core. If full-core symmetry is detected, the input reactor is returned. If not, it then verifies that the input reactor has the expected one-third core symmetry and HEX geometry.

Upon conversion, it loops over the assembly vector of the source one-third core model, copies and rotates each source assembly to create new assemblies, and adds them on the full-core grid. For the center assembly, it modifies its parameters.

Finally, it sets the domain type to full core.

Implementation: Restore a one-third-core geometry to a full-core geometry. I_ARMI_THIRD_TO_FULL_CORE1
signature: restorePreviousGeometry

This method is a reverse process of the method convert. It converts the full-core reactor model back to the original one-third core reactor model by removing the added assemblies and changing the parameters of the center assembly from full core to one third core.

Implementation: Add assemblies along the 120-degree line to a reactor. I_ARMI_ADD_EDGE_ASSEMS0
signature: addEdgeAssemblies

Edge assemblies on the 120-degree symmetric line of a one-third core reactor model are added because they are needed for DIF3D-finite difference or MCNP models. This is done by copying the assemblies from the lower boundary and placing them in their reflective positions on the upper boundary of the symmetry line.

Implementation: Remove assemblies along the 120-degree line from a reactor. I_ARMI_ADD_EDGE_ASSEMS1
signature: removeEdgeAssemblies

This method is the reverse process of the method addEdgeAssemblies. It is needed for the DIF3D-Nodal calculation. It removes the assemblies on the 120-degree symmetry line.

Implementation: Try to preserve the boundaries of fuel and control material. I_ARMI_UMC_NON_UNIFORM
signature: generateCommonMesh

A core-wide mesh is computed via _computeAverageAxialMesh which operates by first collecting all the mesh points for every assembly (allMeshes) and then averaging them together using average1DWithinTolerance. An attempt to preserve fuel and control material boundaries is accomplished by moving fuel region boundaries to accomodate control rod boundaries. Note this behavior only occurs by calling _decuspAxialMesh which is dependent on minimumMeshSize being defined (this is controlled by the uniformMeshMinimumSize setting).

Implementation: Produce a mesh with a size no smaller than a user-specified value. I_ARMI_UMC_MIN_MESH
signature: generateCommonMesh
requirements: R_ARMI_UMC_MIN_MESH

If a minimum mesh size minimumMeshSize is provided, calls _decuspAxialMesh on the core-wide mesh to maintain that minimum size while still attempting to honor fuel and control material boundaries. Relies ultimately on _filterMesh to remove mesh points that violate the minimum size. Note that _filterMesh will always respect the minimum mesh size, even if this means losing a mesh point that represents a fuel or control material boundary.

Implementation: Make a copy of the reactor where the new core has a uniform axial mesh. I_ARMI_UMC
signature: convert
requirements: R_ARMI_UMC

Given a source Reactor, r, as input and when _hasNonUniformAssems is False, a new Reactor is created in initNewReactor. This new Reactor contains copies of select information from the input source Reactor (e.g., Operator, Blueprints, cycle, timeNode, etc). The uniform mesh to be applied to the new Reactor is calculated in _generateUniformMesh (see I_ARMI_UMC_NON_UNIFORM and I_ARMI_UMC_MIN_MESH). New assemblies with this uniform mesh are created in _buildAllUniformAssemblies and added to the new Reactor. Core-level parameters are then mapped from the source Reactor to the new Reactor in _mapStateFromReactorToOther. Finally, the core-wide axial mesh is updated on the new Reactor via updateAxialMesh.

Implementation: Map select parameters from composites on the original mesh to the new mesh. I_ARMI_UMC_PARAM_FORWARD
signature: convert

In _mapStateFromReactorToOther, Core-level parameters are mapped from the source Reactor to the new Reactor. If requested, block-level parameters can be mapped using an averaging equation as described in setAssemblyStateFromOverlaps.

Implementation: Map select parameters from composites on the new mesh to the original mesh. I_ARMI_UMC_PARAM_BACKWARD
signature: applyStateToOriginal

To ensure that the parameters on the original Reactor are from the converted Reactor, the first step is to clear the Reactor-level parameters on the original Reactor (see _clearStateOnReactor). _mapStateFromReactorToOther is then called to map Core-level parameters and, optionally, averaged Block-level parameters (see I_ARMI_UMC_PARAM_FORWARD).

Implementation: Get core symmetry. I_ARMI_R_SYMM
signature: symmetry
requirements: R_ARMI_R_SYMM

This property getter returns the symmetry attribute of the spatialGrid instance attribute. The spatialGrid is an instance of a child of the abstract base class Grid type. The symmetry attribute is an instance of the SymmetryType class, which is a wrapper around the DomainType and BoundaryType enumerations used to classify the domain (e.g., 1/3 core, quarter core, full core) and symmetry boundary conditions (e.g., periodic, reflective, none) of a reactor, respectively.

Only specific combinations of Grid type, DomainType, and BoundaryType are valid. The validity of a user-specified geometry and symmetry is verified by a settings Inspector <armi.operators.settingsValidation.Inspector.

Implementation: Get assembly by name. I_ARMI_R_GET_ASSEM0
signature: getAssemblyByName
requirements: R_ARMI_R_GET_ASSEM

This method returns the assembly with a name matching the value provided as an input parameter to this function. The name of an assembly is based on the assemNum parameter.

Implementation: Get assembly by location. I_ARMI_R_GET_ASSEM1
signature: getAssemblyWithStringLocation
requirements: R_ARMI_R_GET_ASSEM

This method returns the assembly located in the requested location. The location is provided to this method as an input parameter in a string with the format "001-001". For a HexGrid, the first number indicates the hexagonal ring and the second number indicates the position within that ring. For a CartesianGrid, the first number represents the x index and the second number represents the y index. If there is no assembly in the grid at the requested location, this method returns None.

Implementation: Retrieve neighboring assemblies of a given assembly. I_ARMI_R_FIND_NEIGHBORS
signature: findNeighbors

This method takes an Assembly as an input parameter and returns a list of the assemblies neighboring that assembly. There are 6 neighbors in a hexagonal grid and 4 neighbors in a Cartesian grid. The (i, j) indices of the neighbors are provided by getNeighboringCellIndices. For a hexagonal grid, the (i, j) indices are converted to (ring, position) indexing using the core.spatialGrid instance attribute.

The showBlanks option determines whether non-existing assemblies will be indicated with a None in the list or just excluded from the list altogether.

The duplicateAssembliesOnReflectiveBoundary setting only works for 1/3 core symmetry with periodic boundary conditions. For these types of geometries, if this setting is True, neighbor lists for assemblies along a periodic boundary will include the assemblies along the opposite periodic boundary that are effectively neighbors.

Implementation: Construct a mesh based on core blocks. I_ARMI_R_MESH
signature: findAllMeshPoints
requirements: R_ARMI_R_MESH

This method iterates through all of the assemblies provided, or all assemblies in the core if no list of assems is provided, and constructs a tuple of three lists which contain the unique i, j, and k mesh coordinates, respectively. The applySubMesh setting controls whether the mesh will include the submesh coordinates. For a standard assembly-based reactor geometry with a hexagonal or Cartesian assembly grid, this method is only used to produce axial (k) mesh points. If multiple assemblies are provided with different axial meshes, the axial mesh list will contain the union of all unique mesh points. Duplicate mesh points are removed.

Implementation: Retrieve flag from a string. I_ARMI_FLAG_TO_STR0
signature: fromString
requirements: R_ARMI_FLAG_TO_STR

For a string passed as typeSpec, first converts the whole string to uppercase. Then tries to parse the string for any special phrases, as defined in the module dictionary _CONVERSIONS, and converts those phrases to flags directly.

Then it splits the remaining string into words based on spaces. Looping over each of the words, if any word exactly matches a flag name. Otherwise, any numbers are stripped out and the remaining string is matched up to any class attribute names. If any matches are found these are returned as flags.

Implementation: Convert a flag to string. I_ARMI_FLAG_TO_STR1
signature: toString
requirements: R_ARMI_FLAG_TO_STR

This converts the representation of a bunch of flags from typeSpec, which might look like Flags.A|B, into a string with spaces in between the flag names, which would look like 'A B'. This is done via nesting string splitting and replacement actions.

Implementation: Grids can nest. I_ARMI_GRID_NEST
signature: Grid
requirements: R_ARMI_GRID_NEST

The reactor will usually have (i,j,k) coordinates to define a simple mesh for locating objects in the reactor. But inside that mesh can be a smaller mesh to define the layout of pins in a reactor, or fuel pellets in a pin, or the layout of some intricate ex-core structure.

Every time the armi.reactor.grids.locations.IndexLocation of an object in the reactor is returned, ARMI will look to see if the grid this object is in has a parent, and if so, ARMI will try to sum the indices of the two nested grids to give a resultant, more finely-grained grid position. ARMI can only handle grids nested 3 deep.

Implementation: Grids shall be able to repesent 1/3 and full core symmetries. I_ARMI_GRID_SYMMETRY0
signature: symmetry
requirements: R_ARMI_GRID_SYMMETRY

Every grid contains a armi.reactor.geometry.SymmetryType or string that defines a grid as full core or a partial core: 1/3, 1/4, 1/8, or 1/16 core. The idea is that the user can define 1/3 or 1/4 of the reactor, so the analysis can be run faster on a smaller reactor. And if a non-full core reactor grid is defined, the boundaries of the grid can be reflective or periodic, to determine what should happen at the boundaries of the reactor core.

It is important to note, that not all of these geometries will apply to every reactor or core. If your core is made of hexagonal assemblies, then a 1/3 core grid would make sense, but not if your reactor core was made up of square assemblies. Likewise, a hexagonal core would not make be able to support a 1/4 grid. You want to leave assemblies (and other objects) whole when dividing a grid up fractionally.

Implementation: Construct a hexagonal lattice. I_ARMI_GRID_HEX
signature: HexGrid
requirements: R_ARMI_GRID_HEX

This class represents a hexagonal StructuredGrid, that is one where the mesh maps to real, physical coordinates. This hexagonal grid is 2D, and divides the plane up into regular hexagons. That is, each hexagon is symmetric and is precisely flush with six neighboring hexagons. This class only allows for two rotational options: flats up (where two sides of the hexagons are parallel with the X-axis), and points up (where two sides are parallel with the Y-axis).

Implementation: Hexagonal grids can be points-up or flats-up. I_ARMI_GRID_HEX_TYPE
signature: fromPitch
requirements: R_ARMI_GRID_HEX_TYPE

When this method creates a HexGrid object, it can create a hexagonal grid with one of two rotations: flats up (where two sides of the hexagons are parallel with the X-axis), and points up (where two sides are parallel with the Y-axis). While it is possible to imagine the hexagons being rotated at other arbitrary angles, those are not supported here.

Implementation: When creating a hexagonal grid, the user can specify the symmetry. I_ARMI_GRID_SYMMETRY1
signature: fromPitch
requirements: R_ARMI_GRID_SYMMETRY

When this method creates a HexGrid object, it takes as an input the symmetry of the resultant grid. This symmetry can be a string (e.g. "full") or a SymmetryType object (e.g. FULL_CORE). If the grid is not full- core, the method getSymmetricEquivalents() will be usable to map any possible grid cell to the ones that are being modeled in the sub-grid.

Implementation: Equivalent contents in 1/3-core geometries are retrievable. I_ARMI_GRID_EQUIVALENTS
signature: getSymmetricEquivalents

This method takes in (I,J,K) indices, and if this HexGrid is full core, it returns nothing. If this HexGrid is 1/3-core, this method will return the 1/3-core symmetric equivalent of just (I,J). If this grid is any other kind, this method will just return an error; a hexagonal grid with any other symmetry is probably an error.

Implementation: Determine if grid is in first third. I_ARMI_GRID_SYMMETRY_LOC
signature: isInFirstThird

This is a simple helper method to determine if a given locator (from an ArmiObject) is in the first 1/3 of the HexGrid. This method does not attempt to check if this grid is full or 1/3-core. It just does the basic math of dividing up a hex-assembly reactor core into thirds and testing if the given location is in the first 1/3 or not.

Implementation: Store components with multiplicity greater than 1 I_ARMI_GRID_MULT
signature: MultiIndexLocation
requirements: R_ARMI_GRID_MULT

As not all grids are "full core symmetry", ARMI will sometimes need to track multiple positions for a single object: one for each symmetric portion of the reactor. This class doesn't calculate those positions in the reactor, it just tracks the multiple positions given to it. In practice, this class is mostly just a list of IndexLocation objects.

Implementation: Return the location of all instances of grid components with multiplicity greater than 1. I_ARMI_GRID_ELEM_LOC
signature: indices
requirements: R_ARMI_GRID_ELEM_LOC

This method returns the indices of all the IndexLocation objects. To be clear, this does not return the IndexLocation objects themselves. This is designed to be consistent with the Grid's __getitem__() method.

Implementation: Get the coordinates from a location in a grid. I_ARMI_GRID_GLOBAL_POS
signature: getCoordinates

Probably the most common request of a structure grid will be to give the grid indices and return the physical coordinates of the center of the mesh cell. This is super handy in any situation where the coordinates have physical meaning.

The math for finding the centroid turns out to be very easy, as the mesh is defined on the coordinates. So finding the mid-point along one axis is just taking the upper and lower bounds and dividing by two. And this is done for all axes. There are no more complicated situations where we need to find the centroid of a octagon on a rectangular mesh, or the like.

Implementation: Users can define custom parameter serializers. I_ARMI_PARAM_SERIALIZE
signature: Serializer

Important physical parameters are stored in every ARMI object. These parameters represent the plant's state during execution of the model. Currently, this requires that the parameters be serializable to a numpy array of a datatype supported by the h5py package so that the data can be written to, and subsequently read from, an HDF5 file.

This class allows for these parameters to be serialized in a custom manner by providing interfaces for packing and unpacking parameter data. The user or downstream plugin is able to specify how data is serialized if that data is not naturally serializable.

Implementation: Provide a way to signal if a parameter needs updating across processes. I_ARMI_PARAM_PARALLEL
signature: setter
requirements: R_ARMI_PARAM_PARALLEL

Parameters need to be handled properly during parallel code execution. This includes notifying processes if a parameter has been updated by another process. This method allows for setting a parameter's value as well as an attribute that signals whether this parameter has been updated. Future processes will be able to query this attribute so that the parameter's status is properly communicated.

Implementation: Filter parameters to write to DB. I_ARMI_PARAM_DB
signature: toWriteToDB
requirements: R_ARMI_PARAM_DB

This method is called when writing the parameters to the database file. It queries the parameter's saveToDB attribute to ensure that this parameter is desired for saving to the database file. It returns a list of parameters that should be included in the database write operation.

Implementation: The user-specified reactor. I_ARMI_R
signature: Reactor
requirements: R_ARMI_R

The Reactor is the top level of the composite structure, which can represent all components within a reactor core. The reactor contains a Core, which contains a collection of Assembly objects arranged in a hexagonal or Cartesian grid. Each Assembly consists of a stack of Block objects, which are each composed of one or more Component objects. Each Interface is able to interact with the reactor and its child Composites by retrieving data from it or writing new data to it. This is the main medium through which input information and the output of physics calculations is exchanged between interfaces and written to an ARMI database.

Implementation: A user can define a collection of armi locations. I_ARMI_ZONE0
signature: Zone
requirements: R_ARMI_ZONE

The Zone class facilitates the creation of a Zone object representing a collection of locations in the Core. A Zone contains a group of locations in the Core, used to subdivide it for analysis. Each location represents an Assembly or a Block, where a single Zone must contain items of the same type (i.e., Assembly or Block). Methods are provided to add or remove one or more locations to/from the Zone, and similarly, add or remove one or more items with a Core location (i.e., Assemblies or Blocks) to/from the Zone. In addition, several methods are provided to facilitate the retrieval of locations from a Zone by performing functions to check if a location exists in the Zone, looping through the locations in the Zone in alphabetical order, and returning the number of locations in the Zone, etc.

Implementation: A user can define a collection of armi zones. I_ARMI_ZONE1
signature: Zones
requirements: R_ARMI_ZONE

The Zones class facilitates the creation of a Zones object representing a collection of Zone objects. Methods are provided to add or remove one or more Zone to/from the Zones object. Likewise, methods are provided to validate that the zones are mutually exclusive, obtain the location labels of zones, return the Zone object where a particular Assembly or Block resides, sort the Zone objects alphabetically, and summarize the zone definitions. In addition, methods are provided to facilitate the retrieval of Zone objects by name, loop through the Zones in order, and return the number of Zone objects.

Implementation: Log files from different processes are combined. I_ARMI_LOG_MPI
signature: concatenateLogs
requirements: R_ARMI_LOG_MPI

The log files are plain text files. Since ARMI is frequently run in parallel, the situation arises where each ARMI process generates its own plain text log file. This function combines the separate log files, per process, into one log file.

The files are written in numerical order, with the lead process stdout first then the lead process stderr. Then each other process is written to the combined file, in order, stdout then stderr. Finally, the original stdout and stderr files are deleted.

Implementation: A simulation-wide log, with user-specified verbosity. I_ARMI_LOG
signature: RunLogger
requirements: R_ARMI_LOG

Log statements are any text a user wants to record during a run. For instance, basic notifications of what is happening in the run, simple warnings, or hard errors. Every log message has an associated log level, controlled by the "verbosity" of the logging statement in the code. In the ARMI codebase, you can see many examples of logging:

runLog.error("This sort of error might usually terminate the run.")
runLog.warning("Users probably want to know.")
runLog.info("This is the usual verbosity.")
runLog.debug("This is only logged during a debug run.")

The full list of logging levels is defined in _RunLog.getLogLevels(), and the developer specifies the verbosity of a run via _RunLog.setVerbosity().

At the end of the ARMI-based simulation, the analyst will have a full record of potentially interesting information they can use to understand their run.

Implementation: Logging is done to the screen and to file. I_ARMI_LOG_IO
signature: RunLogger
requirements: R_ARMI_LOG_IO

This logger makes it easy for users to add log statements to and ARMI application, and ARMI will control the flow of those log statements. In particular, ARMI overrides the normal Python logging tooling, to allow developers to pipe their log statements to both screen and file. This works for stdout and stderr.

At any place in the ARMI application, developers can interject a plain text logging message, and when that code is hit during an ARMI simulation, the text will be piped to screen and a log file. By default, the logging module only logs to screen, but ARMI adds a FileHandler in the RunLog constructor and in _RunLog.startLog.

Implementation: Settings are used to define an ARMI run. I_ARMI_SETTING0
signature: Settings
requirements: R_ARMI_SETTING

The Settings object is accessible to most ARMI objects through self.cs (for 'case settings'). It acts largely as a dictionary, and setting values are accessed by keys.

The Settings object has a 1-to-1 correspondence with the ARMI settings input file. This file may be created by hand or by a GUI.

Implementation: Define a case title to go with the settings. I_ARMI_SETTINGS_META0
signature: caseTitle
requirements: R_ARMI_SETTINGS_META

Every Settings object has a "case title"; a string for users to help identify their run. This case title is used in log file names, it is printed during a run, it is frequently used to name the settings file. It is designed to be an easy-to-use and easy-to-understand way to keep track of simulations. The general idea here is that the average analyst that is using ARMI will run many ARMI-based simulations, and there needs to be an easy to identify them all.

Implementation: There is a setting for total core power. I_ARMI_SETTINGS_POWER
signature: defineSettings
requirements: R_ARMI_SETTINGS_POWER

ARMI defines a collection of settings by default to be associated with all runs, and one such setting is power. This is the total thermal power of the reactor. This is designed to be the standard power of the reactor core, to be easily set by the user. There is frequently the need to adjust the power of the reactor at different cycles. That is done by setting the powerFractions setting to a list of fractions of this power.

Implementation: Define a comment and a versions list to go with the settings. I_ARMI_SETTINGS_META1
signature: defineSettings
requirements: R_ARMI_SETTINGS_META

Because nuclear analysts have a lot to keep track of when doing various simulations of a reactor, ARMI provides a comment setting that takes an arbitrary string and stores it. This string will be preserved in the settings file and thus in the database, and can provide helpful notes for analysts in the future.

Likewise, it is helpful to know what versions of software were used in an ARMI application. There is a dictionary-like setting called versions that allows users to track the versions of: ARMI, their ARMI application, and the versions of all the plugins in their simulation. While it is always helpful to know what versions of software you run, it is particularly needed in nuclear engineering where demands will be made to track the exact versions of code used in simulations.

Implementation: The setting default is mandatory. I_ARMI_SETTINGS_DEFAULTS
signature: Setting

Setting objects hold all associated information of a setting in ARMI and should typically be accessed through the Settings methods rather than directly. Settings require a mandatory default value.

Setting subclasses can implement custom load and dump methods that can enable serialization (to/from dicts) of custom objects. When you set a setting's value, the value will be unserialized into the custom object and when you call dump, it will be serialized. Just accessing the value will return the actual object in this case.

Implementation: The setting use a human-readable, plain text file as input. I_ARMI_SETTINGS_IO_TXT
signature: SettingsReader

ARMI uses the YAML standard for settings files. ARMI uses industry-standard ruamel.yaml Python libraray to read these files. ARMI does not bend or change the YAML file format standard in any way.

Implementation: Rules to validate and customize a setting's behavior. I_ARMI_SETTINGS_RULES
signature: Query
requirements: R_ARMI_SETTINGS_RULES

This class is meant to represent a generic validation test against a setting. The goal is: developers create new settings and they want to make sure those settings are used correctly. As an implementation, users pass in a condition function to this class that returns True or False based on the setting name and value. And then this class has a resolve method which tests if the condition is met. Optionally, this class also contains a correction function that allows users to automatically correct a bad setting, if the developers can find a clear path forward.

Implementation: Number densities are retrievable from masses. I_ARMI_UTIL_MASS2N_DENS
signature: getNDensFromMasses

Loops over all provided nuclides (given as keys in the massFracs vector) and calculates number densities of each, at a given material density. Mass fractions can be provided either as normalized to 1, or as unnormalized with subsequent normalization calling normalizeNuclideList via the normalize flag.

Implementation: Create MCNP material card. I_ARMI_UTIL_MCNP_MAT_CARD
signature: formatMaterialCard

Loops over a vector of nuclides (of type nuclideBase) provided in densities and formats them into a list of strings consistent with MCNP material card syntax, skipping dummy nuclides and LFPs.

A matNum may optionally be provided for the created material card: if not provided, it is left blank. The desired number of significant figures for the created card can be optionally provided by sigFigs. Nuclides whose number density falls below a threshold (optionally specified by minDens) are set to the threshold value.

The boolean mcnp6Compatible may optionally be provided to include the nuclide library at the end of the vector of individual nuclides using the "nlib=" syntax leveraged by MCNP. If this boolean is turned on, the associated value mcnpLibrary should generally also be provided, as otherwise, the library will be left blank in the resulting material card string.

Implementation: Expand mass fractions to nuclides. I_ARMI_UTIL_EXP_MASS_FRACS
signature: expandElementalMassFracsToNuclides

Given a vector of elements and nuclides with associated mass fractions (massFracs), expands the elements in-place into a set of nuclides using expandElementalNuclideMassFracs. Isotopes to expand into are provided for each element by specifying them with elementExpansionPairs, which maps each element to a list of particular NuclideBases; if left unspecified, all naturally-occurring isotopes are included.

Explicitly specifying the expansion isotopes provides a way for particular naturally-occurring isotopes to be excluded from the expansion, e.g. excluding O-18 from an expansion of elemental oxygen.

Implementation: No two flags have equivalence. I_ARMI_FLAG_DEFINE
signature: Flag
requirements: R_ARMI_FLAG_DEFINE

A bitwise flag class intended to emulate the standard library's enum.Flag, with the added functionality that it allows for extension after the class has been defined. Each Flag is unique; no two Flags are equivalent.

Note that while Python allows for arbitrary-width integers, exceeding the system-native integer size can lead to challenges in storing data, e.g. in an HDF5 file. In this case, the from_bytes() and to_bytes() methods are provided to represent a Flag's values in smaller chunks so that writeability can be maintained.

Implementation: Set of flags are extensible without loss of uniqueness. I_ARMI_FLAG_EXTEND0
signature: extend
requirements: R_ARMI_FLAG_EXTEND

A class method to extend a Flag with a vector of provided additional fields, with field names as keys, without loss of uniqueness. Values for the additional fields can be explicitly specified, or an instance of auto can be supplied.