multiport-interferometer 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multiport_interferometer/__init__.py +7 -0
- multiport_interferometer/bell_class.py +324 -0
- multiport_interferometer/bell_components.py +219 -0
- multiport_interferometer/bell_decomposer.py +213 -0
- multiport_interferometer/bell_models.py +258 -0
- multiport_interferometer/clements_class.py +220 -0
- multiport_interferometer/functions.py +233 -0
- multiport_interferometer/ideal_models.py +77 -0
- multiport_interferometer/sax_models/coupler_50_50_2port.pkl +0 -0
- multiport_interferometer/sax_models/s_arm.pkl +0 -0
- multiport_interferometer/sax_models/s_bend.pkl +0 -0
- multiport_interferometer/sax_models/straight_L10.pkl +0 -0
- multiport_interferometer/sax_models/waveguide_150.pkl +0 -0
- multiport_interferometer-0.0.1.dist-info/METADATA +130 -0
- multiport_interferometer-0.0.1.dist-info/RECORD +18 -0
- multiport_interferometer-0.0.1.dist-info/WHEEL +5 -0
- multiport_interferometer-0.0.1.dist-info/licenses/LICENSE +202 -0
- multiport_interferometer-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
from .functions import sdict_to_matrix, bell_circuit_schematic, decom_to_model
|
|
3
|
+
from .ideal_models import ideal_bell_models, ideal_clements_tbu_model, ideal_mzi, ideal_phaseshifter, ideal_waveguide
|
|
4
|
+
from .bell_decomposer import decompose_bell
|
|
5
|
+
from .bell_class import Bell
|
|
6
|
+
from .clements_class import Clements
|
|
7
|
+
from .bell_models import phase_shifter_model, edge_phaser_model, mzi_model, calibrated_mzi_model, plain_wvgd_model
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Copyright 2026 Carl Abi Nakad
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
'''
|
|
16
|
+
|
|
17
|
+
from . import bell_components
|
|
18
|
+
from . import bell_decomposer
|
|
19
|
+
from . import functions as f
|
|
20
|
+
import sax
|
|
21
|
+
import numpy as np
|
|
22
|
+
try:
|
|
23
|
+
from . import bell_models
|
|
24
|
+
except ImportError:
|
|
25
|
+
bell_models=None
|
|
26
|
+
import gdsfactory as gf
|
|
27
|
+
|
|
28
|
+
class Bell:
|
|
29
|
+
def __init__(self, dimension):
|
|
30
|
+
if not isinstance(dimension, int) or dimension < 2:
|
|
31
|
+
raise ValueError("Dimension must be an integer >= 2")
|
|
32
|
+
self.dimension = dimension
|
|
33
|
+
self.components = self.load_components() #dictionaro of components
|
|
34
|
+
self.gds_component, self.sax_netlist = self.build()
|
|
35
|
+
if bell_models is not None:
|
|
36
|
+
self.models = self.load_models()
|
|
37
|
+
self.phase_offset = bell_models.phase_offset
|
|
38
|
+
self.phase_step = bell_models.phase_step
|
|
39
|
+
else:
|
|
40
|
+
self.models = None
|
|
41
|
+
print("No models.py found. SAX models were not loaded.")
|
|
42
|
+
|
|
43
|
+
def gds(self):
|
|
44
|
+
return self.gds_component
|
|
45
|
+
|
|
46
|
+
def load_components(self):
|
|
47
|
+
return {"tbu":bell_components.tbu,
|
|
48
|
+
"phase_shifter": bell_components.phase_shifter,
|
|
49
|
+
"edge_phase_shifter": bell_components.edge_phase_shifter,
|
|
50
|
+
"waveguide": bell_components.plain_wvgd,
|
|
51
|
+
"edge_waveguide":bell_components.edge_wvgd
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
def load_models(self):
|
|
55
|
+
return {"tbu_model":bell_models.tbu_model,
|
|
56
|
+
"phase_shifter_model": bell_models.phase_shifter_model,
|
|
57
|
+
"edge_phase_shifter_model": bell_models.edge_phase_shifter_model,
|
|
58
|
+
"plain_wvgd_model": bell_models.plain_wvgd_model}
|
|
59
|
+
|
|
60
|
+
def emulate(self, target):
|
|
61
|
+
if np.shape(target)[0] != self.dimension:
|
|
62
|
+
raise ValueError("Target matrix incompatible with Interferometer Dimension")
|
|
63
|
+
decom = bell_decomposer.decompose_bell(target)
|
|
64
|
+
self.values = f.decom_to_model(self.models,decom)
|
|
65
|
+
circuit, info = sax.circuit(netlist=self.sax_netlist, models = self.values)
|
|
66
|
+
S = circuit()
|
|
67
|
+
return S
|
|
68
|
+
def phase_corrected(self, S):
|
|
69
|
+
correction = self.phase_offset + (self.dimension-2)*self.phase_step
|
|
70
|
+
Smat = f.sdict_to_matrix(S)*np.exp(-1j*correction)
|
|
71
|
+
return Smat
|
|
72
|
+
|
|
73
|
+
def build(self):
|
|
74
|
+
bell_components._check_pdk()
|
|
75
|
+
dim = self.dimension
|
|
76
|
+
c = gf.Component()
|
|
77
|
+
mzi_cell = self.components["tbu"]()
|
|
78
|
+
phaser_cell = self.components["phase_shifter"]()
|
|
79
|
+
edge_phaser_cell = self.components["edge_phase_shifter"]()
|
|
80
|
+
plain_wvgd = self.components["waveguide"]()
|
|
81
|
+
edge_wvgd = self.components["edge_waveguide"]()
|
|
82
|
+
|
|
83
|
+
last = dim-2
|
|
84
|
+
end = dim-1
|
|
85
|
+
|
|
86
|
+
instances = {}
|
|
87
|
+
connections = {}
|
|
88
|
+
ports = {}
|
|
89
|
+
|
|
90
|
+
phase_i = [None]*dim
|
|
91
|
+
phase_o = [None]*dim
|
|
92
|
+
phasers = {}
|
|
93
|
+
mzms = {}
|
|
94
|
+
wvgds = {}
|
|
95
|
+
|
|
96
|
+
# creating top boundary connector waveguides
|
|
97
|
+
for j in range(1, dim, 2):
|
|
98
|
+
wvgds[(j)] = c<<plain_wvgd
|
|
99
|
+
instances[f"wvgd_{j}"] = "wvgd"
|
|
100
|
+
|
|
101
|
+
# creating bottom boundary phase shifters
|
|
102
|
+
if dim % 2 == 0:
|
|
103
|
+
for j in range(1, dim, 2):
|
|
104
|
+
phasers[(end, j)] = c << phaser_cell
|
|
105
|
+
instances[f"phaser_{end}_{j}"] = f"phaser_{end}_{j}"
|
|
106
|
+
else:
|
|
107
|
+
for j in range(0, dim, 2):
|
|
108
|
+
phasers[(end, j)] = c << phaser_cell
|
|
109
|
+
instances[f"phaser_{end}_{j}"] = f"phaser_{end}_{j}"
|
|
110
|
+
|
|
111
|
+
# creating input and output edge phase shifters
|
|
112
|
+
if dim%2!=0:
|
|
113
|
+
for i in range(0,dim,2):
|
|
114
|
+
if i>0:
|
|
115
|
+
phaser_o = c << edge_phaser_cell
|
|
116
|
+
phaser_o.mirror_x()
|
|
117
|
+
phase_o[i-1] = phaser_o
|
|
118
|
+
phase_i[i-1] = c<<edge_wvgd
|
|
119
|
+
instances[f"phaser_o{i-1}"] = f"phaser_o{i-1}"
|
|
120
|
+
instances[f"phaser_i{i-1}"] = f"phaser_i{i-1}"
|
|
121
|
+
if i<dim-1:
|
|
122
|
+
phaser_i = c << edge_phaser_cell
|
|
123
|
+
phase_i[i] = phaser_i
|
|
124
|
+
phase_o[i] = c<<edge_wvgd
|
|
125
|
+
instances[f"phaser_i{i}"] = f"phaser_i{i}"
|
|
126
|
+
instances[f"phaser_o{i}"] = f"phaser_o{i}"
|
|
127
|
+
else:
|
|
128
|
+
phase_i[i] = c<<edge_wvgd
|
|
129
|
+
phase_o[i] = c<<edge_wvgd
|
|
130
|
+
instances[f"phaser_i{i}"] = f"phaser_i{i}"
|
|
131
|
+
instances[f"phaser_o{i}"] = f"phaser_o{i}"
|
|
132
|
+
else:
|
|
133
|
+
for i in range(dim):
|
|
134
|
+
if i%2==0:
|
|
135
|
+
if i<dim:
|
|
136
|
+
phaser_i =c<<edge_phaser_cell
|
|
137
|
+
phase_i[i] = phaser_i
|
|
138
|
+
phaser_o = c<<edge_phaser_cell
|
|
139
|
+
phase_o[i] = phaser_o
|
|
140
|
+
instances[f"phaser_i{i}"] = f"phaser_i{i}"
|
|
141
|
+
instances[f"phaser_o{i}"] = f"phaser_o{i}"
|
|
142
|
+
else:
|
|
143
|
+
phase_i[i] = c<<edge_wvgd
|
|
144
|
+
phase_o[i] = c<<edge_wvgd
|
|
145
|
+
instances[f"phaser_i{i}"] = f"phaser_i{i}"
|
|
146
|
+
instances[f"phaser_o{i}"] = f"phaser_o{i}"
|
|
147
|
+
else:
|
|
148
|
+
phase_i[i] = c<<edge_wvgd
|
|
149
|
+
phase_o[i] = c<<edge_wvgd
|
|
150
|
+
instances[f"phaser_i{i}"] = f"phaser_i{i}"
|
|
151
|
+
instances[f"phaser_o{i}"] = f"phaser_o{i}"
|
|
152
|
+
|
|
153
|
+
# creating top and bottom mzis
|
|
154
|
+
for i in range(0, dim, 2):
|
|
155
|
+
# top mode mzis
|
|
156
|
+
mzm_top = c << mzi_cell
|
|
157
|
+
mzms[f"(0_{i})"] = mzm_top
|
|
158
|
+
instances[f"mzm_0_{i}"] = f"mzm_0_{i}"
|
|
159
|
+
if dim % 2 == 0: # even bottom mode mzis
|
|
160
|
+
mzm_bottom = c << mzi_cell
|
|
161
|
+
mzms[f"({last}_{i})"] = mzm_bottom
|
|
162
|
+
instances[f"mzm_{last}_{i}"] = f"mzm_{last}_{i}"
|
|
163
|
+
else: # odd bottom mode mzis
|
|
164
|
+
j = i + 1
|
|
165
|
+
if j > end:
|
|
166
|
+
continue
|
|
167
|
+
mzm_bottom = c << mzi_cell
|
|
168
|
+
mzms[f"({last}_{j})"] = mzm_bottom
|
|
169
|
+
instances[f"mzm_{last}_{j}"] = f"mzm_{last}_{j}"
|
|
170
|
+
|
|
171
|
+
# create middle mzis
|
|
172
|
+
for i in range(1, last):
|
|
173
|
+
if i % 2 == 0:
|
|
174
|
+
for j in np.arange(0, dim, 2):
|
|
175
|
+
mzm = c << mzi_cell
|
|
176
|
+
mzms[f"({i}_{j})"] = mzm
|
|
177
|
+
instances[f"mzm_{i}_{j}"] = f"mzm_{i}_{j}"
|
|
178
|
+
else:
|
|
179
|
+
for j in range(1, dim, 2):
|
|
180
|
+
mzm = c << mzi_cell
|
|
181
|
+
mzms[f"({i}_{j})"] = mzm
|
|
182
|
+
instances[f"mzm_{i}_{j}"] = f"mzm_{i}_{j}"
|
|
183
|
+
|
|
184
|
+
# Connecting Mesh
|
|
185
|
+
for i in range(dim):
|
|
186
|
+
|
|
187
|
+
# Top boundary Connections
|
|
188
|
+
if i == 0:
|
|
189
|
+
if dim % 2 == 0:
|
|
190
|
+
for j in range(0, dim - 2, 2):
|
|
191
|
+
p = wvgds[(j+1)]
|
|
192
|
+
p.connect("o1",mzms[f"({i}_{j})"].ports["o3"])
|
|
193
|
+
mzms[f"({i}_{j+2})"].connect("o2",p.ports["o2"])
|
|
194
|
+
connections[f"mzm_{i}_{j},out0"] = f"wvgd_{j+1},in0"
|
|
195
|
+
connections[f"wvgd_{j+1},out0"] = f"mzm_{i}_{j+2},in0"
|
|
196
|
+
p = wvgds[(end)]
|
|
197
|
+
p.connect("o1",mzms[f"({i}_{dim-2})"].ports["o3"])
|
|
198
|
+
connections[f"mzm_{i}_{dim-2},out0"] =f"wvgd_{end},in0"
|
|
199
|
+
else:
|
|
200
|
+
for j in range(0, dim - 2, 2):
|
|
201
|
+
p = wvgds[(j+1)]
|
|
202
|
+
if j < last:
|
|
203
|
+
p.connect("o1",mzms[f"({i}_{j})"].ports["o3"])
|
|
204
|
+
connections[f"mzm_{i}_{j},out0"] =f"wvgd_{j+1},in0"
|
|
205
|
+
if f"({i}_{j+2})" in mzms:
|
|
206
|
+
mzms[f"({i}_{j+2})"].connect("o2",p.ports["o2"])
|
|
207
|
+
connections[f"wvgd_{j+1},out0"] = f"mzm_{i}_{j+2},in0"
|
|
208
|
+
|
|
209
|
+
# Connecting diagonal mzis
|
|
210
|
+
for j in range(dim):
|
|
211
|
+
key = f"({i}_{j})"
|
|
212
|
+
if key not in mzms:
|
|
213
|
+
continue
|
|
214
|
+
#up-right connection
|
|
215
|
+
if i > 0:
|
|
216
|
+
upper_right = f"({i-1}_{j+1})"
|
|
217
|
+
if upper_right in mzms:
|
|
218
|
+
mzms[key].connect("o3",mzms[upper_right].ports["o1"])
|
|
219
|
+
connections[f"mzm_{i}_{j},out0"] = f"mzm_{i-1}_{j+1},in1"
|
|
220
|
+
# downward-right connection
|
|
221
|
+
target = f"({i+1}_{j+1})"
|
|
222
|
+
if target in mzms:
|
|
223
|
+
mzms[target].connect("o2", mzms[key].ports["o4"])
|
|
224
|
+
connections[f"mzm_{i}_{j},out1"] = f"mzm_{i+1}_{j+1},in0"
|
|
225
|
+
|
|
226
|
+
# bottom boundary connections
|
|
227
|
+
if i == last:
|
|
228
|
+
if dim % 2 == 0:
|
|
229
|
+
for j in range(1, last, 2):
|
|
230
|
+
p = phasers[(end, j)]
|
|
231
|
+
p.connect("o1", mzms[f"({i}_{j-1})"].ports["o4"])
|
|
232
|
+
p.connect("o2", mzms[f"({i}_{j+1})"].ports["o1"])
|
|
233
|
+
connections[f"mzm_{i}_{j-1},out1"] = f"phaser_{end}_{j},in0"
|
|
234
|
+
connections[f"phaser_{end}_{j},out0"] = f"mzm_{i}_{j+1},in1"
|
|
235
|
+
# last bottom phaser
|
|
236
|
+
p = phasers[(end, end)]
|
|
237
|
+
p.connect("o1", mzms[f"({last}_{last})"].ports["o4"])
|
|
238
|
+
connections[f"mzm_{last}_{last},out1"] = f"phaser_{end}_{end},in0"
|
|
239
|
+
else:
|
|
240
|
+
# first bottom phaser
|
|
241
|
+
p = phasers[(end, 0)]
|
|
242
|
+
p.connect("o2", mzms[f"({last}_1)"].ports["o1"])
|
|
243
|
+
connections[f"phaser_{end}_0,out0"] = f"mzm_{last}_1,in1"
|
|
244
|
+
|
|
245
|
+
# last bottom phaser
|
|
246
|
+
p = phasers[(end, end)]
|
|
247
|
+
p.connect("o1",mzms[f"({last}_{last})"].ports["o4"])
|
|
248
|
+
connections[f"mzm_{last}_{last},out1"] = f"phaser_{end}_{end},in0"
|
|
249
|
+
|
|
250
|
+
# intermediate bottom phasers
|
|
251
|
+
for j in range(2, last, 2):
|
|
252
|
+
p = phasers[(end, j)]
|
|
253
|
+
p.connect("o1",mzms[f"({i}_{j-1})"].ports["o4"])
|
|
254
|
+
connections[f"mzm_{i}_{j-1},out1"] = f"phaser_{end}_{j},in0"
|
|
255
|
+
p.connect("o2",mzms[f"({i}_{j+1})"].ports["o1"])
|
|
256
|
+
connections[f"phaser_{end}_{j},out0"] = f"mzm_{i}_{j+1},in1"
|
|
257
|
+
|
|
258
|
+
# connecting input phaseshifters
|
|
259
|
+
for i in range(0, dim - 1, 2):
|
|
260
|
+
phase_i[i].connect("o2",mzms[f"({i}_0)"].ports["o2"])
|
|
261
|
+
connections[f"phaser_i{i},out0"] = f"mzm_{i}_0,in0" #these are sax connections, port naming is swapped with the gds naming because sax component models using standard matrix convention
|
|
262
|
+
phase_i[i+1].connect("o2",mzms[f"({i}_0)"].ports["o1"])
|
|
263
|
+
connections[f"phaser_i{i+1},out0"] = f"mzm_{i}_0,in1"
|
|
264
|
+
c.add_port(f"in{i}",port=phase_i[i].ports["o1"])
|
|
265
|
+
ports[f"in{i}"] = f"phaser_i{i},in0"
|
|
266
|
+
c.add_port(f"in{i+1}",port=phase_i[i+1].ports["o1"])
|
|
267
|
+
ports[f"in{i+1}"] = f"phaser_i{i+1},in0"
|
|
268
|
+
|
|
269
|
+
# odd final input phaseshifter
|
|
270
|
+
if dim % 2 != 0:
|
|
271
|
+
phase_i[end].connect("o2",phasers[(end, 0)].ports["o1"])
|
|
272
|
+
connections[f"phaser_i{end},out0"] = f"phaser_{end}_0,in0"
|
|
273
|
+
c.add_port(f"in{end}",port=phase_i[end].ports["o1"])
|
|
274
|
+
ports[f"in{end}"] = f"phaser_i{end},in0"
|
|
275
|
+
|
|
276
|
+
# Connecting output phase shifters - Even
|
|
277
|
+
if dim % 2 == 0:
|
|
278
|
+
# top mode
|
|
279
|
+
phase_o[0].connect("o2",wvgds[(end)].ports["o2"])
|
|
280
|
+
connections[f"wvgd_{end},out0"] = "phaser_o0,in0"
|
|
281
|
+
c.add_port("out0",port=phase_o[0].ports["o1"])
|
|
282
|
+
ports["out0"] = "phaser_o0,out0"
|
|
283
|
+
|
|
284
|
+
# middle modes
|
|
285
|
+
for i in range(1, end, 2):
|
|
286
|
+
phase_o[i].connect("o2",mzms[f"({i}_{end})"].ports["o3"])
|
|
287
|
+
phase_o[i+1].connect("o2",mzms[f"({i}_{end})"].ports["o4"])
|
|
288
|
+
connections[f"mzm_{i}_{end},out0"] = f"phaser_o{i},in0"
|
|
289
|
+
connections[f"mzm_{i}_{end},out1"] = f"phaser_o{i+1},in0"
|
|
290
|
+
c.add_port(f"out{i}",port=phase_o[i].ports["o1"])
|
|
291
|
+
c.add_port(f"out{i+1}",port=phase_o[i+1].ports["o1"])
|
|
292
|
+
ports[f"out{i}"] = f"phaser_o{i},out0"
|
|
293
|
+
ports[f"out{i+1}"] = f"phaser_o{i+1},out0"
|
|
294
|
+
|
|
295
|
+
# bottom mode
|
|
296
|
+
phase_o[end].connect("o2", phasers[(end, end)].ports["o2"])
|
|
297
|
+
connections[f"phaser_{end}_{end},out0"] = f"phaser_o{end},in0"
|
|
298
|
+
c.add_port(f"out{end}", port=phase_o[end].ports["o1"])
|
|
299
|
+
ports[f"out{end}"] = f"phaser_o{end},out0"
|
|
300
|
+
|
|
301
|
+
# Connecting output phase shifters - Even
|
|
302
|
+
else:
|
|
303
|
+
output_col = end
|
|
304
|
+
for i in range(0, dim - 1, 2):
|
|
305
|
+
phase_o[i].connect("o2", mzms[f"({i}_{output_col})"].ports["o3"])
|
|
306
|
+
connections[f"mzm_{i}_{output_col},out0"] = f"phaser_o{i},in0"
|
|
307
|
+
phase_o[i+1].connect("o2", mzms[f"({i}_{output_col})"].ports["o4"])
|
|
308
|
+
connections[f"mzm_{i}_{output_col},out1"] = f"phaser_o{i+1},in0"
|
|
309
|
+
c.add_port(f"out{i}", port=phase_o[i].ports["o1"])
|
|
310
|
+
ports[f"out{i}"] = f"phaser_o{i},out0"
|
|
311
|
+
c.add_port(f"out{i+1}", port=phase_o[i+1].ports["o1"])
|
|
312
|
+
ports[f"out{i+1}"] = f"phaser_o{i+1},out0"
|
|
313
|
+
|
|
314
|
+
# bottom mode
|
|
315
|
+
phase_o[end].connect("o2", phasers[(end, end)].ports["o2"])
|
|
316
|
+
connections[f"phaser_{end}_{end},out0"] = f"phaser_o{end},in0"
|
|
317
|
+
c.add_port(f"out{end}", port=phase_o[end].ports["o1"])
|
|
318
|
+
ports[f"out{end}"] = f"phaser_o{end},out0"
|
|
319
|
+
|
|
320
|
+
netlist = {}
|
|
321
|
+
netlist["instances"]=instances
|
|
322
|
+
netlist["connections"]=connections
|
|
323
|
+
netlist["ports"]=ports
|
|
324
|
+
return c, netlist
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
'''
|
|
2
|
+
File of my custom gdsfactory component functions to be used in the project
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
import gdsfactory as gf
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
8
|
+
# I use the Si 220 cband pdk from cornerstone
|
|
9
|
+
from cspdk.si220.cband import cells, PDK, LAYER_STACK
|
|
10
|
+
|
|
11
|
+
def activate_pdk():
|
|
12
|
+
PDK.activate()
|
|
13
|
+
|
|
14
|
+
def _check_pdk():
|
|
15
|
+
if gf.get_active_pdk().name != PDK.name:
|
|
16
|
+
raise RuntimeError("Activate the Cornerstone Si220 C-band PDK first.")
|
|
17
|
+
|
|
18
|
+
'''
|
|
19
|
+
The circuit requires the 4 component names:
|
|
20
|
+
'''
|
|
21
|
+
def tbu():
|
|
22
|
+
return mzi()
|
|
23
|
+
def phase_shifter():
|
|
24
|
+
return phaser()
|
|
25
|
+
def edge_phase_shifter():
|
|
26
|
+
return edge_phaser()
|
|
27
|
+
def plain_wvgd():
|
|
28
|
+
return plain()
|
|
29
|
+
#def edge_wvgd(): already exists with correct name
|
|
30
|
+
|
|
31
|
+
'''
|
|
32
|
+
the actual gdscomponents:
|
|
33
|
+
'''
|
|
34
|
+
|
|
35
|
+
phaser_len = 150
|
|
36
|
+
|
|
37
|
+
@gf.cell
|
|
38
|
+
def s_bend():
|
|
39
|
+
c = gf.Component()
|
|
40
|
+
bend = cells.bend_euler()
|
|
41
|
+
up = c << bend
|
|
42
|
+
down = c<< bend
|
|
43
|
+
up.rotate(90)
|
|
44
|
+
down.rotate(-90)
|
|
45
|
+
down.connect("o1", up.ports["o1"])
|
|
46
|
+
c.add_port("o1", port=up.ports["o2"])
|
|
47
|
+
c.add_port("o2", port = down.ports["o2"])
|
|
48
|
+
return c
|
|
49
|
+
|
|
50
|
+
@gf.cell
|
|
51
|
+
def mzi():
|
|
52
|
+
c = gf.Component()
|
|
53
|
+
coupler = cells.coupler()
|
|
54
|
+
phaser = cells.straight_heater_metal(length = phaser_len)
|
|
55
|
+
bend = s_bend()
|
|
56
|
+
i_up = c<<bend
|
|
57
|
+
i_down = c<<bend
|
|
58
|
+
i_down.mirror()
|
|
59
|
+
o_up = c << bend
|
|
60
|
+
o_down = c<<bend
|
|
61
|
+
o_up.mirror()
|
|
62
|
+
top = c<<phaser
|
|
63
|
+
bottom = c<<phaser
|
|
64
|
+
top_left= c<<bend
|
|
65
|
+
top_left.mirror()
|
|
66
|
+
top_right = c<<bend
|
|
67
|
+
bottom_left = c<<bend
|
|
68
|
+
bottom_right = c<<bend
|
|
69
|
+
bottom_right.mirror()
|
|
70
|
+
coupler1 = c<<coupler
|
|
71
|
+
coupler2 = c<<coupler
|
|
72
|
+
i_down.connect("o1", coupler1.ports["o1"])
|
|
73
|
+
i_up.connect("o1", coupler1.ports["o2"])
|
|
74
|
+
top_left.connect("o1", coupler1.ports["o3"])
|
|
75
|
+
bottom_left.connect("o1", coupler1.ports["o4"])
|
|
76
|
+
top.connect("o1", top_left["o2"])
|
|
77
|
+
bottom.connect("o1", bottom_left["o2"])
|
|
78
|
+
top_right.connect("o1", top['o2'])
|
|
79
|
+
bottom_right.connect("o1", bottom['o2'])
|
|
80
|
+
coupler2.connect("o1", bottom_right["o2"])
|
|
81
|
+
coupler2.connect("o2", top_right["o2"])
|
|
82
|
+
o_up.connect('o1', coupler2["o3"])
|
|
83
|
+
o_down.connect('o1', coupler2["o4"])
|
|
84
|
+
c.add_port(name="o1", port=i_down['o2'])
|
|
85
|
+
c.add_port(name = 'o2', port=i_up['o2'])
|
|
86
|
+
c.add_port(name = 'o3', port=o_up['o2'])
|
|
87
|
+
c.add_port(name = 'o4', port=o_down['o2'])
|
|
88
|
+
return c
|
|
89
|
+
|
|
90
|
+
@gf.cell
|
|
91
|
+
def edge_phaser(phase_len=phaser_len):
|
|
92
|
+
c = gf.Component()
|
|
93
|
+
wvgd = cells.straight(cross_section="strip")
|
|
94
|
+
phaser = cells.straight_heater_metal(length= phase_len)
|
|
95
|
+
w = c<<wvgd
|
|
96
|
+
p = c<<phaser
|
|
97
|
+
p.connect("o1", w["o2"])
|
|
98
|
+
c.add_port(name="o1", port = w["o1"])
|
|
99
|
+
c.add_port(name = "o2", port=p["o2"])
|
|
100
|
+
return c
|
|
101
|
+
|
|
102
|
+
@gf.cell
|
|
103
|
+
def edge_wvgd(phase_len=phaser_len):
|
|
104
|
+
c = gf.Component()
|
|
105
|
+
wvgd1 = cells.straight(cross_section="strip")
|
|
106
|
+
wvgd2 = cells.straight(length= phase_len)
|
|
107
|
+
w1 = c<<wvgd1
|
|
108
|
+
w2 = c<<wvgd2
|
|
109
|
+
w2.connect("o1", w1["o2"])
|
|
110
|
+
c.add_port(name="o1", port = w1["o1"])
|
|
111
|
+
c.add_port(name = "o2", port=w2["o2"])
|
|
112
|
+
return c
|
|
113
|
+
|
|
114
|
+
def coupler_single_arm(keep="top"):
|
|
115
|
+
c = gf.Component(f"coupler_single_arm_{keep}_{uuid.uuid4().hex[:6]}")
|
|
116
|
+
|
|
117
|
+
cp = cells.coupler()
|
|
118
|
+
cp = cp.copy()
|
|
119
|
+
cp.flatten()
|
|
120
|
+
|
|
121
|
+
WG_LAYER = gf.get_active_pdk().layers.WG
|
|
122
|
+
wg = cp.extract(layers=[WG_LAYER])
|
|
123
|
+
|
|
124
|
+
bbox = wg.bbox()
|
|
125
|
+
xmin = bbox.left
|
|
126
|
+
xmax = bbox.right
|
|
127
|
+
ymin = bbox.bottom
|
|
128
|
+
ymax = bbox.top
|
|
129
|
+
ymid = (ymin + ymax) / 2
|
|
130
|
+
|
|
131
|
+
if keep == "top":
|
|
132
|
+
y0 = ymid
|
|
133
|
+
height = ymax - ymid
|
|
134
|
+
else:
|
|
135
|
+
y0 = ymin
|
|
136
|
+
height = ymid - ymin
|
|
137
|
+
|
|
138
|
+
clip = gf.components.rectangle(
|
|
139
|
+
size=(xmax - xmin, height),
|
|
140
|
+
layer=WG_LAYER,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
clip_component = gf.Component(f"clip_{uuid.uuid4().hex[:6]}")
|
|
144
|
+
r = clip_component << clip
|
|
145
|
+
r.move((xmin, y0))
|
|
146
|
+
|
|
147
|
+
arm = gf.boolean(
|
|
148
|
+
A=wg,
|
|
149
|
+
B=clip_component,
|
|
150
|
+
operation="and",
|
|
151
|
+
layer=WG_LAYER,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
c << arm
|
|
155
|
+
|
|
156
|
+
cp_original = cells.coupler()
|
|
157
|
+
cp_ports = list(cp_original.ports)
|
|
158
|
+
|
|
159
|
+
if keep == "top":
|
|
160
|
+
selected_ports = [p for p in cp_ports if p.center[1] > ymid]
|
|
161
|
+
else:
|
|
162
|
+
selected_ports = [p for p in cp_ports if p.center[1] < ymid]
|
|
163
|
+
|
|
164
|
+
selected_ports = sorted(selected_ports, key=lambda p: p.center[0])
|
|
165
|
+
|
|
166
|
+
c.add_port(name="o1", port=selected_ports[0])
|
|
167
|
+
c.add_port(name="o2", port=selected_ports[1])
|
|
168
|
+
|
|
169
|
+
return c
|
|
170
|
+
|
|
171
|
+
@gf.cell
|
|
172
|
+
def phaser(phase_len=phaser_len):
|
|
173
|
+
c = gf.Component()
|
|
174
|
+
bottom = coupler_single_arm("bottom")
|
|
175
|
+
bend = s_bend()
|
|
176
|
+
phase_shift = cells.straight_heater_metal(length =phase_len)
|
|
177
|
+
i = c<<bend
|
|
178
|
+
o = c<<bend
|
|
179
|
+
o.mirror()
|
|
180
|
+
left = c<<bend
|
|
181
|
+
left.mirror()
|
|
182
|
+
right = c<<bend
|
|
183
|
+
s1 = c<<bottom
|
|
184
|
+
s2 = c<<bottom
|
|
185
|
+
phaser = c<<phase_shift
|
|
186
|
+
s1.connect("o1", i["o2"])
|
|
187
|
+
left.connect("o1", s1['o2'])
|
|
188
|
+
phaser.connect("o1", left['o2'])
|
|
189
|
+
right.connect('o1', phaser['o2'])
|
|
190
|
+
s2.connect('o1', right['o2'])
|
|
191
|
+
o.connect('o1', s2['o2'])
|
|
192
|
+
c.add_port(name='o1', port=i['o1'])
|
|
193
|
+
c.add_port(name = 'o2', port=o['o2'])
|
|
194
|
+
return c
|
|
195
|
+
|
|
196
|
+
@gf.cell
|
|
197
|
+
def plain(lent=phaser_len):
|
|
198
|
+
c = gf.Component()
|
|
199
|
+
bottom = coupler_single_arm("bottom")
|
|
200
|
+
bend = s_bend()
|
|
201
|
+
straight = cells.straight(length =lent)
|
|
202
|
+
i = c<<bend
|
|
203
|
+
o = c<<bend
|
|
204
|
+
o.mirror()
|
|
205
|
+
left = c<<bend
|
|
206
|
+
left.mirror()
|
|
207
|
+
right = c<<bend
|
|
208
|
+
s1 = c<<bottom
|
|
209
|
+
s2 = c<<bottom
|
|
210
|
+
wvgd = c<<straight
|
|
211
|
+
s1.connect("o1", i["o2"])
|
|
212
|
+
left.connect("o1", s1['o2'])
|
|
213
|
+
wvgd.connect("o1", left['o2'])
|
|
214
|
+
right.connect('o1', wvgd['o2'])
|
|
215
|
+
s2.connect('o1', right['o2'])
|
|
216
|
+
o.connect('o1', s2['o2'])
|
|
217
|
+
c.add_port(name='o1', port=i['o1'])
|
|
218
|
+
c.add_port(name = 'o2', port=o['o2'])
|
|
219
|
+
return c
|