Skip to content

Commit 16e3dec

Browse files
authored
Improve support for wave port boundaries in a SolidModel (#111)
* Add capability to render 1D line segments in SolidModel * WIP tests adding wave ports to SolidModel * WIP tests adding wave ports to SolidModel * Fix formatting * Remove comments * Add boolean to control wave vs lumped ports * Improve waveport specification * WIP single indexable waveport layer * Add wave ports by automatically detecting path intersections with simulation area * Add test and improve wave port intersection handling * Update CHANGELOG and comments * Update CHANGELOG * Remove unused line segment methods and improve test case * Fix formatting * Split SolidModel wave port test into two tests for single and flip chip * Address PR feedback and add warning when wave port path/boundary are not perpendicular
1 parent 820b0ef commit 16e3dec

File tree

11 files changed

+771
-59
lines changed

11 files changed

+771
-59
lines changed

CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ The format of this changelog is based on
77
## Upcoming
88

99
- Added `xor2d` for polygon XOR
10+
- Improved support for wave port boundaries in a `SolidModel`
11+
12+
+ `SolidModelTargets` now take `wave_port_layers`, a list of layer symbols used to define wave port boundary conditions
13+
+ Added support for `LineSegment` in SolidModel
14+
+ Added `add_wave_ports!` to automatically place wave port boundaries where specified paths/routes intersect the simulation area
15+
+ Added option to use wave ports instead of lumped ports in the single transmon example
1016

1117
## 1.6.0 (2025-10-16)
1218

1319
- Improved metadata handling for `LayoutTarget` and `SolidModelTarget`
14-
20+
1521
+ SolidModelTargets will now ignore `NORENDER_META` (the `:norender` layer)
1622
+ SolidModelTargets now take `ignored_layers`, a list of layer symbols which are not rendered
1723
+ LayoutTargets now allow overriding the mapping of `GDSMeta` by setting `target.map_meta_dict[my_gdsmeta] = my_override`, allowing changes to different `GDSMeta` or `nothing` rather than always mapping a `GDSMeta` to itself
@@ -29,7 +35,7 @@ The format of this changelog is based on
2935
## 1.5.0 (2025-10-10)
3036

3137
- Added `auto_speed`, `endpoints_curvature`, and `auto_curvature` keyword options to `bspline!` and `BSplineRouting`
32-
38+
3339
+ `auto_speed` sets the speed at endpoints to avoid sharp bends (minimizing the integrated square of the curvature derivative with respect to arclength)
3440
+ `endpoints_curvature` sets boundary conditions on the curvature (by inserting extra waypoints)
3541
+ `auto_curvature` B-spline sets curvature at endpoints to match previous segment (or to zero if there is no previous segment)

examples/SingleTransmon/SingleTransmon.jl

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module SingleTransmon
55
using FileIO, CSV, DataFrames, JSON, JSONSchema
66
using DeviceLayout, DeviceLayout.SchematicDrivenLayout, DeviceLayout.PreferredUnits
77
import .SchematicDrivenLayout.ExamplePDK
8-
import .SchematicDrivenLayout.ExamplePDK: LayerVocabulary, L1_TARGET, add_bridges!
8+
import .SchematicDrivenLayout.ExamplePDK: LayerVocabulary, L1_TARGET, add_bridges!, add_wave_ports!
99
using .ExamplePDK.Transmons, .ExamplePDK.ReadoutResonators
1010
import .ExamplePDK.SimpleJunctions: ExampleSimpleJunction
1111
import DeviceLayout: uconvert
@@ -24,6 +24,7 @@ using PRIMA
2424
n_meander_turns=5,
2525
hanger_length=500μm,
2626
bend_radius=50μm,
27+
wave_ports::Bool=false,
2728
save_mesh::Bool=false,
2829
save_gds::Bool=false)
2930
@@ -41,6 +42,7 @@ function single_transmon(;
4142
n_meander_turns=5,
4243
hanger_length=500μm,
4344
bend_radius=50μm,
45+
wave_ports::Bool=false,
4446
save_mesh::Bool=false,
4547
save_gds::Bool=false,
4648
mesh_order=2
@@ -90,7 +92,7 @@ function single_transmon(;
9092
bridge=BRIDGE_STYLE
9193
)
9294
## Readout path
93-
readout_length = 2700μm
95+
readout_length = wave_ports ? 4mm : 2700μm
9496
p_readout = Path(
9597
Point(0μm, 0μm);
9698
α0=π / 2,
@@ -101,15 +103,17 @@ function single_transmon(;
101103
straight!(p_readout, readout_length / 2, PATH_STYLE)
102104

103105
# Readout lumped ports - squares on CPW trace, one at each end
104-
csport = CoordinateSystem(uniquename("port"), nm)
105-
render!(
106-
csport,
107-
only_simulated(centered(Rectangle(cpw_width, cpw_width))),
108-
LayerVocabulary.PORT
109-
)
110-
# Attach with port center `cpw_width` from the end (instead of `cpw_width/2`) to avoid corner effects
111-
attach!(p_readout, sref(csport), cpw_width, i=1) # @ start
112-
attach!(p_readout, sref(csport), readout_length / 2 - cpw_width, i=2) # @ end
106+
if !wave_ports
107+
csport = CoordinateSystem(uniquename("port"), nm)
108+
render!(
109+
csport,
110+
only_simulated(centered(Rectangle(cpw_width, cpw_width))),
111+
LayerVocabulary.PORT
112+
)
113+
# Attach with port center `cpw_width` from the end (instead of `cpw_width/2`) to avoid corner effects
114+
attach!(p_readout, sref(csport), cpw_width, i=1) # @ start
115+
attach!(p_readout, sref(csport), readout_length / 2 - cpw_width, i=2) # @ end
116+
end
113117

114118
#### Build schematic graph
115119
g = SchematicGraph("single-transmon")
@@ -130,7 +134,7 @@ function single_transmon(;
130134

131135
#### Prepare solid model
132136
# Specify the extent of the simulation domain.
133-
substrate_x = 4mm
137+
substrate_x = 4mm # wave port domain boundary needs to touch the readout line
134138
substrate_y = 3.7mm
135139

136140
center_xyz = DeviceLayout.center(floorplan)
@@ -144,10 +148,17 @@ function single_transmon(;
144148
# Define rectangle that gets extruded to generate substrate volume
145149
render!(floorplan.coordinate_system, chip, LayerVocabulary.CHIP_AREA)
146150

151+
# Add wave ports
152+
wave_ports && add_wave_ports!(floorplan, [p_readout_node], sim_area, 0.6mm, LayerVocabulary.WAVE_PORT)
153+
147154
check!(floorplan)
148155

149156
# Need to pass generated physical group names so they can be retained
150-
tech = ExamplePDK.singlechip_solidmodel_target("port_1", "port_2", "lumped_element")
157+
if wave_ports
158+
tech = ExamplePDK.singlechip_solidmodel_target("wave_port_1", "wave_port_2", "lumped_element")
159+
else
160+
tech = ExamplePDK.singlechip_solidmodel_target("port_1", "port_2", "lumped_element")
161+
end
151162
sm = SolidModel("test", overwrite=true)
152163

153164
# Adjust mesh_scale to increase the resolution of the mesh, < 1 will result in greater
@@ -178,7 +189,7 @@ function single_transmon(;
178189
end
179190

180191
"""
181-
configfile(sm::SolidModel; palace_build=nothing, solver_order=2, amr=0)
192+
configfile(sm::SolidModel; palace_build=nothing, solver_order=2, amr=0, wave_ports=false)
182193
183194
Given a `SolidModel`, assemble a dictionary defining a configuration file for use within
184195
Palace.
@@ -190,7 +201,7 @@ Palace.
190201
high-order spaces.
191202
- `amr = 0`: Maximum number of adaptive mesh refinement (AMR) iterations.
192203
"""
193-
function configfile(sm::SolidModel; palace_build=nothing, solver_order=2, amr=0)
204+
function configfile(sm::SolidModel; palace_build=nothing, solver_order=2, amr=0, wave_ports=false)
194205
attributes = SolidModels.attributes(sm)
195206

196207
config = Dict(
@@ -234,19 +245,37 @@ function configfile(sm::SolidModel; palace_build=nothing, solver_order=2, amr=0)
234245
"Attributes" => [attributes["exterior_boundary"]],
235246
"Order" => 1
236247
),
248+
(wave_ports ?
249+
(
250+
"WavePort" => [
251+
Dict(
252+
"Index" => 1,
253+
"Attributes" => [attributes["wave_port_1"]]
254+
),
255+
Dict(
256+
"Index" => 2,
257+
"Attributes" => [attributes["wave_port_2"]]
258+
),],
259+
)
260+
: ()
261+
)...,
237262
"LumpedPort" => [
238-
Dict(
239-
"Index" => 1,
240-
"Attributes" => [attributes["port_1"]],
241-
"R" => 50,
242-
"Direction" => "+X"
243-
),
244-
Dict(
245-
"Index" => 2,
246-
"Attributes" => [attributes["port_2"]],
247-
"R" => 50,
248-
"Direction" => "+X"
249-
),
263+
(wave_ports ? () :
264+
(
265+
Dict(
266+
"Index" => 1,
267+
"Attributes" => [attributes["port_1"]],
268+
"R" => 50,
269+
"Direction" => "+X"
270+
),
271+
Dict(
272+
"Index" => 2,
273+
"Attributes" => [attributes["port_2"]],
274+
"R" => 50,
275+
"Direction" => "+X"
276+
),
277+
)
278+
)...,
250279
Dict(
251280
"Index" => 3,
252281
"Attributes" => [attributes["lumped_element"]],
@@ -258,7 +287,7 @@ function configfile(sm::SolidModel; palace_build=nothing, solver_order=2, amr=0)
258287
),
259288
"Solver" => Dict(
260289
"Order" => solver_order,
261-
"Eigenmode" => Dict("N" => 2, "Tol" => 1.0e-6, "Target" => 1, "Save" => 2),
290+
"Eigenmode" => Dict("N" => 2, "Tol" => 1.0e-6, "Target" => 2.5, "Save" => 2),
262291
"Linear" => Dict("Type" => "Default", "Tol" => 1.0e-7, "MaxIts" => 500)
263292
)
264293
)

src/DeviceLayout.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ include("polygons.jl")
365365
import .Polygons:
366366
Polygon,
367367
Ellipse,
368+
LineSegment,
368369
Circle,
369370
ClippedPolygon,
370371
RelativeRounded,
@@ -390,6 +391,7 @@ import .Polygons:
390391
export Polygons,
391392
Polygon,
392393
Ellipse,
394+
LineSegment,
393395
Circle,
394396
ClippedPolygon,
395397
RelativeRounded,

src/polygons.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ import IntervalTrees: IntervalTree, IntervalValue
6363
import IntervalSets.(..)
6464
import IntervalSets.endpoints
6565

66-
export Polygon, ClippedPolygon, Ellipse, Circle
66+
export Polygon, ClippedPolygon, Ellipse, Circle, LineSegment
6767
export circle,
6868
circle_polygon,
6969
clip,
@@ -1517,12 +1517,16 @@ end
15171517

15181518
### cutting algorithm
15191519

1520-
abstract type D1{T} end
1520+
abstract type D1{T} <: GeometryEntity{T} end
15211521
Δy(d1::D1) = d1.p1.y - d1.p0.y
15221522
Δx(d1::D1) = d1.p1.x - d1.p0.x
15231523

15241524
ab(p0, p1) = Point(gety(p1) - gety(p0), getx(p0) - getx(p1))
15251525

1526+
to_polygons(::D1{T}) where {T} = Polygon{T}[]
1527+
1528+
transform(d1::T, f::Transformation) where {T <: D1} = T(f(d1.p0), f(d1.p1))
1529+
15261530
"""
15271531
LineSegment{T} <: D1{T}
15281532

src/schematics/ExamplePDK/ExamplePDK.jl

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ const LAYER_RECORD = (;
6767
simulated_area = GDSMeta(200, 0),
6868
port = GDSMeta(210, 0),
6969
lumped_element = GDSMeta(211, 0),
70+
wave_port = GDSMeta(212, 0),
7071
mesh_control = GDSMeta(220, 0),
7172
integration = GDSMeta(230, 0)
7273
)
@@ -104,10 +105,14 @@ const EXAMPLE_FLIPCHIP_TECHNOLOGY = ProcessTechnology(
104105
(; # Use unrealistic thicknesses for the sake of clearer visualizations
105106
chip_thicknesses=[100μm, 100μm], # [Bottom chip, top chip] (for calculating z height by level)
106107
flipchip_gaps=[80μm], # Space between chip surfaces (for calculating z height by level)
107-
height=(; simulated_area=-1mm), # z height at the bottom of simulation volume
108+
height=(; # z height at the bottom
109+
simulated_area=-1mm,
110+
wave_port=[-160μm, -160μm]
111+
),
108112
thickness=(; # Extrusion distances for various layers
109113
simulated_area=2mm,
110114
chip_area=[100μm, 100μm], # For levelwise layers, specify thickness for each level
115+
wave_port=[400μm, 400μm],
111116
bump=80μm
112117
)
113118
)
@@ -121,8 +126,15 @@ single chip assembly.
121126
const EXAMPLE_SINGLECHIP_TECHNOLOGY = ProcessTechnology(
122127
LAYER_RECORD,
123128
(;
124-
height=(; simulated_area=-1mm), # z height at the bottom of simulation volume
125-
thickness=(; simulated_area=2mm, chip_area=525μm) # Extrusion distances for various layers
129+
height=(; # z height at the bottom
130+
simulated_area=-1mm,
131+
wave_port=-200μm
132+
),
133+
thickness=(; # Extrusion distances for various layers
134+
simulated_area=2mm,
135+
chip_area=525μm,
136+
wave_port=400μm
137+
)
126138
)
127139
)
128140

@@ -153,7 +165,8 @@ const SINGLECHIP_SOLIDMODEL_TARGET = SolidModelTarget(
153165
simulation=true, # Optional simulation-only geometry entities will be rendered
154166
bounding_layers=[:simulated_area], # SIMULATED_AREA defines the simulation bounds
155167
substrate_layers=[:chip_area], # CHIP_AREA will be extruded downward
156-
indexed_layers=[:port, :lumped_element, :integration], # Automatically index these layers
168+
indexed_layers=[:port, :lumped_element, :integration, :wave_port], # Automatically index these layers
169+
wave_port_layers=[:wave_port], # WAVE_PORT are 1D line segments in x-y to be extruded in z
157170
postrender_ops=[ # Manual definition of operations to run after 2D rendering
158171
( # Get metal ground plane by subtracting negative from writeable area
159172
"metal", # Output group name
@@ -242,9 +255,11 @@ const FLIPCHIP_SOLIDMODEL_TARGET = SolidModelTarget(
242255
:metal_positive,
243256
:chip_area, # and for chip and bridge extrusion in opposite directions by layer
244257
:bridge_base,
245-
:bridge
258+
:bridge,
259+
:wave_port
246260
],
247-
indexed_layers=[:port, :lumped_element, :integration],
261+
indexed_layers=[:port, :lumped_element, :integration, :wave_port],
262+
wave_port_layers=[:wave_port],
248263
postrender_ops=[
249264
( # Get metal ground plane by subtracting negative from writeable area
250265
"metal_L1",

0 commit comments

Comments
 (0)