# Foundation Closed Loop This page shows the smallest project-level Monata path that exercises the framework without adding simulator backends: ```text 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 ```python 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 ```python 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`: ```python 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 ```python 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 ```python 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`.