HFSS automatic configuration#

Purpose#

The HFSSAutoConfiguration class (and its factory function create_hfss_auto_configuration) is a production-ready, declarative helper that turns a full-board EDB into analysis-ready HFSS cut-out projects without manual intervention.

Typical use-cases#

  • Post-layout signal-integrity sign-off for high-speed digital interfaces (PCIe, DDR, USB, Ethernet, etc.)

  • Automated impedance, insertion-loss and return-loss validation for every active net in the design

  • Regression testing across design spins—every net is simulated with identical boundary conditions, mesh settings, and port types

  • Design-of-experiments (DoE) where the same schematic is re-simulated with different stack-ups, materials, or solder-ball geometries

What the helper does#

  1. Opens the source EDB (board layout)

  2. Identifies signal and power/ground nets automatically

  3. Picks the best reference net (GND, VSS, AGND …) using a curated regex table

  4. Optionally groups nets into batches (differential pairs are never split)

  5. Creates a cut-out for every batch (bounding box or convex-hull)

  6. Places coaxial or circuit ports on every component that touches the signal nets

  7. Applies solder-ball geometry when supplied (cylinder, sphere, spheroid)

  8. Writes a ready-to-solve HFSS setup (adaptive mesh, broadband sweep, auto-seeding)

  9. Saves an independent EDB project per batch so that simulations can be distributed on a compute farm

The user only supplies:

  • path to the source EDB

  • (optionally) a list of nets or prefix patterns—everything else is auto-discovered

Design philosophy#

  • No manual GUI work—100 % script driven

  • Repeatable—identical settings for every net, every run

  • Scalable—cut-outs + batching keep problem size small enough for overnight turnaround on a 32-core box

  • Extensible—every numeric setting, port type or mesh strategy is exposed as a dataclass field

Minimal quick-start#

from pyedb import Edb
from pyedb.workflows.sipi.hfss_auto_configuration import create_hfss_auto_configuration

cfg = create_hfss_auto_configuration(
    source_edb_path=r"../release/board.aedb",
    target_edb_path=r"../hfss/serdes.aedb",
    batch_size=50,  # max 50 nets per cut-out
    port_type="coaxial",
)
cfg.auto_populate_batch_groups()  # discover nets and group them
cfg.create_projects()  # write one EDB + HFSS setup per batch

The snippet above produces a folder ../hfss/serdes.aedb that contains:

  • a cut-out with ≤ 50 nets

  • coaxial ports on every component pin

  • adaptive mesh 10 GHz–40 GHz sweep

  • ready to be solved

Class and data model#

HFSSAutoConfiguration#

class pyedb.workflows.sipi.hfss_auto_configuration.HFSSAutoConfiguration(edb=None)#
add_batch_group(name: str, nets: Sequence[str] | None = None, *, simulation_setup: SimulationSetup | None = None) BatchGroup#

Append a new BatchGroup to the configuration.

Parameters:
namestr

Descriptive name for the group (will also become the regex pattern when the group is built automatically).

netsSequence[str], optional

List of net names that belong to this batch. If omitted an empty list is assumed and you can fill it later.

simulation_setupSimulationSetup, optional

Per-batch simulation settings. When None the global self.simulation_setup is used.

Returns:
BatchGroup

The freshly created instance (already appended to self.batch_groups) so the caller can further manipulate it if desired.

add_simulation_setup(meshing_frequency: str | float | None = '10GHz', maximum_pass_number: int = 15, start_frequency: str | float | None = 0, stop_frequency: str | float | None = '40GHz', frequency_step: str | float | None = '0.05GHz', replace: bool = True) SimulationSetup#

Create a: class:.SimulationSetup instance and attach it to the configuration.

Parameters:
meshing_frequencyUnion[str,: class:float], default "10GHz"

Driven frequency used during mesh generation.

maximum_pass_numberclass:int, default 15

Maximum number of adaptive passes.

start_frequencyUnion[str,: class:float], default 0

Lower bound of the sweep window.

stop_frequencyUnion[str,: class:float], default "40GHz"

Upper bound of the sweep window.

frequency_stepUnion[str,: class:float], default "0.05GHz"

Linear step size for the frequency sweep.

mesh_operation_sizeUnion[str,: class:float, None], optional

Maximum element size for mesh operations. When None HFSS computes an appropriate value automatically.

replaceclass:bool, default False

Placement strategy for the new setup:

  • False – append a per-batch setup by creating an auxiliary BatchGroup (name="extra_setup") whose BatchGroup.simulation_setup points to the new object.

  • True – overwrite the global: attr:simulation_setup attribute of the current HfssAutoConfig instance.

Returns:
SimulationSetup

The newly created instance (already stored inside the configuration).

Examples

