Source code for romtools.workflows.inverse.vi_run_directory_policy

"""Run-directory policy for VI and MFVI model evaluations.

The VI drivers historically create a separate run directory for every model
sample.  That remains the default because many file-backed models depend on
those directories.  Lightweight in-memory models can opt out through the
public ``create_run_directories`` keyword without disabling VI restart,
history, or iteration-level output directories.
"""

from __future__ import annotations

from contextlib import contextmanager
from contextvars import ContextVar
import warnings

from romtools.workflows.inverse import mf_vi_drivers as _mf
from romtools.workflows.inverse import vi_drivers as _vi
from romtools.workflows.inverse.vi_sample_reuse import (
    mf_vi_with_auto_rom as _BASE_MF_VI_WITH_AUTO_ROM,
    run_mf_vi as _BASE_RUN_MF_VI,
    run_vi as _BASE_RUN_VI,
)


_UNSET = object()
_ACTIVE_CREATE_RUN_DIRECTORIES: ContextVar[object] = ContextVar(
    "romtools_vi_create_run_directories",
    default=None,
)

_ORIGINAL_VI_RUN_ITERATION = _vi.run_vi_iteration
_ORIGINAL_MF_VI_RUN_ITERATION = _mf.run_vi_iteration


def _resolve_create_run_directories(value) -> bool:
    inherited = _ACTIVE_CREATE_RUN_DIRECTORIES.get()
    if value is _UNSET:
        return True if inherited is None else bool(inherited)
    if not isinstance(value, bool):
        raise TypeError("create_run_directories must be a bool")
    return value


@contextmanager
def _run_directory_policy(create_run_directories):
    inherited = _ACTIVE_CREATE_RUN_DIRECTORIES.get()
    resolved = _resolve_create_run_directories(create_run_directories)
    if inherited is None and resolved:
        warnings.warn(
            "create_run_directories=True creates a separate run directory for "
            "every VI/MFVI model evaluation. This can cause significant "
            "filesystem overhead or inode pressure for lightweight or in-memory "
            "models. Set create_run_directories=False only when the model does "
            "not require per-evaluation run directories.",
            UserWarning,
            stacklevel=3,
        )
    token = _ACTIVE_CREATE_RUN_DIRECTORIES.set(resolved)
    try:
        yield resolved
    finally:
        _ACTIVE_CREATE_RUN_DIRECTORIES.reset(token)


def _directory_aware_vi_iteration(*args, **kwargs):
    kwargs.setdefault(
        "create_run_directories",
        _resolve_create_run_directories(_UNSET),
    )
    return _ORIGINAL_VI_RUN_ITERATION(*args, **kwargs)


def _directory_aware_mf_vi_iteration(*args, **kwargs):
    kwargs.setdefault(
        "create_run_directories",
        _resolve_create_run_directories(_UNSET),
    )
    return _ORIGINAL_MF_VI_RUN_ITERATION(*args, **kwargs)


# The original VI and MFVI drivers import run_vi_iteration into their module
# namespaces. Patch those module-level references once so all evaluation paths,
# including line searches and sample-reuse controllers, inherit the public
# directory policy. The resolved boolean is passed explicitly before any
# multiprocessing workers are launched.
_vi.run_vi_iteration = _directory_aware_vi_iteration
_mf.run_vi_iteration = _directory_aware_mf_vi_iteration


[docs] def run_vi(*args, create_run_directories=_UNSET, **kwargs): """Run VI with optional per-evaluation run-directory creation.""" with _run_directory_policy(create_run_directories): return _BASE_RUN_VI(*args, **kwargs)
[docs] def run_mf_vi(*args, create_run_directories=_UNSET, **kwargs): """Run MFVI with optional per-evaluation run-directory creation.""" with _run_directory_policy(create_run_directories): return _BASE_RUN_MF_VI(*args, **kwargs)
[docs] def mf_vi_with_auto_rom(*args, create_run_directories=_UNSET, **kwargs): """Run auto-ROM MFVI with the same run-directory policy.""" with _run_directory_policy(create_run_directories): return _BASE_MF_VI_WITH_AUTO_ROM(*args, **kwargs)