Palace Width Sweep: Parallel Simulation
Sweep the microstrip width from 5 to 50 um (5 points), run all simulations in parallel on GDSFactory+ cloud using the non-blocking API, then compare S11 and S21 across widths.
Requirements:
- IHP PDK:
uv pip install ihp-gdsfactory - GDSFactory+ account for cloud simulation
Define the sweep
Build components and configure simulations
import gdsfactory as gf
from ihp import LAYER, PDK, cells
from gsim.common.stack import get_stack
from gsim.palace import DrivenSim
PDK.activate()
stack = get_stack(air_above=300.0) # auto-detects active PDK
sims = []
for w in widths:
# Build component
c = gf.Component()
r1 = c << cells.straight_metal(length=1000, width=w)
r = c.get_region(layer=LAYER.TopMetal2drawing)
r_sized = r.sized(+20000)
c.add_polygon(r_sized, layer=LAYER.Metal1drawing)
c.add_ports(r1.ports)
# Configure simulation
sim = DrivenSim()
sim.set_output_dir(f"./palace-sim-w{w:.1f}")
sim.set_geometry(c)
sim.set_stack(stack)
for port in c.ports:
sim.add_port(
port.name, from_layer="metal1", to_layer="topmetal2", geometry="via"
)
sim.set_driven(fmin=1e9, fmax=100e9, num_points=80)
sim.mesh(preset="default")
sims.append(sim)
print(f"Configured {len(sims)} simulations")
[34m04:40:04[0m | [33m[22mWARNING [0m | [33m[22mSmall conductor feature detected (2.000 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.[0m
/tmp/ipykernel_4622/3909240475.py:9: UserWarning: get_stack(air_above/air_below) is deprecated and ignored. Use simulation.set_airbox(margin_x=..., margin_y=..., z_above=..., z_below=...).
stack = get_stack(air_above=300.0) # auto-detects active PDK
[34m04:40:05[0m | [33m[22mWARNING [0m | [33m[22mAbsorbing boundary is applied to solid dielectric surfaces (sin, sio2) because the model has no air region, which produces large spurious insertion loss and reflection. Add an airbox via set_airbox(margin_x=..., margin_y=..., z_above=..., z_below=...). Note that get_stack(air_above=...) is deprecated and ignored.[0m
[34m04:40:07[0m | [33m[22mWARNING [0m | [33m[22mAbsorbing boundary is applied to solid dielectric surfaces (sin, sio2) because the model has no air region, which produces large spurious insertion loss and reflection. Add an airbox via set_airbox(margin_x=..., margin_y=..., z_above=..., z_below=...). Note that get_stack(air_above=...) is deprecated and ignored.[0m
[34m04:40:09[0m | [33m[22mWARNING [0m | [33m[22mAbsorbing boundary is applied to solid dielectric surfaces (sin, sio2) because the model has no air region, which produces large spurious insertion loss and reflection. Add an airbox via set_airbox(margin_x=..., margin_y=..., z_above=..., z_below=...). Note that get_stack(air_above=...) is deprecated and ignored.[0m
[34m04:40:11[0m | [33m[22mWARNING [0m | [33m[22mAbsorbing boundary is applied to solid dielectric surfaces (sin, sio2) because the model has no air region, which produces large spurious insertion loss and reflection. Add an airbox via set_airbox(margin_x=..., margin_y=..., z_above=..., z_below=...). Note that get_stack(air_above=...) is deprecated and ignored.[0m
[34m04:40:13[0m | [33m[22mWARNING [0m | [33m[22mAbsorbing boundary is applied to solid dielectric surfaces (sin, sio2) because the model has no air region, which produces large spurious insertion loss and reflection. Add an airbox via set_airbox(margin_x=..., margin_y=..., z_above=..., z_below=...). Note that get_stack(air_above=...) is deprecated and ignored.[0m
Configured 5 simulations
Upload and start all jobs (non-blocking)
# Upload and start all jobs without waiting
job_ids = []
for sim in sims:
job_id = sim.run(wait=False, check_cache=True)
job_ids.append(job_id)
print(f"Started {len(job_ids)} jobs: {job_ids}")
Info : Reading 'palace-sim-w2.0/palace.msh'...
Info : 5322 nodes
Info : 35421 elements
Info : Done reading 'palace-sim-w2.0/palace.msh'
Info : Reading 'palace-sim-w2.0/palace.msh'...
Info : 5322 nodes
Info : 35421 elements
Info : Done reading 'palace-sim-w2.0/palace.msh'
Cache hit: reusing job 01a01b3e-37cb-7052-b0fc-163c9c15866d
Info : Reading 'palace-sim-w6.0/palace.msh'...
Info : 5722 nodes
Info : 38443 elements
Info : Done reading 'palace-sim-w6.0/palace.msh'
Info : Reading 'palace-sim-w6.0/palace.msh'...
Info : 5722 nodes
Info : 38443 elements
Info : Done reading 'palace-sim-w6.0/palace.msh'
Cache hit: reusing job 01a01b3e-3efd-78c2-89ae-b777da8732ec
Info : Reading 'palace-sim-w10.0/palace.msh'...
Info : 5796 nodes
Info : 39011 elements
Info : Done reading 'palace-sim-w10.0/palace.msh'
Info : Reading 'palace-sim-w10.0/palace.msh'...
Info : 5796 nodes
Info : 39011 elements
Info : Done reading 'palace-sim-w10.0/palace.msh'
Cache hit: reusing job 01a01b3e-4606-7d22-9b1a-51a3cf658603
Info : Reading 'palace-sim-w14.0/palace.msh'...
Info : 5784 nodes
Info : 38804 elements
Info : Done reading 'palace-sim-w14.0/palace.msh'
Info : Reading 'palace-sim-w14.0/palace.msh'...
Info : 5784 nodes
Info : 38804 elements
Info : Done reading 'palace-sim-w14.0/palace.msh'
Cache hit: reusing job 01a01b3e-4e32-71f3-8b23-f89c2e07778a
Info : Reading 'palace-sim-w18.0/palace.msh'...
Info : 6115 nodes
Info : 41268 elements
Info : Done reading 'palace-sim-w18.0/palace.msh'
Info : Reading 'palace-sim-w18.0/palace.msh'...
Info : 6115 nodes
Info : 41268 elements
Info : Done reading 'palace-sim-w18.0/palace.msh'
Cache hit: reusing job 01a01b3e-5593-7f61-9433-7bebb174223c
Started 5 jobs: ['01a01b3e-37cb-7052-b0fc-163c9c15866d', '01a01b3e-3efd-78c2-89ae-b777da8732ec', '01a01b3e-4606-7d22-9b1a-51a3cf658603', '01a01b3e-4e32-71f3-8b23-f89c2e07778a', '01a01b3e-5593-7f61-9433-7bebb174223c']
Wait for all jobs to complete
import gsim
# Poll all jobs concurrently, download and parse results
results = gsim.wait_for_results(job_ids)
Waiting for 5 jobs...
palace-3eda85cc completed 0m 00s
palace-b33058c6 completed 0m 00s
palace-a84f6542 completed 0m 00s
palace-3e5d40b1 completed 0m 00s
palace-ba58c8f8 completed 0m 00s
Extracting results.tar.gz...
Downloaded 11 files to /home/runner/work/simulation-templates/simulation-templates/docs/notebooks/sim-data-palace-3eda85cc
Extracting results.tar.gz...
Downloaded 11 files to /home/runner/work/simulation-templates/simulation-templates/docs/notebooks/sim-data-palace-b33058c6
Extracting results.tar.gz...
Downloaded 11 files to /home/runner/work/simulation-templates/simulation-templates/docs/notebooks/sim-data-palace-a84f6542
Extracting results.tar.gz...
Downloaded 11 files to /home/runner/work/simulation-templates/simulation-templates/docs/notebooks/sim-data-palace-3e5d40b1
Extracting results.tar.gz...
Downloaded 11 files to /home/runner/work/simulation-templates/simulation-templates/docs/notebooks/sim-data-palace-ba58c8f8
Plot S11 and S21 comparison
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6, 6))
for w, sp in zip(widths, results, strict=True):
ax1.plot(sp.freq, sp.s11.db, label=f"w={w:.1f} um")
ax2.plot(sp.freq, sp.s21.db, label=f"w={w:.1f} um")
ax1.set(
xlabel="Frequency (GHz)", ylabel="|S11| (dB)", title="S11 — Return Loss vs Width"
)
ax1.legend()
ax1.grid(True)
ax2.set(
xlabel="Frequency (GHz)", ylabel="|S21| (dB)", title="S21 — Insertion Loss vs Width"
)
ax2.legend()
ax2.grid(True)
plt.tight_layout()