>>> cfg = HfssAutoConfig()
>>> # global setup
>>> cfg.add_simulation_setup(frequency_max="60GHz", replace=True)
>>> # per-batch setup
>>> cfg.add_simulation_setup(frequency_step="0.1GHz")
add_solder_ball(ref_des: str, shape: str = 'cylinder', diameter: str | float | None = None, mid_diameter: str | float | None = None, height: str | float | None = None) SolderBallsInfo#

Append a new SolderBallsInfo entry to the configuration.

Parameters:
ref_desstr

Reference designator of the component to which the solder-ball definition applies (e.g. "U1").

shapestr, default "cylinder"

Geometric model used for the solder ball. Supported values are "cylinder", "sphere", "spheroid", etc.

diameterstr | float | None, optional

Nominal diameter. When None HFSS auto-evaluates the value from the footprint.

mid_diameterstr | float | None, optional

Middle diameter required only for spheroid shapes. Ignored for all other geometries.

heightstr | float | None, optional

Ball height. When None HFSS computes an appropriate value automatically.

Returns:
SolderBallsInfo

The newly created instance (already appended to solder_balls). The object can be further edited in-place by the caller if desired.

Examples

>>> cfg = HfssAutoConfig()
>>> cfg.add_solder_ball("U1", diameter="0.3mm", height="0.2mm")
>>> cfg.add_solder_ball(
...     "U2",
...     shape="spheroid",
...     diameter="0.25mm",
...     mid_diameter="0.35mm",
...     height="0.18mm",
... )
auto_populate_batch_groups(pattern: str | list[str] | None = None) None#

Automatically create and populate batch_groups from the current signal_nets.

This is a thin convenience wrapper around group_nets_by_prefix(). It only executes when both:

  • auto_evaluate_batch_groups is True, and

  • signal_nets is non-empty.

Parameters:
patternstr | list [str] | None, optional

POSIX ERE prefix pattern(s) that control which nets are grouped.

  • None (default) – activate auto-discovery mode: nets are clustered heuristically and then split into chunks of size batch_size.

  • str – treat the single string as a prefix pattern (automatically anchored: pattern + ".*").

  • list [str] – each list element becomes its own prefix pattern; one BatchGroup is created per list entry, regardless of batch_size.

create_projects()#
group_nets_by_prefix(prefix_patterns: Sequence[str] | None = None) Dict[str, List[List[str]]]#

Group signal nets into disjoint batches while preserving differential pairs.

Parameters:
prefix_patternsSequence[str], optional

POSIX ERE patterns that define the prefixes to be grouped. Example: ["PCIe", "USB"] ➜ interpreted as ["PCIe.*", "USB.*"]. If None patterns are derived heuristically from the data set (see _infer_prefix_patterns()).

Returns:
Dict[str, List[List[str]]]

Keys are the original / generated pattern strings. Values are lists of batches; each batch is an alphabetically sorted list of net names. When prefix_patterns was supplied the list contains exactly one element (the complete group); in auto-discovery mode the list may contain multiple slices sized according to batch_size.

Notes

  • Differential recognition strips the suffixes _[PN], _[ML], _[+-] (case-insensitive).

  • The function updates the instance attribute batch_groups in place.

Examples

Explicit grouping (production intent):

>>> cfg.signal_nets = ["PCIe_RX0_P", "PCIe_RX0_N", "PCIe_TX0_P",
...                    "USB3_DP", "USB3_DN", "DDR4_A0", "DDR4_A1"]
>>> cfg.batch_size = 1_000          # ignored when patterns are supplied
>>> cfg.group_nets_by_prefix(["PCIe", "USB"])
{'PCIe.*': [['PCIe_RX0_N', 'PCIe_RX0_P', 'PCIe_TX0_P']],
 'USB.*':  [['USB3_DN', 'USB3_DP']]}

Auto-discovery with batching:

>>> cfg.group_nets_by_prefix()      # batch_size = 2
{'PCIe.*': [['PCIe_RX0_N', 'PCIe_RX0_P'], ['PCIe_TX0_P']],
 'USB.*':  [['USB3_DN', 'USB3_DP']],
 'DDR4.*': [['DDR4_A0', 'DDR4_A1']]}

Simulation setup#

class pyedb.workflows.sipi.hfss_auto_configuration.SimulationSetup(meshing_frequency: 'Union[str, float]' = '10GHz', maximum_pass_number: 'int' = 15, start_frequency: 'Union[str, float]' = 0, stop_frequency: 'Union[str, float]' = '40GHz', frequency_step: 'Union[str, float]' = '0.05GHz')#
frequency_step: str | float = '0.05GHz'#
maximum_pass_number: int = 15#
meshing_frequency: str | float = '10GHz'#
start_frequency: str | float = 0#
stop_frequency: str | float = '40GHz'#

