photonforge 1.3.1__cp310-cp310-win_amd64.whl → 1.3.2__cp310-cp310-win_amd64.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.
- photonforge/__init__.py +17 -12
- photonforge/_backend/default_project.py +398 -22
- photonforge/circuit_base.py +5 -40
- photonforge/extension.cp310-win_amd64.pyd +0 -0
- photonforge/live_viewer.py +2 -2
- photonforge/{analytic_models.py → models/analytic.py} +47 -23
- photonforge/models/circuit.py +684 -0
- photonforge/{data_model.py → models/data.py} +4 -4
- photonforge/{tidy3d_model.py → models/tidy3d.py} +772 -10
- photonforge/parametric.py +60 -28
- photonforge/plotting.py +1 -1
- photonforge/pretty.py +1 -1
- photonforge/thumbnails/electrical_absolute.svg +8 -0
- photonforge/thumbnails/electrical_adder.svg +9 -0
- photonforge/thumbnails/electrical_amplifier.svg +5 -0
- photonforge/thumbnails/electrical_differential.svg +6 -0
- photonforge/thumbnails/electrical_integral.svg +8 -0
- photonforge/thumbnails/electrical_multiplier.svg +9 -0
- photonforge/thumbnails/filter.svg +8 -0
- photonforge/thumbnails/optical_amplifier.svg +5 -0
- photonforge/thumbnails.py +10 -38
- photonforge/time_steppers/amplifier.py +353 -0
- photonforge/{analytic_time_steppers.py → time_steppers/analytic.py} +191 -2
- photonforge/{circuit_time_stepper.py → time_steppers/circuit.py} +6 -5
- photonforge/time_steppers/filter.py +400 -0
- photonforge/time_steppers/math.py +331 -0
- photonforge/{modulator_time_steppers.py → time_steppers/modulator.py} +9 -20
- photonforge/{s_matrix_time_stepper.py → time_steppers/s_matrix.py} +3 -3
- photonforge/{sink_time_steppers.py → time_steppers/sink.py} +6 -8
- photonforge/{source_time_steppers.py → time_steppers/source.py} +20 -18
- photonforge/typing.py +5 -0
- photonforge/utils.py +89 -15
- {photonforge-1.3.1.dist-info → photonforge-1.3.2.dist-info}/METADATA +2 -2
- {photonforge-1.3.1.dist-info → photonforge-1.3.2.dist-info}/RECORD +37 -27
- photonforge/circuit_model.py +0 -335
- photonforge/eme_model.py +0 -816
- {photonforge-1.3.1.dist-info → photonforge-1.3.2.dist-info}/WHEEL +0 -0
- {photonforge-1.3.1.dist-info → photonforge-1.3.2.dist-info}/entry_points.txt +0 -0
- {photonforge-1.3.1.dist-info → photonforge-1.3.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
import numpy
|
|
5
|
+
|
|
6
|
+
from ..extension import (
|
|
7
|
+
Component,
|
|
8
|
+
Expression,
|
|
9
|
+
TimeStepper,
|
|
10
|
+
register_time_stepper_class,
|
|
11
|
+
)
|
|
12
|
+
from ..typing import FieldAmplitude, annotate
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ExpressionTimeStepper(TimeStepper):
|
|
16
|
+
r"""Expression time stepper for electrical signals.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
expressions: Dictionary of expression strings for all output ports,
|
|
20
|
+
indexed by name.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
Example time stepper to be used on a component with 3 electrical
|
|
24
|
+
ports: "E0", "E1", "E2". The first has a 0.1 reflection coefficient,
|
|
25
|
+
the second has no output (zero, by default), and the third has a
|
|
26
|
+
non-linear combination of all 3 inputs.
|
|
27
|
+
|
|
28
|
+
>>> time_stepper = pf.ExpressionTimeStepper(
|
|
29
|
+
... expressions={
|
|
30
|
+
... "E0": "0.1 * E0",
|
|
31
|
+
... "E2": "0.5 * E0 * E2 + abs(E1) * E1",
|
|
32
|
+
... }
|
|
33
|
+
... )
|
|
34
|
+
|
|
35
|
+
Note:
|
|
36
|
+
Expressions can use all operators supported by :class:`Expression`.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, *, expressions: dict[str, str]):
|
|
40
|
+
super().__init__(expressions=expressions)
|
|
41
|
+
|
|
42
|
+
def setup_state(self, *, component: Component, **kwargs):
|
|
43
|
+
"""Initialize internal state.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
component: Component representing the time stepper.
|
|
47
|
+
kwargs: Unused.
|
|
48
|
+
"""
|
|
49
|
+
expressions = self.parametric_kwargs["expressions"]
|
|
50
|
+
component_ports = sorted(component.select_ports("electrical"))
|
|
51
|
+
if any(port not in component_ports for port in expressions):
|
|
52
|
+
raise RuntimeError(
|
|
53
|
+
f"Not all ports required by ExpressionTimeStepper were found in component "
|
|
54
|
+
f"{component.name!r}."
|
|
55
|
+
)
|
|
56
|
+
self.keys = tuple(port + "@0" for port in component_ports)
|
|
57
|
+
self.expression = Expression(
|
|
58
|
+
component_ports, [expressions.get(port, "0.0") for port in component_ports]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def step_single(
|
|
62
|
+
self,
|
|
63
|
+
inputs: numpy.ndarray,
|
|
64
|
+
outputs: numpy.ndarray,
|
|
65
|
+
time_index: int,
|
|
66
|
+
update_state: bool,
|
|
67
|
+
shutdown: bool,
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Take a single time step on the given inputs.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
inputs: Input values at the current time step. Must be a 1D array of
|
|
73
|
+
complex values ordered
|
|
74
|
+
according to :attr:`keys`.
|
|
75
|
+
outputs: Pre-allocated output array where results will be stored.
|
|
76
|
+
Same size and type as ``inputs``.
|
|
77
|
+
time_index: Time series index for the current input.
|
|
78
|
+
update_state: Whether to update the internal stepper state.
|
|
79
|
+
shutdown: Whether this is the last call to the single stepping
|
|
80
|
+
function for the provided :class:`TimeSeries`.
|
|
81
|
+
"""
|
|
82
|
+
numpy.copyto(outputs, self.expression(*inputs.real))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class DifferentialTimeStepper(TimeStepper):
|
|
86
|
+
r"""Time stepper for differentiating electrical signals in time.
|
|
87
|
+
|
|
88
|
+
The "backwards" scheme, implements:
|
|
89
|
+
|
|
90
|
+
.. math:: y_k = s \frac{x_k - x_{k-1}}{\Delta t}
|
|
91
|
+
|
|
92
|
+
whereas the "central" scheme uses:
|
|
93
|
+
|
|
94
|
+
.. math:: y_k = s \frac{x_k - x_{k-2}}{2 \Delta t}
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
scheme: Differentiation scheme.
|
|
98
|
+
scale: Output scaling factor :math:`s`.
|
|
99
|
+
output_port: Name of the port used as output. If ``None``, the last
|
|
100
|
+
port is used (in the sorted list of ports).
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
*,
|
|
106
|
+
scheme: Literal["backwards", "central"] = "backwards",
|
|
107
|
+
scale: annotate(float, units="s") = 1.0,
|
|
108
|
+
output_port: str | None = None,
|
|
109
|
+
):
|
|
110
|
+
super().__init__(scheme=scheme, scale=scale, output_port=output_port)
|
|
111
|
+
|
|
112
|
+
def setup_state(self, *, time_step: float, component: Component, **kwargs):
|
|
113
|
+
"""Initialize internal state.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
time_step: The interval between time steps (in seconds).
|
|
117
|
+
component: Component representing the time stepper.
|
|
118
|
+
kwargs: Unused.
|
|
119
|
+
"""
|
|
120
|
+
p = self.parametric_kwargs
|
|
121
|
+
component_ports = sorted(component.select_ports("electrical"))
|
|
122
|
+
if len(component_ports) != 2:
|
|
123
|
+
raise RuntimeError(
|
|
124
|
+
f"DifferentialTimeStepper can only be used on components with 2 electrical "
|
|
125
|
+
f"ports. Component {component.name!r} has {len(component_ports)}."
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
output_port = p["output_port"]
|
|
129
|
+
if output_port == component_ports[0]:
|
|
130
|
+
component_ports = (component_ports[1], component_ports[0])
|
|
131
|
+
elif output_port is not None and output_port != component_ports[1]:
|
|
132
|
+
raise RuntimeError(
|
|
133
|
+
f"Port {output_port!r} required by DifferentialTimeStepper was not found in "
|
|
134
|
+
f"component {component.name!r}."
|
|
135
|
+
)
|
|
136
|
+
self.keys = tuple(p + "@0" for p in component_ports)
|
|
137
|
+
|
|
138
|
+
scheme = p["scheme"]
|
|
139
|
+
if scheme not in ("backwards", "central"):
|
|
140
|
+
raise ValueError("DifferentialTimeStepper 'scheme' must be 'backwards' or 'central'.")
|
|
141
|
+
self._buffer = numpy.empty(1 if scheme == "backwards" else 2)
|
|
142
|
+
self._scale = p["scale"] / (self._buffer.size * time_step)
|
|
143
|
+
|
|
144
|
+
self.reset()
|
|
145
|
+
|
|
146
|
+
def reset(self):
|
|
147
|
+
self._buffer[:] = 0.0
|
|
148
|
+
|
|
149
|
+
def step_single(
|
|
150
|
+
self,
|
|
151
|
+
inputs: numpy.ndarray,
|
|
152
|
+
outputs: numpy.ndarray,
|
|
153
|
+
time_index: int,
|
|
154
|
+
update_state: bool,
|
|
155
|
+
shutdown: bool,
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Take a single time step on the given inputs.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
inputs: Input values at the current time step. Must be a 1D array of
|
|
161
|
+
complex values ordered
|
|
162
|
+
according to :attr:`keys`.
|
|
163
|
+
outputs: Pre-allocated output array where results will be stored.
|
|
164
|
+
Same size and type as ``inputs``.
|
|
165
|
+
time_index: Time series index for the current input.
|
|
166
|
+
update_state: Whether to update the internal stepper state.
|
|
167
|
+
shutdown: Whether this is the last call to the single stepping
|
|
168
|
+
function for the provided :class:`TimeSeries`.
|
|
169
|
+
"""
|
|
170
|
+
x = inputs[0].real
|
|
171
|
+
outputs[1] = self._scale * (x - self._buffer[0])
|
|
172
|
+
|
|
173
|
+
if update_state:
|
|
174
|
+
if self._buffer.size == 1:
|
|
175
|
+
self._buffer[0] = x
|
|
176
|
+
else:
|
|
177
|
+
self._buffer[0] = self._buffer[1]
|
|
178
|
+
self._buffer[1] = x
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class IntegralTimeStepper(TimeStepper):
|
|
182
|
+
r"""Time stepper for integrating electrical signals in time.
|
|
183
|
+
|
|
184
|
+
.. math:: y_k = y_{k - 1} + s x_k \Delta t
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
scale: Input scaling factor :math:`s`.
|
|
188
|
+
start_value: Starting output value after reset.
|
|
189
|
+
limits: Output value limits.
|
|
190
|
+
output_port: Name of the port used as output. If ``None``, the last
|
|
191
|
+
port is used (in the sorted list of ports).
|
|
192
|
+
reset_port: Name of the port used as reset signal. If ``None``, the
|
|
193
|
+
reset feature is disabled.
|
|
194
|
+
reset_trigger: Type of edge used for triggering a reset.
|
|
195
|
+
reset_tolerance: Value change tolerance for triggering a reset.
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
def __init__(
|
|
199
|
+
self,
|
|
200
|
+
*,
|
|
201
|
+
scale: annotate(float, units="s⁻¹") = 1.0,
|
|
202
|
+
start_value: FieldAmplitude = 0.0,
|
|
203
|
+
limits: annotate(Sequence[FieldAmplitude | None], minItems=2, maxItems=2) = (
|
|
204
|
+
None,
|
|
205
|
+
None,
|
|
206
|
+
),
|
|
207
|
+
output_port: str | None = None,
|
|
208
|
+
reset_port: str | None = None,
|
|
209
|
+
reset_trigger: Literal["fall", "rise", "both"] = "rise",
|
|
210
|
+
reset_tolerance: float = 0.0,
|
|
211
|
+
):
|
|
212
|
+
super().__init__(
|
|
213
|
+
scale=scale,
|
|
214
|
+
start_value=start_value,
|
|
215
|
+
limits=limits,
|
|
216
|
+
output_port=output_port,
|
|
217
|
+
reset_port=reset_port,
|
|
218
|
+
reset_trigger=reset_trigger,
|
|
219
|
+
reset_tolerance=reset_tolerance,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def setup_state(self, *, time_step: float, component: Component, **kwargs):
|
|
223
|
+
"""Initialize internal state.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
time_step: The interval between time steps (in seconds).
|
|
227
|
+
component: Component representing the time stepper.
|
|
228
|
+
kwargs: Unused.
|
|
229
|
+
"""
|
|
230
|
+
p = self.parametric_kwargs
|
|
231
|
+
reset_port = p["reset_port"]
|
|
232
|
+
output_port = p["output_port"]
|
|
233
|
+
|
|
234
|
+
required_ports = 2 if reset_port is None else 3
|
|
235
|
+
component_ports = sorted(component.select_ports("electrical"))
|
|
236
|
+
if len(component_ports) != required_ports:
|
|
237
|
+
raise RuntimeError(
|
|
238
|
+
f"IntegralTimeStepper can only be used on components with {required_ports} "
|
|
239
|
+
f"electrical ports. Component {component.name!r} has {len(component_ports)}."
|
|
240
|
+
)
|
|
241
|
+
for required in (output_port, reset_port):
|
|
242
|
+
if required is not None and required not in component_ports:
|
|
243
|
+
raise RuntimeError(
|
|
244
|
+
f"Port {required!r} required by IntegralTimeStepper was not found in "
|
|
245
|
+
f"component {component.name!r}."
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if reset_port is not None:
|
|
249
|
+
component_ports.remove(reset_port)
|
|
250
|
+
if reset_port == output_port:
|
|
251
|
+
raise ValueError("'output_port' and 'reset_port' cannot be the same.")
|
|
252
|
+
|
|
253
|
+
if output_port is None:
|
|
254
|
+
input_port, output_port = component_ports
|
|
255
|
+
else:
|
|
256
|
+
component_ports.remove(output_port)
|
|
257
|
+
input_port = component_ports[0]
|
|
258
|
+
|
|
259
|
+
if reset_port is None:
|
|
260
|
+
self.keys = (input_port + "@0", output_port + "@0")
|
|
261
|
+
else:
|
|
262
|
+
self.keys = (input_port + "@0", output_port + "@0", reset_port + "@0")
|
|
263
|
+
|
|
264
|
+
self._reset_trigger = p["reset_trigger"]
|
|
265
|
+
if self._reset_trigger not in ("fall", "rise", "both"):
|
|
266
|
+
raise ValueError(
|
|
267
|
+
"IntegralTimeStepper 'reset_trigger' must be 'fall', 'rise', or 'both'."
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
self._scale = p["scale"] * time_step
|
|
271
|
+
self._y0 = p["start_value"]
|
|
272
|
+
self._reset_tolerance = abs(p["reset_tolerance"])
|
|
273
|
+
|
|
274
|
+
self._y_lo, self._y_hi = p["limits"]
|
|
275
|
+
if self._y_lo is None:
|
|
276
|
+
self._y_lo = -numpy.inf
|
|
277
|
+
if self._y_hi is None:
|
|
278
|
+
self._y_hi = numpy.inf
|
|
279
|
+
|
|
280
|
+
self.reset()
|
|
281
|
+
|
|
282
|
+
def reset(self):
|
|
283
|
+
self._y = self._y0
|
|
284
|
+
self._r = 0.0
|
|
285
|
+
|
|
286
|
+
def step_single(
|
|
287
|
+
self,
|
|
288
|
+
inputs: numpy.ndarray,
|
|
289
|
+
outputs: numpy.ndarray,
|
|
290
|
+
time_index: int,
|
|
291
|
+
update_state: bool,
|
|
292
|
+
shutdown: bool,
|
|
293
|
+
) -> None:
|
|
294
|
+
"""Take a single time step on the given inputs.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
inputs: Input values at the current time step. Must be a 1D array of
|
|
298
|
+
complex values ordered
|
|
299
|
+
according to :attr:`keys`.
|
|
300
|
+
outputs: Pre-allocated output array where results will be stored.
|
|
301
|
+
Same size and type as ``inputs``.
|
|
302
|
+
time_index: Time series index for the current input.
|
|
303
|
+
update_state: Whether to update the internal stepper state.
|
|
304
|
+
shutdown: Whether this is the last call to the single stepping
|
|
305
|
+
function for the provided :class:`TimeSeries`.
|
|
306
|
+
"""
|
|
307
|
+
if len(self.keys) == 3:
|
|
308
|
+
r = inputs[2].real
|
|
309
|
+
else:
|
|
310
|
+
r = 0.0
|
|
311
|
+
|
|
312
|
+
edge = r - self._r
|
|
313
|
+
if len(self.keys) == 3 and (
|
|
314
|
+
(edge > self._reset_tolerance and self._reset_trigger != "fall")
|
|
315
|
+
or (-edge > self._reset_tolerance and self._reset_trigger != "rise")
|
|
316
|
+
):
|
|
317
|
+
y = self._y0
|
|
318
|
+
else:
|
|
319
|
+
y = self._y + self._scale * inputs[0].real
|
|
320
|
+
|
|
321
|
+
y = min(self._y_hi, max(self._y_lo, y))
|
|
322
|
+
outputs[1] = y
|
|
323
|
+
|
|
324
|
+
if update_state:
|
|
325
|
+
self._y = y
|
|
326
|
+
self._r = r
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
register_time_stepper_class(ExpressionTimeStepper)
|
|
330
|
+
register_time_stepper_class(DifferentialTimeStepper)
|
|
331
|
+
register_time_stepper_class(IntegralTimeStepper)
|
|
@@ -5,9 +5,7 @@ from typing import Literal
|
|
|
5
5
|
|
|
6
6
|
import numpy
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from .circuit_base import _gather_status
|
|
10
|
-
from .extension import (
|
|
8
|
+
from ..extension import (
|
|
11
9
|
Component,
|
|
12
10
|
Interpolator,
|
|
13
11
|
Port,
|
|
@@ -16,8 +14,9 @@ from .extension import (
|
|
|
16
14
|
TimeStepper,
|
|
17
15
|
register_time_stepper_class,
|
|
18
16
|
)
|
|
19
|
-
from .
|
|
20
|
-
from .
|
|
17
|
+
from ..models.analytic import _port_with_x_section
|
|
18
|
+
from ..models.tidy3d import _isotropic_uniform, _ModeSolverRunner
|
|
19
|
+
from ..typing import (
|
|
21
20
|
Coordinate,
|
|
22
21
|
Frequency,
|
|
23
22
|
Impedance,
|
|
@@ -27,7 +26,7 @@ from .typing import (
|
|
|
27
26
|
TimeDelay,
|
|
28
27
|
annotate,
|
|
29
28
|
)
|
|
30
|
-
from
|
|
29
|
+
from ..utils import C_0, _gather_status, route_length
|
|
31
30
|
|
|
32
31
|
|
|
33
32
|
def _impedance_monitor(time_stepper, runner):
|
|
@@ -220,10 +219,7 @@ class PhaseModTimeStepper(TimeStepper):
|
|
|
220
219
|
"PhaseModTimeStepper can only be used in components with 2 optical and 1 "
|
|
221
220
|
"electrical ports."
|
|
222
221
|
)
|
|
223
|
-
self.
|
|
224
|
-
self._opt0 = ports[0] + "@0"
|
|
225
|
-
self._opt1 = ports[1] + "@0"
|
|
226
|
-
self.keys = (self._opt0, self._opt1, self._e)
|
|
222
|
+
self.keys = (ports[0] + "@0", ports[1] + "@0", e_ports[0] + "@0")
|
|
227
223
|
|
|
228
224
|
p = self.parametric_kwargs
|
|
229
225
|
|
|
@@ -308,9 +304,7 @@ class PhaseModTimeStepper(TimeStepper):
|
|
|
308
304
|
if update_state:
|
|
309
305
|
self._v = v
|
|
310
306
|
if self._delay > 0:
|
|
311
|
-
|
|
312
|
-
a1 = inputs[1]
|
|
313
|
-
self._buffer[self._index] = (a0, a1)
|
|
307
|
+
self._buffer[self._index] = inputs[:2]
|
|
314
308
|
self._index = (self._index + 1) % self._delay
|
|
315
309
|
|
|
316
310
|
|
|
@@ -485,10 +479,7 @@ class TerminatedModTimeStepper(TimeStepper):
|
|
|
485
479
|
f"Optical input port {port_name!r} not found in component {component.name!r}."
|
|
486
480
|
)
|
|
487
481
|
e_port = e_ports[0]
|
|
488
|
-
self.
|
|
489
|
-
self._opt0 = ports[0] + "@0"
|
|
490
|
-
self._opt1 = ports[1] + "@0"
|
|
491
|
-
self.keys = (self._opt0, self._opt1, self._e)
|
|
482
|
+
self.keys = (ports[0] + "@0", ports[1] + "@0", e_port + "@0")
|
|
492
483
|
|
|
493
484
|
f_nyquist = 0.5 / time_step
|
|
494
485
|
self._freqs = numpy.linspace(0, f_nyquist, p["fir_taps"])
|
|
@@ -784,9 +775,7 @@ class TerminatedModTimeStepper(TimeStepper):
|
|
|
784
775
|
self._v[1:] = self._v[:-1]
|
|
785
776
|
self._v[0] = v_in
|
|
786
777
|
if self._delay > 0:
|
|
787
|
-
|
|
788
|
-
a1 = inputs[1]
|
|
789
|
-
self._buffer[self._index] = (a0, a1)
|
|
778
|
+
self._buffer[self._index] = inputs[:2]
|
|
790
779
|
self._index = (self._index + 1) % self._delay
|
|
791
780
|
|
|
792
781
|
|
|
@@ -6,8 +6,8 @@ from typing import Any, Literal
|
|
|
6
6
|
|
|
7
7
|
import numpy
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
9
|
+
from ..cache import _pole_residue_fit_cache
|
|
10
|
+
from ..extension import (
|
|
11
11
|
Component,
|
|
12
12
|
PoleResidueMatrix,
|
|
13
13
|
SMatrix,
|
|
@@ -19,7 +19,7 @@ from .extension import (
|
|
|
19
19
|
pole_residue_fit,
|
|
20
20
|
register_time_stepper_class,
|
|
21
21
|
)
|
|
22
|
-
from
|
|
22
|
+
from ..typing import Frequency, NonNegativeInt, PositiveFloat, PositiveInt, TimeDelay
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class SMatrixTimeStepper(TimeStepper):
|
|
@@ -2,9 +2,8 @@ import numpy
|
|
|
2
2
|
from numpy.random import SeedSequence, default_rng
|
|
3
3
|
from scipy.signal import firwin2
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from .typing import (
|
|
5
|
+
from ..extension import Component, Interpolator, TimeStepper, register_time_stepper_class
|
|
6
|
+
from ..typing import (
|
|
8
7
|
Frequency,
|
|
9
8
|
Impedance,
|
|
10
9
|
NonNegativeFloat,
|
|
@@ -13,7 +12,8 @@ from .typing import (
|
|
|
13
12
|
TimeDelay,
|
|
14
13
|
annotate,
|
|
15
14
|
)
|
|
16
|
-
from
|
|
15
|
+
from ..utils import Q as _Q
|
|
16
|
+
from .modulator import _get_impedance_runner
|
|
17
17
|
|
|
18
18
|
_PINK_TAPS = 2048
|
|
19
19
|
_PINK_SAMPLES = 100
|
|
@@ -202,9 +202,7 @@ class PhotodiodeTimeStepper(TimeStepper):
|
|
|
202
202
|
"PhotodiodeTimeStepper can only be used in components with 1 optical port and 1 "
|
|
203
203
|
"electrical port."
|
|
204
204
|
)
|
|
205
|
-
self.
|
|
206
|
-
self._e_port = e_ports[0] + "@0"
|
|
207
|
-
self.keys = (self._port, self._e_port)
|
|
205
|
+
self.keys = (ports[0] + "@0", e_ports[0] + "@0")
|
|
208
206
|
|
|
209
207
|
self._time_step = time_step
|
|
210
208
|
|
|
@@ -222,7 +220,7 @@ class PhotodiodeTimeStepper(TimeStepper):
|
|
|
222
220
|
tau = p["current_time_constant"]
|
|
223
221
|
if tau <= 0:
|
|
224
222
|
tau = 100 * time_step
|
|
225
|
-
self._current_factor = 1 - numpy.exp(-time_step / tau)
|
|
223
|
+
self._current_factor = 1.0 - numpy.exp(-time_step / tau)
|
|
226
224
|
|
|
227
225
|
self._filter = None
|
|
228
226
|
if p["filter_frequency"] > 0:
|
|
@@ -7,7 +7,7 @@ import numpy
|
|
|
7
7
|
from numpy.random import SeedSequence, default_rng
|
|
8
8
|
from scipy.integrate import solve_ivp
|
|
9
9
|
|
|
10
|
-
from
|
|
10
|
+
from ..extension import (
|
|
11
11
|
Component,
|
|
12
12
|
Interpolator,
|
|
13
13
|
TimeStepper,
|
|
@@ -16,9 +16,9 @@ from .extension import (
|
|
|
16
16
|
_prbs31,
|
|
17
17
|
register_time_stepper_class,
|
|
18
18
|
)
|
|
19
|
-
from
|
|
20
|
-
from .typing import (
|
|
19
|
+
from ..typing import (
|
|
21
20
|
Angle,
|
|
21
|
+
FieldAmplitude,
|
|
22
22
|
Fraction,
|
|
23
23
|
Frequency,
|
|
24
24
|
Impedance,
|
|
@@ -30,7 +30,8 @@ from .typing import (
|
|
|
30
30
|
TimeDelay,
|
|
31
31
|
annotate,
|
|
32
32
|
)
|
|
33
|
-
from
|
|
33
|
+
from ..utils import C_0, H, Q
|
|
34
|
+
from .modulator import _get_impedance_runner
|
|
34
35
|
|
|
35
36
|
RIN = annotate(NonNegativeFloat, label="Relative Intensity Noise", units="1/Hz")
|
|
36
37
|
|
|
@@ -110,8 +111,7 @@ class CWLaserTimeStepper(TimeStepper):
|
|
|
110
111
|
raise RuntimeError(
|
|
111
112
|
"CWLaserTimeStepper can only be used in components with 1 optical port."
|
|
112
113
|
)
|
|
113
|
-
self.
|
|
114
|
-
self.keys = (self._port,)
|
|
114
|
+
self.keys = (next(iter(ports)) + "@0",)
|
|
115
115
|
|
|
116
116
|
p = self.parametric_kwargs
|
|
117
117
|
|
|
@@ -347,19 +347,24 @@ class DMLaserTimeStepper(TimeStepper):
|
|
|
347
347
|
"PhotodiodeTimeStepper can only be used in components with 1 optical port and 1 "
|
|
348
348
|
"electrical port."
|
|
349
349
|
)
|
|
350
|
-
self.
|
|
351
|
-
self._e_port = e_ports[0] + "@0"
|
|
352
|
-
self.keys = (self._port, self._e_port)
|
|
350
|
+
self.keys = (ports[0] + "@0", e_ports[0] + "@0")
|
|
353
351
|
|
|
354
352
|
self._time_step = time_step
|
|
355
353
|
|
|
356
354
|
p = self.parametric_kwargs
|
|
355
|
+
|
|
357
356
|
self._r = complex(p["reflection"])
|
|
357
|
+
|
|
358
|
+
n_group = p["n_group"]
|
|
359
|
+
if n_group == 0.0:
|
|
360
|
+
raise RuntimeError("'n_group' cannot be zero.")
|
|
361
|
+
self._vg_a0 = C_0 * 1e-6 / n_group * p["differential_gain"]
|
|
362
|
+
|
|
358
363
|
self._kp = float(
|
|
359
364
|
(H * carrier_frequency * p["quantum_efficiency"] * p["active_region_volume"])
|
|
360
365
|
/ (2.0 * p["confinement_factor"] * p["photon_lifetime"])
|
|
361
366
|
)
|
|
362
|
-
|
|
367
|
+
|
|
363
368
|
self._tau_n = p["carrier_lifetime"]
|
|
364
369
|
self._gamma = p["confinement_factor"]
|
|
365
370
|
self._eps = p["gain_compression_factor"]
|
|
@@ -548,8 +553,7 @@ class OpticalPulseTimeStepper(TimeStepper):
|
|
|
548
553
|
raise RuntimeError(
|
|
549
554
|
"OpticalPulseTimeStepper can only be used in components with 1 optical port."
|
|
550
555
|
)
|
|
551
|
-
self.
|
|
552
|
-
self.keys = (self._port,)
|
|
556
|
+
self.keys = (next(iter(ports)) + "@0",)
|
|
553
557
|
|
|
554
558
|
repetition_rate = p["repetition_rate"]
|
|
555
559
|
if repetition_rate > 0.5 / time_step:
|
|
@@ -747,8 +751,7 @@ class OpticalNoiseTimeStepper(TimeStepper):
|
|
|
747
751
|
raise RuntimeError(
|
|
748
752
|
"OpticalNoiseTimeStepper can only be used in components with 1 optical port."
|
|
749
753
|
)
|
|
750
|
-
self.
|
|
751
|
-
self.keys = (self._port,)
|
|
754
|
+
self.keys = (next(iter(ports)) + "@0",)
|
|
752
755
|
|
|
753
756
|
p = self.parametric_kwargs
|
|
754
757
|
|
|
@@ -835,8 +838,8 @@ class WaveformTimeStepper(TimeStepper):
|
|
|
835
838
|
self,
|
|
836
839
|
*,
|
|
837
840
|
frequency: Frequency,
|
|
838
|
-
amplitude:
|
|
839
|
-
offset:
|
|
841
|
+
amplitude: FieldAmplitude = 1,
|
|
842
|
+
offset: FieldAmplitude = 0,
|
|
840
843
|
start: Time | None = None,
|
|
841
844
|
stop: Time | None = None,
|
|
842
845
|
waveform: Literal["sine", "triangle", "trapezoid", "raised-cosine", "gaussian"] = "sine",
|
|
@@ -883,8 +886,7 @@ class WaveformTimeStepper(TimeStepper):
|
|
|
883
886
|
raise RuntimeError(
|
|
884
887
|
"WaveformTimeStepper can only be used in components with 1 electrical port."
|
|
885
888
|
)
|
|
886
|
-
self.
|
|
887
|
-
self.keys = (self._port,)
|
|
889
|
+
self.keys = (next(iter(ports)) + "@0",)
|
|
888
890
|
|
|
889
891
|
freq = p["frequency"]
|
|
890
892
|
if freq > 0.5 / time_step:
|
photonforge/typing.py
CHANGED
|
@@ -265,7 +265,12 @@ Voltage = annotate(float, units="V") #:
|
|
|
265
265
|
Impedance = annotate(complex, units="Ω") #:
|
|
266
266
|
|
|
267
267
|
Power = annotate(float, minimum=0, units="W") #:
|
|
268
|
+
Power_dBm = annotate(float, units="dBm") #:
|
|
268
269
|
|
|
270
|
+
FieldAmplitude = annotate(float, units="√W") #:
|
|
271
|
+
ComplexFieldAmplitude = annotate(complex, units="√W") #:
|
|
272
|
+
|
|
273
|
+
Gain = annotate(float, units="dB") #:
|
|
269
274
|
Loss = annotate(float, minimum=0, units="dB") #:
|
|
270
275
|
PropagationLoss = annotate(float, minimum=0, units="dB/μm") #:
|
|
271
276
|
|