This script is used to perform a load-pull analysis for a power amplifier (or any DUT). A load variation is carried out in the reflection coefficient space (Γ-plane). For each Γ point, an equivalent output load (R+L or R+C) is generated automatically, followed by a harmonic-balance simulation using Xyce.
From the harmonic-balance results generated by Xyce (hb_fd.HB.FD.csv), the following quantities are extracted: Output power Pout (in dBm) at the fundamental frequency Magnitudes of the output currents at the fundamental and harmonics |I(k·f0)|
As a result, a CSV file and a Smith chart visualization are generated.
- Load-pull sweep in the Γ-plane
- Automatic distinction between inductive and capacitive loads:
- X > 0 → R + L
- X < 0 → R + C
- Harmonic-balance simulation with Xyce
- Automatic skipping of DC-failed points
- Output:
- CSV file containing valid load-pull results
- Smith chart with color-coded output power
- Xyce (must be callable from the command line as “Xyce”)
- Python version 3.10 or newer
- numpy
- pandas
- click
- matplotlib
- pysmithchart
- dataclasses
When using “uv run”, dependencies can be installed automatically.
The script uses exactly ONE netlist template. In the template, the output load must be defined using a placeholder that is automatically replaced during the sweep.
Instead of using a textual placeholder, the load-pull system identifies a load anchor element directly in the netlist.
The user must provide the name of a resistor element that defines where the load is connected, for example:
RLoad _net3 out 50This resistor acts as a structural anchor. At runtime, the script locates this element by name, determines the two connected nodes, and replaces the single resistor with a parameterized load-pull network. The anchor element is specified via the command line:
--load-name RLoadThe anchor resistor is replaced by the following unified load-pull topology:
.param RLP=50
.param LLP=1e-12
.param CLP=1e-12
.param USEL=1
.param USEC=0
.param GAMMAR=0
.param GAMMAI=0
.param ZR=0
.param ZI=0
LBf0 _net3 nLP1 0.663n
CLP0 nLP1 0 0.530p
LLP2 nLP1 nLP 0.663n
R_Loadpull nLP nZ {RLP}
RselL nZ nL {USEL*1e-6 + (1-USEL)*1e12}
L_Loadpull nL 0 {LLP}
RselC nZ nC {USEC*1e-6 + (1-USEC)*1e12}
C_Loadpull nC 0 {CLP}
Rbleed_nZ nC 0 100meg
Cb_harm _net3 nHP1 0.265p
LHP nHP1 0 1.326n
CHP2 nHP1 nHP 0.265p
- RLoad represents the real part of the load impedance.
- <node_in> and <node_out> are automatically extracted from the original anchor element.
- The internal node nZ is an auxiliary node used to form a series connection and to ensure a valid DC path.
- Two large bleed resistor (e.g., 100 MΩ) provides a DC leakage path, improving DC operating-point convergence.
Two parallel reactive branches are present:
- Inductive branch (RselL + L_Loadpull)
- Inductive branch (RselL + L_Loadpull)
The active branch is selected via the parameters USEL and USEC:
-
Inductive load (X > 0):
-
- USEL = 1, USEC = 0
-
- The inductive branch is enabled (RselL ≈ 1e-6 Ω)
-
- The capacitive branch is effectively disconnected (RselC ≈ 1e12 Ω)
-
Capacitive load (X < 0):
-
- USEL = 0, USEC = 1
-
- The capacitive branch is enabled (RselC ≈ 1e-6 Ω)
-
- The inductive branch is effectively disconnected (RselL ≈ 1e12 Ω)
This approach avoids maintaining separate netlists for inductive and capacitive loads and enables efficient parameter sweeping using a single unified load-pull topology.
- The user only provides the template netlist (with the anchor resistor).
- The Python script generates a new “stepped netlist” automatically:
-
- It replaces the anchor resistor by the parameterized load-pull topology.
-
- It inserts the required .param definitions (RLP/LLP/CLP/USEL/USEC, plus optional debug parameters like GAMMAR/GAMMAI/ZR/ZI).
-
- It generates a Γ grid in Python and converts Γ → Z (50Ω reference).
-
- It writes all sweep points into a Xyce .DATA table.
-
- It adds: .STEP data=<table_name> .DATA <table_name> … (all Γ rows)
-
- It calls Xyce once on the generated netlist. Typical stepped table columns include:
- GAMMAR, GAMMAI (Γ real/imag)
- ZR, ZI (Z real/imag, for logging/debug)
- RLP, LLP, CLP, USEL, USEC (load implementation parameters)
ADS load-pull can freely control the termination at:
- Fundamental (f0): Γ is swept (load varies)
- Harmonics (k·f0, k≥2): often set to Γ=0 (i.e., 50Ω) to emulate matched harmonic terminations Xyce does not provide an “ideal port” that independently enforces Γ(f0) and Γ(2f0/3f0/…) the same way ADS can. To approximate the ADS setup, this project inserts a diplexer (low-pass + high-pass) at the DUT output node:
- Low-pass path (LPF):
-
- Designed such that the fundamental f0 mainly flows into the load-pull impedance Z (Γ swept).
- High-pass path (HPF):
-
- Designed such that higher harmonics mainly flow into a fixed 50Ω termination (Γ=0). In other words:
- Fundamental termination ≈ swept Z(Γ)
- Harmonic termination ≈ 50Ω (matched) Notes:
- The diplexer is made of L/C elements, so it is not perfectly ideal.
- The cutoff frequency is chosen between f0 and 2f0 (e.g., around 12 GHz for f0=8 GHz) to route 8 GHz to LPF and 16 GHz+ to HPF.
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name RloadBy default, output files are stored in the same directory as the netlist.
uv run load-pull-optimization_mpi --helpPath to the netlist template:
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name RloadSpecifies the name of the resistor element in the netlist that acts as the load anchor for the load-pull network:
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name RloadMaximum radius |Γ| for the load-pull sweep:
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name Rload --r-max 0.95Number of grid points N in [-r_max, r_max]. The effective number of simulated points is approximately:
(pi / 4) * N²Example:
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name Rload --n-grid 31Fundamental frequency in Hz for which Pout and harmonics are evaluated:
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name Rload --freq 8e9Output directory for CSV and PNG files. If not specified: output is written to the netlist directory If specified: directory is created automatically if required Example:
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name Rload --output_dir ./resultsNumber of MPI processes used to run Xyce. Controls parallel execution of the sweep. This accelerates large Γ sweeps significantly. Example:
uv uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name Rload --mpi 8MPI launcher command. If not specified:
- Windows → defaults to mpiexec
- Linux/macOS → defaults to mpirun You can use this option if:
- MPI launcher is not in PATH
- You want to specify a full path to mpiexec/mpirun
- You use a non-default MPI distribution Example (Windows full path):
uv run load-pull-optimization_mpi.py test-amplifier-new.spice --load-name Rload --mpi 8 --mpiexec "C:\Program Files\Microsoft MPI\Bin\mpiexec.exe"<netlist_name>_load_pull_data_xyce.csv
Contains the following columns:
- Gamma_real, Gamma_imag
- Z_real, Z_imag
- R, L, C
- p_out_dbm
- I_harm1_mag, I_harm2_mag, I_harm3_mag, ... Points with DC failures are not included in this file
<netlist_name>_load_pull_smith.png
Each point corresponds to a Γ value Color scale represents output power Pout (dBm) DC-failed points are not displayed
If Xyce terminates with an error code (returncode ≠ 0) (e.g., “DC step #1 failed”):
- The point is considered invalid
- p_out_dbm is set to NaN
- The point is not plotted
- The point does not appear in the final CSV This behavior is typical and appropriate for load-pull analyses.
Output power is calculated as:
P = Re{ V · I* }Harmonic currents |I(k·f0)| are magnitudes of the complex HB current phasors. The reported currents are RMS values.
uv load-pull-optimization_mpi.py test-amplifier-new.spice --loda-name Rload --r-max 0.9 --n-grid 25 --freq 8e9 --z0 50 --output_dir ./lp_result --mpi 8 --mpiexec "C:\Program Files\Microsoft MPI\Bin\mpiexec.exe"