Configuration API guide and complete example#
The pyedb.configuration package provides a Python interface for
building the same configuration payload described in Configuration file architecture.
Instead of manually authoring JSON, you populate a CfgData object and then pass it directly
to Configuration.run with no
serialization step required.
Tip
The easiest way to obtain a builder when you already have an open EDB session
is edb.configuration.create_config_builder(). This keeps everything
within the edb.configuration namespace and avoids a direct import of
CfgData.
Why use the configuration API?#
The programmatic API is useful when you want to:
build configurations from templates or scripts,
reuse helper functions across projects,
validate values in Python before writing the file,
generate only the sections you need,
pass a builder directly to
run()without touching the file system, andmove between dictionary, JSON, and TOML forms.
Two entry points#
There are two equivalent ways to start a programmatic configuration:
1. From an open EDB session (recommended)
# No extra imports needed – the builder is created inside the session.
cfg = edb.configuration.create_config_builder()
cfg.general.anti_pads_always_on = False
cfg.nets.add_signal_nets(["SIG1", "CLK"])
edb.configuration.run(cfg) # load + apply in one call
2. Standalone (scripts, templates, CI)
from pyedb.configuration import CfgData
cfg = CfgData()
cfg.general.anti_pads_always_on = False
cfg.nets.add_signal_nets(["SIG1", "CLK"])
# Apply to an open session later:
edb.configuration.run(cfg)
# Or persist to a file for review or version control:
cfg.to_json("my_config.json")
Session-aware get() methods#
When a builder is created via edb.configuration.create_config_builder() it
is bound to the live EDB session. Each section exposes a get() (or
get_layer / get_material / get_definition / get_instance)
helper that retrieves an existing database object and wraps it in the
corresponding builder. This avoids having to define again objects that already
exist in the design.
Call |
Returns |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The matching registered setup builder ( |
Each get() call caches the result—calling it twice with the same name
returns the same object. If the object was already registered (for example, via
add), the cached entry is returned instead.
cfg = edb.configuration.create_config_builder()
# ── Existing component ──────────────────────────────────────────────────
u1 = cfg.components.get("U1")
u1.set_solder_ball_properties("cylinder", "150um", "100um")
u1.set_ic_die_properties("flip_chip", orientation="chip_down")
# ── Existing layer ──────────────────────────────────────────────────────
top = cfg.stackup.get_layer("top")
top.set_huray_roughness("0.1um", "2.9")
top.set_etching(factor=0.4)
# ── Existing material ───────────────────────────────────────────────────
cu = cfg.stackup.get_material("copper")
cu.conductivity = 5.6e7
# ── Net classification ──────────────────────────────────────────────────
info = cfg.nets.get("GND") # also adds GND to power_nets if it is one
print(info["classification"]) # 'power_ground'
# ── Existing padstack definition ────────────────────────────────────────
via_def = cfg.padstacks.get_definition("via_0.2")
via_def.hole_plating_thickness = "30um"
# ── Existing padstack instance ──────────────────────────────────────────
via = cfg.padstacks.get_instance("via_A1")
via.set_backdrill("L3", "0.25mm", drill_from_bottom=True)
# ── Existing pin group ──────────────────────────────────────────────────
pg = cfg.pin_groups.get("pg_VDD")
print(pg.pins)
# ── Registered setup ────────────────────────────────────────────────────
cfg.setups.add_hfss_setup("hfss_bb", adapt_type="broadband")
setup = cfg.setups.get("hfss_bb") # retrieve the same object
setup.add_frequency_sweep("sw2", start="1GHz", stop="20GHz", step_or_count=100)
edb.configuration.run(cfg)
Generated API reference#
Use the generated AutoAPI pages when you want full class signatures, member lists, or direct links to a specific configuration builder implementation.
Page |
When to use it |
|---|---|
Browse every configuration submodule from one landing page. |
|
Review |
|
Inspect HFSS and SIwave setup classes including sweep helpers. |
|
Inspect |
|
Review explicit low-level terminal payload classes. |
|
Inspect material, roughness, etching, and layer builders. |
|
Review component, package, and model-assignment helpers. |
|
Inspect padstack definitions, instances, and backdrill payloads. |
|
Review air-box, dielectric extent, and cutout configuration classes. |
Applying a configuration#
run() is the single method to both load and apply a configuration.
You can pass any of the supported input types directly:
Input type |
Behaviour |
|---|---|
|
Serialized to a dict via |
|
Merged with the existing |
|
JSON or TOML file loaded from disk then applied. |
|
Applies the previously loaded |
# Variant A – builder (most ergonomic)
cfg = edb.configuration.create_config_builder()
cfg.nets.add_signal_nets(["SIG"])
edb.configuration.run(cfg)
# Variant B – dictionary
edb.configuration.run({"nets": {"signal_nets": ["SIG"]}})
# Variant C – file path
edb.configuration.run("my_config.json")
# Variant D – apply previously loaded data
edb.configuration.load("base.json")
edb.configuration.load("overlay.json") # merges on top
edb.configuration.run()
Terminal Info helpers#
For ports, sources, and probes, use
TerminalInfo factory methods instead of
writing raw terminal dictionaries by hand.
Factory call |
Use when … |
|---|---|
|
targeting a named pin on a component. |
|
connecting to a net on a specific component. |
|
referencing a pre-defined pin group. |
|
targeting a named padstack instance (coax ports). |
|
placing a terminal at an exact XY coordinate on a layer. |
|
auto-resolving a reference terminal from a ground net. |
from pyedb.configuration import TerminalInfo
TerminalInfo.pin("A1", reference_designator="U1")
TerminalInfo.net("VDD", reference_designator="U1")
TerminalInfo.pin_group("pg_VDD")
TerminalInfo.padstack("via_1")
TerminalInfo.coordinates("top", 0.001, 0.002, "SIG")
TerminalInfo.nearest_pin("GND", search_radius="5mm")
Complete example#
The following example uses edb.configuration.create_config_builder() to
obtain a builder from an open session, populates all major sections, then
applies the configuration with a single run() call.
from pyedb import Edb
from pyedb.configuration import TerminalInfo
edb = Edb("my_design.aedb")
# ----------------------------------------------------------------
# Obtain a builder from the active session
# ----------------------------------------------------------------
cfg = edb.configuration.create_config_builder()
# ----------------------------------------------------------------
# General design options
# ----------------------------------------------------------------
cfg.general.spice_model_library = "/models/spice"
cfg.general.s_parameter_library = "/models/snp"
cfg.general.anti_pads_always_on = False
cfg.general.suppress_pads = True
# ----------------------------------------------------------------
# Stackup
# ----------------------------------------------------------------
cfg.stackup.add_material("copper", conductivity=5.8e7)
cfg.stackup.add_material("fr4", permittivity=4.4, dielectric_loss_tangent=0.02)
cfg.stackup.add_signal_layer(
"top", material="copper", fill_material="fr4", thickness="35um"
)
cfg.stackup.add_dielectric_layer("diel1", material="fr4", thickness="100um")
cfg.stackup.add_signal_layer(
"bot", material="copper", fill_material="fr4", thickness="35um"
)
# ----------------------------------------------------------------
# Nets
# ----------------------------------------------------------------
cfg.nets.add_signal_nets(["DDR4_DQ0", "DDR4_DQ1", "CLK"])
cfg.nets.add_power_ground_nets(["VDD", "VCC", "GND"])
# Optional: store reference nets so they can be forwarded to add_cutout
cfg.nets.add_reference_nets(["GND"])
# ----------------------------------------------------------------
# Components
# ----------------------------------------------------------------
r1 = cfg.components.add("R1", part_type="resistor", enabled=True)
r1.add_pin_pair_rlc("1", "2", resistance="100ohm", resistance_enabled=True)
c1 = cfg.components.add("C1", part_type="capacitor")
c1.add_pin_pair_rlc("1", "2", capacitance="100nF", capacitance_enabled=True)
u1 = cfg.components.add("U1", part_type="ic")
u1.set_ic_die_properties("flip_chip", orientation="chip_down")
u1.set_solder_ball_properties("cylinder", "150um", "100um")
u1.set_port_properties(reference_height="50um")
# ----------------------------------------------------------------
# Padstacks
# ----------------------------------------------------------------
cfg.padstacks.add_definition(
"via_0.2", material="copper", hole_plating_thickness="25um"
)
via = cfg.padstacks.add_instance(name="v1", net_name="GND", layer_range=["top", "bot"])
via.set_backdrill("L3", "0.25mm", drill_from_bottom=True)
# ----------------------------------------------------------------
# Pin groups
# ----------------------------------------------------------------
cfg.pin_groups.add("pg_VDD", "U1", net="VDD")
cfg.pin_groups.add("pg_GND", "U1", pins=["A1", "A2", "B1"])
# ----------------------------------------------------------------
# Explicit low-level terminals (optional; most users use ports/sources)
# ----------------------------------------------------------------
cfg.terminals.add_pin_group_terminal("t_vdd", "pg_VDD", 50, "port")
cfg.terminals.add_pin_group_terminal(
"t_gnd", "pg_GND", 50, "port", reference_terminal="t_vdd"
)
cfg.terminals.add_bundle_terminal("bundle_demo", ["t_vdd", "t_gnd"])
# ----------------------------------------------------------------
# Ports
# ----------------------------------------------------------------
cfg.ports.add_circuit_port(
"port_U1",
positive_terminal=TerminalInfo.pin_group("pg_VDD"),
negative_terminal=TerminalInfo.pin_group("pg_GND"),
impedance=50,
)
cfg.ports.add_wave_port(
"wport1",
primitive_name="trace1",
point_on_edge=[0.001, 0.002],
horizontal_extent_factor=6,
)
# Coax port – padstack shortcut (most common)
cfg.ports.add_coax_port("coax1", padstack="v1")
# Coax port – net shortcut (distributed if >1 matching pin)
cfg.ports.add_coax_port("coax_vdd", net="VDD", reference_designator="U1")
# Coax port – pin shortcut
cfg.ports.add_coax_port("coax_a1", pin="A1", reference_designator="U1", impedance=50)
# ----------------------------------------------------------------
# Sources and probes
# ----------------------------------------------------------------
cfg.sources.add_current_source(
"isrc1",
positive_terminal=TerminalInfo.pin_group("pg_VDD"),
negative_terminal=TerminalInfo.pin_group("pg_GND"),
magnitude=0.5,
)
cfg.sources.add_voltage_source(
"vsrc1",
positive_terminal=TerminalInfo.net("VDD"),
negative_terminal=TerminalInfo.net("GND"),
magnitude=1.0,
)
cfg.probes.add(
"probe1",
positive_terminal=TerminalInfo.net("DDR4_DQ0"),
negative_terminal=TerminalInfo.net("GND"),
)
# ----------------------------------------------------------------
# Simulation setups
# ----------------------------------------------------------------
# ----- HFSS setup -----
hfss = cfg.setups.add_hfss_setup(
"hfss_bb",
adapt_type="broadband", # "single" | "broadband" | "multi_frequencies"
)
# Adaptive refinement – only one of these three is active at a time:
hfss.set_broadband_adaptive(
low_freq="1GHz",
high_freq="20GHz",
max_passes=20,
max_delta=0.02,
)
# hfss.set_single_frequency_adaptive(freq="5GHz", max_passes=20, max_delta=0.02)
# hfss.add_multi_frequency_adaptive("5GHz", max_passes=20, max_delta=0.02)
# hfss.add_multi_frequency_adaptive("10GHz")
# Automatic mesh seeding
hfss.set_auto_mesh_operation(
enabled=True,
trace_ratio_seeding=3.0,
signal_via_side_number=12,
)
# Length-based mesh operation
hfss.add_length_mesh_operation(
name="mesh1",
nets_layers_list={"DDR4_DQ0": ["top"]},
max_length="0.5mm",
max_elements=1000,
restrict_length=True,
refine_inside=False,
)
# Frequency sweep – Option A: inline (start/stop/step_or_count in one call)
hfss.add_frequency_sweep(
"sweep1",
start="1GHz",
stop="20GHz",
step_or_count=100,
distribution="linear_count", # or "log_count" | "linear_scale" | "log_scale" | "single"
sweep_type="interpolation",
enforce_passivity=True,
)
# Frequency sweep – Option B: chained (add multiple ranges on one sweep)
sweep2 = hfss.add_frequency_sweep(
"sweep2",
sweep_type="interpolation",
use_q3d_for_dc=False,
compute_dc_point=False,
enforce_causality=False,
enforce_passivity=True,
adv_dc_extrapolation=False,
)
sweep2.add_linear_count_frequencies("1GHz", "20GHz", 100)
sweep2.add_single_frequency("5GHz")
# ----- SIwave AC setup -----
siwave_ac = cfg.setups.add_siwave_ac_setup(
"siw_ac",
si_slider_position=2, # 0=Speed | 1=Balanced | 2=Accuracy
pi_slider_position=1,
use_si_settings=True,
)
# Inline sweep (start/stop/step_or_count) – no separate chaining call needed
siwave_ac.add_frequency_sweep(
"siw_sw1",
start="1kHz",
stop="1GHz",
step_or_count=100,
distribution="log_count",
compute_dc_point=False,
enforce_passivity=True,
)
# ----- SIwave DC setup -----
cfg.setups.add_siwave_dc_setup(
"siw_dc",
dc_slider_position=1, # 0=Speed | 1=Balanced | 2=Accuracy
export_dc_thermal_data=True,
)
# ----------------------------------------------------------------
# Boundaries
# ----------------------------------------------------------------
cfg.boundaries.set_radiation_boundary()
cfg.boundaries.set_air_box_extents(0.15, truncate_at_ground=True)
cfg.boundaries.set_dielectric_extent(
"BoundingBox", expansion_size=0.001, honor_user_dielectric=True
)
# ----------------------------------------------------------------
# Operations (cutout)
# ----------------------------------------------------------------
# Option A: explicit lists
cfg.operations.add_cutout(
signal_nets=["DDR4_DQ0", "CLK"],
reference_nets=["GND"],
extent_type="ConvexHull", # case-insensitive
expansion_size=0.002,
auto_identify_nets_enabled=True,
)
# Option B: reuse previously stored net lists (no duplication)
cfg.operations.add_cutout(
signal_nets=cfg.nets.signal_nets,
reference_nets=cfg.nets.reference_nets,
extent_type="ConvexHull",
)
cfg.operations.generate_auto_hfss_regions = True
# ----------------------------------------------------------------
# Model assignments
# ----------------------------------------------------------------
cfg.s_parameters.add(
"cap_model",
component_definition="CAP_100nF",
file_path="/snp/cap.s2p",
reference_net="GND",
)
cfg.spice_models.add(
"ic_spice",
component_definition="IC_U1",
file_path="/spice/ic.sp",
sub_circuit_name="IC_TOP",
)
# ----------------------------------------------------------------
# Thermal package definitions
# ----------------------------------------------------------------
pkg = cfg.package_definitions.add(
"PKG_U1",
component_definition="IC_U1",
apply_to_all=True,
maximum_power="5W",
theta_jb="10C/W",
theta_jc="5C/W",
height="1mm",
)
pkg.set_heatsink(
fin_base_height="0.5mm",
fin_height="3mm",
fin_orientation="x_oriented",
fin_spacing="1mm",
fin_thickness="0.2mm",
)
# ----------------------------------------------------------------
# Variables
# ----------------------------------------------------------------
cfg.variables.add("trace_width", "0.15mm", "Default trace width")
cfg.variables.add("$project_temp", "25cel")
# ----------------------------------------------------------------
# Modeler geometry
# ----------------------------------------------------------------
cfg.modeler.add_trace(
"trace1", "top", "0.15mm", net_name="DDR4_DQ0", path=[[0.0, 0.0], [0.01, 0.0]]
)
cfg.modeler.add_rectangular_plane(
"bot",
"gnd_plane",
"GND",
lower_left_point=[-0.05, -0.05],
upper_right_point=[0.05, 0.05],
)
cfg.modeler.delete_primitives_by_layer(["old_layer"])
# ----------------------------------------------------------------
# Apply everything to the open EDB design in one call
# ----------------------------------------------------------------
edb.configuration.run(cfg)
Persisting the configuration#
CfgData can be passed directly to edb.configuration.run(),
no .to_dict() call is required:
cfg = edb.configuration.create_config_builder()
cfg.nets.add_signal_nets(["SIG1", "CLK"])
cfg.nets.add_power_ground_nets(["VDD", "GND"])
# Pass the builder directly — to_dict() is called internally.
edb.configuration.run(cfg)
If you also want to save the configuration for review, archiving, or reuse in
another project, serialize the builder before (or instead of) calling run():
# Inspect as a plain dict (optional — not needed to call run())
payload = cfg.to_dict()
# Write to disk
cfg.to_json("my_project_config.json")
cfg.to_toml("my_project_config.toml")
# Apply the saved file later via file path
edb.configuration.run("my_project_config.json")
Note
edb.configuration.run(cfg) accepts a CfgData instance
directly and handles the serialization step internally. You only need
cfg.to_dict() when you want to inspect the payload programmatically or
pass it to another API that expects a plain dictionary.
Round-trip helpers#
The root builder supports round-tripping from existing dictionaries, JSON, or TOML files. This is useful for loading a template, tweaking specific values, and re-exporting.
from pyedb.configuration import CfgData
# Load from a file, modify, and save back
cfg = CfgData.from_json("base_config.json")
cfg.general.suppress_pads = True
cfg.stackup.add_material("silver", conductivity=6.3e7)
cfg.to_json("modified_config.json")
# Load from a dict
cfg2 = CfgData.from_dict({"nets": {"signal_nets": ["CLK"]}})
cfg2.to_toml("nets_only.toml")
Practical recommendations#
Use
edb.configuration.create_config_builder()when working inside an active EDB session. It avoids the extra import and keeps calling conventions consistent.Call
edb.configuration.run(cfg)to load and apply in a single statement. PassNone(the default) to apply previously loaded data again.Prefer
TerminalInfofactory methods over hand-written terminal dictionaries.Build only the sections you need: empty sections are omitted by
CfgData.to_dict()so the serialized payload stays minimal.Persist to JSON / TOML when you want a reviewed artifact kept in version control and applied without a Python script.
Store reusable snippets as plain Python functions that accept and return a
CfgDatainstance. Composing builders is straightforward.