samplemaker-sparrow 5.4.8__tar.gz → 5.4.9__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 (73) hide show
  1. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/.github/workflows/build_wheels.yml +2 -2
  2. samplemaker_sparrow-5.4.9/.github/workflows/ruff.yml +19 -0
  3. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/.gitignore +4 -0
  4. samplemaker_sparrow-5.4.9/.python-version +1 -0
  5. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/PKG-INFO +2 -1
  6. samplemaker_sparrow-5.4.9/flake.lock +61 -0
  7. samplemaker_sparrow-5.4.9/flake.nix +96 -0
  8. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/pyproject.toml +50 -14
  9. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/__init__.py +8 -8
  10. samplemaker_sparrow-5.4.9/src/samplemaker/_legacy.py +142 -0
  11. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/baselib/__init__.py +2 -0
  12. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/baselib/devices.py +90 -18
  13. samplemaker_sparrow-5.4.9/src/samplemaker/baselib/waveguides.py +688 -0
  14. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/devices.py +894 -447
  15. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/gdsreader.py +58 -27
  16. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/gdswriter.py +146 -134
  17. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/layout.py +617 -311
  18. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/makers.py +170 -137
  19. samplemaker_sparrow-5.4.9/src/samplemaker/phc.py +686 -0
  20. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/resources/__init__.py +1 -2
  21. samplemaker_sparrow-5.4.9/src/samplemaker/routers.py +419 -0
  22. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/sequencer.py +125 -63
  23. samplemaker_sparrow-5.4.9/src/samplemaker/shapes.py +4381 -0
  24. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/viewers.py +100 -52
  25. samplemaker_sparrow-5.4.9/tests/__init__.py +4 -0
  26. samplemaker_sparrow-5.4.9/tests/conftest.py +60 -0
  27. samplemaker_sparrow-5.4.9/tests/dummy.py +107 -0
  28. samplemaker_sparrow-5.4.9/tests/fakes.py +51 -0
  29. samplemaker_sparrow-5.4.9/tests/test_baselib_devices.py +128 -0
  30. samplemaker_sparrow-5.4.9/tests/test_baselib_waveguides.py +332 -0
  31. samplemaker_sparrow-5.4.9/tests/test_devices.py +691 -0
  32. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tests/test_gdsreader.py +6 -9
  33. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tests/test_gdswriter.py +7 -8
  34. samplemaker_sparrow-5.4.9/tests/test_layout.py +839 -0
  35. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tests/test_makers.py +26 -25
  36. samplemaker_sparrow-5.4.9/tests/test_phc.py +369 -0
  37. samplemaker_sparrow-5.4.9/tests/test_routers.py +217 -0
  38. samplemaker_sparrow-5.4.9/tests/test_sequencer.py +504 -0
  39. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tests/test_shapes.py +127 -123
  40. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/00_Tutorial_BasicDrawing.py +6 -6
  41. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/01_Tutorial_Shapes.py +2 -2
  42. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/02_Tutorial_CellReferences.py +5 -5
  43. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/03_Tutorial_GroupManipulation.py +3 -3
  44. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/04_Tutorial_Boolean.py +2 -2
  45. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/05_Tutorial_Devices.py +6 -6
  46. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/06_Tutorial_DeviceTables.py +2 -2
  47. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/07_Tutorial_Waveguides.py +2 -2
  48. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/08_Tutorial_WaveguideDevices.py +6 -6
  49. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/09_Tutorial_Circuits.py +3 -3
  50. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/10_Tutorial_ElectricalPorts.py +6 -4
  51. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/11_Tutorial_LayoutAssembly.py +5 -5
  52. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/12_Tutorial_ImportingCircuits.py +4 -4
  53. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/TutorialCollection.py +3 -3
  54. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/uv.lock +186 -1
  55. samplemaker_sparrow-5.4.8/src/samplemaker/baselib/waveguides.py +0 -358
  56. samplemaker_sparrow-5.4.8/src/samplemaker/phc.py +0 -528
  57. samplemaker_sparrow-5.4.8/src/samplemaker/routers.py +0 -352
  58. samplemaker_sparrow-5.4.8/src/samplemaker/shapes.py +0 -2362
  59. samplemaker_sparrow-5.4.8/tests/fixtures.py +0 -19
  60. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  61. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/.github/ISSUE_TEMPLATE/fault.yml +0 -0
  62. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  63. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/.github/ISSUE_TEMPLATE/improvement.yml +0 -0
  64. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/BUILD.md +0 -0
  65. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/LICENSE.md +0 -0
  66. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/README.md +0 -0
  67. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/boopy/CMakeLists.txt +0 -0
  68. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/boopy/boopy.cpp +0 -0
  69. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/documentation.md +0 -0
  70. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/resources/boopy.pyi +0 -0
  71. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/src/samplemaker/resources/sm_stencil_font.txt +0 -0
  72. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/.pylint.d/12_Tutorial_LayoutAssembly1.stats +0 -0
  73. {samplemaker_sparrow-5.4.8 → samplemaker_sparrow-5.4.9}/tutorials/CircuitFile.txt +0 -0
