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#
Opens the source EDB (board layout)
Identifies signal and power/ground nets automatically
Picks the best reference net (GND, VSS, AGND …) using a curated regex table
Optionally groups nets into batches (differential pairs are never split)
Creates a cut-out for every batch (bounding box or convex-hull)
Places coaxial or circuit ports on every component that touches the signal nets
Applies solder-ball geometry when supplied (cylinder, sphere, spheroid)
Writes a ready-to-solve HFSS setup (adaptive mesh, broadband sweep, auto-seeding)
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:
- name
str Descriptive name for the group (will also become the regex pattern when the group is built automatically).
- nets
Sequence[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_setup
SimulationSetup,optional Per-batch simulation settings. When None the global
self.simulation_setupis used.
- name
- Returns:
BatchGroupThe 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_frequency
Union[str,: class:float], default"10GHz" Driven frequency used during mesh generation.
- maximum_pass_numberclass:int, default
15 Maximum number of adaptive passes.
- start_frequency
Union[str,: class:float], default0 Lower bound of the sweep window.
- stop_frequency
Union[str,: class:float], default"40GHz" Upper bound of the sweep window.
- frequency_step
Union[str,: class:float], default"0.05GHz" Linear step size for the frequency sweep.
- mesh_operation_size
Union[str,: class:float,None],optional Maximum element size for mesh operations. When
NoneHFSS computes an appropriate value automatically.- replaceclass:bool, default
False Placement strategy for the new setup:
False– append a per-batch setup by creating an auxiliaryBatchGroup(name="extra_setup") whoseBatchGroup.simulation_setuppoints to the new object.True– overwrite the global: attr:simulation_setup attribute of the currentHfssAutoConfiginstance.
- meshing_frequency
- Returns:
SimulationSetupThe 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
SolderBallsInfoentry to the configuration.- Parameters:
- ref_des
str Reference designator of the component to which the solder-ball definition applies (e.g.
"U1").- shape
str,default"cylinder" Geometric model used for the solder ball. Supported values are
"cylinder","sphere","spheroid", etc.- diameter
str|float|None,optional Nominal diameter. When
NoneHFSS auto-evaluates the value from the footprint.- mid_diameter
str|float|None,optional Middle diameter required only for spheroid shapes. Ignored for all other geometries.
- height
str|float|None,optional Ball height. When
NoneHFSS computes an appropriate value automatically.
- ref_des
- Returns:
SolderBallsInfoThe 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_groupsfrom the currentsignal_nets.This is a thin convenience wrapper around
group_nets_by_prefix(). It only executes when both:auto_evaluate_batch_groupsisTrue, andsignal_netsis non-empty.
- Parameters:
- pattern
str|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 sizebatch_size.str– treat the single string as a prefix pattern (automatically anchored:pattern + ".*").list[str] – each list element becomes its own prefix pattern; oneBatchGroupis created per list entry, regardless ofbatch_size.
- pattern
- 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_patterns
Sequence[str],optional POSIX ERE patterns that define the prefixes to be grouped. Example:
["PCIe", "USB"]➜ interpreted as["PCIe.*", "USB.*"]. IfNonepatterns are derived heuristically from the data set (see_infer_prefix_patterns()).
- 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_groupsin 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')#
BatchGroup#
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)#
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 sizebatch_size.Explicit mode (
pattern=["PCIe", "USB"]) One batch group per pattern is created;batch_sizeis 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.