A fault campaign

660 simulations written to a dataset folder; then the same campaign interrupted after two chunks and resumed, giving an identical dataset.

python examples/03_campaign.py
# %% [markdown]
# # A resumable fault campaign
#
# Runs 660 RC-filter simulations and resumes completed chunks on a second run.
# Also runnable as `python examples/03_campaign.py`.
# %%

import shutil
import sys
from pathlib import Path

examples_dir = next(
    (root / "examples" for root in (Path.cwd(), *Path.cwd().parents)
     if (root / "examples" / "rc_study.py").is_file()),
    None,
)
if examples_dir is not None:
    sys.path.insert(0, str(examples_dir))

# %%
from rc_study import WORKERS, campaign  # noqa: E402

from spicefault.experiments import run_chunks, simulate_sample  # noqa: E402

# %% [markdown]
# ## Validate and run the campaign
#
# The campaign contains 660 simulations. Validation checks definitions before ngspice runs.
# %%
if __name__ == "__main__":  # workers are processes: needed on macOS and Windows
    run = campaign()

    print("checking the definitions against the circuit:")
    print(" ", run.validate())

    print("\nbefore:", run.status())
    run.run(workers=WORKERS, chunk=200, progress=False)
    print("after: ", run.status())

    print("\nper fault:")
    print(run.summary())

    samples, waveforms, manifest = run.load()
    print(f"\n{len(samples)} samples, waveforms {waveforms.shape}")
    print("columns:", list(samples.columns))
    print("written by", manifest["spicefault_version"], "with", manifest["simulator"],
          "on", manifest["workers"], "workers")  # fmt: skip

# %% [markdown]
# ## Resume an interrupted campaign
#
# Simulate two chunks, then resume the remaining samples. The resumed dataset should match
# the uninterrupted dataset apart from wall-clock timing.
# %%
if __name__ == "__main__":
    interrupted = campaign("rc_interrupted")
    shutil.rmtree(interrupted.out_dir, ignore_errors=True)
    experiment = interrupted.experiment
    first = experiment.plan()[:400]  # two chunks of 200 are completed, then it stops
    run_chunks(first, simulate_sample, experiment, interrupted.out_dir / "parts",
               n_points=experiment.waveform.n_points, workers=WORKERS, chunk=200,
               progress=False)  # fmt: skip
    print("\ninterrupted:", interrupted.status())
    interrupted.run(workers=WORKERS, chunk=200, progress=False)
    resumed = interrupted.dataset()
    print("resumed:    ", interrupted.status())
    print("the manifest says it was resumed:", resumed.manifest.resumed)
    same = resumed.samples.drop(columns="elapsed_s").equals(
        run.dataset().samples.drop(columns="elapsed_s")
    )
    print("the resumed dataset is identical to the uninterrupted one:", same)