Skip to content

Myna Developer Guide

This guide focuses on extending Myna. For local validation details, see Testing. For documentation maintenance, see Documentation.

Local development workflow

Check that the current shell can find uv and has writable caches expected by the repository:

python3 scripts/check_dev_tools.py

Install development dependencies from the repository root with:

uv sync --frozen --extra dev

Then rerun the preflight to verify that pytest, ruff, mkdocs, and pre-commit are available through uv run:

python3 scripts/check_dev_tools.py

Some coding-agent and container shells do not inherit the same PATH or writable home cache directories as an interactive terminal. If the preflight reports cache errors, set writable cache locations before running uv or pre-commit:

export UV_CACHE_DIR=/tmp/uv-cache
export PRE_COMMIT_HOME=/tmp/pre-commit-cache

In PowerShell, use:

$env:UV_CACHE_DIR = "/tmp/uv-cache"
$env:PRE_COMMIT_HOME = "/tmp/pre-commit-cache"

Run the default validation loop with:

uv run ruff format
uv run ruff check
uv run pytest

If you change dependencies, run uv lock and commit the updated uv.lock. If you change documentation, run uv run python scripts/check_docs_harness.py; for MkDocs pages, generate API docs with uv run scripts/group_docs.py and then run uv run mkdocs build --strict.

Developing new workflow components

The most common activity that will require development is implementing a new workflow component to enable additional functionality. Before starting to implement a new component you should answer several questions:

Will this component require input files?

For example, a microstructure simulation may require thermal or solidification data. If the component does require input files, then check if an existing myna.core.files class exists with the required properties.

If there is no existing class, then you will have to implement a new myna.core.files class. The myna.core.files.FileVTK is an example of a minimally implemented class. It defines a file type (.vtk) and a basic check to determine if the file is valid, i.e., if it has the extension of .vtk.

What metadata does the component require from the build?

For example, a microstructure simulation would likely require the name of the material to correctly set material properties. Check if existing classes for what you need are defined in myna.core.metadata.

If class do not exist for one or more of required metadata values, then you will have to make additional classes for each of the require metadata. There are two types of metadata base classes, file-based and value-based metadata. The file-based metadata base classes are defined in myna.core.metadata.file and define file metadata as being associated with a build, a part, or a layer. The value-based metadata base classes are defined in myna.core.metadata.data and can be associated with a build or a part.

What kind of file will the simulation output?

For example, a microstructure simulation may output a 3D volume of grain orientations. Similar to the input files, check if an existing myna.core.files class exists with the the required properties.

If there is no existing class, then you will have to create one. In addition to the same considerations as input files, you may also want to implement get_values_for_sync functionality if you intend for contents from that file to be uploaded back to the database, however, this is optional (more on that later).

Implementing the workflow component

With the above questions addressed, it is now time to implement a new component class. An good example of a component class with the features mentioned above is myna.core.components.ComponentSolidificationPart. That class does not require a particular input file, it outputs a file class of FileGV and requires several pieces of metadata. Additionally, it is associated with layer-wise part simulations, hence the extension of the component types self.types.extend(["part", "layer"]). The main functionality of components are largely defined in the base Component class with the intention to keep implementing new components fairly lightweight.

Once a new component is implemented, the myna config functionality will be enabled, regardless of if there is an existing application. This will allow you to test that the correct metadata is being supplied to the case directories. When composing your input file, just provide an arbitrary name for the application, e.g., "test".

Documentation impact for component types

Adding a new component class or component type under src/myna/core/components/ uses an expected Myna extension point, but it still touches an architecture-sensitive path. The docs harness cannot infer from the diff whether a component change only extends existing behavior or changes subsystem boundaries, control flow, or public extension patterns.

If the change introduces a new public component pattern, component type, workflow behavior, command, input shape, or extension point, update this guide and possibly ARCHITECTURE.md. If the change only adds a concrete component using the existing extension pattern, local docs harness and pre-commit checks may fail unless you provide an explicit no-docs-needed rationale:

MYNA_DOCS_HARNESS_NO_ARCH_DOCS_REASON="extends an existing component extension point without changing architecture boundaries" \
  uv run python scripts/check_docs_harness.py

For PR CI, include the same decision in the PR body outside HTML comments:

Architecture/docs: no update needed - extends an existing component extension point without changing architecture boundaries.

Developing new applications

Once a new component is implemented, you will have to implement a corresponding application (app) to use with myna run. Applications consist of up to three stage modules:

  1. configure.py: Module that configures the files within each Myna case folder generated during myna config. At the end of this stage, each case folder should be a valid case directory for whatever model is being run.
  2. execute.py: Module that executes the model for each case.
  3. postprocess.py: Module that converts the output of the model into the required myna file format for the component. This may be part of execute.py, as well.

These stage modules are imported and called sequentially by Component.run_component. Each module should define a function named for the stage (configure, execute, or postprocess) or a main() function. The stages are mainly separated for clarity of the app functionality and for handling runtime issues. Technically all functionality can be in one stage, however, this may get confusing so it is recommended to split functions into three stages. If the model is not Python-based, these stages can simply wrap other commands as needed. All steps are optional and if one of these modules is not present, it will be ignored.

Many of the already implemented apps use the argparse library to parse user-specified inputs. In the input file, configure, execute, and postprocess allow users to pass options to each stage for the app. Any parameters that you wish to have accessible to users are intended to be adjusted through such options, which are exposed to the active stage through sys.argv in the format --key value or --key for Boolean flags. For Boolean flags, the assumed behavior is False if the flag is not passed and True if the flag is passed.

Applications that derive from MynaApp should read workflow state from app attributes such as self.input_file, self.step_name, self.last_step_name, and self.step_index. Avoid reading MYNA_* environment variables directly in new app code; those names are only populated temporarily while a stage is running for compatibility with existing wrappers and direct stage invocation in Myna 1.x. That env-var fallback is deprecated and will be removed in Myna 2.0.

Because stage modules are imported and executed in-process, they should restore any global process state they modify, especially the current working directory. Prefer myna.core.utils.working_directory() over bare os.chdir() calls.

It is likely that your app will require a template directory, or a set of input files for your model that get copied into every case. If you are using a template directory, then the intended functionality is that during configure.py the template folder is copied into each of the case directory and then updated. Updating the files inside the original template folder should be avoided.

Versioning applications

There is base MynaApp functionality to extract a version number from process output given a regex pattern to match. This can be used to have branching behavior in a Myna application based on the version of the executable that is being used. Adding in branching logic instead of updating the logic to handle only the most recent version of a code is generally preferable for backwards compatibility.