@@ -25,7 +25,7 @@ jobs:
25
25
  - name: Build wheels
26
26
  uses: pypa/cibuildwheel@v3.3.0
27
27
 
28
- - uses: actions/upload-artifact@v4
28
+ - uses: actions/upload-artifact@v7
29
29
  with:
30
30
  name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
31
31
  path: ./wheelhouse/*.whl
@@ -41,7 +41,7 @@ jobs:
41
41
  - name: Build SDist
42
42
  run: pipx run build --sdist
43
43
 
44
- - uses: actions/upload-artifact@v4
44
+ - uses: actions/upload-artifact@v7
45
45
  with:
46
46
  name: cibw-sdist
47
47
  path: dist/*.tar.gz
@@ -0,0 +1,19 @@
1
+ name: Ruff Check
2
+ on: push
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - name: Install Python
9
+ uses: actions/setup-python@v5
10
+ with:
11
+ python-version: "3.13"
12
+ - name: Install dependencies
13
+ run: |
14
+ python -m pip install --upgrade pip
15
+ pip install ruff==0.15.1
16
+ - name: Run Ruff Linter
17
+ run: ruff check --output-format=github .
18
+ - name: Check Ruff formatting
19
+ run: ruff format --check --diff .
@@ -65,9 +65,13 @@ wheelhouse/
65
65
  *.so
66
66
  *.pyd
67
67
  *.dll
68
+ result
68
69
 
69
70
  # CMake artifacts
70
71
  CMakeFiles/
71
72
  CMakeCache.txt
72
73
  cmake_install.cmake
73
74
  Makefile
75
+
76
+ # Pytest coverage reports
77
+ .coverage
@@ -0,0 +1 @@
1
+ 3.13.13
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: samplemaker-sparrow
3
- Version: 5.4.8
3
+ Version: 5.4.9
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
@@ -19,6 +19,7 @@ Classifier: Operating System :: Unix
19
19
  Classifier: Operating System :: POSIX :: Linux
20
20
  Project-URL: Repository, https://github.com/SparrowQuantum/samplemaker.git
21
21
  Requires-Python: <3.15,>=3.11
22
+ Requires-Dist: asteval>=1.0.9
22
23
  Requires-Dist: matplotlib>=3.10.5
23
24
  Requires-Dist: numpy==2.*
24
25
  Description-Content-Type: text/markdown
@@ -0,0 +1,61 @@
1
+ {
2
+ "nodes": {
3
+ "flake-utils": {
4
+ "inputs": {
5
+ "systems": "systems"
6
+ },
7
+ "locked": {
8
+ "lastModified": 1731533236,
9
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
10
+ "owner": "numtide",
11
+ "repo": "flake-utils",
12
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
13
+ "type": "github"
14
+ },
15
+ "original": {
16
+ "owner": "numtide",
17
+ "repo": "flake-utils",
18
+ "type": "github"
19
+ }
20
+ },
21
+ "nixpkgs": {
22
+ "locked": {
23
+ "lastModified": 1780749050,
24
+ "narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=",
25
+ "owner": "NixOS",
26
+ "repo": "nixpkgs",
27
+ "rev": "a799d3e3886da994fa307f817a6bc705ae538eeb",
28
+ "type": "github"
29
+ },
30
+ "original": {
31
+ "owner": "NixOS",
32
+ "ref": "nixos-unstable",
33
+ "repo": "nixpkgs",
34
+ "type": "github"
35
+ }
36
+ },
37
+ "root": {
38
+ "inputs": {
39
+ "flake-utils": "flake-utils",
40
+ "nixpkgs": "nixpkgs"
41
+ }
42
+ },
43
+ "systems": {
44
+ "locked": {
45
+ "lastModified": 1681028828,
46
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47
+ "owner": "nix-systems",
48
+ "repo": "default",
49
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50
+ "type": "github"
51
+ },
52
+ "original": {
53
+ "owner": "nix-systems",
54
+ "repo": "default",
55
+ "type": "github"
56
+ }
57
+ }
58
+ },
59
+ "root": "root",
60
+ "version": 7
61
+ }
@@ -0,0 +1,96 @@
1
+ {
2
+ description = "samplemaker-sparrow lithographic mask design package";
3
+
4
+ inputs = {
5
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6
+ flake-utils.url = "github:numtide/flake-utils";
7
+ };
8
+
9
+ outputs = {
10
+ nixpkgs,
11
+ flake-utils,
12
+ ...
13
+ }:
14
+ flake-utils.lib.eachDefaultSystem (
15
+ system: let
16
+ pkgs = nixpkgs.legacyPackages.${system};
17
+ python = pkgs.python313;
18
+ pyPkgs = python.pkgs;
19
+
20
+ pyProj = pkgs.lib.trivial.importTOML ./pyproject.toml;
21
+ samplemaker = pyPkgs.buildPythonPackage {
22
+ pname = "samplemaker-sparrow";
23
+ version = pyProj.project.version;
24
+ pyproject = true;
25
+
26
+ src = ./.;
27
+
28
+ build-system = [
29
+ pyPkgs.scikit-build-core
30
+ pyPkgs.pybind11
31
+ ];
32
+
33
+ nativeBuildInputs = [
34
+ pkgs.cmake
35
+ pkgs.ninja
36
+ ];
37
+
38
+ buildInputs = [
39
+ pkgs.boost.dev
40
+ ];
41
+
42
+ dependencies = [
43
+ pyPkgs.matplotlib
44
+ pyPkgs.numpy
45
+ ];
46
+
47
+ # scikit-build-core invokes cmake internally; skip Nix's cmake configure hook
48
+ dontUseCmakeConfigure = true;
49
+
50
+ meta = with pkgs.lib; {
51
+ description = "Lithographic mask design package";
52
+ homepage = "https://github.com/SparrowQuantum/samplemaker";
53
+ license = licenses.bsd3;
54
+ };
55
+ };
56
+
57
+ devEnv = python.withPackages (ps: [
58
+ samplemaker
59
+ ps.pytest
60
+ ps.pytest-cov
61
+ ]);
62
+
63
+ fmtPackage = pkgs.writeShellScriptBin "fmt" ''
64
+ ${pkgs.alejandra}/bin/alejandra . --quiet
65
+ '';
66
+ in {
67
+ packages = {
68
+ default = samplemaker;
69
+ samplemaker = samplemaker;
70
+ };
71
+
72
+ devShells = {
73
+ default = pkgs.mkShell {
74
+ packages = [
75
+ pkgs.ruff
76
+ pkgs.alejandra
77
+ pkgs.deadnix
78
+ devEnv
79
+ ];
80
+ };
81
+ uv = pkgs.mkShell {
82
+ packages = with pkgs; [
83
+ uv
84
+ cmake
85
+ ninja
86
+ boost.dev
87
+ alejandra
88
+ deadnix
89
+ ];
90
+ };
91
+ };
92
+
93
+ formatter = fmtPackage;
94
+ }
95
+ );
96
+ }
@@ -5,19 +5,16 @@ build-backend = "scikit_build_core.build"
5
5
  [project]
6
6
  name = "samplemaker-sparrow"
7
7
  authors = [
8
- { name = "Leonardo Midolo", email = "midolo@nbi.ku.dk"},
9
- { name = "Jeppe Roulund", email = "jeppe.roulund@sparrowquantum.com"}
8
+ { name = "Leonardo Midolo", email = "midolo@nbi.ku.dk" },
9
+ { name = "Jeppe Roulund", email = "jeppe.roulund@sparrowquantum.com" },
10
10
  ]
11
11
  description = "Lithographic mask design package"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.11,<3.15"
14
14
  license-files = ["LICENSE.md"]
15
15
  license = "BSD-3-Clause"
16
- version = "5.4.8"
17
- dependencies = [
18
- "matplotlib>=3.10.5",
19
- "numpy==2.*",
20
- ]
16
+ version = "5.4.9"
17
+ dependencies = ["asteval>=1.0.9", "matplotlib>=3.10.5", "numpy==2.*"]
21
18
  classifiers = [
22
19
  "Programming Language :: C++",
23
20
  "Programming Language :: Python",
@@ -36,6 +33,50 @@ classifiers = [
36
33
  [project.urls]
37
34
  Repository = "https://github.com/SparrowQuantum/samplemaker.git"
38
35
 
36
+ [tool.ruff]
37
+ extend-exclude = ["tutorials/*"]
38
+
39
+ [tool.ruff.lint]
40
+ extend-select = [
41
+ "D", # Pydocstyle
42
+ "E", # Pycodestyle errors
43
+ "W", # Pycodestyle warnings
44
+ "ANN", # flake8-annotations
45
+ "BLE", # flake8-blind-except
46
+ "B", # flake8-bugbear
47
+ "S", # flake8-bandit
48
+ "A", # flake8-builtins
49
+ "C4", # flake8-comprehensions
50
+ "T100", # flake8-debugger
51
+ "EM", # flake8-errmsg
52
+ "FIX", # flake8-fixme
53
+ "INP", # flake8-no-pep420
54
+ "PIE", # flake8-pie
55
+ "PYI", # flake8-pyi
56
+ "PT", # flake8-pytest-style
57
+ "RSE", # flake8-raise
58
+ "RET", # flake8-return
59
+ "SIM", # flake8-simplify
60
+ "TC", # flake8-type-checking
61
+ "ARG", # flake8-unused-arguments
62
+ "PTH", # flake8-use-pathlib
63
+ "I", # isort
64
+ "NPY", # Numpy specific rules
65
+ "UP", # Pyupgrade
66
+ "F", # pyflakes
67
+ "PL", # pylint
68
+ "FURB", # refurb
69
+ "RUF", # ruff
70
+ "PGH", # pygrep-hooks
71
+ "PERF", # perflint
72
+ "N", # pep8-naming
73
+ ]
74
+ ignore = ["D203", "D213", "PLR2004", "PLR091"]
75
+
76
+ [tool.ruff.lint.extend-per-file-ignores]
77
+ "tests/*" = ["D1", "S101", "ARG"]
78
+ "*.pyi" = ["N802"]
79
+
39
80
  [tool.scikit-build]
40
81
  minimum-version = "0.11.6"
41
82
  build-dir = "build/{wheel_tag}"
@@ -57,12 +98,7 @@ before-all = "brew install boost"
57
98
 
58
99
  [tool.cibuildwheel.windows]
59
100
  environment = { BOOST_ROOT = "C:/vcpkg/installed/x64-windows" }
60
- before-all = [
61
- "vcpkg install boost-polygon:x64-windows",
62
- ]
101
+ before-all = ["vcpkg install boost-polygon:x64-windows"]
63
102
 
64
103
  [dependency-groups]
65
- dev = [
66
- "pytest==9.0.2",
67
- "ruff==0.15.1",
68
- ]
104
+ dev = ["pytest==9.0.2", "pytest-cov==7.1.0", "ruff==0.15.1"]
@@ -1,9 +1,9 @@
1
- """
1
+ """The Python version of Sample Maker.
2
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.
3
+ Sample Maker is a scripting tool for designing lithographic masks in the GDSII format.
4
+ The package `samplemaker` comes with different tools and submodules for the creation
5
+ and manipulation of basic shapes, periodic shapes, sequences (e.g. waveguides),
6
+ circuits, and complex devices.
7
7
 
8
8
  The code has been developed primarily for nanophotonics, but it can be easily extended
9
9
  to different applications in micro and nano device fabrication.
@@ -13,9 +13,9 @@ University of Copenhagen). It is based on the MATLAB(R) code developed by Leonar
13
13
  Midolo between 2013 and 2019. The first version of the rewritten Python code has been
14
14
  released in October 2021.
15
15
 
16
- This software has been realized with the financial support from
17
- the European Research Council (ERC) under the European Unions Horizon 2020 research and
18
- innovation programme (Grant agreement No. 949043, NANOMEQ).
16
+ This software has been realized with the financial support from the European Research
17
+ Council (ERC) under the European Union's Horizon 2020 research and innovation programme
18
+ (Grant agreement No. 949043, NANOMEQ).
19
19
 
20
20
  .. include:: ./documentation.md
21
21
  """
@@ -0,0 +1,142 @@
1
+ """Common helper functions used for backwards compatibility.
2
+
3
+ The main purpose of these functions is to assist in replacing uppercase function/method
4
+ keyword arguments with lowercase ones, while still allowing the uppercase ones to be
5
+ used for a transition period.
6
+
7
+ Optional argument example
8
+ -------------------------
9
+
10
+ To replace an optional argument (in this case Npts -> npts), do the following:
11
+
12
+ def my_func(npts: int = 100, **kwargs: int) -> None:
13
+ npts = get_optional_kwarg("npts", npts, 100, "Npts", kwargs)
14
+ ensure_empty_kwargs("my_func", kwargs)
15
+
16
+ # rest of function body
17
+ ...
18
+
19
+ Required argument example
20
+ -------------------------
21
+
22
+ To replace a required argument (in this case Npts -> npts), do the following:
23
+
24
+ def my_func(npts: int | MissingType = MISSING, **kwargs: int) -> None:
25
+ npts = get_kwarg("npts", npts, "Npts", kwargs)
26
+ ensure_empty_kwargs("my_func", kwargs)
27
+ check_missing_args("my_func", npts=npts)
28
+
29
+ # Filter out the MissingType type hint for the rest of the function body.
30
+ npts = ensure_arg_type("npts", npts)
31
+
32
+ # Rest of function body
33
+ ...
34
+
35
+ If there are required arguments following the one being replaced, they should also be
36
+ given a default value of MISSING, after which check_missing_args should be called with
37
+ all of the required arguments:
38
+
39
+ def my_func(
40
+ npts: int | MissingType = MISSING,
41
+ other_arg: str | MissingType = MISSING,
42
+ **kwargs: int,
43
+ ) -> None:
44
+ npts = get_kwarg("npts", npts, "Npts", kwargs)
45
+ ensure_empty_kwargs("my_func", kwargs)
46
+ check_missing_args("my_func", npts=npts, other_arg=other_arg)
47
+
48
+ # We cast both npts and other_arg this time
49
+ npts = ensure_arg_type("npts", npts)
50
+ other_arg = ensure_arg_type("other_arg", other_arg)
51
+
52
+ # Rest of function body
53
+ ...
54
+
55
+ """
56
+
57
+ import warnings
58
+ from typing import Any, TypeVar
59
+
60
+ T = TypeVar("T")
61
+
62
+
63
+ # Sentinel for missing arguments
64
+ class MissingType:
65
+ pass
66
+
67
+
68
+ MISSING = MissingType()
69
+
70
+
71
+ def get_kwarg(
72
+ new_name: str, new_value: T | MissingType, legacy_name: str, kwargs: dict[str, T]
73
+ ) -> T | MissingType:
74
+ if legacy_name in kwargs:
75
+ if not isinstance(new_value, MissingType):
76
+ msg = (
77
+ f"Cannot specify both {new_name} and {legacy_name}. "
78
+ f"Please use {new_name} only."
79
+ )
80
+ raise TypeError(msg)
81
+ warnings.warn(
82
+ f"Passing {legacy_name} as a keyword argument is deprecated and "
83
+ f"will be removed in a future version. Use {new_name} instead.",
84
+ DeprecationWarning,
85
+ stacklevel=3,
86
+ )
87
+ return kwargs.pop(legacy_name)
88
+ return new_value
89
+
90
+
91
+ def get_optional_kwarg(
92
+ new_name: str,
93
+ new_value: T,
94
+ default_value: T,
95
+ legacy_name: str,
96
+ kwargs: dict[str, T],
97
+ ) -> T:
98
+ if legacy_name in kwargs:
99
+ if new_value != default_value:
100
+ msg = (
101
+ f"Cannot specify both {new_name} and {legacy_name}. "
102
+ f"Please use {new_name} only."
103
+ )
104
+ raise TypeError(msg)
105
+ warnings.warn(
106
+ f"Passing {legacy_name} as a keyword argument is deprecated and "
107
+ f"will be removed in a future version. Use {new_name} instead.",
108
+ DeprecationWarning,
109
+ stacklevel=3,
110
+ )
111
+ return kwargs.pop(legacy_name)
112
+ return new_value
113
+
114
+
115
+ def check_missing_args(func_name: str, **kwargs: Any) -> None: # noqa: ANN401
116
+ missing_args = [name for name, value in kwargs.items() if value is MISSING]
117
+ if missing_args:
118
+ n_missing = len(missing_args)
119
+ if n_missing == 1:
120
+ arg_word = "argument"
121
+ arg_str = missing_args[0]
122
+ else:
123
+ arg_word = "arguments"
124
+ arg_str = ", ".join(missing_args[:-1]) + f", and {missing_args[-1]}"
125
+ msg = f"{func_name}() missing {n_missing} required {arg_word}: {arg_str}"
126
+ raise TypeError(msg)
127
+
128
+
129
+ def ensure_arg_type(name: str, value: T | MissingType) -> T:
130
+ if isinstance(value, MissingType):
131
+ msg = f"Missing required argument, {name}."
132
+ raise TypeError(msg)
133
+ return value
134
+
135
+
136
+ def ensure_empty_kwargs(func_name: str, kwargs: dict[str, Any]) -> None:
137
+ if not kwargs:
138
+ return
139
+ arg_word = "argument" if len(kwargs) == 1 else "arguments"
140
+ unexpected = ", ".join(kwargs.keys())
141
+ msg = f"{func_name}() got unexpected keyword {arg_word}: {unexpected}"
142
+ raise TypeError(msg)
@@ -1,3 +1,5 @@
1
+ """Example device library."""
2
+
1
3
  import samplemaker.baselib.devices # noqa: F401
2
4
 
3
5
  print("Base library loaded")
@@ -1,5 +1,4 @@
1
- """
2
- Base device library.
1
+ """Base device library.
3
2
 
4
3
  This is a collection of some simple demo devices distributed with the base version of
5
4
  `samplemaker`. It can be used as template for creating new libraries or to learn how to
@@ -15,15 +14,32 @@ import numpy as np
15
14
 
16
15
  import samplemaker.makers as sm
17
16
  from samplemaker.baselib.waveguides import BaseWaveguidePort, BaseWaveguideSequencer
18
- from samplemaker.devices import Device, registerDevicesInModule
17
+ from samplemaker.devices import Device, register_devices_in_module
18
+ from samplemaker.shapes import GeomGroup
19
19
 
20
20
 
21
21
  class CrossMark(Device):
22
- def initialize(self):
22
+ """Generic cross marker for lithographic mask alignment."""
23
+
24
+ def initialize(self) -> None:
25
+ """Initialize the cross mark device.
26
+
27
+ Returns
28
+ -------
29
+ None
30
+
31
+ """
23
32
  self.set_name("BASELIB_CMARK")
24
33
  self.set_description("Generic cross marker for mask alignment.")
25
34
 
26
- def parameters(self):
35
+ def parameters(self) -> None:
36
+ """Define the parameters of the cross mark.
37
+
38
+ Returns
39
+ -------
40
+ None
41
+
42
+ """
27
43
  self.addparameter(
28
44
  param_name="length1",
29
45
  default_value=20,
@@ -69,7 +85,15 @@ class CrossMark(Device):
69
85
  param_type=float,
70
86
  )
71
87
 
72
- def geom(self):
88
+ def geom(self) -> GeomGroup:
89
+ """Define the geometry of the cross mark.
90
+
91
+ Returns
92
+ -------
93
+ GeomGroup
94
+ The geometry of the cross mark.
95
+
96
+ """
73
97
  p = self.get_params()
74
98
  cross = sm.make_rect(0, 0, p["length1"], p["width1"], layer=1)
75
99
  cross += sm.make_rect(0, 0, p["width1"], p["length1"], layer=1)
@@ -92,11 +116,27 @@ class CrossMark(Device):
92
116
 
93
117
 
94
118
  class DirectionalCoupler(Device):
95
- def initialize(self):
119
+ """Simple symmetric directional coupler."""
120
+
121
+ def initialize(self) -> None:
122
+ """Initialize the directional coupler device.
123
+
124
+ Returns
125
+ -------
126
+ None
127
+
128
+ """
96
129
  self.set_name("BASELIB_DCPL")
97
130
  self.set_description("Simple symmetric directional coupler")
98
131
 
99
- def parameters(self):
132
+ def parameters(self) -> None:
133
+ """Define the parameters of the directional coupler.
134
+
135
+ Returns
136
+ -------
137
+ None
138
+
139
+ """
100
140
  self.addparameter("length", 20, "Coupling length", float)
101
141
  self.addparameter(
102
142
  param_name="width",
@@ -126,7 +166,15 @@ class DirectionalCoupler(Device):
126
166
  param_range=(3, np.inf),
127
167
  )
128
168
 
129
- def geom(self):
169
+ def geom(self) -> GeomGroup:
170
+ """Define the geometry of the directional coupler.
171
+
172
+ Returns
173
+ -------
174
+ GeomGroup
175
+ The geometry of the directional coupler.
176
+
177
+ """
130
178
  p = self.get_params()
131
179
  # Draw the upper arm, then mirror
132
180
  off = p["input_dist"] / 2
@@ -136,11 +184,11 @@ class DirectionalCoupler(Device):
136
184
  ss = BaseWaveguideSequencer(seq)
137
185
  dc = ss.run()
138
186
  dc2 = dc.copy()
139
- dc2.mirrorX(ltot / 2)
187
+ dc2.mirror_x(ltot / 2)
140
188
  dc += dc2
141
189
  dc.translate(-ltot / 2, off + p["gap"] / 2 + p["width"] / 2)
142
190
  dc3 = dc.copy()
143
- dc3.mirrorY(0)
191
+ dc3.mirror_y(0)
144
192
  dc += dc3
145
193
 
146
194
  # Add ports
@@ -157,11 +205,27 @@ class DirectionalCoupler(Device):
157
205
 
158
206
 
159
207
  class FocusingGratingCoupler(Device):
160
- def initialize(self):
208
+ """Apodized focusing grating coupler."""
209
+
210
+ def initialize(self) -> None:
211
+ """Initialize the grating coupler device.
212
+
213
+ Returns
214
+ -------
215
+ None
216
+
217
+ """
161
218
  self.set_name("BASELIB_FGC")
162
219
  self.set_description("Grating coupler demo.")
163
220
 
164
- def parameters(self):
221
+ def parameters(self) -> None:
222
+ """Define the parameters for the grating coupler.
223
+
224
+ Returns
225
+ -------
226
+ None
227
+
228
+ """
165
229
  self.addparameter(
166
230
  param_name="w0",
167
231
  default_value=0.3,
@@ -229,7 +293,15 @@ class FocusingGratingCoupler(Device):
229
293
  param_type=bool,
230
294
  )
231
295
 
232
- def geom(self):
296
+ def geom(self) -> GeomGroup:
297
+ """Define the grating coupler geometry.
298
+
299
+ Returns
300
+ -------
301
+ GeomGroup
302
+ The geometry of the grating coupler.
303
+
304
+ """
233
305
  # Grating first
234
306
  p = self.get_params()
235
307
  theta = math.radians(p["theta"])
@@ -244,7 +316,7 @@ class FocusingGratingCoupler(Device):
244
316
  nr_apo = p["nr_Apo"]
245
317
  ff_coef = p["ff_coef"]
246
318
 
247
- g = sm.GeomGroup()
319
+ g = GeomGroup()
248
320
  for q in range(q0, qn):
249
321
  b = q * p0
250
322
  x0 = b * b * math.sin(theta) / (q * lambda0)
@@ -258,8 +330,8 @@ class FocusingGratingCoupler(Device):
258
330
  g += sm.make_arc(
259
331
  x0=x0,
260
332
  y0=0,
261
- rX=a,
262
- rY=b,
333
+ rx=a,
334
+ ry=b,
263
335
  rot=0,
264
336
  w=w,
265
337
  a1=-div_angle - 5,
@@ -292,4 +364,4 @@ class FocusingGratingCoupler(Device):
292
364
 
293
365
 
294
366
  # Register all devices here in this module
295
- registerDevicesInModule(__name__)
367
+ register_devices_in_module(__name__)