samplemaker-sparrow 5.4.4__cp313-cp313-win_arm64.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.
- samplemaker/__init__.py +43 -0
- samplemaker/baselib/__init__.py +5 -0
- samplemaker/baselib/devices.py +161 -0
- samplemaker/baselib/waveguides.py +320 -0
- samplemaker/devices.py +1403 -0
- samplemaker/documentation.md +65 -0
- samplemaker/gdsreader.py +252 -0
- samplemaker/gdswriter.py +368 -0
- samplemaker/layout.py +1118 -0
- samplemaker/makers.py +586 -0
- samplemaker/phc.py +484 -0
- samplemaker/resources/__init__.py +7 -0
- samplemaker/resources/boopy.cp313-win_arm64.pyd +0 -0
- samplemaker/resources/boopy.pyi +20 -0
- samplemaker/resources/sm_stencil_font.txt +747 -0
- samplemaker/routers.py +340 -0
- samplemaker/sequencer.py +306 -0
- samplemaker/shapes.py +2265 -0
- samplemaker/viewers.py +235 -0
- samplemaker_sparrow-5.4.4.dist-info/METADATA +40 -0
- samplemaker_sparrow-5.4.4.dist-info/RECORD +23 -0
- samplemaker_sparrow-5.4.4.dist-info/WHEEL +5 -0
- samplemaker_sparrow-5.4.4.dist-info/licenses/LICENSE.md +11 -0
samplemaker/__init__.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
This is the Python version of Sample Maker, a scripting tool for designing
|
|
4
|
+
lithographic masks in the GDSII format. Package `samplemaker` comes
|
|
5
|
+
with different tools and submodules for the creation and manipulation of basic
|
|
6
|
+
shapes, periodic shapes, sequences (e.g. waveguides), circuits, and complex
|
|
7
|
+
devices.
|
|
8
|
+
|
|
9
|
+
The code has been developed primarily for nanophotonics, but it can be easily
|
|
10
|
+
extended to different applications in micro and nano device fabrication.
|
|
11
|
+
|
|
12
|
+
Sample Maker is developed and maintained by Leonardo Midolo (Niels Bohr Institute,
|
|
13
|
+
University of Copenhagen). It is based on the MATLAB(R) code developed by Leonardo Midolo
|
|
14
|
+
between 2013 and 2019. The first version of the rewritten Python code has been released in October 2021.
|
|
15
|
+
|
|
16
|
+
This software has been realized with the financial support from
|
|
17
|
+
the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (Grant agreement No. 949043, NANOMEQ).
|
|
18
|
+
|
|
19
|
+
.. include:: ./documentation.md
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from typing import ( # noqa: F401
|
|
23
|
+
cast, Any, Callable, Dict, Generator, Iterable, List, Mapping, NewType,
|
|
24
|
+
Optional, Set, Tuple, Type, TypeVar, Union,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__version__ = "5.4.3"
|
|
28
|
+
|
|
29
|
+
__pdoc__: Dict[str, Union[bool, str]] = {}
|
|
30
|
+
__pdoc__["samplemaker.Tutorials"]=False
|
|
31
|
+
__pdoc__["samplemaker.tests"]=False
|
|
32
|
+
__pdoc__["samplemaker.resources"]=False
|
|
33
|
+
__pdoc__["samplemaker.gdsreader"]=False
|
|
34
|
+
__pdoc__["samplemaker.devices.DevicePort"]=False
|
|
35
|
+
|
|
36
|
+
# The LayoutPool contains all the current layout, this class should generally not
|
|
37
|
+
# be used directly, but only through the Mask class.
|
|
38
|
+
LayoutPool = dict() # connects a SREF name to a particular geomgroup in the current memory
|
|
39
|
+
# Additional cache pool
|
|
40
|
+
_DevicePool = dict() # connects a device hash to a SREF to be instantiated
|
|
41
|
+
_DeviceLocalParamPool = dict() # connects a device hash to local parameters created by the call to geom()
|
|
42
|
+
_DeviceCountPool = dict() # connects a device name to a device count
|
|
43
|
+
_BoundingBoxPool = dict() # connects a SREF name to its bounding box
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Base device library.
|
|
4
|
+
|
|
5
|
+
This is a collection of some simple demo devices distributed with the base
|
|
6
|
+
version of `samplemaker`. It can be used as template for creating new libraries
|
|
7
|
+
or to learn how to design them.
|
|
8
|
+
|
|
9
|
+
Note that individual device methods are not documented but should be readable
|
|
10
|
+
and self-explanatory.
|
|
11
|
+
|
|
12
|
+
The library provides the following devices:
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
import math
|
|
18
|
+
from samplemaker.devices import Device, registerDevicesInModule
|
|
19
|
+
import samplemaker.makers as sm
|
|
20
|
+
from samplemaker.baselib.waveguides import BaseWaveguideSequencer, BaseWaveguidePort
|
|
21
|
+
|
|
22
|
+
class CrossMark(Device):
|
|
23
|
+
def initialize(self):
|
|
24
|
+
self.set_name("BASELIB_CMARK")
|
|
25
|
+
self.set_description("Generic cross marker for mask alignment.")
|
|
26
|
+
|
|
27
|
+
def parameters(self):
|
|
28
|
+
self.addparameter("length1", 20, "Length of inner cross",float)
|
|
29
|
+
self.addparameter("length2", 10, "Length of outer cross",float)
|
|
30
|
+
self.addparameter("width1", 0.5, "Width of inner cross", float)
|
|
31
|
+
self.addparameter("width2", 2, "width of outer cross", float)
|
|
32
|
+
self.addparameter("layer", 4, "Layer to use for cross", int,(0,255))
|
|
33
|
+
self.addparameter("mark_number",0,"Places a square in the corner, use 0 to remove", float, (0,4))
|
|
34
|
+
self.addparameter("square_size",10,"Size of the square in the corner", float)
|
|
35
|
+
|
|
36
|
+
def geom(self):
|
|
37
|
+
p = self.get_params();
|
|
38
|
+
cross = sm.make_rect(0, 0, p["length1"], p["width1"],layer=1)
|
|
39
|
+
cross += sm.make_rect(0, 0, p["width1"], p["length1"],layer=1)
|
|
40
|
+
cross.boolean_union(1)
|
|
41
|
+
ocross = sm.make_rect(p["length1"]/2,0,p["length2"],p["width2"],numkey=4)
|
|
42
|
+
for i in range(4):
|
|
43
|
+
c = ocross.copy()
|
|
44
|
+
c.rotate(0, 0, 90*i)
|
|
45
|
+
cross+=c
|
|
46
|
+
if(p["mark_number"]>0):
|
|
47
|
+
rot = 90*(p["mark_number"]-1)
|
|
48
|
+
square = sm.make_rect(p["length1"]/2+p["length2"],
|
|
49
|
+
p["length1"]/2+p["length2"],
|
|
50
|
+
p["square_size"],p["square_size"],numkey=1)
|
|
51
|
+
square.rotate(0,0,rot)
|
|
52
|
+
cross+=square
|
|
53
|
+
|
|
54
|
+
cross.set_layer(p["layer"])
|
|
55
|
+
return cross
|
|
56
|
+
|
|
57
|
+
class DirectionalCoupler(Device):
|
|
58
|
+
def initialize(self):
|
|
59
|
+
self.set_name("BASELIB_DCPL")
|
|
60
|
+
self.set_description("Simple symmetric directional coupler")
|
|
61
|
+
|
|
62
|
+
def parameters(self):
|
|
63
|
+
self.addparameter("length", 20, "Coupling length",float)
|
|
64
|
+
self.addparameter("width", 0.3, "Width of the waveguides in the coupling section",float,(0.01,1))
|
|
65
|
+
self.addparameter("gap", 0.5, "Distance between waveguides in the coupling section", float)
|
|
66
|
+
self.addparameter("input_dist", 5, "Distance between waveguides at input", float,(0.01,np.inf))
|
|
67
|
+
self.addparameter("input_len", 7, "Length of the input section from input to coupling", float, (3,np.inf))
|
|
68
|
+
|
|
69
|
+
def geom(self):
|
|
70
|
+
p = self.get_params();
|
|
71
|
+
# Draw the upper arm, then mirror
|
|
72
|
+
off = p["input_dist"]/2
|
|
73
|
+
clen = (p["input_len"]-1)/2
|
|
74
|
+
Ltot = p["length"]+p["input_len"]*2
|
|
75
|
+
seq = [['T',1,p["width"]],["C",-off,clen],
|
|
76
|
+
['S',p["length"]/2]]
|
|
77
|
+
ss = BaseWaveguideSequencer(seq)
|
|
78
|
+
dc = ss.run()
|
|
79
|
+
dc2 = dc.copy()
|
|
80
|
+
dc2.mirrorX(Ltot/2)
|
|
81
|
+
dc+=dc2
|
|
82
|
+
dc.translate(-Ltot/2, off+p["gap"]/2+p["width"]/2)
|
|
83
|
+
dc3 = dc.copy()
|
|
84
|
+
dc3.mirrorY(0)
|
|
85
|
+
dc+=dc3
|
|
86
|
+
# Add ports
|
|
87
|
+
XP = Ltot/2
|
|
88
|
+
YP = off+p["gap"]/2+p["width"]/2
|
|
89
|
+
self.addlocalport(BaseWaveguidePort(-XP, YP, "west", ss.options["defaultWidth"], "p1"))
|
|
90
|
+
self.addlocalport(BaseWaveguidePort( XP, YP, "east", ss.options["defaultWidth"], "p2"))
|
|
91
|
+
self.addlocalport(BaseWaveguidePort(-XP,-YP, "west", ss.options["defaultWidth"], "p3"))
|
|
92
|
+
self.addlocalport(BaseWaveguidePort( XP,-YP, "east", ss.options["defaultWidth"], "p4"))
|
|
93
|
+
|
|
94
|
+
return dc
|
|
95
|
+
|
|
96
|
+
class FocusingGratingCoupler(Device):
|
|
97
|
+
def initialize(self):
|
|
98
|
+
self.set_name("BASELIB_FGC")
|
|
99
|
+
self.set_description("Grating coupler demo.")
|
|
100
|
+
|
|
101
|
+
def parameters(self):
|
|
102
|
+
self.addparameter('w0',0.3,'Width of the waveguide at the start', float)
|
|
103
|
+
self.addparameter('pitch',0.355,'Grating default pitch', float)
|
|
104
|
+
self.addparameter('ff',0.5,'Fill factor', float)
|
|
105
|
+
self.addparameter('theta',10,'Emission angle at central wavelength', float)
|
|
106
|
+
self.addparameter('lambda0',0.94,'Central wavelength', float)
|
|
107
|
+
self.addparameter('nr_Apo',11,'nr of the 1st arc with pitch and ff',int)
|
|
108
|
+
self.addparameter('ff_coef',0.5,'min ff_apod = ff_coef*ff', float);
|
|
109
|
+
self.addparameter('order_start',10,'Starting period', int);
|
|
110
|
+
self.addparameter('order',15,'Number of periods', int);
|
|
111
|
+
self.addparameter('diverg_angle',20,'GRT divergence angle/2, deg', float);
|
|
112
|
+
self.addparameter('pre_split',True,'Split in quads = false', bool);
|
|
113
|
+
|
|
114
|
+
def geom(self):
|
|
115
|
+
# Grating first
|
|
116
|
+
p = self.get_params();
|
|
117
|
+
theta = math.radians(p["theta"])
|
|
118
|
+
div_angle = p["diverg_angle"]
|
|
119
|
+
q0 = p["order_start"]
|
|
120
|
+
qN = q0+p["order"]+1
|
|
121
|
+
lambda0 = p["lambda0"]
|
|
122
|
+
pitch = p["pitch"]
|
|
123
|
+
n = math.sin(theta)+lambda0/pitch # Effective refractive index
|
|
124
|
+
p0 = lambda0/math.sqrt(n*n-np.power(math.sin(theta),2));
|
|
125
|
+
ff = p["ff"]
|
|
126
|
+
nr_Apo = p["nr_Apo"]
|
|
127
|
+
ff_coef = p["ff_coef"]
|
|
128
|
+
|
|
129
|
+
g = sm.GeomGroup()
|
|
130
|
+
for q in range(q0,qN):
|
|
131
|
+
b = q*p0
|
|
132
|
+
x0 = b*b*math.sin(theta)/(q*lambda0);
|
|
133
|
+
a = b*b*n/(q*lambda0);
|
|
134
|
+
if (q <= q0+nr_Apo-1):
|
|
135
|
+
ff_chi = ff-(1-ff_coef)*ff/(nr_Apo-2)*(q0+nr_Apo-q);
|
|
136
|
+
else:
|
|
137
|
+
ff_chi = ff
|
|
138
|
+
|
|
139
|
+
w = ff_chi*pitch
|
|
140
|
+
g+=sm.make_arc(x0, 0, a, b, 0, w, -div_angle-5, div_angle+5,
|
|
141
|
+
layer=3,to_poly=True,vertices=40,split=p["pre_split"])
|
|
142
|
+
|
|
143
|
+
# waveguide
|
|
144
|
+
Ltaper = 1
|
|
145
|
+
Gtaper = qN*pitch
|
|
146
|
+
Wtaper = Gtaper*math.tan(math.radians(div_angle))*2
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
seq = [["T",Ltaper,p["w0"]],
|
|
150
|
+
["CENTER",0,0],
|
|
151
|
+
["T",Gtaper,Wtaper],['STATE','w',2.5],['S',1]]
|
|
152
|
+
|
|
153
|
+
ss = BaseWaveguideSequencer(seq)
|
|
154
|
+
g += ss.run()
|
|
155
|
+
|
|
156
|
+
self.addlocalport(BaseWaveguidePort(-Ltaper, 0, "west", p["w0"], "p1"))
|
|
157
|
+
|
|
158
|
+
return g
|
|
159
|
+
|
|
160
|
+
# Register all devices here in this module
|
|
161
|
+
registerDevicesInModule(__name__)
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Base waveguide library.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Implements a simple waveguide sequencer and optical ports.
|
|
7
|
+
This module can be used as template to develop different waveguide libraries.
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import math
|
|
12
|
+
from copy import deepcopy
|
|
13
|
+
import numpy as np
|
|
14
|
+
from samplemaker.devices import DevicePort
|
|
15
|
+
from samplemaker.shapes import GeomGroup
|
|
16
|
+
import samplemaker.sequencer as smseq
|
|
17
|
+
import samplemaker.makers as sm
|
|
18
|
+
from samplemaker.routers import WaveguideConnect
|
|
19
|
+
|
|
20
|
+
# First step in defining a waveguide library is to define a sequencer
|
|
21
|
+
# and its dictionary.
|
|
22
|
+
|
|
23
|
+
# Let's define some options for the BaseWaveguide sequencer
|
|
24
|
+
def BaseWaveguideOptions():
|
|
25
|
+
BaseWaveguidesOptions = smseq.default_options()
|
|
26
|
+
# Let's define the default waveguide layer
|
|
27
|
+
BaseWaveguidesOptions["wgLayer"] = 1
|
|
28
|
+
# For waveguide bends, let's use a fixed resolution
|
|
29
|
+
BaseWaveguidesOptions["bendResolution"] = 30
|
|
30
|
+
# Let's define the default waveguide width
|
|
31
|
+
BaseWaveguidesOptions["defaultWidth"] = 0.3
|
|
32
|
+
return BaseWaveguidesOptions
|
|
33
|
+
|
|
34
|
+
# Let's define the sequencer state class
|
|
35
|
+
# We could use the default, but we would like to store
|
|
36
|
+
# the current waveguide width as well using the parameter 'w'
|
|
37
|
+
class BaseWaveguideState(smseq.SequencerState):
|
|
38
|
+
def __init__(self):
|
|
39
|
+
"""
|
|
40
|
+
The sequencer state for BaseWaveguide library.
|
|
41
|
+
Defines 'w' as current waveguide width.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
None.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
super().__init__()
|
|
49
|
+
self.state["w"]=0 # The value will be set by the INIT command
|
|
50
|
+
|
|
51
|
+
# Let's define the INIT command, which is always the first to execute
|
|
52
|
+
def BaseWaveguideINIT(state, options):
|
|
53
|
+
smseq.__initState(state,options)
|
|
54
|
+
if(not options["__no_init__"]):
|
|
55
|
+
state['w'] = options['defaultWidth']
|
|
56
|
+
|
|
57
|
+
# The S command to go straight
|
|
58
|
+
def BaseWaveguideS(args,state,options)->GeomGroup:
|
|
59
|
+
"""
|
|
60
|
+
Draw straight waveguide
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
args : list
|
|
65
|
+
1 argument: waveguide length.
|
|
66
|
+
state : dict
|
|
67
|
+
Current state.
|
|
68
|
+
options : dict
|
|
69
|
+
The sequencer options.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
samplemaker.shapes.GeomGroup
|
|
74
|
+
The waveguide geometry.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
dist = args[0]
|
|
78
|
+
if(dist==0):
|
|
79
|
+
return GeomGroup()
|
|
80
|
+
|
|
81
|
+
# Let's draw a simple rectangle
|
|
82
|
+
wg = sm.make_rect(0,0,dist,state['w'],numkey=4,layer=options["wgLayer"])
|
|
83
|
+
# Now rotate and translate according to pointer orientation
|
|
84
|
+
wg.rotate_translate(state['x'], state['y'], state['a'])
|
|
85
|
+
# Finally, update the state
|
|
86
|
+
state['x']+=dist*math.cos(math.radians(state['a']))
|
|
87
|
+
state['y']+=dist*math.sin(math.radians(state['a']))
|
|
88
|
+
state['__OL__']+=dist
|
|
89
|
+
return wg
|
|
90
|
+
|
|
91
|
+
# The B command to make a circular bend
|
|
92
|
+
def BaseWaveguideB(args,state,options)->GeomGroup:
|
|
93
|
+
"""
|
|
94
|
+
Draw circular bend waveguide
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
args : list
|
|
99
|
+
2 arguments: angle of bend (in degrees), radius of bend.
|
|
100
|
+
state : dict
|
|
101
|
+
Current state.
|
|
102
|
+
options : dict
|
|
103
|
+
The sequencer options.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
samplemaker.shapes.GeomGroup
|
|
108
|
+
The waveguide geometry.
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
angle = args[0]
|
|
112
|
+
radius = args[1]
|
|
113
|
+
if(angle==0):
|
|
114
|
+
return GeomGroup()
|
|
115
|
+
|
|
116
|
+
wg = sm.make_arc(0, radius, radius, radius,
|
|
117
|
+
-90, state['w'], 0, abs(angle),
|
|
118
|
+
vertices=options["bendResolution"],
|
|
119
|
+
to_poly=True,layer=options["wgLayer"])
|
|
120
|
+
xf = radius*math.sin(math.radians(abs(angle)))
|
|
121
|
+
yf = radius*(1-math.cos(math.radians(abs(angle))))
|
|
122
|
+
if(angle<0):
|
|
123
|
+
wg.mirrorY(0)
|
|
124
|
+
yf=-yf
|
|
125
|
+
ept = sm.make_dot(xf, yf) # helps calculating the end point
|
|
126
|
+
# Now rotate and translate according to pointer orientation
|
|
127
|
+
wg.rotate_translate(state['x'], state['y'], state['a'])
|
|
128
|
+
ept.rotate_translate(state['x'], state['y'], state['a'])
|
|
129
|
+
# Finally, update the state
|
|
130
|
+
state['x']=ept.x
|
|
131
|
+
state['y']=ept.y
|
|
132
|
+
state['a']+=angle
|
|
133
|
+
state['__OL__']+=radius*2*math.pi/360*abs(angle)
|
|
134
|
+
|
|
135
|
+
return wg
|
|
136
|
+
|
|
137
|
+
def BaseWaveguideC(args, state, options)->GeomGroup:
|
|
138
|
+
"""
|
|
139
|
+
Draw cosine bend waveguide. While keeping the same direciton,
|
|
140
|
+
bend the waveguide using a cosine function.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
args : list
|
|
145
|
+
2 arguments: offset (in um), radius of bend.
|
|
146
|
+
state : dict
|
|
147
|
+
Current state.
|
|
148
|
+
options : dict
|
|
149
|
+
The sequencer options.
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
samplemaker.shapes.GeomGroup
|
|
154
|
+
The waveguide geometry.
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
off = args[0]
|
|
158
|
+
radius = args[1]
|
|
159
|
+
delta = 0.01 # at the very beginning and at the end go straight by delta
|
|
160
|
+
radius -= delta
|
|
161
|
+
if(radius ==0):
|
|
162
|
+
return GeomGroup()
|
|
163
|
+
N = options['bendResolution']
|
|
164
|
+
amp = math.pi*off/4/radius
|
|
165
|
+
t = np.linspace(0,2,N);
|
|
166
|
+
s = [math.asin(math.tan(math.atan(amp)*x)/amp) for x in t if x<1]
|
|
167
|
+
s+= [math.asin(math.tan(math.atan(amp)*(x-2))/amp)+math.pi for x in t if x>=1]
|
|
168
|
+
s = np.array(s)
|
|
169
|
+
xpts = s/math.pi*2*radius + state['x']
|
|
170
|
+
ypts = off*(np.cos(s+math.pi)+1)/2 + state['y']
|
|
171
|
+
xpts = np.append(xpts[0],xpts+delta)
|
|
172
|
+
xpts = np.append(xpts,xpts[-1]+delta)
|
|
173
|
+
ypts = np.append(ypts[0],ypts)
|
|
174
|
+
ypts = np.append(ypts,ypts[-1])
|
|
175
|
+
OL = np.sum(np.sqrt(np.power(np.ediff1d(xpts),2)+np.power(np.ediff1d(ypts),2)))
|
|
176
|
+
wg = sm.make_path(xpts, ypts, state['w'],to_poly=1,layer=options["wgLayer"])
|
|
177
|
+
outdot = sm.make_dot(xpts[-1],ypts[-1])
|
|
178
|
+
wg.rotate(state['x'],state['y'],state['a'])
|
|
179
|
+
outdot.rotate(state["x"],state["y"],state["a"])
|
|
180
|
+
state['x']=outdot.x
|
|
181
|
+
state['y']=outdot.y
|
|
182
|
+
state["__OL__"]+=OL
|
|
183
|
+
return wg
|
|
184
|
+
|
|
185
|
+
def BaseWaveguideT(args, state, options)->GeomGroup:
|
|
186
|
+
"""
|
|
187
|
+
Draw linear taper
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
args : list
|
|
192
|
+
2 arguments: length of taper (in um), final width (if <0, the defaultWidth value is used).
|
|
193
|
+
state : dict
|
|
194
|
+
Current state.
|
|
195
|
+
options : dict
|
|
196
|
+
The sequencer options.
|
|
197
|
+
|
|
198
|
+
Returns
|
|
199
|
+
-------
|
|
200
|
+
samplemaker.shapes.GeomGroup
|
|
201
|
+
The waveguide geometry.
|
|
202
|
+
|
|
203
|
+
"""
|
|
204
|
+
dist = args[0]
|
|
205
|
+
wf = args[1]
|
|
206
|
+
if(dist==0):
|
|
207
|
+
return GeomGroup()
|
|
208
|
+
if(wf < 0): wf = options["defaultWidth"]
|
|
209
|
+
a = math.radians(state['a'])
|
|
210
|
+
xf = state['x']+dist*math.cos(a)
|
|
211
|
+
yf = state['y']+dist*math.sin(a)
|
|
212
|
+
wg = sm.make_tapered_path([state['x'],xf], [state['y'],yf], [state['w'],wf],
|
|
213
|
+
layer=options["wgLayer"])
|
|
214
|
+
state['x']=xf
|
|
215
|
+
state['y']=yf
|
|
216
|
+
state['w']=wf
|
|
217
|
+
state["__OL__"]+=dist
|
|
218
|
+
return wg
|
|
219
|
+
|
|
220
|
+
def BaseWaveguideOFF(args,state,options)->GeomGroup:
|
|
221
|
+
"""
|
|
222
|
+
Offset the waveguide (jumps left or right of waveguide)
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
args : list
|
|
227
|
+
1 argument: offset (in um), positive means on left of waveguide direction.
|
|
228
|
+
state : dict
|
|
229
|
+
Current state.
|
|
230
|
+
options : dict
|
|
231
|
+
The sequencer options.
|
|
232
|
+
|
|
233
|
+
Returns
|
|
234
|
+
-------
|
|
235
|
+
samplemaker.shapes.GeomGroup
|
|
236
|
+
The waveguide geometry.
|
|
237
|
+
|
|
238
|
+
"""
|
|
239
|
+
off = args[0]
|
|
240
|
+
a = math.radians(state['a']+90)
|
|
241
|
+
state['x']+=off*math.cos(a)
|
|
242
|
+
state['y']+=off*math.sin(a)
|
|
243
|
+
return GeomGroup()
|
|
244
|
+
|
|
245
|
+
def BaseWaveguideCommands() -> dict:
|
|
246
|
+
"""
|
|
247
|
+
Creates the dictionary with the command list and corresponding
|
|
248
|
+
functions.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
dict
|
|
253
|
+
The command list to be used by the sequencer.
|
|
254
|
+
|
|
255
|
+
"""
|
|
256
|
+
command_list=smseq.default_command_list()
|
|
257
|
+
command_list["INIT"] = (0,BaseWaveguideINIT)
|
|
258
|
+
command_list["S"] = (1,BaseWaveguideS)
|
|
259
|
+
command_list["B"] = (2,BaseWaveguideB)
|
|
260
|
+
command_list["C"] = (2,BaseWaveguideC)
|
|
261
|
+
command_list["T"] = (2,BaseWaveguideT)
|
|
262
|
+
command_list["OFF"] = (1,BaseWaveguideOFF)
|
|
263
|
+
return command_list
|
|
264
|
+
|
|
265
|
+
# Finally, create a custom sequencer
|
|
266
|
+
class BaseWaveguideSequencer(smseq.Sequencer):
|
|
267
|
+
def __init__(self,seq):
|
|
268
|
+
"""
|
|
269
|
+
Creates a custom sequencer for simple waveguides.
|
|
270
|
+
|
|
271
|
+
Parameters
|
|
272
|
+
----------
|
|
273
|
+
seq : list
|
|
274
|
+
The sequence to be executed.
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
None.
|
|
279
|
+
|
|
280
|
+
"""
|
|
281
|
+
opts = BaseWaveguideOptions()
|
|
282
|
+
state = BaseWaveguideState()
|
|
283
|
+
cmds = BaseWaveguideCommands()
|
|
284
|
+
super().__init__(seq,opts,state,cmds)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# some global connector options
|
|
288
|
+
BaseWaveguideConnectorOptions = {"bending_radius":3,
|
|
289
|
+
"sequencer_options":BaseWaveguideOptions()}
|
|
290
|
+
|
|
291
|
+
def BaseWaveguideConnector(port1: DevicePort,port2: DevicePort) -> GeomGroup:
|
|
292
|
+
res = WaveguideConnect(port1, port2,BaseWaveguideConnectorOptions["bending_radius"])
|
|
293
|
+
if(res[0]==True):
|
|
294
|
+
so = BaseWaveguideSequencer(res[1])
|
|
295
|
+
so.options = deepcopy(BaseWaveguideConnectorOptions["sequencer_options"])
|
|
296
|
+
g = so.run()
|
|
297
|
+
g.rotate_translate(port1.x0,port1.y0,math.degrees(port1.angle()))
|
|
298
|
+
return g
|
|
299
|
+
else:
|
|
300
|
+
return GeomGroup()
|
|
301
|
+
|
|
302
|
+
# Now let's create a new DevicePort with a connector function
|
|
303
|
+
class BaseWaveguidePort(DevicePort):
|
|
304
|
+
def __init__(self,x0: float, y0 : float,orient: str ="East",width: float =None,name: str =None):
|
|
305
|
+
orient = orient.lower()
|
|
306
|
+
horizontal = True
|
|
307
|
+
forward = True
|
|
308
|
+
if(orient=="west" or orient=="w"):
|
|
309
|
+
forward=False
|
|
310
|
+
if(orient=="north" or orient=="n"):
|
|
311
|
+
horizontal=False
|
|
312
|
+
if(orient=="south" or orient=="s"):
|
|
313
|
+
horizontal=False
|
|
314
|
+
forward=False
|
|
315
|
+
|
|
316
|
+
super().__init__(x0,y0,horizontal,forward)
|
|
317
|
+
self.width = width
|
|
318
|
+
self.name=name
|
|
319
|
+
self.connector_function=BaseWaveguideConnector
|
|
320
|
+
|