samplemaker-sparrow 5.4.6__tar.gz → 5.4.8__tar.gz

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.
Files changed (68) hide show
  1. samplemaker_sparrow-5.4.8/.github/ISSUE_TEMPLATE/config.yml +1 -0
  2. samplemaker_sparrow-5.4.8/.github/ISSUE_TEMPLATE/fault.yml +81 -0
  3. samplemaker_sparrow-5.4.8/.github/ISSUE_TEMPLATE/feature_request.yml +42 -0
  4. samplemaker_sparrow-5.4.8/.github/ISSUE_TEMPLATE/improvement.yml +34 -0
  5. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/.gitignore +4 -1
  6. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/PKG-INFO +4 -4
  7. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/pyproject.toml +13 -4
  8. samplemaker_sparrow-5.4.8/src/samplemaker/__init__.py +48 -0
  9. samplemaker_sparrow-5.4.8/src/samplemaker/baselib/__init__.py +3 -0
  10. samplemaker_sparrow-5.4.8/src/samplemaker/baselib/devices.py +295 -0
  11. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/baselib/waveguides.py +151 -112
  12. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/devices.py +614 -527
  13. samplemaker_sparrow-5.4.8/src/samplemaker/gdsreader.py +274 -0
  14. samplemaker_sparrow-5.4.8/src/samplemaker/gdswriter.py +437 -0
  15. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/layout.py +463 -340
  16. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/makers.py +262 -158
  17. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/phc.py +231 -185
  18. samplemaker_sparrow-5.4.8/src/samplemaker/routers.py +352 -0
  19. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/sequencer.py +150 -127
  20. samplemaker_sparrow-5.4.8/src/samplemaker/shapes.py +2362 -0
  21. samplemaker_sparrow-5.4.8/src/samplemaker/viewers.py +248 -0
  22. samplemaker_sparrow-5.4.8/tests/fixtures.py +19 -0
  23. samplemaker_sparrow-5.4.8/tests/test_gdsreader.py +246 -0
  24. samplemaker_sparrow-5.4.8/tests/test_gdswriter.py +253 -0
  25. samplemaker_sparrow-5.4.8/tests/test_makers.py +519 -0
  26. samplemaker_sparrow-5.4.8/tests/test_shapes.py +2005 -0
  27. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/00_Tutorial_BasicDrawing.py +13 -12
  28. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/01_Tutorial_Shapes.py +34 -34
  29. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/02_Tutorial_CellReferences.py +26 -24
  30. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/03_Tutorial_GroupManipulation.py +24 -23
  31. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/04_Tutorial_Boolean.py +23 -21
  32. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/05_Tutorial_Devices.py +53 -43
  33. samplemaker_sparrow-5.4.8/tutorials/06_Tutorial_DeviceTables.py +66 -0
  34. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/07_Tutorial_Waveguides.py +45 -34
  35. samplemaker_sparrow-5.4.8/tutorials/08_Tutorial_WaveguideDevices.py +124 -0
  36. samplemaker_sparrow-5.4.8/tutorials/09_Tutorial_Circuits.py +105 -0
  37. samplemaker_sparrow-5.4.8/tutorials/10_Tutorial_ElectricalPorts.py +126 -0
  38. samplemaker_sparrow-5.4.8/tutorials/11_Tutorial_LayoutAssembly.py +74 -0
  39. samplemaker_sparrow-5.4.8/tutorials/12_Tutorial_ImportingCircuits.py +36 -0
  40. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/TutorialCollection.py +27 -21
  41. samplemaker_sparrow-5.4.8/uv.lock +606 -0
  42. samplemaker_sparrow-5.4.6/src/samplemaker/__init__.py +0 -36
  43. samplemaker_sparrow-5.4.6/src/samplemaker/baselib/__init__.py +0 -4
  44. samplemaker_sparrow-5.4.6/src/samplemaker/baselib/devices.py +0 -160
  45. samplemaker_sparrow-5.4.6/src/samplemaker/gdsreader.py +0 -251
  46. samplemaker_sparrow-5.4.6/src/samplemaker/gdswriter.py +0 -367
  47. samplemaker_sparrow-5.4.6/src/samplemaker/routers.py +0 -339
  48. samplemaker_sparrow-5.4.6/src/samplemaker/shapes.py +0 -2263
  49. samplemaker_sparrow-5.4.6/src/samplemaker/viewers.py +0 -234
  50. samplemaker_sparrow-5.4.6/tutorials/06_Tutorial_DeviceTables.py +0 -60
  51. samplemaker_sparrow-5.4.6/tutorials/08_Tutorial_WaveguideDevices.py +0 -93
  52. samplemaker_sparrow-5.4.6/tutorials/09_Tutorial_Circuits.py +0 -96
  53. samplemaker_sparrow-5.4.6/tutorials/10_Tutorial_ElectricalPorts.py +0 -114
  54. samplemaker_sparrow-5.4.6/tutorials/11_Tutorial_LayoutAssembly.py +0 -74
  55. samplemaker_sparrow-5.4.6/tutorials/12_Tutorial_ImportingCircuits.py +0 -39
  56. samplemaker_sparrow-5.4.6/uv.lock +0 -588
  57. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/.github/workflows/build_wheels.yml +0 -0
  58. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/BUILD.md +0 -0
  59. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/LICENSE.md +0 -0
  60. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/README.md +0 -0
  61. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/boopy/CMakeLists.txt +0 -0
  62. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/boopy/boopy.cpp +0 -0
  63. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/documentation.md +0 -0
  64. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/resources/__init__.py +0 -0
  65. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/resources/boopy.pyi +0 -0
  66. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/src/samplemaker/resources/sm_stencil_font.txt +0 -0
  67. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/.pylint.d/12_Tutorial_LayoutAssembly1.stats +0 -0
  68. {samplemaker_sparrow-5.4.6 → samplemaker_sparrow-5.4.8}/tutorials/CircuitFile.txt +0 -0
