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,486 @@
|
|
|
1
|
+
# #!/usr/bin/env python
|
|
2
|
+
# # -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import numpy
|
|
5
|
+
from MPSPlots import colormaps
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
8
|
+
from matplotlib import colors
|
|
9
|
+
|
|
10
|
+
from SuPyMode.utils import (
|
|
11
|
+
interpret_slice_number_and_itr,
|
|
12
|
+
slice_to_itr,
|
|
13
|
+
get_symmetrized_vector,
|
|
14
|
+
)
|
|
15
|
+
from SuPyMode.boundaries import BoundaryValue
|
|
16
|
+
from SuPyMode.supermode import SUPERMODE
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Field:
|
|
20
|
+
"""
|
|
21
|
+
Represents a field derived from a supermode in a modal analysis framework.
|
|
22
|
+
|
|
23
|
+
This class extends functionality from a parent supermode class to manage field data operations,
|
|
24
|
+
including retrieving and processing field data for visualization and analysis.
|
|
25
|
+
|
|
26
|
+
Attributes
|
|
27
|
+
----------
|
|
28
|
+
supermode : SUPERMODE
|
|
29
|
+
Reference to the parent supermode object that provides source data.
|
|
30
|
+
data : numpy.ndarray
|
|
31
|
+
The field data retrieved from the parent supermode binding.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, supermode: SUPERMODE):
|
|
35
|
+
"""
|
|
36
|
+
Initialize the Field object with a reference to a parent supermode.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
supermode : SUPERMODE
|
|
41
|
+
The parent supermode from which this field is derived.
|
|
42
|
+
"""
|
|
43
|
+
self.supermode = supermode
|
|
44
|
+
self.data = self.supermode.get_fields()
|
|
45
|
+
|
|
46
|
+
def _get_symmetrize_vector(self, *args, **kwargs):
|
|
47
|
+
"""
|
|
48
|
+
Get the symmetrization vector from the parent supermode.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
Any
|
|
53
|
+
The symmetrization vector computed by the parent supermode.
|
|
54
|
+
"""
|
|
55
|
+
return self.supermode._get_symmetrize_vector(*args, **kwargs)
|
|
56
|
+
|
|
57
|
+
def _get_axis_vector(self, supermode: object, add_symmetries: bool = True) -> tuple:
|
|
58
|
+
"""
|
|
59
|
+
Computes the full axis vectors, optionally including symmetries.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
add_symmetries : bool, optional, default=True
|
|
64
|
+
Whether to include symmetries when computing the axis vectors.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
tuple
|
|
69
|
+
A tuple containing the full x-axis and y-axis vectors.
|
|
70
|
+
"""
|
|
71
|
+
full_x_axis = self.supermode.model_parameters.x_vector
|
|
72
|
+
full_y_axis = self.supermode.model_parameters.y_vector
|
|
73
|
+
|
|
74
|
+
if not add_symmetries:
|
|
75
|
+
return full_x_axis, full_y_axis
|
|
76
|
+
|
|
77
|
+
if supermode.boundaries.right in [
|
|
78
|
+
BoundaryValue.Symmetric,
|
|
79
|
+
BoundaryValue.AntiSymmetric,
|
|
80
|
+
]:
|
|
81
|
+
full_x_axis = get_symmetrized_vector(full_x_axis, symmetry_type="last")
|
|
82
|
+
full_x_axis.sort()
|
|
83
|
+
|
|
84
|
+
if supermode.boundaries.left in [
|
|
85
|
+
BoundaryValue.Symmetric,
|
|
86
|
+
BoundaryValue.AntiSymmetric,
|
|
87
|
+
]:
|
|
88
|
+
full_x_axis = get_symmetrized_vector(full_x_axis, symmetry_type="first")
|
|
89
|
+
full_x_axis.sort()
|
|
90
|
+
|
|
91
|
+
if supermode.boundaries.top in [
|
|
92
|
+
BoundaryValue.Symmetric,
|
|
93
|
+
BoundaryValue.AntiSymmetric,
|
|
94
|
+
]:
|
|
95
|
+
full_y_axis = get_symmetrized_vector(full_y_axis, symmetry_type="last")
|
|
96
|
+
full_y_axis.sort()
|
|
97
|
+
|
|
98
|
+
if supermode.boundaries.bottom in [
|
|
99
|
+
BoundaryValue.Symmetric,
|
|
100
|
+
BoundaryValue.AntiSymmetric,
|
|
101
|
+
]:
|
|
102
|
+
full_y_axis = get_symmetrized_vector(full_y_axis, symmetry_type="first")
|
|
103
|
+
full_y_axis.sort()
|
|
104
|
+
|
|
105
|
+
return full_x_axis, full_y_axis
|
|
106
|
+
|
|
107
|
+
def get_axis(
|
|
108
|
+
self, supermode: object, slice_number: int, add_symmetries: bool = True
|
|
109
|
+
) -> tuple:
|
|
110
|
+
"""
|
|
111
|
+
Computes the scaled axis vectors for a specific slice, optionally including symmetries.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
slice_number : int
|
|
116
|
+
The slice index for which to compute the axis vectors.
|
|
117
|
+
add_symmetries : bool, optional, default=True
|
|
118
|
+
Whether to include symmetries in the computed axis vectors.
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
tuple
|
|
123
|
+
A tuple containing the scaled x-axis and y-axis vectors for the given slice.
|
|
124
|
+
"""
|
|
125
|
+
itr = supermode.model_parameters.itr_list[slice_number]
|
|
126
|
+
x_axis, y_axis = self._get_axis_vector(
|
|
127
|
+
supermode=supermode, add_symmetries=add_symmetries
|
|
128
|
+
)
|
|
129
|
+
return (x_axis * itr, y_axis * itr)
|
|
130
|
+
|
|
131
|
+
def get_norm(self, slice_number: int) -> float:
|
|
132
|
+
"""
|
|
133
|
+
Calculate the norm of the field for a specific slice.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
slice_number : int
|
|
138
|
+
The slice number for which to calculate the norm.
|
|
139
|
+
|
|
140
|
+
Returns
|
|
141
|
+
-------
|
|
142
|
+
float
|
|
143
|
+
The norm of the field for the specified slice.
|
|
144
|
+
"""
|
|
145
|
+
return self.supermode.get_norm(slice_number)
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def itr_list(self) -> numpy.ndarray:
|
|
149
|
+
"""
|
|
150
|
+
Provides a list of iteration indices available for the fields.
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
numpy.ndarray
|
|
155
|
+
An array of iteration indices.
|
|
156
|
+
"""
|
|
157
|
+
return self.supermode.model_parameters.itr_list
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def parent_superset(self) -> object:
|
|
161
|
+
"""
|
|
162
|
+
Access the parent set of the supermode.
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
object
|
|
167
|
+
The parent set object.
|
|
168
|
+
"""
|
|
169
|
+
return self.supermode.parent_set
|
|
170
|
+
|
|
171
|
+
def get_field(
|
|
172
|
+
self, slice_number: int = None, itr: float = None, add_symmetries: bool = True
|
|
173
|
+
) -> numpy.ndarray:
|
|
174
|
+
"""
|
|
175
|
+
Retrieve a specific field adjusted for boundary conditions and optionally add symmetries.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
slice_number : int, optional
|
|
180
|
+
The slice number to retrieve.
|
|
181
|
+
itr : float, optional
|
|
182
|
+
The iteration to use for retrieving the field.
|
|
183
|
+
add_symmetries : bool, optional, default=True
|
|
184
|
+
Whether to add boundary symmetries to the field.
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
numpy.ndarray
|
|
189
|
+
The requested field as a numpy array.
|
|
190
|
+
|
|
191
|
+
Raises
|
|
192
|
+
------
|
|
193
|
+
AssertionError
|
|
194
|
+
If neither or both of `slice_number` and `itr` are defined.
|
|
195
|
+
"""
|
|
196
|
+
slice_list, itr_list = interpret_slice_number_and_itr(
|
|
197
|
+
itr_baseline=self.itr_list, itr_list=itr, slice_list=slice_number
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
fields = self.supermode.get_fields()
|
|
201
|
+
|
|
202
|
+
fields = numpy.take(fields, slice_list, axis=0)
|
|
203
|
+
if add_symmetries:
|
|
204
|
+
fields = self._get_symmetrized_field(fields)
|
|
205
|
+
|
|
206
|
+
return fields
|
|
207
|
+
|
|
208
|
+
def normalize_field(
|
|
209
|
+
self, field: numpy.ndarray, itr: float, norm_type: str = "L2"
|
|
210
|
+
) -> numpy.ndarray:
|
|
211
|
+
"""
|
|
212
|
+
Normalize a field array based on a specified normalization method.
|
|
213
|
+
|
|
214
|
+
Parameters
|
|
215
|
+
----------
|
|
216
|
+
field : numpy.ndarray
|
|
217
|
+
The field to normalize.
|
|
218
|
+
itr : float
|
|
219
|
+
The iteration value for normalization scaling.
|
|
220
|
+
norm_type : str, optional, default='L2'
|
|
221
|
+
The type of normalization ('max', 'center', 'L2', 'cmt').
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
numpy.ndarray
|
|
226
|
+
The normalized field.
|
|
227
|
+
"""
|
|
228
|
+
match norm_type.lower():
|
|
229
|
+
case "max":
|
|
230
|
+
norm = abs(field).max()
|
|
231
|
+
case "center":
|
|
232
|
+
idx_x_center = numpy.argmin(
|
|
233
|
+
abs(self.supermode.parent_set.coordinate_system.x_vector)
|
|
234
|
+
)
|
|
235
|
+
idx_y_center = numpy.argmin(
|
|
236
|
+
abs(self.supermode.parent_set.coordinate_system.y_vector)
|
|
237
|
+
)
|
|
238
|
+
center_value = field[idx_x_center, idx_y_center]
|
|
239
|
+
norm = center_value
|
|
240
|
+
case "l2":
|
|
241
|
+
dx_scaled = self.supermode.parent_set.coordinate_system.dx * itr
|
|
242
|
+
dy_scaled = self.supermode.parent_set.coordinate_system.dy * itr
|
|
243
|
+
norm = numpy.sqrt(
|
|
244
|
+
numpy.trapz(
|
|
245
|
+
numpy.trapz(numpy.square(field), dx=dy_scaled, axis=0),
|
|
246
|
+
dx=dx_scaled,
|
|
247
|
+
axis=0,
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
case "cmt":
|
|
251
|
+
dx_scaled = self.supermode.parent_set.coordinate_system.dx * itr
|
|
252
|
+
dy_scaled = self.supermode.parent_set.coordinate_system.dy * itr
|
|
253
|
+
norm = numpy.sqrt(
|
|
254
|
+
numpy.trapz(
|
|
255
|
+
numpy.trapz(numpy.square(field), dx=dx_scaled, axis=0),
|
|
256
|
+
dx=dy_scaled,
|
|
257
|
+
axis=0,
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
return field / norm
|
|
262
|
+
|
|
263
|
+
def _get_symmetrized_field_and_axis(self, field: numpy.ndarray) -> tuple:
|
|
264
|
+
"""
|
|
265
|
+
Generate a symmetrical version of the input field mesh according to defined boundary conditions.
|
|
266
|
+
|
|
267
|
+
Parameters
|
|
268
|
+
----------
|
|
269
|
+
field : numpy.ndarray
|
|
270
|
+
The 2D field mesh to be symmetrized.
|
|
271
|
+
|
|
272
|
+
Returns
|
|
273
|
+
-------
|
|
274
|
+
tuple
|
|
275
|
+
A tuple containing the x-axis, y-axis, and the symmetrized field.
|
|
276
|
+
|
|
277
|
+
Raises
|
|
278
|
+
------
|
|
279
|
+
AssertionError
|
|
280
|
+
If the input is not a 2D array.
|
|
281
|
+
"""
|
|
282
|
+
x_axis, y_axis = self._get_axis_vector(add_symmetries=True)
|
|
283
|
+
|
|
284
|
+
field = self._get_symmetrized_field(field=field)
|
|
285
|
+
|
|
286
|
+
return x_axis, y_axis, field
|
|
287
|
+
|
|
288
|
+
def _get_symmetrized_field(self, field: numpy.ndarray) -> numpy.ndarray:
|
|
289
|
+
"""
|
|
290
|
+
Retrieve the field and axis data adjusted for symmetry.
|
|
291
|
+
|
|
292
|
+
This method generates symmetrized versions of the field and its corresponding axis vectors.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
field : numpy.ndarray
|
|
297
|
+
The field data array to be symmetrized.
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
numpy.ndarray
|
|
302
|
+
The symmetrized field data.
|
|
303
|
+
|
|
304
|
+
Raises
|
|
305
|
+
------
|
|
306
|
+
AssertionError
|
|
307
|
+
If the input field is not a 2D array.
|
|
308
|
+
"""
|
|
309
|
+
field = field.squeeze()
|
|
310
|
+
assert (
|
|
311
|
+
field.ndim == 2
|
|
312
|
+
), f"Expected a 2-dimensional array, but got {field.ndim}-dimensional."
|
|
313
|
+
|
|
314
|
+
symmetric_field = field[:, -2::-1]
|
|
315
|
+
match self.supermode.boundaries.left:
|
|
316
|
+
case BoundaryValue.Symmetric:
|
|
317
|
+
field = numpy.c_[symmetric_field, field]
|
|
318
|
+
|
|
319
|
+
case BoundaryValue.AntiSymmetric:
|
|
320
|
+
field = numpy.c_[-symmetric_field, field]
|
|
321
|
+
|
|
322
|
+
match self.supermode.boundaries.right:
|
|
323
|
+
case BoundaryValue.Symmetric:
|
|
324
|
+
field = numpy.c_[field, symmetric_field]
|
|
325
|
+
|
|
326
|
+
case BoundaryValue.AntiSymmetric:
|
|
327
|
+
field = numpy.c_[field, -symmetric_field]
|
|
328
|
+
|
|
329
|
+
symmetric_field = field[-2::-1, :]
|
|
330
|
+
match self.supermode.boundaries.top:
|
|
331
|
+
case BoundaryValue.Symmetric:
|
|
332
|
+
field = numpy.r_[field, symmetric_field]
|
|
333
|
+
|
|
334
|
+
case BoundaryValue.AntiSymmetric:
|
|
335
|
+
field = numpy.r_[field, -symmetric_field]
|
|
336
|
+
|
|
337
|
+
match self.supermode.boundaries.bottom:
|
|
338
|
+
case BoundaryValue.Symmetric:
|
|
339
|
+
field = numpy.r_[symmetric_field, field]
|
|
340
|
+
|
|
341
|
+
case BoundaryValue.AntiSymmetric:
|
|
342
|
+
field = numpy.r_[-symmetric_field, field]
|
|
343
|
+
|
|
344
|
+
return field
|
|
345
|
+
|
|
346
|
+
def plot(
|
|
347
|
+
self,
|
|
348
|
+
itr_list: list[float] = [],
|
|
349
|
+
slice_list: list[int] = [0, -1],
|
|
350
|
+
add_symmetries: bool = True,
|
|
351
|
+
show_colorbar: bool = False,
|
|
352
|
+
show: bool = True,
|
|
353
|
+
) -> None:
|
|
354
|
+
"""
|
|
355
|
+
Plot the field for specified iterations or slice numbers.
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
itr_list : list of float, optional
|
|
360
|
+
List of iterations to evaluate the field.
|
|
361
|
+
slice_list : list of int, optional, default=[0, -1]
|
|
362
|
+
List of slices to evaluate the field.
|
|
363
|
+
add_symmetries : bool, optional, default=True
|
|
364
|
+
Whether to include boundary symmetries in the plot.
|
|
365
|
+
show_mode_label : bool, optional, default=True
|
|
366
|
+
Whether to show the mode label.
|
|
367
|
+
show_itr : bool, optional, default=True
|
|
368
|
+
Whether to show the iteration value.
|
|
369
|
+
show_colorbar : bool, optional, default=False
|
|
370
|
+
Whether to show the colorbar.
|
|
371
|
+
show_slice : bool, optional, default=True
|
|
372
|
+
Whether to show the slice number.
|
|
373
|
+
"""
|
|
374
|
+
slice_list, itr_list = interpret_slice_number_and_itr(
|
|
375
|
+
itr_baseline=self.itr_list, itr_list=itr_list, slice_list=slice_list
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
unit_size = numpy.array([len(slice_list), 1])
|
|
379
|
+
figure, axes = plt.subplots(*unit_size, figsize=3 * numpy.flip(unit_size))
|
|
380
|
+
|
|
381
|
+
for n, (itr, slice_number) in enumerate(zip(itr_list, slice_list)):
|
|
382
|
+
self.render_on_ax(
|
|
383
|
+
ax=axes[n],
|
|
384
|
+
slice_number=slice_number,
|
|
385
|
+
add_symmetries=add_symmetries,
|
|
386
|
+
show_colorbar=show_colorbar,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
plt.tight_layout()
|
|
390
|
+
|
|
391
|
+
if show:
|
|
392
|
+
plt.show()
|
|
393
|
+
|
|
394
|
+
def render_on_ax(
|
|
395
|
+
self,
|
|
396
|
+
ax: plt.Axes,
|
|
397
|
+
slice_number: int,
|
|
398
|
+
show_mode_label: bool = True,
|
|
399
|
+
show_itr: bool = True,
|
|
400
|
+
show_slice: bool = True,
|
|
401
|
+
show_colorbar: bool = False,
|
|
402
|
+
add_symmetries: bool = True,
|
|
403
|
+
) -> None:
|
|
404
|
+
"""
|
|
405
|
+
Render the mode field at the given slice number into the input axis.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
ax (plt.Axes): The matplotlib axis to render the field on.
|
|
409
|
+
slice_number (int): The slice number to render.
|
|
410
|
+
show_mode_label (bool): Whether to show the mode label.
|
|
411
|
+
show_itr (bool): Whether to show the iteration value.
|
|
412
|
+
show_slice (bool): Whether to show the slice number.
|
|
413
|
+
show_colorbar (bool): Whether to show the colorbar.
|
|
414
|
+
add_symmetries (bool): Whether to include boundary symmetries.
|
|
415
|
+
"""
|
|
416
|
+
title = self.get_plot_mode_field_title(
|
|
417
|
+
slice_number=slice_number,
|
|
418
|
+
show_mode_label=show_mode_label,
|
|
419
|
+
show_itr=show_itr,
|
|
420
|
+
show_slice=show_slice,
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
ax.set_title(title)
|
|
424
|
+
ax.set_aspect("equal")
|
|
425
|
+
|
|
426
|
+
field = self.get_field(slice_number=slice_number, add_symmetries=add_symmetries)
|
|
427
|
+
|
|
428
|
+
x, y = self.get_axis(
|
|
429
|
+
supermode=self.supermode,
|
|
430
|
+
slice_number=slice_number,
|
|
431
|
+
add_symmetries=add_symmetries,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
mappable = ax.pcolormesh(
|
|
435
|
+
x * 1e6,
|
|
436
|
+
y * 1e6,
|
|
437
|
+
field,
|
|
438
|
+
cmap=colormaps.blue_black_red,
|
|
439
|
+
norm=colors.CenteredNorm(),
|
|
440
|
+
)
|
|
441
|
+
plt.setp(ax.get_xticklabels(), visible=False)
|
|
442
|
+
|
|
443
|
+
if show_colorbar:
|
|
444
|
+
divider = make_axes_locatable(ax)
|
|
445
|
+
cax = divider.append_axes("right", size="5%", pad=0.05)
|
|
446
|
+
|
|
447
|
+
figure = ax.get_figure()
|
|
448
|
+
figure.colorbar(mappable, cax=cax, orientation="vertical")
|
|
449
|
+
|
|
450
|
+
def get_plot_mode_field_title(
|
|
451
|
+
self, slice_number: int, show_mode_label: bool, show_itr: bool, show_slice: bool
|
|
452
|
+
) -> str:
|
|
453
|
+
"""
|
|
454
|
+
Constructs a title for the field plot based on the mode, iteration, and slice number.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
slice_number (int): The slice number for which the field is plotted.
|
|
458
|
+
show_mode_label (bool): Flag to include the mode label in the title.
|
|
459
|
+
show_itr (bool): Flag to include the iteration number in the title.
|
|
460
|
+
show_slice (bool): Flag to include the slice number in the title.
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
str: The constructed title for the plot.
|
|
464
|
+
"""
|
|
465
|
+
title = ""
|
|
466
|
+
|
|
467
|
+
if show_mode_label:
|
|
468
|
+
title += f"{self.supermode.stylized_label}"
|
|
469
|
+
|
|
470
|
+
if show_itr or show_slice:
|
|
471
|
+
itr = slice_to_itr(
|
|
472
|
+
itr_list=self.supermode.model_parameters.itr_list,
|
|
473
|
+
slice_number=slice_number,
|
|
474
|
+
)
|
|
475
|
+
title += "\n"
|
|
476
|
+
|
|
477
|
+
if show_slice:
|
|
478
|
+
title += f"slice: {slice_number}"
|
|
479
|
+
|
|
480
|
+
if show_itr:
|
|
481
|
+
title += f" itr: {itr:.3f}"
|
|
482
|
+
|
|
483
|
+
return title
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
# -
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# #!/usr/bin/env python
|
|
2
|
+
# # -*- coding: utf-8 -*-
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from MPSPlots.helper import post_mpl_plot
|
|
5
|
+
|
|
6
|
+
from SuPyMode.supermode import SUPERMODE
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Index:
|
|
10
|
+
"""
|
|
11
|
+
Represents the effective refractive index of a mode derived from a supermode in optical fiber simulations.
|
|
12
|
+
|
|
13
|
+
This class extends from `InheritFromSuperMode` for accessing supermode-related data and `BaseSingleModePlot`
|
|
14
|
+
for plotting functionalities tailored to visualize the effective refractive index.
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
supermode : SUPERMODE
|
|
19
|
+
The supermode instance to which this Index object is linked.
|
|
20
|
+
data : numpy.ndarray
|
|
21
|
+
The effective refractive index data for the mode derived from the parent supermode.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, supermode: SUPERMODE):
|
|
25
|
+
"""
|
|
26
|
+
Initialize an Index object with a reference to a parent supermode.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
supermode : SUPERMODE
|
|
31
|
+
The parent supermode object that provides the base mode data.
|
|
32
|
+
"""
|
|
33
|
+
self.supermode = supermode
|
|
34
|
+
self.data = self.supermode.get_index()
|
|
35
|
+
|
|
36
|
+
@post_mpl_plot
|
|
37
|
+
def plot(self, ax: plt.Axes = None, show: bool = True) -> plt.Figure:
|
|
38
|
+
"""
|
|
39
|
+
Plot the propagation constant for a single mode.
|
|
40
|
+
|
|
41
|
+
This method generates a plot of the propagation constants as a function of the inverse taper ratio (ITR),
|
|
42
|
+
formatted according to the predefined plot style.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
ax : matplotlib.axes.Axes, optional
|
|
47
|
+
The axis on which to plot. If `None`, a new axis is created (default is `None`).
|
|
48
|
+
show : bool, optional
|
|
49
|
+
Whether to display the plot immediately (default is `True`).
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
matplotlib.figure.Figure
|
|
54
|
+
The figure object containing the generated plot.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
if ax is None:
|
|
58
|
+
figure, ax = plt.subplots(1, 1)
|
|
59
|
+
else:
|
|
60
|
+
figure = ax.figure
|
|
61
|
+
|
|
62
|
+
ax.plot(
|
|
63
|
+
self.supermode.model_parameters.itr_list,
|
|
64
|
+
self.data,
|
|
65
|
+
label=f"{self.supermode.stylized_label}",
|
|
66
|
+
linewidth=2,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
ax.legend()
|
|
70
|
+
|
|
71
|
+
ax.set(
|
|
72
|
+
xlabel="Inverse taper ratio",
|
|
73
|
+
ylabel="Effective refractive index",
|
|
74
|
+
ylim=[1.44, 1.455],
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return figure
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# -
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# #!/usr/bin/env python
|
|
2
|
+
# # -*- coding: utf-8 -*-
|
|
3
|
+
import numpy
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
from MPSPlots.helper import post_mpl_plot
|
|
6
|
+
|
|
7
|
+
from SuPyMode.supermode import SUPERMODE
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NormalizedCoupling:
|
|
11
|
+
"""
|
|
12
|
+
Represents the normalized mode coupling between modes of different supermodes in optical fiber simulations.
|
|
13
|
+
|
|
14
|
+
This class extends from `InheritFromSuperMode` for accessing supermode-related data and provides plotting functionalities
|
|
15
|
+
tailored to visualize mode coupling comparisons.
|
|
16
|
+
|
|
17
|
+
Attributes
|
|
18
|
+
----------
|
|
19
|
+
supermode : SUPERMODE
|
|
20
|
+
The supermode instance to which this NormalizedCoupling object is linked.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, supermode: SUPERMODE):
|
|
25
|
+
"""
|
|
26
|
+
Initialize a NormalizedCoupling object with a reference to a parent supermode.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
supermode : SUPERMODE
|
|
31
|
+
The parent supermode object that provides the base mode data.
|
|
32
|
+
"""
|
|
33
|
+
self.supermode = supermode
|
|
34
|
+
|
|
35
|
+
def get_values(self, other_supermode: SUPERMODE) -> numpy.ndarray:
|
|
36
|
+
"""
|
|
37
|
+
Calculate the normalized mode coupling between the parent supermode and another specified supermode.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
other_supermode : SUPERMODE
|
|
42
|
+
The supermode with which to compare the parent supermode.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
numpy.ndarray
|
|
47
|
+
An array of normalized mode coupling values, adjusted for computational compatibility.
|
|
48
|
+
"""
|
|
49
|
+
output = self.supermode.get_normalized_coupling_with_mode(other_supermode)
|
|
50
|
+
|
|
51
|
+
if not self.supermode.is_computation_compatible(other_supermode):
|
|
52
|
+
output *= 0
|
|
53
|
+
|
|
54
|
+
return output
|
|
55
|
+
|
|
56
|
+
@post_mpl_plot
|
|
57
|
+
def plot(self, other_supermode: SUPERMODE, ax: plt.Axes = None) -> plt.Figure:
|
|
58
|
+
"""
|
|
59
|
+
Plot the normalized mode coupling between the parent supermode and another specified supermode.
|
|
60
|
+
|
|
61
|
+
This method generates a plot of the normalized coupling as a function of the inverse taper ratio (ITR),
|
|
62
|
+
formatted according to the predefined plot style.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
other_supermode : SUPERMODE
|
|
67
|
+
The supermode to compare against.
|
|
68
|
+
ax : matplotlib.axes.Axes
|
|
69
|
+
The axis on which to plot.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
matplotlib.figure.Figure
|
|
74
|
+
The figure object containing the generated plot.
|
|
75
|
+
"""
|
|
76
|
+
if ax is None:
|
|
77
|
+
figure, ax = plt.subplots(nrows=1, ncols=1)
|
|
78
|
+
else:
|
|
79
|
+
figure = ax.figure
|
|
80
|
+
|
|
81
|
+
ax.set_xlabel("Inverse taper ratio")
|
|
82
|
+
ax.set_ylabel("Mode coupling")
|
|
83
|
+
|
|
84
|
+
if not self.supermode.is_computation_compatible(other_supermode):
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
y = self.get_values(other_supermode=other_supermode)
|
|
88
|
+
|
|
89
|
+
label = f"{self.supermode.stylized_label} - {other_supermode.stylized_label}"
|
|
90
|
+
|
|
91
|
+
ax.plot(self.supermode.model_parameters.itr_list, abs(y), label=label)
|
|
92
|
+
|
|
93
|
+
ax.legend()
|
|
94
|
+
|
|
95
|
+
return figure
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# -
|