AC Sensitivity Analysis#

Compute sensitivity of AC values to device parameters.

import os
import sys
from pathlib import Path

# Set working directory to the PDK root if running from scripts/
if Path.cwd().name == "scripts":
    os.chdir(Path.cwd().parent)

# Ensure ngspice shared library can be found (macOS homebrew)
if sys.platform == "darwin" and "/opt/homebrew/lib" not in os.environ.get(
    "DYLD_LIBRARY_PATH", ""
):
    os.environ["DYLD_LIBRARY_PATH"] = "/opt/homebrew/lib:" + os.environ.get(
        "DYLD_LIBRARY_PATH", ""
    )

import numpy as np
import pandas as pd
import holoviews as hv
from nyancad.watch import watch_project_dir, file_schematic
from nyancad.netlist import inspice_netlist
from InSpice import Simulator

hv.extension("bokeh")

Parameters#

SCHEMATIC = "rcfilter"  # stem of the .nyancir file
CORNER = "mos_tt"

# -- AC sensitivity parameters --
OUTPUT_VAR = "v(P2)"  # output variable
VARIATION = "dec"  # point spacing: "dec", "oct", or "lin"
NUM_POINTS = 10  # number of points
START_FREQ = 100  # start frequency (Hz)
STOP_FREQ = 1e5  # stop frequency (Hz)

Load schematic and build netlist#

project = watch_project_dir(".")
schem_data = await file_schematic(project, SCHEMATIC)
spice = await inspice_netlist(SCHEMATIC, schem_data, corner=CORNER)
print(spice)
.title schematic
VV1 net0 GND DC 0 AC 1
CC1 P2 net0 1u
RR1 P2 GND 1k

Run AC sensitivity analysis#

simulator = Simulator.factory(simulator="ngspice-shared")

for osdi in [
    "ihp/models/ngspice/osdi/psp103.osdi",
    "ihp/models/ngspice/osdi/psp103_nqs.osdi",
    "ihp/models/ngspice/osdi/r3_cmc.osdi",
    "ihp/models/ngspice/osdi/mosvar.osdi",
]:
    simulator._ngspice_shared.exec_command(f"osdi {osdi}")

simulation = simulator.simulation(spice)
try:
    analysis = simulation.ac_sensitivity(
        output_variable=OUTPUT_VAR,
        variation=VARIATION,
        number_of_points=NUM_POINTS,
        start_frequency=START_FREQ,
        stop_frequency=STOP_FREQ,
    )
    print("AC sensitivity analysis complete.")
except Exception as _e:
    # ngspice 46 / InSpice: the generated `.sens <out> <var> <n> <f1> <f2>` command
    # omits the required 'AC' keyword, so ngspice can't parse the analysis spec.
    # Upstream bug — will work once InSpice patches AcSensitivityAnalysisParameters.
    print(f"AC sensitivity failed: {_e}")
    analysis = None
Warning: can't find the initialization file spinit.
Newer Ngspice version that could be unsupported 46
AC sensitivity failed: Simulation failed

Results#

if analysis is None:
    print("No analysis data (see previous cell).")
    df = None
else:
    available = [*analysis.nodes.keys(), *analysis.branches.keys()]
    print("Available vectors:", available)
    df = pd.DataFrame(index=np.array(analysis.frequency))
    for vec in analysis.nodes.keys():
        df[vec] = np.array(analysis[vec])
df
No analysis data (see previous cell).
if df is None or df.empty:
    print("No data to plot.")
else:
    plot = hv.NdOverlay(
        {
            k: hv.Curve(
                (df.index, np.abs(df[k])), "Frequency (Hz)", "Sensitivity"
            ).opts(logx=True, logy=True)
            for k in df.columns
        }
    )
    plot.opts(responsive=True, height=500)
No data to plot.