You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

109 lines
3.5 KiB

"""Tests for XSPICE code model plugin loading and simulation."""
import os
import pytest
from pyngspice import _cpp_available
def _get_codemodel_dir():
"""Return the path to pre-built .cm files."""
import pyngspice
return os.path.join(os.path.dirname(pyngspice.__file__), "codemodels")
def _cm_path(name):
"""Return full path to a .cm file."""
return os.path.join(_get_codemodel_dir(), f"{name}.cm")
CM_CATEGORIES = ["analog", "digital", "spice2poly", "table", "tlines", "xtradev", "xtraevt"]
@pytest.mark.skipif(not _cpp_available, reason="C++ extension not built")
class TestCodeModelLoading:
"""Test that all pre-built .cm plugins load successfully."""
@pytest.mark.parametrize("category", CM_CATEGORIES)
def test_load_cm(self, category):
"""Each .cm plugin should exist and load without error."""
from pyngspice import Simulator
path = _cm_path(category)
assert os.path.exists(path), f"{category}.cm not found at {path}"
assert os.path.getsize(path) > 0, f"{category}.cm is empty"
sim = Simulator()
sim.initialize()
ok = sim.command(f"codemodel {path}")
assert ok, f"Failed to load {category}.cm"
def test_load_all(self):
"""All 7 .cm plugins should load into a single simulator instance."""
from pyngspice import Simulator
sim = Simulator()
sim.initialize()
for category in CM_CATEGORIES:
path = _cm_path(category)
if os.path.exists(path):
ok = sim.command(f"codemodel {path}")
assert ok, f"Failed to load {category}.cm"
@pytest.mark.skipif(not _cpp_available, reason="C++ extension not built")
class TestCodeModelSimulation:
"""Test running simulations with XSPICE code models."""
def _load_analog(self, sim):
"""Load analog.cm into a simulator instance."""
path = _cm_path("analog")
if not os.path.exists(path):
pytest.skip("analog.cm not built")
sim.command(f"codemodel {path}")
def test_gain_dc(self):
"""Gain block with gain=3.0 should triple the input voltage."""
from pyngspice import Simulator
sim = Simulator()
sim.initialize()
self._load_analog(sim)
sim.command("circbyline Gain DC Test")
sim.command("circbyline V1 in 0 DC 2.0")
sim.command("circbyline A1 in out gain_model")
sim.command("circbyline .model gain_model gain(gain=3.0)")
sim.command("circbyline R1 out 0 1k")
sim.command("circbyline .dc V1 0 5 1")
sim.command("circbyline .end")
sim.command("run")
vecs = sim.all_vectors()
assert "out" in vecs
assert "in" in vecs
def test_gain_sweep_values(self):
"""Verify gain block output values match expected gain * input."""
from pyngspice import Simulator
sim = Simulator()
sim.initialize()
self._load_analog(sim)
sim.command("circbyline Gain Sweep Test")
sim.command("circbyline V1 in 0 DC 1.0")
sim.command("circbyline A1 in out gain_model")
sim.command("circbyline .model gain_model gain(gain=2.0)")
sim.command("circbyline R1 out 0 1k")
sim.command("circbyline .dc V1 0 5 1")
sim.command("circbyline .end")
sim.command("run")
# Use ngspice print command and check output
sim.clear_output()
sim.command("print out")
output = sim.get_output()
# Output should contain values that are 2x the input
assert "out" in output.lower()