Foundation Closed Loop¶
This page shows the smallest project-level Monata path that exercises the framework without adding simulator backends:
Project -> Library -> Cell -> generated views -> SimTask -> SimResult -> Spec
It uses the existing ngspice-subprocess backend. Xyce, VACASK, binary
rawfile parsing, and richer model-deck projection remain deferred capabilities.
Create a Project and Library¶
from monata.workspace.project import Project
project = Project("work/closed_loop")
lib = project.create_library("analog", tech_model_paths=[])
assert project.list_libraries() == ["analog"]
assert Project("work/closed_loop").get_library("analog").name == "analog"
Project is the workspace container. It records libraries and experiments; it
does not introduce a separate workspace service or database.
Add a Cell and Generated Views¶
cell = lib.create_cell("rc_probe", description="foundation closed-loop cell")
(cell.path / "schematic.py").write_text(
"from monata.netlist import SubCircuit\n"
"\n"
"class RcProbe(SubCircuit):\n"
" NAME = 'rc_probe'\n"
" NODES = ('inp', 'out', 'gnd')\n"
"\n"
" def build(self):\n"
" self.resistor('load', 'inp', 'out', '1k')\n"
" self.capacitor('hold', 'out', 'gnd', '1n')\n"
)
cell.create_view("schematic", entry="schematic.py", cls_name="RcProbe")
symbol_path = cell.generate_symbol()
netlist_path = cell.generate_netlist()
Generated symbol and netlist views are marked as generated. Monata refuses to
overwrite user-modified generated views unless force=True is explicit.
Register Model Assets Explicitly¶
Existing .osdi assets can be registered and converted into paths suitable for
SimTask.osdi_paths:
from monata.models import ModelRegistry
models = ModelRegistry(auto_discover=False)
models.register("mos", "models/bsimcmg.osdi", module_name="bsimcmg")
osdi_paths = models.osdi_paths("mos")
This is an explicit preparation step. Simulation does not implicitly compile,
discover, or load models beyond the paths passed into SimTask.
Run the Existing ngspice Backend¶
from monata.measure.spec import Spec
from monata.netlist import Circuit
from monata.sim.core import DCSpec, LocalExecutor, SimTask
circuit = Circuit("foundation dc smoke")
circuit.subckt(cell["schematic"].load()())
circuit.voltage("1", "in", "0", "0")
circuit.instance("probe", ("in", "out", "0"), "rc_probe")
circuit.resistor("sense", "out", "0", "1g")
task = SimTask(
circuit=circuit,
analysis_spec=DCSpec(source="V1", start=0, stop=1, step=0.5),
output_names=["out"],
metadata={"project": project.name, "library": lib.name, "cell": cell.name},
)
result = LocalExecutor(max_workers=1).submit(task).result()
if result.status != "ok":
raise RuntimeError(result.error_message)
final_vout = Spec("final_vout", lambda sim: float(sim.waveforms["out"][-1]), min=0.99, max=1.01)
assert final_vout.evaluate(result).passed
Always check result.status before evaluating measurements.
Save the Result in an Experiment¶
experiment = project.new_experiment("dc_smoke")
experiment.save_results("nominal", result)
loaded = experiment.load_results("nominal")
assert loaded.status == "ok"
This completes the foundation loop: project metadata can be reloaded, the
schematic view supplies the subcircuit used by simulation, generated artifacts
are present on disk, model assets are explicit references, simulation uses the
existing backend, and measurements consume the returned SimResult.