SuPyMode 2.4.3__cp313-cp313-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.
- SuPyMode/__init__.py +9 -0
- SuPyMode/_version.py +24 -0
- SuPyMode/boundaries.cp313-win_amd64.pyd +0 -0
- SuPyMode/directories.py +51 -0
- SuPyMode/eigensolver.cp313-win_amd64.pyd +0 -0
- SuPyMode/mesh.cp313-win_amd64.pyd +0 -0
- SuPyMode/model_parameters.cp313-win_amd64.pyd +0 -0
- SuPyMode/plotter.py +325 -0
- SuPyMode/propagation.py +202 -0
- SuPyMode/representation/__init__.py +23 -0
- SuPyMode/representation/adiabatic.py +111 -0
- SuPyMode/representation/beating_length.py +95 -0
- SuPyMode/representation/beta.py +78 -0
- SuPyMode/representation/eigen_value.py +79 -0
- SuPyMode/representation/field.py +486 -0
- SuPyMode/representation/index.py +80 -0
- SuPyMode/representation/normalized_coupling.py +98 -0
- SuPyMode/solver.py +203 -0
- SuPyMode/supermode.cp313-win_amd64.pyd +0 -0
- SuPyMode/superset.py +701 -0
- SuPyMode/superset_plots.py +497 -0
- SuPyMode/taper.cp313-win_amd64.pyd +0 -0
- SuPyMode/utils.py +196 -0
- SuPyMode/validation_data/SBB_figure_4_16_a/LP01-LP02.csv +98 -0
- SuPyMode/validation_data/SBB_figure_4_16_a/LP01-LP21.csv +88 -0
- SuPyMode/validation_data/SBB_figure_4_16_a/LP01-LP41.csv +86 -0
- SuPyMode/validation_data/SBB_figure_4_16_a/LP11-LP12.csv +82 -0
- SuPyMode/validation_data/SBB_figure_4_16_a/LP11-LP31.csv +95 -0
- SuPyMode/validation_data/SBB_figure_4_16_a/LP11-LP51.csv +81 -0
- SuPyMode/validation_data/SBB_figure_4_16_b/LP01-LP02.csv +285 -0
- SuPyMode/validation_data/SBB_figure_4_16_b/LP01-LP21.csv +75 -0
- SuPyMode/validation_data/SBB_figure_4_16_b/LP01-LP41.csv +237 -0
- SuPyMode/validation_data/SBB_figure_4_16_b/LP11-LP12.csv +126 -0
- SuPyMode/validation_data/SBB_figure_4_16_b/LP11-LP31.csv +156 -0
- SuPyMode/validation_data/SBB_figure_4_16_b/LP11-LP51.csv +224 -0
- SuPyMode/workflow.py +253 -0
- supymode-2.4.3.dist-info/METADATA +241 -0
- supymode-2.4.3.dist-info/RECORD +40 -0
- supymode-2.4.3.dist-info/WHEEL +5 -0
- supymode-2.4.3.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
import numpy
|
|
7
|
+
from matplotlib.backends.backend_pdf import PdfPages
|
|
8
|
+
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
9
|
+
from MPSPlots import helper
|
|
10
|
+
|
|
11
|
+
from SuPyMode.taper import AlphaProfile
|
|
12
|
+
from SuPyMode.supermode import SUPERMODE
|
|
13
|
+
from SuPyMode.utils import (
|
|
14
|
+
parse_filename,
|
|
15
|
+
interpret_slice_number_and_itr,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from SuPyMode.superset import SuperSet
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SuperSetPlots(object):
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def test_valid_input(
|
|
26
|
+
user_input, valid_inputs: list, variable_name: str = ""
|
|
27
|
+
) -> None:
|
|
28
|
+
if user_input not in valid_inputs:
|
|
29
|
+
raise ValueError(
|
|
30
|
+
f"[{variable_name}] user_input: {user_input} argument not valid. Valid choices are: {valid_inputs}"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def interpret_mode_of_interest(
|
|
35
|
+
superset: "SuperSet", mode_of_interest: str | SUPERMODE | list[SUPERMODE]
|
|
36
|
+
) -> list[SUPERMODE]:
|
|
37
|
+
"""
|
|
38
|
+
Resolves the mode of interest from user input to the appropriate list of SUPERMODE instances
|
|
39
|
+
based on the specified criteria or direct references.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
superset : SuperSet
|
|
44
|
+
Superset containing the available modes.
|
|
45
|
+
mode_of_interest : str or SUPERMODE or list of SUPERMODE
|
|
46
|
+
A category ("fundamental", "non-fundamental", or "all"), one mode,
|
|
47
|
+
or an explicit list of modes.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
list of SUPERMODE
|
|
52
|
+
Modes selected by the request.
|
|
53
|
+
|
|
54
|
+
Raises
|
|
55
|
+
------
|
|
56
|
+
ValueError
|
|
57
|
+
If the category or input type is unsupported.
|
|
58
|
+
"""
|
|
59
|
+
from SuPyMode.supermode import SUPERMODE
|
|
60
|
+
|
|
61
|
+
if isinstance(mode_of_interest, str):
|
|
62
|
+
match mode_of_interest:
|
|
63
|
+
case "fundamental":
|
|
64
|
+
return superset.fundamental_supermodes
|
|
65
|
+
case "non-fundamental":
|
|
66
|
+
return superset.non_fundamental_supermodes
|
|
67
|
+
case "all":
|
|
68
|
+
return superset.supermodes
|
|
69
|
+
case _:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"Unrecognized mode category '{mode_of_interest}'. Expected 'fundamental', 'non-fundamental', or 'all'."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if isinstance(mode_of_interest, SUPERMODE):
|
|
75
|
+
return [mode_of_interest]
|
|
76
|
+
|
|
77
|
+
if isinstance(mode_of_interest, list) and all(
|
|
78
|
+
isinstance(item, SUPERMODE) for item in mode_of_interest
|
|
79
|
+
):
|
|
80
|
+
return mode_of_interest
|
|
81
|
+
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"mode_of_interest must be either 'fundamental', 'non-fundamental', 'all', a SUPERMODE instance, or a list of SUPERMODE instances."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
@helper.pre_plot(nrows=1, ncols=1)
|
|
87
|
+
def plot_index(
|
|
88
|
+
self,
|
|
89
|
+
axes: plt.Axes,
|
|
90
|
+
mode_of_interest: list[SUPERMODE] = "all",
|
|
91
|
+
) -> plt.Figure:
|
|
92
|
+
"""
|
|
93
|
+
Plot the effective index for each mode as a function of inverse taper ratio (ITR).
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
axes : matplotlib.axes.Axes
|
|
98
|
+
The axes object on which to plot.
|
|
99
|
+
mode_of_interest : list of SUPERMODE
|
|
100
|
+
List of modes to be plotted.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
plt.Figure
|
|
105
|
+
The figure object containing the generated plots. This can be customized further or saved to a file.
|
|
106
|
+
"""
|
|
107
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
108
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
for mode in mode_of_interest:
|
|
112
|
+
mode.index.plot(ax=axes, show=False)
|
|
113
|
+
|
|
114
|
+
axes.legend()
|
|
115
|
+
|
|
116
|
+
@helper.pre_plot(nrows=1, ncols=1)
|
|
117
|
+
def plot_beta(
|
|
118
|
+
self, axes: plt.Axes, mode_of_interest: list[SUPERMODE] | str = "all"
|
|
119
|
+
) -> plt.Figure:
|
|
120
|
+
"""
|
|
121
|
+
Plot the effective propagation constant for each mode as a function of inverse taper ratio (ITR).
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
axes : matplotlib.axes.Axes
|
|
126
|
+
The axes object on which to plot.
|
|
127
|
+
mode_of_interest : list of SUPERMODE
|
|
128
|
+
List of modes to be plotted.
|
|
129
|
+
|
|
130
|
+
Examples
|
|
131
|
+
--------
|
|
132
|
+
>>> fig, ax = plt.subplots()
|
|
133
|
+
>>> superset_plots.plot_index(ax=ax, mode_of_interest=[mode1, mode2])
|
|
134
|
+
>>> plt.show()
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
plt.Figure
|
|
139
|
+
The figure object containing the generated plots. This can be customized further or saved to a file.
|
|
140
|
+
"""
|
|
141
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
142
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
for mode in mode_of_interest:
|
|
146
|
+
mode.beta.plot(ax=axes, show=False)
|
|
147
|
+
|
|
148
|
+
axes.legend()
|
|
149
|
+
|
|
150
|
+
@helper.pre_plot(nrows=1, ncols=1)
|
|
151
|
+
def plot_eigen_value(
|
|
152
|
+
self, axes: plt.Axes, mode_of_interest: list[SUPERMODE] | str = "all"
|
|
153
|
+
) -> plt.Figure:
|
|
154
|
+
"""
|
|
155
|
+
Plot the computed eigen-values for each mode as a function of inverse taper ratio (ITR).
|
|
156
|
+
|
|
157
|
+
Parameters
|
|
158
|
+
----------
|
|
159
|
+
axes : matplotlib.axes.Axes
|
|
160
|
+
The axes object on which to plot.
|
|
161
|
+
mode_of_interest : list of SUPERMODE
|
|
162
|
+
List of modes to be plotted.
|
|
163
|
+
|
|
164
|
+
Examples
|
|
165
|
+
--------
|
|
166
|
+
>>> fig, ax = plt.subplots()
|
|
167
|
+
>>> superset_plots.plot_index(ax=ax, mode_of_interest=[mode1, mode2])
|
|
168
|
+
>>> plt.show()
|
|
169
|
+
|
|
170
|
+
Returns
|
|
171
|
+
-------
|
|
172
|
+
plt.Figure
|
|
173
|
+
The figure object containing the generated plots. This can be customized further or saved to a file.
|
|
174
|
+
"""
|
|
175
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
176
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
for mode in mode_of_interest:
|
|
180
|
+
mode.eigenvalue.plot(ax=axes, show=False)
|
|
181
|
+
|
|
182
|
+
axes.legend()
|
|
183
|
+
|
|
184
|
+
@helper.pre_plot(nrows=1, ncols=1)
|
|
185
|
+
def plot_beating_length(
|
|
186
|
+
self,
|
|
187
|
+
axes: plt.Axes,
|
|
188
|
+
mode_of_interest: list[SUPERMODE] = "all",
|
|
189
|
+
combination: list = "pairs",
|
|
190
|
+
) -> plt.Figure:
|
|
191
|
+
"""
|
|
192
|
+
Render a figure representing the beating length for each mode combination as a function of inverse taper ratio (ITR).
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
axes : matplotlib.axes.Axes
|
|
197
|
+
The axes object on which to plot.
|
|
198
|
+
mode_of_interest : list of SUPERMODE
|
|
199
|
+
List of modes to be plotted.
|
|
200
|
+
combination : list
|
|
201
|
+
List of mode combinations.
|
|
202
|
+
|
|
203
|
+
Returns
|
|
204
|
+
-------
|
|
205
|
+
plt.Figure
|
|
206
|
+
The figure object containing the generated plots. This can be customized further or saved to a file.
|
|
207
|
+
"""
|
|
208
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
209
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
combination = self.interpret_combination(
|
|
213
|
+
mode_of_interest=mode_of_interest, combination=combination
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
for mode_0, mode_1 in combination:
|
|
217
|
+
mode_0.beating_length.plot(ax=axes, other_supermode=mode_1)
|
|
218
|
+
|
|
219
|
+
axes.legend()
|
|
220
|
+
|
|
221
|
+
@helper.pre_plot(nrows=1, ncols=1)
|
|
222
|
+
def plot_normalized_coupling(
|
|
223
|
+
self,
|
|
224
|
+
axes: plt.Axes,
|
|
225
|
+
mode_of_interest: list[SUPERMODE] | str = "all",
|
|
226
|
+
combination: list | str = "pairs",
|
|
227
|
+
) -> plt.Figure:
|
|
228
|
+
"""
|
|
229
|
+
Render a figure representing the normalized coupling for each mode combination as a function of inverse taper ratio (ITR).
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
ax : matplotlib.axes.Axes
|
|
234
|
+
The axes object on which to plot.
|
|
235
|
+
mode_of_interest : list of SUPERMODE
|
|
236
|
+
List of modes to be plotted.
|
|
237
|
+
combination : list
|
|
238
|
+
List of mode combinations.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
plt.Figure
|
|
243
|
+
The figure object containing the generated plots. This can be customized further or saved to a file.
|
|
244
|
+
"""
|
|
245
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
246
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
combination = self.interpret_combination(
|
|
250
|
+
mode_of_interest=mode_of_interest, combination=combination
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
for mode_0, mode_1 in combination:
|
|
254
|
+
mode_0.normalized_coupling.plot(ax=axes, other_supermode=mode_1, show=False)
|
|
255
|
+
|
|
256
|
+
@helper.pre_plot(nrows=1, ncols=1)
|
|
257
|
+
def plot_adiabatic(
|
|
258
|
+
self,
|
|
259
|
+
axes: plt.Axes,
|
|
260
|
+
mode_of_interest: list[SUPERMODE] | str = "all",
|
|
261
|
+
combination: list | str = "pairs",
|
|
262
|
+
add_profile: list[AlphaProfile] = None,
|
|
263
|
+
) -> plt.Figure:
|
|
264
|
+
"""
|
|
265
|
+
Render a figure representing the adiabatic criterion for each mode combination as a function of inverse taper ratio (ITR).
|
|
266
|
+
|
|
267
|
+
Parameters
|
|
268
|
+
----------
|
|
269
|
+
ax : matplotlib.axes.Axes
|
|
270
|
+
The axes object on which to plot.
|
|
271
|
+
mode_of_interest : list of SUPERMODE
|
|
272
|
+
List of modes to be plotted.
|
|
273
|
+
combination : list
|
|
274
|
+
List of mode combinations.
|
|
275
|
+
add_profile : list of AlphaProfile, optional
|
|
276
|
+
List of profiles to add to the plot (default is an empty list).
|
|
277
|
+
|
|
278
|
+
Returns
|
|
279
|
+
-------
|
|
280
|
+
plt.Figure
|
|
281
|
+
The figure object containing the generated plots. This can be customized further or saved to a file.
|
|
282
|
+
"""
|
|
283
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
284
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
combination = self.interpret_combination(
|
|
288
|
+
mode_of_interest=mode_of_interest, combination=combination
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
for mode_0, mode_1 in combination:
|
|
292
|
+
mode_0.adiabatic.plot(ax=axes, other_supermode=mode_1, show=False)
|
|
293
|
+
|
|
294
|
+
if add_profile is not None:
|
|
295
|
+
for profile in numpy.atleast_1d(add_profile):
|
|
296
|
+
profile.render_adiabatic_factor_vs_itr_on_ax(ax=axes, line_style="--")
|
|
297
|
+
|
|
298
|
+
@helper.post_mpl_plot
|
|
299
|
+
def plot_field(
|
|
300
|
+
self,
|
|
301
|
+
mode_of_interest: list = "all",
|
|
302
|
+
itr_list: list[float] = None,
|
|
303
|
+
slice_list: list[int] = None,
|
|
304
|
+
show_mode_label: bool = True,
|
|
305
|
+
show_itr: bool = True,
|
|
306
|
+
show_slice: bool = True,
|
|
307
|
+
) -> plt.Figure:
|
|
308
|
+
"""
|
|
309
|
+
Render the mode field for different ITR values or slice numbers.
|
|
310
|
+
|
|
311
|
+
Parameters
|
|
312
|
+
----------
|
|
313
|
+
mode_of_interest : list, optional
|
|
314
|
+
List of modes to be plotted (default is 'all').
|
|
315
|
+
itr_list : list of float, optional
|
|
316
|
+
List of ITR values for plotting (default is None).
|
|
317
|
+
slice_list : list of int, optional
|
|
318
|
+
List of slice numbers for plotting (default is None).
|
|
319
|
+
show_mode_label : bool, optional
|
|
320
|
+
Flag to display mode labels (default is True).
|
|
321
|
+
show_itr : bool, optional
|
|
322
|
+
Flag to display ITR values (default is True).
|
|
323
|
+
show_slice : bool, optional
|
|
324
|
+
Flag to display slice numbers (default is True).
|
|
325
|
+
|
|
326
|
+
Returns
|
|
327
|
+
-------
|
|
328
|
+
plt.Figure
|
|
329
|
+
The figure object containing the generated plots. This can be customized further or saved to a file.
|
|
330
|
+
"""
|
|
331
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
332
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Interpret input lists
|
|
336
|
+
slice_list, itr_list = interpret_slice_number_and_itr(
|
|
337
|
+
itr_baseline=self.model_parameters.itr_list,
|
|
338
|
+
itr_list=itr_list,
|
|
339
|
+
slice_list=slice_list,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
mode_of_interest = self.interpret_mode_of_interest(
|
|
343
|
+
superset=self, mode_of_interest=mode_of_interest
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
n_mode = len(mode_of_interest)
|
|
347
|
+
n_slice = len(slice_list)
|
|
348
|
+
grid_size = numpy.array([n_slice, n_mode])
|
|
349
|
+
|
|
350
|
+
figure, axes = plt.subplots(
|
|
351
|
+
*grid_size,
|
|
352
|
+
figsize=3 * numpy.flip(grid_size),
|
|
353
|
+
squeeze=False,
|
|
354
|
+
sharex=False,
|
|
355
|
+
sharey=False,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Plot each mode field on the grid
|
|
359
|
+
for m, mode in enumerate(mode_of_interest):
|
|
360
|
+
for n, slice_number in enumerate(slice_list):
|
|
361
|
+
mode.field.render_on_ax(
|
|
362
|
+
ax=axes[n, m],
|
|
363
|
+
slice_number=slice_number,
|
|
364
|
+
show_mode_label=show_mode_label,
|
|
365
|
+
show_itr=show_itr,
|
|
366
|
+
show_slice=show_slice,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
return figure
|
|
370
|
+
|
|
371
|
+
@helper.post_mpl_plot
|
|
372
|
+
def plot_geometry(self) -> plt.Figure:
|
|
373
|
+
"""
|
|
374
|
+
Render the geometry of the taper on the provided axes.
|
|
375
|
+
|
|
376
|
+
Parameters
|
|
377
|
+
----------
|
|
378
|
+
ax : matplotlib.axes.Axes
|
|
379
|
+
The axes object on which to plot the geometry.
|
|
380
|
+
|
|
381
|
+
Returns
|
|
382
|
+
-------
|
|
383
|
+
plt.Figure
|
|
384
|
+
The figure object containing the generated plot. This can be customized further or saved to a file.
|
|
385
|
+
"""
|
|
386
|
+
figure, ax = plt.subplots(figsize=(5, 5))
|
|
387
|
+
|
|
388
|
+
pcm = ax.pcolormesh(
|
|
389
|
+
self.model_parameters.x_vector,
|
|
390
|
+
self.model_parameters.y_vector,
|
|
391
|
+
self.model_parameters.mesh,
|
|
392
|
+
shading="auto",
|
|
393
|
+
cmap="Blues",
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
ax.set_aspect("equal")
|
|
397
|
+
|
|
398
|
+
divider = make_axes_locatable(ax)
|
|
399
|
+
cax = divider.append_axes("right", size="5%", pad=0.05)
|
|
400
|
+
|
|
401
|
+
figure.colorbar(pcm, cax=cax)
|
|
402
|
+
|
|
403
|
+
return figure
|
|
404
|
+
|
|
405
|
+
def plot(self, plot_type: str, **kwargs) -> None:
|
|
406
|
+
"""
|
|
407
|
+
General plotting function to handle different types of supermode plots.
|
|
408
|
+
|
|
409
|
+
Parameters
|
|
410
|
+
----------
|
|
411
|
+
plot_type : str
|
|
412
|
+
The type of plot to generate. Options include 'index', 'beta', 'eigen-value', etc.
|
|
413
|
+
**kwargs : dict
|
|
414
|
+
Additional keyword arguments for specific plot configurations.
|
|
415
|
+
|
|
416
|
+
Raises
|
|
417
|
+
------
|
|
418
|
+
ValueError
|
|
419
|
+
If an unrecognized plot type is specified.
|
|
420
|
+
"""
|
|
421
|
+
match plot_type.lower().replace("_", "-"):
|
|
422
|
+
case "geometry":
|
|
423
|
+
return self.plot_geometry(**kwargs)
|
|
424
|
+
case "index":
|
|
425
|
+
return self.plot_index(**kwargs)
|
|
426
|
+
case "beta":
|
|
427
|
+
return self.plot_beta(**kwargs)
|
|
428
|
+
case "eigen-value":
|
|
429
|
+
return self.plot_eigen_value(**kwargs)
|
|
430
|
+
case "normalized-coupling":
|
|
431
|
+
return self.plot_normalized_coupling(**kwargs)
|
|
432
|
+
case "overlap":
|
|
433
|
+
return self.plot_overlap(**kwargs)
|
|
434
|
+
case "adiabatic":
|
|
435
|
+
return self.plot_adiabatic(**kwargs)
|
|
436
|
+
case "field":
|
|
437
|
+
return self.plot_field(**kwargs)
|
|
438
|
+
case "beating-length":
|
|
439
|
+
return self.plot_beating_length(**kwargs)
|
|
440
|
+
case "normalized-adiabatic":
|
|
441
|
+
return self.plot_normalized_adiabatic(**kwargs)
|
|
442
|
+
case _:
|
|
443
|
+
raise ValueError(
|
|
444
|
+
f"Invalid plot type: {plot_type}. Options are: geometry, index, beta, eigen-value, adiabatic, normalized-adiabatic, normalized-coupling, field, beating-length"
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
@parse_filename
|
|
448
|
+
def generate_pdf_report(
|
|
449
|
+
self,
|
|
450
|
+
filename: str = "auto",
|
|
451
|
+
itr_list: list[float] | None = None,
|
|
452
|
+
slice_list: list[int] | None = None,
|
|
453
|
+
mode_of_interest: list[SUPERMODE] | str = "all",
|
|
454
|
+
combination: str = "pairs",
|
|
455
|
+
) -> None:
|
|
456
|
+
"""
|
|
457
|
+
Generate a full report of the coupler properties as a PDF file.
|
|
458
|
+
|
|
459
|
+
Parameters
|
|
460
|
+
----------
|
|
461
|
+
filename : str, optional, default="auto"
|
|
462
|
+
Name of the report file to be output. If "auto", a default name will be generated based on the current timestamp.
|
|
463
|
+
directory : str, optional, default='.'
|
|
464
|
+
Directory to save the report.
|
|
465
|
+
itr_list : list of float, optional, default=None
|
|
466
|
+
List of ITR values to evaluate the mode field. If None, all available ITR values will be used.
|
|
467
|
+
slice_list : list of int, optional, default=None
|
|
468
|
+
List of slice values to evaluate the mode field. If None, all available slices will be used.
|
|
469
|
+
mode_of_interest : list, optional, default='all'
|
|
470
|
+
List of modes to consider in the report. If 'all', all available modes will be included.
|
|
471
|
+
combination : str, optional, default='specific'
|
|
472
|
+
Method for selecting mode combinations ('specific' or 'pairs').
|
|
473
|
+
|
|
474
|
+
Examples
|
|
475
|
+
--------
|
|
476
|
+
>>> superset_plots.generate_pdf_report(filename="coupler_report", itr_list=[0.1, 0.2, 0.5], mode_of_interest=['LP01', 'LP11'])
|
|
477
|
+
This will generate a PDF report named 'coupler_report.pdf' containing plots for the specified modes at given ITR values.
|
|
478
|
+
"""
|
|
479
|
+
kwargs = dict(show=False, mode_of_interest=mode_of_interest)
|
|
480
|
+
|
|
481
|
+
figure_list = [
|
|
482
|
+
self.plot_geometry(show=False),
|
|
483
|
+
self.plot_field(itr_list=itr_list, slice_list=slice_list, **kwargs),
|
|
484
|
+
self.plot_index(**kwargs),
|
|
485
|
+
self.plot_beta(**kwargs),
|
|
486
|
+
self.plot_normalized_coupling(**kwargs, combination=combination),
|
|
487
|
+
self.plot_adiabatic(show=False, combination=combination),
|
|
488
|
+
]
|
|
489
|
+
|
|
490
|
+
pp = PdfPages(filename.with_suffix(".pdf"))
|
|
491
|
+
|
|
492
|
+
for fig in figure_list:
|
|
493
|
+
fig.savefig(pp, format="pdf")
|
|
494
|
+
|
|
495
|
+
pp.close()
|
|
496
|
+
|
|
497
|
+
plt.close()
|
|
Binary file
|
SuPyMode/utils.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
from typing import Iterable, Callable, List, Union
|
|
4
|
+
import numpy
|
|
5
|
+
import pickle
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from pathvalidate import sanitize_filepath
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
from SuPyMode.directories import user_data_directory
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def interpret_slice_number_and_itr(
|
|
15
|
+
itr_baseline: numpy.ndarray,
|
|
16
|
+
slice_list: Union[int, List[int], None] = None,
|
|
17
|
+
itr_list: Union[float, List[float], None] = None,
|
|
18
|
+
sort_slice_number: bool = True,
|
|
19
|
+
) -> tuple:
|
|
20
|
+
"""
|
|
21
|
+
Interprets slice numbers and corresponding inverse taper ratios (ITRs), returning arrays of slice numbers
|
|
22
|
+
and their respective ITRs based on the provided lists of slices and ITRs.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
- itr_baseline (numpy.ndarray): Array of baseline ITR values, typically equidistant, representing the full range.
|
|
26
|
+
- slice_list (Union[int, List[int]], optional): A single slice index or a list of slice indices. If None and itr_list is also None, defaults to [0, -1].
|
|
27
|
+
- itr_list (Union[float, List[float]], optional): A single ITR value or a list of ITR values to be converted to slice indices. If provided, slice_list defaults to an empty list unless specified.
|
|
28
|
+
- sort_slice_number (bool, optional): Whether to sort the resulting slice numbers and corresponding ITRs in descending order. Default is False.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
- Tuple[numpy.ndarray, numpy.ndarray]: Two numpy arrays, the first containing slice indices and the second containing the corresponding ITR values.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
- ValueError: If the provided ITRs or slice indices are outside the bounds of the baseline ITR array.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if slice_list is None and itr_list is None:
|
|
38
|
+
slice_list = [0, -1]
|
|
39
|
+
elif itr_list is not None and slice_list is None:
|
|
40
|
+
slice_list = []
|
|
41
|
+
|
|
42
|
+
slice_list = numpy.atleast_1d(slice_list)
|
|
43
|
+
itr_list = numpy.atleast_1d(itr_list) if itr_list is not None else numpy.array([])
|
|
44
|
+
|
|
45
|
+
# Logic to convert ITRs to slice indices, and combine lists
|
|
46
|
+
slice_from_itr = (
|
|
47
|
+
itr_to_slice(itr_baseline, itr=itr_list)
|
|
48
|
+
if itr_list.size > 0
|
|
49
|
+
else numpy.array([])
|
|
50
|
+
)
|
|
51
|
+
total_slice_list = numpy.concatenate([slice_list, slice_from_itr]).astype(int)
|
|
52
|
+
|
|
53
|
+
total_itr_list = slice_to_itr(itr_baseline, slice_number=total_slice_list)
|
|
54
|
+
|
|
55
|
+
# Sort if required
|
|
56
|
+
if sort_slice_number:
|
|
57
|
+
indices = numpy.argsort(total_itr_list)
|
|
58
|
+
total_slice_list = total_slice_list[indices][::-1] # Descending order
|
|
59
|
+
total_itr_list = total_itr_list[indices][::-1]
|
|
60
|
+
|
|
61
|
+
return total_slice_list, total_itr_list
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def parse_filename(save_function: Callable) -> Callable:
|
|
65
|
+
@wraps(save_function)
|
|
66
|
+
def wrapper(superset, filename: str, directory: str = "auto", **kwargs):
|
|
67
|
+
directory = user_data_directory if directory == "auto" else directory
|
|
68
|
+
|
|
69
|
+
filename = Path(filename)
|
|
70
|
+
|
|
71
|
+
filename = sanitize_filepath(filename)
|
|
72
|
+
|
|
73
|
+
filename = Path(directory).joinpath(filename)
|
|
74
|
+
|
|
75
|
+
save_function(superset, filename=filename, **kwargs)
|
|
76
|
+
|
|
77
|
+
logging.info(f"Saving data into: {filename}")
|
|
78
|
+
|
|
79
|
+
return filename
|
|
80
|
+
|
|
81
|
+
return wrapper
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def load_superset(filename: str, directory: str = "default"):
|
|
85
|
+
"""
|
|
86
|
+
Saves the superset instance as a serialized pickle file.
|
|
87
|
+
|
|
88
|
+
:param filename: The filename
|
|
89
|
+
:type filename: str
|
|
90
|
+
"""
|
|
91
|
+
if directory == "default":
|
|
92
|
+
directory = user_data_directory
|
|
93
|
+
|
|
94
|
+
filename = Path(directory).joinpath(filename).with_suffix(".pickle")
|
|
95
|
+
|
|
96
|
+
with open(filename, "rb") as input_file:
|
|
97
|
+
superset = pickle.load(input_file)
|
|
98
|
+
|
|
99
|
+
return superset
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def itr_to_slice(itr_list: numpy.ndarray, itr: float | list) -> int | list:
|
|
103
|
+
"""
|
|
104
|
+
Converts an inverse taper ratio (itr) or a list of itrs to their closest corresponding slice numbers
|
|
105
|
+
in the provided itr_list. If a single itr is provided, returns a single slice number. If a list of
|
|
106
|
+
itrs is provided, returns a list of slice numbers.
|
|
107
|
+
|
|
108
|
+
Parameters:
|
|
109
|
+
itr_list (np.ndarray): Array of itrs, typically representing a continuous range of values.
|
|
110
|
+
itr (Union[float, List[float]]): A single itr value or a list of itr values.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Union[int, List[int]]: The closest slice number or list of slice numbers corresponding to the provided itr or itrs.
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
if numpy.isscalar(itr):
|
|
117
|
+
return numpy.argmin(abs(itr_list - itr))
|
|
118
|
+
|
|
119
|
+
if isinstance(itr_list, Iterable):
|
|
120
|
+
return [numpy.argmin(abs(itr_list - value)) for value in itr]
|
|
121
|
+
|
|
122
|
+
raise TypeError("itr must be a float or a list of floats.")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def slice_to_itr(itr_list: numpy.ndarray, slice_number: int | list) -> float | list:
|
|
126
|
+
"""
|
|
127
|
+
Converts a slice number or a list of slice numbers to their corresponding inverse taper ratios (itrs)
|
|
128
|
+
from the provided itr_list. If a single slice number is provided, returns a single itr value.
|
|
129
|
+
If a list of slice numbers is provided, returns a list of itr values.
|
|
130
|
+
|
|
131
|
+
Parameters:
|
|
132
|
+
- itr_list (np.ndarray): Array of itrs, typically representing a continuous range of values.
|
|
133
|
+
- slice_number (Union[int, List[int]]): A single slice index or a list of slice indices.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
- Union[float, List[float]]: The itr or list of itrs corresponding to the given slice numbers.
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
if numpy.isscalar(slice_number):
|
|
140
|
+
return itr_list[slice_number]
|
|
141
|
+
|
|
142
|
+
if isinstance(slice_number, Iterable):
|
|
143
|
+
return numpy.take(itr_list, slice_number)
|
|
144
|
+
|
|
145
|
+
raise TypeError("slice_number must be a scalar or a list of scalars.")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_symmetrized_vector(
|
|
149
|
+
vector: numpy.ndarray, symmetry_type: str = "last"
|
|
150
|
+
) -> numpy.ndarray:
|
|
151
|
+
"""
|
|
152
|
+
Generate a symmetric version of the input vector based on the specified symmetry type.
|
|
153
|
+
|
|
154
|
+
Parameters:
|
|
155
|
+
-----------
|
|
156
|
+
vector : numpy.ndarray
|
|
157
|
+
A one-dimensional array for which the symmetric version is to be calculated.
|
|
158
|
+
symmetry_type : str, optional
|
|
159
|
+
Type of symmetry to apply. Supported types:
|
|
160
|
+
- 'last': Symmetrize using the last element as reference.
|
|
161
|
+
- 'first': Symmetrize using the first element as reference.
|
|
162
|
+
Default is 'last'.
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
numpy.ndarray
|
|
167
|
+
A new vector that is the symmetrized version of the input vector.
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
-------
|
|
171
|
+
ValueError
|
|
172
|
+
If the input vector is not one-dimensional or the symmetry type is unsupported.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
if vector.ndim != 1:
|
|
176
|
+
raise ValueError(
|
|
177
|
+
f"Expected a 1-dimensional vector, but got a {vector.ndim}-dimensional vector instead."
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if symmetry_type.lower() not in ["last", "first"]:
|
|
181
|
+
raise ValueError("Symmetry type must be 'last' or 'first'.")
|
|
182
|
+
|
|
183
|
+
size = len(vector)
|
|
184
|
+
dx = numpy.diff(vector)[0] # More robust than assuming vector[1] - vector[0]
|
|
185
|
+
|
|
186
|
+
if symmetry_type.lower() == "last":
|
|
187
|
+
start_value = vector[-1]
|
|
188
|
+
expanded = numpy.arange(0, 2 * size - 1) * dx
|
|
189
|
+
return start_value - expanded[::-1] if dx > 0 else start_value + expanded[::-1]
|
|
190
|
+
else: # 'first'
|
|
191
|
+
start_value = vector[0]
|
|
192
|
+
expanded = numpy.arange(0, 2 * size - 1) * dx
|
|
193
|
+
return start_value + expanded
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# -
|