BatchGroup#

class pyedb.workflows.sipi.hfss_auto_configuration.BatchGroup(name: 'str' = '', nets: 'List[str]' = <factory>, simulation_setup: 'SimulationSetup' = None)#
name: str = ''#
nets: List[str]#
simulation_setup: SimulationSetup = None#

SolderBallsInfo#

class pyedb.workflows.sipi.hfss_auto_configuration.SolderBallsInfo(ref_des: 'str' = '', shape: 'str' = 'cylinder', diameter: 'Optional[Union[str, float]]' = None, mid_diameter: 'Optional[Union[str, float]]' = None, height: 'Optional[Union[str, float]]' = None)#
diameter: str | float | None = None#
height: str | float | None = None#
mid_diameter: str | float | None = None#
ref_des: str = ''#
shape: str = 'cylinder'#

Factory function#

Net grouping logic#

Differential-pair preservation#

The helper recognises the suffixes _P/_N, _M/_P, _+/- (case-insensitive) and keeps those nets in the same batch regardless of the requested batch_size.

Prefix patterns#

  • Auto-discovery mode (pattern=None) Nets are clustered by longest common prefix; each cluster is then split into chunks of size batch_size.

  • Explicit mode (pattern=["PCIe", "USB"]) One batch group per pattern is created; batch_size is ignored. Patterns are automatically treated as POSIX ERE anchored at the start: PCIe.*, USB.*.

Reference-net selection#

The first net that matches any of the following regexes (case-insensitive) is used as reference:

^GND\d*$          ^VSS\w*          ^DGND$          ^AGND$
^PGND$            ^EGND$           ^SGND$          ^REF$
^VREF[A-Z0-9]*    ^VR[A-Z0-9]*     ^VTT$           ^0V$
^GND_plane$       ^GROUND$         ^SENSE\d*$      ^KSENSE\w*
… (≈ 30 patterns in total)

If multiple candidates exist, the one whose name contains the string “GND” is preferred; the rest become power nets.

Port creation details#

Coaxial ports#

A coaxial cylinder is constructed normal to the component pin. The outer radius is derived from the pad-stack anti-pad; the inner radius from the finished hole size. When solder_balls are supplied the 3-D model is extended by the ball height and diameter.

Circuit ports#

A two-pin circuit port is placed between the signal pin and the nearest reference (ground) pin on the same component. If create_pin_group=True all pins of the same net are shorted into a single pin-group before the port is attached.

Mesh and sweep defaults#

All values can be overridden globally (simulation_setup) or per batch (BatchGroup.simulation_setup).

File and folder layout#

After create_projects() finishes you obtain:

<target_edb_parent>/
└── batch_groups/
    ├── PCIe_RX0.aedb/          # cut-out, 43 nets
    ├── PCIe_TX0.aedb/          # cut-out, 37 nets
    ├── USB3.aedb/              # cut-out,  8 nets
    └── DDR4_A0_A07.aedb/       # cut-out, 50 nets

Each *.aedb folder is an independent HFSS project that can be opened, solved and post-processed separately.

Logging and error handling#

  • Every operation is logged through the native EDB logger (INFO level)

  • Missing components, duplicate net names or impossible cut-outs raise before any file is written

  • If a batch group ends up with only one net, it is automatically merged into the largest compatible group to avoid degenerate simulations

Performance notes#

  • Typical cut-out + port creation time: 2–5 s per batch (201 GB DDR4 board, 3000 nets, 32 cores)

  • Memory footprint: < 2 GB per batch because only the clipped geometry is kept in memory

  • Scales linearly with number of batches—jobs can be dispatched to an HPC cluster independently

API reference index#

Examples#

Complete PCIe Gen-4 sign-off#

cfg = create_hfss_auto_configuration(
    source_edb_path=r"../../design/PCIE_gen4.aedb",
    batch_group_folder=r"../../hfss_gen4",
    batch_size=30,
    port_type="coaxial",
    solder_balls=[
        {
            "ref_des": "U1",
            "shape": "spheroid",
            "diameter": "0.35mm",
            "mid_diameter": "0.45mm",
            "height": "0.25mm",
        },
    ],
)
cfg.auto_populate_batch_groups(pattern=["PCIe"])  # only PCIe nets
cfg.create_projects()

Troubleshooting#

“No reference net found” → Add your ground name to ref_patterns or set reference_net manually.

“Empty batch group” → Check that signal_nets is non-empty and that the supplied prefix patterns actually match net names in the design.

“Project fails to solve” → Inspect the cut-out in AEDT: look for overlapping ports or missing reference pins; reduce batch_size to isolate the problematic net.

License#

MIT License, see file header for full text.