Overview of Flash-X architecture
Flash-X is a component based software system where different permutations and combinations of various components generate different applications. Some aspects of Flash-X architecture are adapted from FLASH , but it is fundamentally a new software with an architecture designed for use with heterogeneous platforms. Portability on heterogeneous platforms is achieved through a new orchestration system for applications (ORCHA) that is designed to be language agnostic and adaptable for future changes in the computing platforms. ORCHA has a collection of tools that address three main major concerns from the applications perspective described below.
As mentioned earlier Flash-X is not a monolithic application code; instead, it should be viewed as a collection of components that are selectively grouped to form various applications. Users specify which components should be included in a simulation, define a rough discretization/parallelization layout, and assign their own initial conditions, boundary conditions, and problem setup to create a unique application executable. In Flash-X terminology, a component that implements an exclusive portion of the code’s functionality is called a unit. A typical Flash-X simulation requires a proper subset of the units available in the code. Thus, it is important to distinguish between the entire Flash-X source code and a given Flash-X application.
Unit
A Flash-X unit provides well-defined functionality and publishes an Application Programming Interface (API), a collection of routines through which other units can interact with it. A unit can have multiple alternative implementations of varying complexity and for different purposes. A unit can have an arbitrary number of subunits that provide subsets of the unit’s functionality, though in practice the number of subunits remains low. Units must include a null implementation for every routine in their API at the top level of their hierarchy. This feature permits an application to easily exclude a unit without the need to modify code elsewhere. For example, the input/output unit can be easily turned on and off for testing purposes, by using the null implementations.
Flash-X implements its inheritance, extensibility, and modularity through its configuration layer. This layer consists of a collection of text Config files that reside at various levels of the code organization, and the setup tool which interprets the Config files. The two primary functions of this layer are to configure a single application from the Flash-X source tree, and to implement inheritance and customizability in the code.
Unit architecture abstracts the computational complexity of the unit from its public interfaces, and controls the scope of various data items owned by the unit. A unit’s API provides interfaces for modifying the state of the solution and for accessing and modifying data it owns that may be needed by other units. Units can have one or more subunits which are groupings of self-contained functionality. The concept subunits formalizes the selective use of a subset of a unit’s functionality, and the possibility of multiple alternative implementations of the same subset. Subunits implement disjoint subsets of a unit’s API, where none of the subsets can be a null set. The union of all subsets constituting various subunits must be exactly equal to the unit API. Every unit has at least a Main subunit that implements the bulk of the unit’s functionality, including its initialization. The Main subunit is also the custodian of all the unit-scope data. Subunits and other finer-grained components can have their own data modules that are only visible to the routines and functions within those components. In other words, a data module placed in a directory is only visible to routines in functions in that directory or its sub-directories, but not to its parent or ancestor directories.
Flash-X Inheritance
Flash-X inheritance is implemented through the Unix directory structure
and the setup tool. When the setup
tool parses the source tree, it
treats each child or subdirectory as inheriting all of the Config and
Makefile files in its parent’s directory. While source files at a given
level of the directory hierarchy override files with the same name at
higher levels, Makefiles and configuration files are cumulative. Since
functions can have multiple implementations, selection for a specific
application follows a few simple rules described in the following
Figures.
However, we must take care that this special use of the directory
structure for inheritance does not interfere with its traditional use for organization. We avoid any problems by means of a careful naming convention that allows clear distinction between organizational and namespace directories. See for naming conventions.
Customization
The Simulation unit in the code is treated differently from all other units because this is where the application is defined. The parsing of Config file begins from that of the application being configured. Additionally, this unit also provides the mechanism for customization. The inheritance implemented by the setup tool replaces any file or a macro definition assembled during the configuration of hte application if another file or macro of the same name is encountered in the Simulation unit. Thus a user desirous of customizing any part of the source code can do so by simply placing an identically named file or macro in a file with “.ini” extenstion in the directory that they created for their Simulation.
Unit Test Framework
In keeping with good software practice, Flash-X incorporates a unit test
framework that allows for rigorous testing and easy isolation of errors.
The components of the unit test show up in two different places in the
Flash-X source tree. One is a dedicated path in the Simulation
unit,
Simulation/SimulationMain/unitTest/UnitTestName
, where
UnitTestName is the name of a specific unit test. The other place is a
subdirectory called unitTest
, somewhere in the hierarchy of the
corresponding unit which implements a function Unit_unitTest
and any
helper functions it may need. The primary reason for organizing unit
tests in this somewhat confusing way is that unit tests are special
cases of simulation setups that also need extensive access to internal
data of the unit being tested. By splitting the unit test into two
places, it is possible to meet both requirements without violating unit
encapsulation. We illustrate the functioning of the unit test framework
with the unit test of the Eos
unit. For more details please see .
The Eos
unit test needs its own version of the routine
Driver/Driver_evolveAll
which makes a call to its Eos_unitTest
routine. The initial conditions specification and unit test specific
Driver_evolveAll
are placed in
Simulation/SimulationMain/unitTest/Eos
, since the Simulation
unit allows any substitute Flash-X function to be placed in the specific
simulation directory. The function Eos_unitTest
resides in
physics/Eos/unitTest
, and therefore has access to all internal
Eos
data structures and helper functions.