Architecture#

Architecture documentation explains how MC/DC translates flexible Python model definitions into particle-transport execution:

MC/DC's architecture flow from model definition through simulation compilation and runtime preparation to shared transport, which runs with Python, Numba-CPU, or Numba-GPU execution modes.

The architecture flow follows a Monte Carlo transport model from definition through one common preparation path, then branches into the Python, Numba-CPU, or Numba-GPU execution modes. The figure above emphasizes three major model-to-execution stages:

  1. Simulation compilation discovers the Python objects owned by a mcdc.Simulation, deduplicates them, and assigns simulation-local identifiers. Simulation Compilation explains model ownership, discovery, and finalization.

  2. Runtime preparation converts the compiled model into the structured simulation state and flat data array consumed by transport. Runtime Data Layout explains this numerical representation and its generated access helpers, mcdc_get and mcdc_set.

  3. Transport execution runs the common, adaptable transport implementation in the selected execution mode. Transport Execution explains how Python, Numba-CPU, and Numba-GPU execute it, including GPU code generation, memory placement, and Harmonize scheduling.

After transport, main.run_simulation passes the completed runtime state to mcdc.output for result aggregation and HDF5 serialization. This final results-and-output stage completes the calculation lifecycle but remains outside the shared transport implementation shown in the figure.

Visit Python-First, Numba-Accelerated Design for the rationale, boundaries, and tradeoffs that shape MC/DC’s architecture. It explains why method development begins in unrestricted Python mode and may progress through Numba-CPU to Numba-GPU.

Component Responsibility#

The source tree follows the same separation of responsibilities shown in the architecture flow. Model-facing components define, collect, and finalize the simulation. Code-factory components coordinate object discovery and generate its runtime representation. Transport components implement the numerical algorithms shared by all execution modes. Output components aggregate and serialize completed results. For methods development, object_/ and transport/ form the primary extension surface. The code_factory/ package implements the framework-level compilation and generation bridge between them. The table maps each component to its corresponding architecture role. Paths in the component column are relative to the top-level mcdc/ package.

Component

Architecture role

Responsibility

object_/

Model definition and compilation

Defines Python-side model classes and their object-local finalization hooks.

mcdc.Simulation in object_/simulation.py

Model definition and control

Owns model roots and configuration, resolves model-wide finalization, and controls compilation, visualization, and execution.

config.py

Calculation configuration

Parses command-line controls, configures process-wide execution behavior, and applies supported simulation-setting overrides before compilation.

constant.py

Shared static definitions

Defines named numerical codes, event flags, numerical limits, and tolerances used across model, transport, and output components.

code_factory/python_objects_compiler.py

Simulation compilation

Coordinates recursive discovery, registration, and model-wide finalization.

main.run_simulation

Calculation orchestration

Coordinates runtime preparation, transport execution, result generation, runtime reporting, and backend finalization.

print_.py

Diagnostics and reporting

Centralizes fatal errors, master-rank messages, calculation progress, and runtime summaries used across the model, transport, and output stages.

main.prepare

Runtime preparation

Coordinates framework-level packing, execution-resource allocation, backend configuration, and external runtime state.

code_factory/literals_generator.py and literals.py

Runtime preparation

Derive and expose simulation-specific values that compiled transport requires as literals.

code_factory/numba_layers_generator.py

Support generation and runtime preparation

Generates the shared Numba support and derives problem-dependent dtypes and prepared runtime state.

numba_types.py, mcdc_get/, and mcdc_set/

Generated Numba support

Define dtypes for the shared runtime schema, pure factories for problem-dependent dtypes, and accessors for variable-length fields in data.

code_factory/rebuild_numba_support.py

Development-time generation

Runs the support generator after changes to the object model or generation logic.

Runtime simulation and data

Prepared runtime state

Store fixed-layout state and variable-length numerical data generated by numba_layers_generator.py.

transport/

Shared transport

Implements the particle-transport algorithms used by every execution mode.

output.py

Results and output

Aggregates completed tally results and writes settings, tallies, eigenvalue data, saved particles, and runtime measurements to HDF5.

The mcdc/object_ modules, mcdc.Simulation, and python_objects_compiler.py implement the model-definition and simulation-compilation stages. Simulation Compilation explains their relationships, while Extending the Object Model explains how contributors can extend them.

main.prepare and numba_layers_generator.py use the generated Numba support to create the runtime simulation and data objects that form the data boundary between model compilation and transport. Runtime Data Layout explains the complete representation, Generated Numba Support and Problem-Dependent Dtypes distinguishes its three lifetimes, and Rebuilding Numba Support gives the object model development workflow.

The mcdc/transport package implements the shared-transport stage. Transport Execution explains how the execution modes run it. Writing Numba-Compatible Transport Code provides practical rules for extending its algorithms.

Results and Output#

After the selected transport driver returns, main.run_simulation calls output.generate_output with the prepared simulation record, flat data array, and Python mcdc.Simulation. output.py reads fixed fields directly, uses mcdc_get for variable-length runtime data, and retains Python-side settings and names where they form part of the output schema.

The module writes the primary HDF5 file, serializes tally and eigenvalue results, optionally saves particles, recombines census-based tally files, and appends runtime measurements. It may reshape or aggregate finalized results for storage, but particle tracking and tally scoring remain responsibilities of transport/.

When a new result must persist after a run, define and prepare its runtime storage first, populate it during the appropriate transport or closeout stage, and add only the serialization step to output.py. Changes to the user-visible HDF5 structure should also update the corresponding user documentation, regression coverage, and CHANGELOG.md entry.

Utility Module Scope#

util denotes helpers shared within the package that contains the module. The complete import path therefore defines the helper’s architectural scope.

Module

Scope

util.py

Contains framework-neutral helpers shared across top-level MC/DC packages, currently the nested-list flatten operation.

object_/util.py

Supports Python model construction and finalization, including distribution conversion, validation, motion processing, and model-side reference data.

transport/util.py

Provides Numba-compatible helpers shared across transport domains, including binning, interpolation, atomic updates, local arrays, and backend-neutral simulation access.

transport/physics/util.py

Contains numerical helpers shared specifically by neutron and electron physics implementations.

code_factory/gpu/transport/util.py

Implements GPU-compatible replacements and compiler lowering for the adaptable operations exposed by transport/util.py.

Place a new helper in the narrowest package that contains all of its consumers. Model-construction helpers may use ordinary Python and NumPy behavior, while helpers reachable from transport/ must follow the compiled-execution constraints described in Writing Numba-Compatible Transport Code. Hardware-specific replacements belong in the corresponding backend adaptation. Promote a helper to a broader util.py only when multiple sibling components genuinely share it, and avoid treating any utility module as a collection for otherwise unrelated code.