Architecture#

This page defines the repository structure and public API organization rules for FRIDOM.

These rules are strict for new code. Existing code may contain legacy patterns that should only be normalized when there is a clear reason to do so.

Package structure#

The main package code lives in src/fridom/.

The repository is structured around a core framework and model-specific packages:

  • src/fridom/framework/ contains the reusable core framework

  • src/fridom/shallowwater/ contains the shallow water model

  • src/fridom/nonhydro/ contains the non-hydrostatic model

  • src/fridom/hydrostatic/ contains the hydrostatic model

When adding new code, place it in the most specific existing subpackage that matches its responsibility.

Public API organization#

FRIDOM uses lazypimp to define package-level lazy exports.

In lazily-loaded packages, public modules, classes, and functions are declared twice in __init__.py:

  • once in the TYPE_CHECKING block for static analysis and editor support

  • once in all_modules_by_origin and/or all_imports_by_origin for lazy runtime exports

These declarations must remain synchronized.

If a symbol should be importable from the package namespace, it must be added to the appropriate lazy export table.

If a symbol is internal-only, do not add it to the package-level lazy export tables.

File naming#

For new code, prefer one public class per file.

The filename should be the snake_case form of the class name.

Examples:

  • ScalarField -> scalar_field.py

  • ModelSettings -> model_settings.py

  • PressureGradientTendency -> pressure_gradient_tendency.py

Existing multi-class files may remain as legacy exceptions.

New exceptions should be made only when the public classes are tightly coupled or splitting them would reduce clarity.

Template for __init__.py#

In lazily-loaded packages, use the following structure:

from typing import TYPE_CHECKING

from lazypimp import setup

# ================================================================
#  Disable lazy loading for type checking
# ================================================================
if TYPE_CHECKING:  # pragma: no cover
    from . import subpackage
    from .my_class import MyClass
    from .my_function import my_function

# ================================================================
#  Setup lazy loading
# ================================================================
base = "fridom.example"

all_modules_by_origin = {
    base: ["subpackage"],
}

all_imports_by_origin = {
    f"{base}.my_class": ["MyClass"],
    f"{base}.my_function": ["my_function"],
}

setup(__name__, all_modules_by_origin, all_imports_by_origin)

Use all_modules_by_origin for subpackages or submodules that should be available from the package namespace.

Use all_imports_by_origin for classes, functions, and other symbols that should be available from the package namespace.

Every public symbol imported in the TYPE_CHECKING block should have a matching lazy export entry.

Do not#

  • do not add a public symbol to a lazily-loaded package without updating both the TYPE_CHECKING imports and the lazy export tables

  • do not let the TYPE_CHECKING block and lazy export tables drift apart

  • do not place unrelated public classes in the same file without a clear reason

  • do not add public symbols to broad package roots when a more specific subpackage already exists