@@ -0,0 +1 @@
1
+ blank_issues_enabled: false
@@ -0,0 +1,81 @@
1
+ name: Fault
2
+ description: Report a bug or unexpected behavior
3
+ title: "Fault: "
4
+ labels: ["fault", "needs-triage"]
5
+ body:
6
+ - type: checkboxes
7
+ id: preflight
8
+ attributes:
9
+ label: Before filing
10
+ options:
11
+ - label: I have searched existing issues and this is not a duplicate
12
+ required: true
13
+
14
+ - type: textarea
15
+ id: what
16
+ attributes:
17
+ label: What is broken?
18
+ description: Which tool, system, or process is affected?
19
+ placeholder: "e.g. The data export stops partway through and shows an error"
20
+ validations:
21
+ required: true
22
+
23
+ - type: dropdown
24
+ id: regression
25
+ attributes:
26
+ label: Is this a regression?
27
+ description: Did this work correctly before?
28
+ options:
29
+ - "Unknown"
30
+ - "Yes - it worked before"
31
+ - "No - it has never worked"
32
+ validations:
33
+ required: false
34
+
35
+ - type: textarea
36
+ id: environment
37
+ attributes:
38
+ label: Where did this happen?
39
+ placeholder: |
40
+ - Computer / OS:
41
+ - Software version:
42
+ - Equipment:
43
+ validations:
44
+ required: true
45
+
46
+ - type: textarea
47
+ id: reproduce
48
+ attributes:
49
+ label: What were you doing when it happened?
50
+ description: Step by step if possible.
51
+ placeholder: |
52
+ 1.
53
+ 2.
54
+ 3.
55
+ validations:
56
+ required: true
57
+
58
+ - type: textarea
59
+ id: expected
60
+ attributes:
61
+ label: What did you expect to happen?
62
+ placeholder: "e.g. The export completed and a file appeared in the output folder"
63
+ validations:
64
+ required: true
65
+
66
+ - type: textarea
67
+ id: actual
68
+ attributes:
69
+ label: What actually happened?
70
+ placeholder: "e.g. The program showed an error message and closed"
71
+ validations:
72
+ required: true
73
+
74
+ - type: textarea
75
+ id: logs
76
+ attributes:
77
+ label: Error messages (if any)
78
+ description: Copy and paste any error text here.
79
+ render: shell
80
+ validations:
81
+ required: false
@@ -0,0 +1,42 @@
1
+ name: Feature request
2
+ description: Suggest an idea for this project
3
+ title: "Feature: "
4
+ labels: ["feature", "needs-triage"]
5
+ body:
6
+ - type: textarea
7
+ id: need
8
+ attributes:
9
+ label: What problem does this solve?
10
+ description: Focus on the need, not the solution.
11
+ placeholder: "e.g. We have to manually copy files after every run, which takes time and is easy to forget"
12
+ validations:
13
+ required: true
14
+ - type: textarea
15
+ id: proposal
16
+ attributes:
17
+ label: What would you like it to do?
18
+ placeholder: "e.g. Automatically save results to a shared folder at the end of each run"
19
+ validations:
20
+ required: true
21
+ - type: textarea
22
+ id: constraints
23
+ attributes:
24
+ label: Any requirements or limitations?
25
+ placeholder: "e.g. Must work without an internet connection; needs to support existing file formats"
26
+ validations:
27
+ required: false
28
+ - type: textarea
29
+ id: acceptance
30
+ attributes:
31
+ label: How will we know it is done?
32
+ placeholder: |
33
+ - [ ]
34
+ - [ ]
35
+ validations:
36
+ required: true
37
+ - type: textarea
38
+ id: context
39
+ attributes:
40
+ label: Anything else?
41
+ validations:
42
+ required: false
@@ -0,0 +1,34 @@
1
+ name: Improvement
2
+ description: Suggest an improvement to this project
3
+ title: "Improvement: "
4
+ labels: ["improvement", "needs-triage"]
5
+ body:
6
+ - type: textarea
7
+ id: current
8
+ attributes:
9
+ label: What is the problem with how it works today?
10
+ placeholder: "e.g. The search only matches exact words, so it is easy to miss results with slightly different names"
11
+ validations:
12
+ required: true
13
+ - type: textarea
14
+ id: proposed
15
+ attributes:
16
+ label: What would better look like?
17
+ placeholder: "e.g. Search should still find results when words are partially typed or spelled differently"
18
+ validations:
19
+ required: true
20
+ - type: textarea
21
+ id: acceptance
22
+ attributes:
23
+ label: How will we know it is done?
24
+ placeholder: |
25
+ - [ ]
26
+ - [ ]
27
+ validations:
28
+ required: true
29
+ - type: textarea
30
+ id: context
31
+ attributes:
32
+ label: Anything else?
33
+ validations:
34
+ required: false
@@ -26,6 +26,9 @@ dist/
26
26
  # JetBrains IDE
