Extract template registries into templates.py

The two inline registries (_TEMPLATES, _ASC_TEMPLATES) plus their ~30
generator-function imports were pure data living in server.py. Move them
to a dedicated templates.py as NETLIST_TEMPLATES / ASC_TEMPLATES, and add
resolve_template() (netlist-first precedence) + all_template_names() to
replace the '.get() or .get()' dual-lookup dance that tune_circuit used.

server.py: 3257 -> 2949 lines. Registries verified byte-identical to the
originals (keys, params, descriptions, func identities); netlist-first
precedence for names in both registries preserved; live tune_circuit /
create_from_template / generate_schematic unchanged.
This commit is contained in:
Ryan Malloy 2026-06-20 00:45:24 -06:00
parent 7c4f09cbb3
commit c71b3bbb4c
2 changed files with 339 additions and 332 deletions

View File

@ -21,51 +21,6 @@ import numpy as np
from fastmcp import FastMCP
from fastmcp.prompts import Message
from .asc_generator import (
generate_boost_converter as generate_boost_converter_asc,
)
from .asc_generator import (
generate_buck_converter as generate_buck_converter_asc,
)
from .asc_generator import (
generate_colpitts_oscillator as generate_colpitts_asc,
)
from .asc_generator import (
generate_common_emitter_amp as generate_ce_amp_asc,
)
from .asc_generator import (
generate_current_mirror as generate_current_mirror_asc,
)
from .asc_generator import (
generate_differential_amp as generate_diff_amp_asc,
)
from .asc_generator import (
generate_h_bridge as generate_h_bridge_asc,
)
from .asc_generator import (
generate_instrumentation_amp as generate_inamp_asc,
)
from .asc_generator import (
generate_inverting_amp,
)
from .asc_generator import (
generate_ldo_regulator as generate_ldo_asc,
)
from .asc_generator import (
generate_non_inverting_amp as generate_noninv_amp_asc,
)
from .asc_generator import (
generate_rc_lowpass as generate_rc_lowpass_asc,
)
from .asc_generator import (
generate_sallen_key_lowpass as generate_sallen_key_asc,
)
from .asc_generator import (
generate_transimpedance_amp as generate_tia_asc,
)
from .asc_generator import (
generate_voltage_divider as generate_voltage_divider_asc,
)
from .batch import (
run_monte_carlo,
run_parameter_sweep,
@ -82,24 +37,7 @@ from .drc import run_drc as _run_drc
from .log_parser import parse_log
from .models import search_models as _search_models
from .models import search_subcircuits as _search_subcircuits
from .netlist import (
Netlist,
boost_converter,
buck_converter,
colpitts_oscillator,
common_emitter_amplifier,
current_mirror,
differential_amplifier,
h_bridge,
instrumentation_amplifier,
inverting_amplifier,
ldo_regulator,
non_inverting_amplifier,
rc_lowpass,
sallen_key_lowpass,
transimpedance_amplifier,
voltage_divider,
)
from .netlist import Netlist
from .noise_analysis import (
compute_noise_metrics,
compute_spot_noise,
@ -122,6 +60,12 @@ from .spicebook import (
)
from .stability import compute_stability_metrics
from .svg_plot import plot_bode, plot_spectrum, plot_timeseries, plot_timeseries_multi
from .templates import (
ASC_TEMPLATES,
NETLIST_TEMPLATES,
all_template_names,
resolve_template,
)
from .touchstone import parse_touchstone, s_param_to_db
from .waveform_expr import WaveformCalculator
from .waveform_math import (
@ -1200,7 +1144,7 @@ async def tune_circuit(
Args:
template: Template name (from list_templates). Works with both
netlist templates (_TEMPLATES) and schematic templates (_ASC_TEMPLATES).
netlist templates (NETLIST_TEMPLATES) and schematic templates (ASC_TEMPLATES).
params: Component value overrides (e.g., {"r": "2.2k", "c": "47n"}).
Use list_templates to see available parameters.
targets: Performance targets to check against. Each key is a metric name,
@ -1213,14 +1157,12 @@ async def tune_circuit(
Returns:
Dict with metrics, target comparison, and tuning suggestions.
"""
# --- 1. Look up template ---
tmpl = _TEMPLATES.get(template)
is_netlist = tmpl is not None
if tmpl is None:
tmpl = _ASC_TEMPLATES.get(template)
if tmpl is None:
names = sorted(set(list(_TEMPLATES.keys()) + list(_ASC_TEMPLATES.keys())))
# --- 1. Look up template (netlist registry first) ---
found = resolve_template(template)
if found is None:
names = all_template_names()
return {"error": f"Unknown template '{template}'. Available: {', '.join(names)}"}
tmpl, is_netlist = found
# --- 2. Build effective params ---
effective_params: dict[str, str] = dict(tmpl["params"])
@ -1773,103 +1715,6 @@ async def monte_carlo(
# ============================================================================
_ASC_TEMPLATES: dict[str, dict] = {
"rc_lowpass": {
"func": generate_rc_lowpass_asc,
"description": "RC lowpass filter with AC analysis",
"params": {"r": "1k", "c": "100n"},
},
"voltage_divider": {
"func": generate_voltage_divider_asc,
"description": "Resistive voltage divider with .op analysis",
"params": {"r1": "10k", "r2": "10k", "vin": "5"},
},
"inverting_amp": {
"func": generate_inverting_amp,
"description": "Inverting op-amp amplifier (gain = -Rf/Rin)",
"params": {"rin": "10k", "rf": "100k"},
},
"non_inverting_amp": {
"func": generate_noninv_amp_asc,
"description": "Non-inverting op-amp amplifier (gain = 1 + Rf/Rin)",
"params": {"rin": "10k", "rf": "100k"},
},
"common_emitter_amp": {
"func": generate_ce_amp_asc,
"description": "Common-emitter BJT amplifier with voltage divider bias",
"params": {
"rc": "2.2k", "rb1": "56k", "rb2": "12k", "re": "1k",
"cc_in": "10u", "cc_out": "10u", "ce": "47u",
"vcc": "12", "bjt_model": "2N2222",
},
},
"colpitts_oscillator": {
"func": generate_colpitts_asc,
"description": "Colpitts oscillator with LC tank and BJT",
"params": {
"ind": "1u", "c1": "100p", "c2": "100p",
"rb": "47k", "rc": "1k", "re": "470",
"vcc": "12", "bjt_model": "2N2222",
},
},
"differential_amp": {
"func": generate_diff_amp_asc,
"description": "Differential amplifier with op-amp",
"params": {"r1": "10k", "r2": "10k", "r3": "10k", "r4": "10k"},
},
"buck_converter": {
"func": generate_buck_converter_asc,
"description": "Buck (step-down) converter with NMOS switch",
"params": {
"ind": "10u", "c_out": "100u", "r_load": "10",
"v_in": "12", "duty_cycle": "0.5", "freq": "100k",
"mosfet_model": "IRF540N", "diode_model": "1N5819",
},
},
"ldo_regulator": {
"func": generate_ldo_asc,
"description": "LDO voltage regulator with PMOS pass transistor",
"params": {
"r1": "10k", "r2": "10k", "pass_transistor": "IRF9540N",
"v_in": "8", "v_ref": "2.5",
},
},
"h_bridge": {
"func": generate_h_bridge_asc,
"description": "H-bridge motor driver with 4 NMOS transistors",
"params": {"v_supply": "12", "r_load": "10", "mosfet_model": "IRF540N"},
},
"sallen_key_lowpass": {
"func": generate_sallen_key_asc,
"description": "Sallen-Key lowpass filter (unity gain, 2nd order)",
"params": {"r1": "10k", "r2": "10k", "c1": "10n", "c2": "10n"},
},
"boost_converter": {
"func": generate_boost_converter_asc,
"description": "Boost (step-up) converter with NMOS switch",
"params": {
"ind": "10u", "c_out": "100u", "r_load": "50",
"v_in": "5", "duty_cycle": "0.5", "freq": "100k",
},
},
"instrumentation_amp": {
"func": generate_inamp_asc,
"description": "3-opamp instrumentation amplifier",
"params": {"r1": "10k", "r2": "10k", "r3": "10k", "r_gain": "10k"},
},
"current_mirror": {
"func": generate_current_mirror_asc,
"description": "BJT current mirror with reference and load",
"params": {"r_ref": "10k", "r_load": "1k", "vcc": "12"},
},
"transimpedance_amp": {
"func": generate_tia_asc,
"description": "Transimpedance amplifier (current to voltage)",
"params": {"rf": "100k", "cf": "1p", "i_source": "1u"},
},
}
@mcp.tool()
def generate_schematic(
template: str,
@ -1894,11 +1739,11 @@ def generate_schematic(
Use list_templates to see available parameters for each template.
output_path: Where to save the .asc file (None = auto in /tmp)
"""
if template not in _ASC_TEMPLATES:
names = ", ".join(sorted(_ASC_TEMPLATES))
if template not in ASC_TEMPLATES:
names = ", ".join(sorted(ASC_TEMPLATES))
return {"error": f"Unknown template '{template}'. Available: {names}"}
entry = _ASC_TEMPLATES[template]
entry = ASC_TEMPLATES[template]
call_params: dict[str, str | float] = {}
if params:
@ -2143,159 +1988,6 @@ def create_netlist(
# CIRCUIT TEMPLATE TOOLS
# ============================================================================
# Registry of netlist templates with parameter metadata
_TEMPLATES: dict[str, dict] = {
"voltage_divider": {
"func": voltage_divider,
"description": "Resistive voltage divider with .op or custom analysis",
"params": {"v_in": "5", "r1": "10k", "r2": "10k", "sim_type": "op"},
},
"rc_lowpass": {
"func": rc_lowpass,
"description": "RC lowpass filter with AC sweep",
"params": {"r": "1k", "c": "100n", "f_start": "1", "f_stop": "1meg"},
},
"inverting_amplifier": {
"func": inverting_amplifier,
"description": "Inverting op-amp (gain = -Rf/Rin), +/-15V supply",
"params": {"r_in": "10k", "r_f": "100k", "opamp_model": "LT1001"},
},
"non_inverting_amplifier": {
"func": non_inverting_amplifier,
"description": "Non-inverting op-amp (gain = 1 + Rf/Rin), +/-15V supply",
"params": {"r_in": "10k", "r_f": "100k", "opamp_model": "LT1001"},
},
"differential_amplifier": {
"func": differential_amplifier,
"description": "Diff amp: Vout = (R2/R1)*(V2-V1), +/-15V supply",
"params": {
"r1": "10k",
"r2": "10k",
"r3": "10k",
"r4": "10k",
"opamp_model": "LT1001",
},
},
"common_emitter_amplifier": {
"func": common_emitter_amplifier,
"description": "BJT common-emitter with voltage divider bias",
"params": {
"rc": "2.2k",
"rb1": "56k",
"rb2": "12k",
"re": "1k",
"cc1": "10u",
"cc2": "10u",
"ce": "47u",
"vcc": "12",
"bjt_model": "2N2222",
},
},
"buck_converter": {
"func": buck_converter,
"description": "Step-down DC-DC converter with MOSFET switch",
"params": {
"ind": "10u",
"c_out": "100u",
"r_load": "10",
"v_in": "12",
"duty_cycle": "0.5",
"freq": "100k",
"mosfet_model": "IRF540N",
"diode_model": "1N5819",
},
},
"ldo_regulator": {
"func": ldo_regulator,
"description": "LDO regulator: Vout = Vref * (1 + R1/R2)",
"params": {
"opamp_model": "LT1001",
"r1": "10k",
"r2": "10k",
"pass_transistor": "IRF9540N",
"v_in": "8",
"v_ref": "2.5",
},
},
"colpitts_oscillator": {
"func": colpitts_oscillator,
"description": "LC oscillator: f ~ 1/(2pi*sqrt(L*Cseries))",
"params": {
"ind": "1u",
"c1": "100p",
"c2": "100p",
"rb": "47k",
"rc": "1k",
"re": "470",
"vcc": "12",
"bjt_model": "2N2222",
},
},
"h_bridge": {
"func": h_bridge,
"description": "4-MOSFET H-bridge motor driver with dead time",
"params": {
"v_supply": "12",
"r_load": "10",
"mosfet_model": "IRF540N",
},
},
"sallen_key_lowpass": {
"func": sallen_key_lowpass,
"description": "Sallen-Key lowpass filter (unity gain, 2nd order)",
"params": {
"r1": "10k",
"r2": "10k",
"c1": "10n",
"c2": "10n",
"opamp_model": "LT1001",
},
},
"boost_converter": {
"func": boost_converter,
"description": "Step-up DC-DC converter with MOSFET switch",
"params": {
"ind": "10u",
"c_out": "100u",
"r_load": "50",
"v_in": "5",
"duty_cycle": "0.5",
"freq": "100k",
"mosfet_model": "IRF540N",
"diode_model": "1N5819",
},
},
"instrumentation_amplifier": {
"func": instrumentation_amplifier,
"description": "3-opamp instrumentation amp: gain = 1 + 2*R1/R_gain",
"params": {
"r1": "10k",
"r2": "10k",
"r3": "10k",
"r_gain": "10k",
},
},
"current_mirror": {
"func": current_mirror,
"description": "BJT current mirror with reference resistor",
"params": {
"r_ref": "10k",
"r_load": "1k",
"vcc": "12",
"bjt_model": "2N2222",
},
},
"transimpedance_amplifier": {
"func": transimpedance_amplifier,
"description": "TIA: converts input current to output voltage (Vout = -Iph * Rf)",
"params": {
"rf": "100k",
"cf": "1p",
"i_source": "1u",
},
},
}
@mcp.tool()
def create_from_template(
@ -2329,13 +2021,13 @@ def create_from_template(
params: Optional dict of parameter overrides (all values as strings)
output_path: Where to save .cir file (None = auto in /tmp)
"""
template = _TEMPLATES.get(template_name)
template = NETLIST_TEMPLATES.get(template_name)
if template is None:
return {
"error": f"Unknown template '{template_name}'",
"available_templates": [
{"name": k, "description": v["description"], "params": v["params"]}
for k, v in _TEMPLATES.items()
for k, v in NETLIST_TEMPLATES.items()
],
}
@ -2386,9 +2078,9 @@ def list_templates() -> dict:
"description": info["description"],
"params": info["params"],
}
for name, info in _TEMPLATES.items()
for name, info in NETLIST_TEMPLATES.items()
],
"total_count": len(_TEMPLATES),
"total_count": len(NETLIST_TEMPLATES),
}
@ -2639,11 +2331,11 @@ def resource_templates() -> str:
templates = {
"netlist_templates": [
{"name": name, "description": info["description"], "params": info["params"]}
for name, info in _TEMPLATES.items()
for name, info in NETLIST_TEMPLATES.items()
],
"schematic_templates": [
{"name": name, "description": info["description"], "params": info["params"]}
for name, info in _ASC_TEMPLATES.items()
for name, info in ASC_TEMPLATES.items()
],
}
return json.dumps(templates, indent=2)
@ -2653,8 +2345,8 @@ def resource_templates() -> str:
def resource_template_detail(name: str) -> str:
"""Detailed information about a specific circuit template."""
# Check both registries
netlist_info = _TEMPLATES.get(name)
asc_info = _ASC_TEMPLATES.get(name)
netlist_info = NETLIST_TEMPLATES.get(name)
asc_info = ASC_TEMPLATES.get(name)
if not netlist_info and not asc_info:
return json.dumps({"error": f"Template '{name}' not found"})

315
src/mcltspice/templates.py Normal file
View File

@ -0,0 +1,315 @@
"""Circuit template registries.
Two registries map template names to builder functions and default params:
- NETLIST_TEMPLATES -> .netlist builders, producing SPICE .cir netlists
- ASC_TEMPLATES -> .asc_generator builders, producing graphical schematics
Some names appear in both (e.g. rc_lowpass). resolve_template() applies a
netlist-first precedence so a name available in both resolves to its netlist
form, matching the historical lookup order in tune_circuit.
"""
from .asc_generator import (
generate_boost_converter,
generate_buck_converter,
generate_colpitts_oscillator,
generate_common_emitter_amp,
generate_current_mirror,
generate_differential_amp,
generate_h_bridge,
generate_instrumentation_amp,
generate_inverting_amp,
generate_ldo_regulator,
generate_non_inverting_amp,
generate_rc_lowpass,
generate_sallen_key_lowpass,
generate_transimpedance_amp,
generate_voltage_divider,
)
from .netlist import (
boost_converter,
buck_converter,
colpitts_oscillator,
common_emitter_amplifier,
current_mirror,
differential_amplifier,
h_bridge,
instrumentation_amplifier,
inverting_amplifier,
ldo_regulator,
non_inverting_amplifier,
rc_lowpass,
sallen_key_lowpass,
transimpedance_amplifier,
voltage_divider,
)
# Graphical .asc schematic templates (generate_schematic, tune_circuit fallback)
ASC_TEMPLATES: dict[str, dict] = {
"rc_lowpass": {
"func": generate_rc_lowpass,
"description": "RC lowpass filter with AC analysis",
"params": {"r": "1k", "c": "100n"},
},
"voltage_divider": {
"func": generate_voltage_divider,
"description": "Resistive voltage divider with .op analysis",
"params": {"r1": "10k", "r2": "10k", "vin": "5"},
},
"inverting_amp": {
"func": generate_inverting_amp,
"description": "Inverting op-amp amplifier (gain = -Rf/Rin)",
"params": {"rin": "10k", "rf": "100k"},
},
"non_inverting_amp": {
"func": generate_non_inverting_amp,
"description": "Non-inverting op-amp amplifier (gain = 1 + Rf/Rin)",
"params": {"rin": "10k", "rf": "100k"},
},
"common_emitter_amp": {
"func": generate_common_emitter_amp,
"description": "Common-emitter BJT amplifier with voltage divider bias",
"params": {
"rc": "2.2k", "rb1": "56k", "rb2": "12k", "re": "1k",
"cc_in": "10u", "cc_out": "10u", "ce": "47u",
"vcc": "12", "bjt_model": "2N2222",
},
},
"colpitts_oscillator": {
"func": generate_colpitts_oscillator,
"description": "Colpitts oscillator with LC tank and BJT",
"params": {
"ind": "1u", "c1": "100p", "c2": "100p",
"rb": "47k", "rc": "1k", "re": "470",
"vcc": "12", "bjt_model": "2N2222",
},
},
"differential_amp": {
"func": generate_differential_amp,
"description": "Differential amplifier with op-amp",
"params": {"r1": "10k", "r2": "10k", "r3": "10k", "r4": "10k"},
},
"buck_converter": {
"func": generate_buck_converter,
"description": "Buck (step-down) converter with NMOS switch",
"params": {
"ind": "10u", "c_out": "100u", "r_load": "10",
"v_in": "12", "duty_cycle": "0.5", "freq": "100k",
"mosfet_model": "IRF540N", "diode_model": "1N5819",
},
},
"ldo_regulator": {
"func": generate_ldo_regulator,
"description": "LDO voltage regulator with PMOS pass transistor",
"params": {
"r1": "10k", "r2": "10k", "pass_transistor": "IRF9540N",
"v_in": "8", "v_ref": "2.5",
},
},
"h_bridge": {
"func": generate_h_bridge,
"description": "H-bridge motor driver with 4 NMOS transistors",
"params": {"v_supply": "12", "r_load": "10", "mosfet_model": "IRF540N"},
},
"sallen_key_lowpass": {
"func": generate_sallen_key_lowpass,
"description": "Sallen-Key lowpass filter (unity gain, 2nd order)",
"params": {"r1": "10k", "r2": "10k", "c1": "10n", "c2": "10n"},
},
"boost_converter": {
"func": generate_boost_converter,
"description": "Boost (step-up) converter with NMOS switch",
"params": {
"ind": "10u", "c_out": "100u", "r_load": "50",
"v_in": "5", "duty_cycle": "0.5", "freq": "100k",
},
},
"instrumentation_amp": {
"func": generate_instrumentation_amp,
"description": "3-opamp instrumentation amplifier",
"params": {"r1": "10k", "r2": "10k", "r3": "10k", "r_gain": "10k"},
},
"current_mirror": {
"func": generate_current_mirror,
"description": "BJT current mirror with reference and load",
"params": {"r_ref": "10k", "r_load": "1k", "vcc": "12"},
},
"transimpedance_amp": {
"func": generate_transimpedance_amp,
"description": "Transimpedance amplifier (current to voltage)",
"params": {"rf": "100k", "cf": "1p", "i_source": "1u"},
},
}
# SPICE .cir netlist templates (create_from_template, list_templates, tune_circuit)
NETLIST_TEMPLATES: dict[str, dict] = {
"voltage_divider": {
"func": voltage_divider,
"description": "Resistive voltage divider with .op or custom analysis",
"params": {"v_in": "5", "r1": "10k", "r2": "10k", "sim_type": "op"},
},
"rc_lowpass": {
"func": rc_lowpass,
"description": "RC lowpass filter with AC sweep",
"params": {"r": "1k", "c": "100n", "f_start": "1", "f_stop": "1meg"},
},
"inverting_amplifier": {
"func": inverting_amplifier,
"description": "Inverting op-amp (gain = -Rf/Rin), +/-15V supply",
"params": {"r_in": "10k", "r_f": "100k", "opamp_model": "LT1001"},
},
"non_inverting_amplifier": {
"func": non_inverting_amplifier,
"description": "Non-inverting op-amp (gain = 1 + Rf/Rin), +/-15V supply",
"params": {"r_in": "10k", "r_f": "100k", "opamp_model": "LT1001"},
},
"differential_amplifier": {
"func": differential_amplifier,
"description": "Diff amp: Vout = (R2/R1)*(V2-V1), +/-15V supply",
"params": {
"r1": "10k",
"r2": "10k",
"r3": "10k",
"r4": "10k",
"opamp_model": "LT1001",
},
},
"common_emitter_amplifier": {
"func": common_emitter_amplifier,
"description": "BJT common-emitter with voltage divider bias",
"params": {
"rc": "2.2k",
"rb1": "56k",
"rb2": "12k",
"re": "1k",
"cc1": "10u",
"cc2": "10u",
"ce": "47u",
"vcc": "12",
"bjt_model": "2N2222",
},
},
"buck_converter": {
"func": buck_converter,
"description": "Step-down DC-DC converter with MOSFET switch",
"params": {
"ind": "10u",
"c_out": "100u",
"r_load": "10",
"v_in": "12",
"duty_cycle": "0.5",
"freq": "100k",
"mosfet_model": "IRF540N",
"diode_model": "1N5819",
},
},
"ldo_regulator": {
"func": ldo_regulator,
"description": "LDO regulator: Vout = Vref * (1 + R1/R2)",
"params": {
"opamp_model": "LT1001",
"r1": "10k",
"r2": "10k",
"pass_transistor": "IRF9540N",
"v_in": "8",
"v_ref": "2.5",
},
},
"colpitts_oscillator": {
"func": colpitts_oscillator,
"description": "LC oscillator: f ~ 1/(2pi*sqrt(L*Cseries))",
"params": {
"ind": "1u",
"c1": "100p",
"c2": "100p",
"rb": "47k",
"rc": "1k",
"re": "470",
"vcc": "12",
"bjt_model": "2N2222",
},
},
"h_bridge": {
"func": h_bridge,
"description": "4-MOSFET H-bridge motor driver with dead time",
"params": {
"v_supply": "12",
"r_load": "10",
"mosfet_model": "IRF540N",
},
},
"sallen_key_lowpass": {
"func": sallen_key_lowpass,
"description": "Sallen-Key lowpass filter (unity gain, 2nd order)",
"params": {
"r1": "10k",
"r2": "10k",
"c1": "10n",
"c2": "10n",
"opamp_model": "LT1001",
},
},
"boost_converter": {
"func": boost_converter,
"description": "Step-up DC-DC converter with MOSFET switch",
"params": {
"ind": "10u",
"c_out": "100u",
"r_load": "50",
"v_in": "5",
"duty_cycle": "0.5",
"freq": "100k",
"mosfet_model": "IRF540N",
"diode_model": "1N5819",
},
},
"instrumentation_amplifier": {
"func": instrumentation_amplifier,
"description": "3-opamp instrumentation amp: gain = 1 + 2*R1/R_gain",
"params": {
"r1": "10k",
"r2": "10k",
"r3": "10k",
"r_gain": "10k",
},
},
"current_mirror": {
"func": current_mirror,
"description": "BJT current mirror with reference resistor",
"params": {
"r_ref": "10k",
"r_load": "1k",
"vcc": "12",
"bjt_model": "2N2222",
},
},
"transimpedance_amplifier": {
"func": transimpedance_amplifier,
"description": "TIA: converts input current to output voltage (Vout = -Iph * Rf)",
"params": {
"rf": "100k",
"cf": "1p",
"i_source": "1u",
},
},
}
def resolve_template(name: str) -> tuple[dict, bool] | None:
"""Resolve a template name to (entry, is_netlist), netlist registry first.
Returns None if the name is in neither registry. Netlist-first precedence
preserves the historical lookup order: a name present in both registries
(e.g. "rc_lowpass") resolves to its netlist form.
"""
if name in NETLIST_TEMPLATES:
return NETLIST_TEMPLATES[name], True
if name in ASC_TEMPLATES:
return ASC_TEMPLATES[name], False
return None
def all_template_names() -> list[str]:
"""Sorted union of every template name across both registries."""
return sorted(set(NETLIST_TEMPLATES) | set(ASC_TEMPLATES))