27
27
  .idea/
28
28
 
29
+ # Visual Studio Code
30
+ .vscode/
31
+
29
32
  # Unit test reports
30
33
  TEST*.xml
31
34
 
@@ -54,7 +57,7 @@ Thumbs.db
54
57
  *.gds
55
58
  src/boopy/*.pro.user
56
59
  html/
57
- tests/
60
+
58
61
  # Python build artifacts
59
62
  build/
60
63
  *.egg-info/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: samplemaker-sparrow
3
- Version: 5.4.6
3
+ Version: 5.4.8
4
4
  Summary: Lithographic mask design package
5
5
  Author-Email: Leonardo Midolo <midolo@nbi.ku.dk>, Jeppe Roulund <jeppe.roulund@sparrowquantum.com>
6
6
  License-Expression: BSD-3-Clause
@@ -9,17 +9,17 @@ Classifier: Programming Language :: C++
9
9
  Classifier: Programming Language :: Python
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3 :: Only
12
- Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
16
  Classifier: Operating System :: Microsoft :: Windows
17
17
  Classifier: Operating System :: MacOS
18
18
  Classifier: Operating System :: Unix
19
19
  Classifier: Operating System :: POSIX :: Linux
20
20
  Project-URL: Repository, https://github.com/SparrowQuantum/samplemaker.git
21
- Requires-Python: <3.14,>=3.10
22
- Requires-Dist: matplotlib>=3.7.5
21
+ Requires-Python: <3.15,>=3.11
22
+ Requires-Dist: matplotlib>=3.10.5
23
23
  Requires-Dist: numpy==2.*
24
24
  Description-Content-Type: text/markdown
25
25
 
@@ -10,12 +10,12 @@ authors = [
10
10
  ]
11
11
  description = "Lithographic mask design package"
12
12
  readme = "README.md"
13
- requires-python = ">=3.10,<3.14"
13
+ requires-python = ">=3.11,<3.15"
14
14
  license-files = ["LICENSE.md"]
15
15
  license = "BSD-3-Clause"
16
- version = "5.4.6"
16
+ version = "5.4.8"
17
17
  dependencies = [
18
- "matplotlib>=3.7.5",
18
+ "matplotlib>=3.10.5",
19
19
  "numpy==2.*",
20
20
  ]
21
21
  classifiers = [
@@ -23,10 +23,10 @@ classifiers = [
23
23
  "Programming Language :: Python",
24
24
  "Programming Language :: Python :: 3",
25
25
  "Programming Language :: Python :: 3 :: Only",
26
- "Programming Language :: Python :: 3.10",
27
26
  "Programming Language :: Python :: 3.11",
28
27
  "Programming Language :: Python :: 3.12",
29
28
  "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: 3.14",
30
30
  "Operating System :: Microsoft :: Windows",
31
31
  "Operating System :: MacOS",
32
32
  "Operating System :: Unix",
@@ -45,6 +45,9 @@ wheel.packages = ["src/samplemaker"]
45
45
 
46
46
  [tool.cibuildwheel]
47
47
  skip = ["*-musllinux*", "*-win32*"]
48
+ test-requires = "pytest"
49
+ test-sources = ["tests"]
50
+ test-command = "pytest {project}/tests"
48
51
 
49
52
  [tool.cibuildwheel.linux]
50
53
  before-all = "yum install -y boost-devel"
@@ -57,3 +60,9 @@ environment = { BOOST_ROOT = "C:/vcpkg/installed/x64-windows" }
57
60
  before-all = [
58
61
  "vcpkg install boost-polygon:x64-windows",
59
62
  ]
63
+
64
+ [dependency-groups]
65
+ dev = [
66
+ "pytest==9.0.2",
67
+ "ruff==0.15.1",
68
+ ]
@@ -0,0 +1,48 @@
1
+ """
2
+
3
+ This is the Python version of Sample Maker, a scripting tool for designing lithographic
4
+ masks in the GDSII format. Package `samplemaker` comes with different tools and
5
+ submodules for the creation and manipulation of basic shapes, periodic shapes, sequences
6
+ (e.g. waveguides), circuits, and complex devices.
7
+
8
+ The code has been developed primarily for nanophotonics, but it can be easily extended
9
+ to different applications in micro and nano device fabrication.
10
+
11
+ Sample Maker is developed and maintained by Leonardo Midolo (Niels Bohr Institute,
12
+ University of Copenhagen). It is based on the MATLAB(R) code developed by Leonardo
13
+ Midolo between 2013 and 2019. The first version of the rewritten Python code has been
14
+ 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
18
+ innovation programme (Grant agreement No. 949043, NANOMEQ).
19
+
20
+ .. include:: ./documentation.md
21
+ """
22
+
23
+ __pdoc__: dict[str, bool | str] = {
24
+ "samplemaker.Tutorials": False,
25
+ "samplemaker.tests": False,
26
+ "samplemaker.resources": False,
27
+ "samplemaker.gdsreader": False,
28
+ "samplemaker.devices.DevicePort": False,
29
+ }
30
+
31
+ # The LayoutPool contains all the current layout, this class should generally not
32
+ # be used directly, but only through the Mask class.
33
+ LayoutPool = {} # connects a SREF name to a particular geomgroup in the current memory
34
+
35
+ # Additional cache pool:
36
+
37
+ # _DevicePool Connects a device hash to a SREF to be instantiated:
38
+ _DevicePool = {}
39
+
40
+ # _DeviceLocalParamPool connects a device hash to local parameters created by the call
41
+ # to geom():
42
+ _DeviceLocalParamPool = {}
43
+
44
+ # _DeviceCountPool connects a device name to a device count
45
+ _DeviceCountPool = {}
46
+
47
+ # _BoundingBoxPool connects a SREF name to its bounding box
48
+ _BoundingBoxPool = {}
@@ -0,0 +1,3 @@
1
+ import samplemaker.baselib.devices # noqa: F401
2
+
3
+ print("Base library loaded")
@@ -0,0 +1,295 @@
1
+ """
2
+ Base device library.
3
+
4
+ This is a collection of some simple demo devices distributed with the base version of
5
+ `samplemaker`. It can be used as template for creating new libraries or to learn how to
6
+ design them.
7
+
8
+ Note that individual device methods are not documented but should be readable and
9
+ self-explanatory.
10
+ """
11
+
12
+ import math
13
+
14
+ import numpy as np
15
+
16
+ import samplemaker.makers as sm
17
+ from samplemaker.baselib.waveguides import BaseWaveguidePort, BaseWaveguideSequencer
18
+ from samplemaker.devices import Device, registerDevicesInModule
19
+
20
+
21
+ class CrossMark(Device):
22
+ def initialize(self):
23
+ self.set_name("BASELIB_CMARK")
24
+ self.set_description("Generic cross marker for mask alignment.")
25
+
26
+ def parameters(self):
27
+ self.addparameter(
28
+ param_name="length1",
29
+ default_value=20,
30
+ param_description="Length of inner cross",
31
+ param_type=float,
32
+ )
33
+ self.addparameter(
34
+ param_name="length2",
35
+ default_value=10,
36
+ param_description="Length of outer cross",
37
+ param_type=float,
38
+ )
39
+ self.addparameter(
40
+ param_name="width1",
41
+ default_value=0.5,
42
+ param_description="Width of inner cross",
43
+ param_type=float,
44
+ )
45
+ self.addparameter(
46
+ param_name="width2",
47
+ default_value=2,
48
+ param_description="width of outer cross",
49
+ param_type=float,
50
+ )
51
+ self.addparameter(
52
+ param_name="layer",
53
+ default_value=4,
54
+ param_description="Layer to use for cross",
55
+ param_type=int,
56
+ param_range=(0, 255),
57
+ )
58
+ self.addparameter(
59
+ param_name="mark_number",
60
+ default_value=0,
61
+ param_description="Places a square in the corner, use 0 to remove",
62
+ param_type=float,
63
+ param_range=(0, 4),
64
+ )
65
+ self.addparameter(
66
+ param_name="square_size",
67
+ default_value=10,
68
+ param_description="Size of the square in the corner",
69
+ param_type=float,
70
+ )
71
+
72
+ def geom(self):
73
+ p = self.get_params()
74
+ cross = sm.make_rect(0, 0, p["length1"], p["width1"], layer=1)
75
+ cross += sm.make_rect(0, 0, p["width1"], p["length1"], layer=1)
76
+ cross.boolean_union(1)
77
+ ocross = sm.make_rect(p["length1"] / 2, 0, p["length2"], p["width2"], numkey=4)
78
+ for i in range(4):
79
+ c = ocross.copy()
80
+ c.rotate(0, 0, 90 * i)
81
+ cross += c
82
+ if p["mark_number"] > 0:
83
+ rot = 90 * (p["mark_number"] - 1)
84
+ sq_dim = p["length1"] / 2 + p["length2"]
85
+ sq_size = p["square_size"]
86
+ square = sm.make_rect(sq_dim, sq_dim, sq_size, sq_size, numkey=1)
87
+ square.rotate(0, 0, rot)
88
+ cross += square
89
+
90
+ cross.set_layer(p["layer"])
91
+ return cross
92
+
93
+
94
+ class DirectionalCoupler(Device):
95
+ def initialize(self):
96
+ self.set_name("BASELIB_DCPL")
97
+ self.set_description("Simple symmetric directional coupler")
98
+
99
+ def parameters(self):
100
+ self.addparameter("length", 20, "Coupling length", float)
101
+ self.addparameter(
102
+ param_name="width",
103
+ default_value=0.3,
104
+ param_description="Width of the waveguides in the coupling section",
105
+ param_type=float,
106
+ param_range=(0.01, 1),
107
+ )
108
+ self.addparameter(
109
+ param_name="gap",
110
+ default_value=0.5,
111
+ param_description="Distance between waveguides in the coupling section",
112
+ param_type=float,
113
+ )
114
+ self.addparameter(
115
+ param_name="input_dist",
116
+ default_value=5,
117
+ param_description="Distance between waveguides at input",
118
+ param_type=float,
119
+ param_range=(0.01, np.inf),
120
+ )
121
+ self.addparameter(
122
+ param_name="input_len",
123
+ default_value=7,
124
+ param_description="Length of the input section from input to coupling",
125
+ param_type=float,
126
+ param_range=(3, np.inf),
127
+ )
128
+
129
+ def geom(self):
130
+ p = self.get_params()
131
+ # Draw the upper arm, then mirror
132
+ off = p["input_dist"] / 2
133
+ clen = (p["input_len"] - 1) / 2
134
+ ltot = p["length"] + p["input_len"] * 2
135
+ seq = [["T", 1, p["width"]], ["C", -off, clen], ["S", p["length"] / 2]]
136
+ ss = BaseWaveguideSequencer(seq)
137
+ dc = ss.run()
138
+ dc2 = dc.copy()
139
+ dc2.mirrorX(ltot / 2)
140
+ dc += dc2
141
+ dc.translate(-ltot / 2, off + p["gap"] / 2 + p["width"] / 2)
142
+ dc3 = dc.copy()
143
+ dc3.mirrorY(0)
144
+ dc += dc3
145
+
146
+ # Add ports
147
+ xp = ltot / 2
148
+ yp = off + p["gap"] / 2 + p["width"] / 2
149
+ nw_port = BaseWaveguidePort(-xp, yp, "west", ss.options["defaultWidth"], "p1")
150
+ ne_port = BaseWaveguidePort(xp, yp, "east", ss.options["defaultWidth"], "p2")
151
+ sw_port = BaseWaveguidePort(-xp, -yp, "west", ss.options["defaultWidth"], "p3")
152
+ se_port = BaseWaveguidePort(xp, -yp, "east", ss.options["defaultWidth"], "p4")
153
+ for port in (nw_port, ne_port, sw_port, se_port):
154
+ self.addlocalport(port)
155
+
156
+ return dc
157
+
158
+
159
+ class FocusingGratingCoupler(Device):
160
+ def initialize(self):
161
+ self.set_name("BASELIB_FGC")
162
+ self.set_description("Grating coupler demo.")
163
+
164
+ def parameters(self):
165
+ self.addparameter(
166
+ param_name="w0",
167
+ default_value=0.3,
168
+ param_description="Width of the waveguide at the start",
169
+ param_type=float,
170
+ )
171
+ self.addparameter(
172
+ param_name="pitch",
173
+ default_value=0.355,
174
+ param_description="Grating default pitch",
175
+ param_type=float,
176
+ )
177
+ self.addparameter(
178
+ param_name="ff",
179
+ default_value=0.5,
180
+ param_description="Fill factor",
181
+ param_type=float,
182
+ )
183
+ self.addparameter(
184
+ param_name="theta",
185
+ default_value=10,
186
+ param_description="Emission angle at central wavelength",
187
+ param_type=float,
188
+ )
189
+ self.addparameter(
190
+ param_name="lambda0",
191
+ default_value=0.94,
192
+ param_description="Central wavelength",
193
+ param_type=float,
194
+ )
195
+ self.addparameter(
196
+ param_name="nr_Apo",
197
+ default_value=11,
198
+ param_description="nr of the 1st arc with pitch and ff",
199
+ param_type=int,
200
+ )
201
+ self.addparameter(
202
+ param_name="ff_coef",
203
+ default_value=0.5,
204
+ param_description="min ff_apod = ff_coef*ff",
205
+ param_type=float,
206
+ )
207
+ self.addparameter(
208
+ param_name="order_start",
209
+ default_value=10,
210
+ param_description="Starting period",
211
+ param_type=int,
212
+ )
213
+ self.addparameter(
214
+ param_name="order",
215
+ default_value=15,
216
+ param_description="Number of periods",
217
+ param_type=int,
218
+ )
219
+ self.addparameter(
220
+ param_name="diverg_angle",
221
+ default_value=20,
222
+ param_description="GRT divergence angle/2, deg",
223
+ param_type=float,
224
+ )
225
+ self.addparameter(
226
+ param_name="pre_split",
227
+ default_value=True,
228
+ param_description="Split in quads = false",
229
+ param_type=bool,
230
+ )
231
+
232
+ def geom(self):
233
+ # Grating first
234
+ p = self.get_params()
235
+ theta = math.radians(p["theta"])
236
+ div_angle = p["diverg_angle"]
237
+ q0 = p["order_start"]
238
+ qn = q0 + p["order"] + 1
239
+ lambda0 = p["lambda0"]
240
+ pitch = p["pitch"]
241
+ n = math.sin(theta) + lambda0 / pitch # Effective refractive index
242
+ p0 = lambda0 / math.sqrt(n * n - np.power(math.sin(theta), 2))
243
+ ff = p["ff"]
244
+ nr_apo = p["nr_Apo"]
245
+ ff_coef = p["ff_coef"]
246
+
247
+ g = sm.GeomGroup()
248
+ for q in range(q0, qn):
249
+ b = q * p0
250
+ x0 = b * b * math.sin(theta) / (q * lambda0)
251
+ a = b * b * n / (q * lambda0)
252
+ if q <= q0 + nr_apo - 1:
253
+ ff_chi = ff - (1 - ff_coef) * ff / (nr_apo - 2) * (q0 + nr_apo - q)
254
+ else:
255
+ ff_chi = ff
256
+
257
+ w = ff_chi * pitch
258
+ g += sm.make_arc(
259
+ x0=x0,
260
+ y0=0,
261
+ rX=a,
262
+ rY=b,
263
+ rot=0,
264
+ w=w,
265
+ a1=-div_angle - 5,
266
+ a2=div_angle + 5,
267
+ layer=3,
268
+ to_poly=True,
269
+ vertices=40,
270
+ split=p["pre_split"],
271
+ )
272
+
273
+ # waveguide
274
+ l_taper = 1
275
+ g_taper = qn * pitch
276
+ w_taper = g_taper * math.tan(math.radians(div_angle)) * 2
277
+
278
+ seq = [
279
+ ["T", l_taper, p["w0"]],
280
+ ["CENTER", 0, 0],
281
+ ["T", g_taper, w_taper],
282
+ ["STATE", "w", 2.5],
283
+ ["S", 1],
284
+ ]
285
+
286
+ ss = BaseWaveguideSequencer(seq)
287
+ g += ss.run()
288
+
289
+ self.addlocalport(BaseWaveguidePort(-l_taper, 0, "west", p["w0"], "p1"))
290
+
291
+ return g
292
+
293
+
294
+ # Register all devices here in this module
295
+ registerDevicesInModule(__name__)