PyMieSim 3.6.0__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.
Files changed (101) hide show
  1. PyMieSim/__init__.py +16 -0
  2. PyMieSim/__main__.py +9 -0
  3. PyMieSim/_version.py +21 -0
  4. PyMieSim/binary/__init__.py +0 -0
  5. PyMieSim/binary/interface_detector.cp310-win_amd64.pyd +0 -0
  6. PyMieSim/binary/interface_detector.cp311-win_amd64.pyd +0 -0
  7. PyMieSim/binary/interface_detector.cp312-win_amd64.pyd +0 -0
  8. PyMieSim/binary/interface_detector.cp313-win_amd64.pyd +0 -0
  9. PyMieSim/binary/interface_experiment.cp310-win_amd64.pyd +0 -0
  10. PyMieSim/binary/interface_experiment.cp311-win_amd64.pyd +0 -0
  11. PyMieSim/binary/interface_experiment.cp312-win_amd64.pyd +0 -0
  12. PyMieSim/binary/interface_experiment.cp313-win_amd64.pyd +0 -0
  13. PyMieSim/binary/interface_scatterer.cp310-win_amd64.pyd +0 -0
  14. PyMieSim/binary/interface_scatterer.cp311-win_amd64.pyd +0 -0
  15. PyMieSim/binary/interface_scatterer.cp312-win_amd64.pyd +0 -0
  16. PyMieSim/binary/interface_scatterer.cp313-win_amd64.pyd +0 -0
  17. PyMieSim/binary/interface_sets.cp310-win_amd64.pyd +0 -0
  18. PyMieSim/binary/interface_sets.cp311-win_amd64.pyd +0 -0
  19. PyMieSim/binary/interface_sets.cp312-win_amd64.pyd +0 -0
  20. PyMieSim/binary/interface_sets.cp313-win_amd64.pyd +0 -0
  21. PyMieSim/binary/interface_source.cp310-win_amd64.pyd +0 -0
  22. PyMieSim/binary/interface_source.cp311-win_amd64.pyd +0 -0
  23. PyMieSim/binary/interface_source.cp312-win_amd64.pyd +0 -0
  24. PyMieSim/binary/interface_source.cp313-win_amd64.pyd +0 -0
  25. PyMieSim/binary/libcpp_coordinates.a +0 -0
  26. PyMieSim/binary/libcpp_detector.a +0 -0
  27. PyMieSim/binary/libcpp_experiment.a +0 -0
  28. PyMieSim/binary/libcpp_fibonacci.a +0 -0
  29. PyMieSim/binary/libcpp_mode_field.a +0 -0
  30. PyMieSim/binary/libcpp_sets.a +0 -0
  31. PyMieSim/binary/libcpp_source.a +0 -0
  32. PyMieSim/directories.py +31 -0
  33. PyMieSim/experiment/__init__.py +1 -0
  34. PyMieSim/experiment/dataframe_subclass.py +220 -0
  35. PyMieSim/experiment/detector/__init__.py +2 -0
  36. PyMieSim/experiment/detector/base.py +169 -0
  37. PyMieSim/experiment/detector/coherent_mode.py +50 -0
  38. PyMieSim/experiment/detector/photodiode.py +52 -0
  39. PyMieSim/experiment/scatterer/__init__.py +4 -0
  40. PyMieSim/experiment/scatterer/base.py +98 -0
  41. PyMieSim/experiment/scatterer/core_shell.py +82 -0
  42. PyMieSim/experiment/scatterer/cylinder.py +63 -0
  43. PyMieSim/experiment/scatterer/sphere.py +66 -0
  44. PyMieSim/experiment/setup.py +356 -0
  45. PyMieSim/experiment/source/__init__.py +2 -0
  46. PyMieSim/experiment/source/base.py +85 -0
  47. PyMieSim/experiment/source/gaussian.py +60 -0
  48. PyMieSim/experiment/source/planewave.py +69 -0
  49. PyMieSim/experiment/utils.py +132 -0
  50. PyMieSim/gui/__init__.py +0 -0
  51. PyMieSim/gui/helper.py +60 -0
  52. PyMieSim/gui/interface.py +136 -0
  53. PyMieSim/gui/section.py +606 -0
  54. PyMieSim/mesh.py +368 -0
  55. PyMieSim/polarization.py +174 -0
  56. PyMieSim/single/__init__.py +48 -0
  57. PyMieSim/single/detector/__init__.py +2 -0
  58. PyMieSim/single/detector/base.py +271 -0
  59. PyMieSim/single/detector/coherent.py +99 -0
  60. PyMieSim/single/detector/uncoherent.py +105 -0
  61. PyMieSim/single/representations.py +734 -0
  62. PyMieSim/single/scatterer/__init__.py +4 -0
  63. PyMieSim/single/scatterer/base.py +405 -0
  64. PyMieSim/single/scatterer/core_shell.py +126 -0
  65. PyMieSim/single/scatterer/cylinder.py +113 -0
  66. PyMieSim/single/scatterer/sphere.py +108 -0
  67. PyMieSim/single/source/__init__.py +3 -0
  68. PyMieSim/single/source/base.py +7 -0
  69. PyMieSim/single/source/gaussian.py +137 -0
  70. PyMieSim/single/source/planewave.py +97 -0
  71. PyMieSim/special_functions.py +81 -0
  72. PyMieSim/units.py +130 -0
  73. PyMieSim/validation_data/bohren_huffman/figure_810.csv +245 -0
  74. PyMieSim/validation_data/bohren_huffman/figure_87.csv +2 -0
  75. PyMieSim/validation_data/bohren_huffman/figure_88.csv +2 -0
  76. PyMieSim/validation_data/pymiescatt/example_coreshell_0.csv +41 -0
  77. PyMieSim/validation_data/pymiescatt/example_coreshell_1.csv +401 -0
  78. PyMieSim/validation_data/pymiescatt/example_shpere_0.csv +51 -0
  79. PyMieSim/validation_data/pymiescatt/example_shpere_1.csv +801 -0
  80. PyMieSim/validation_data/pymiescatt/example_shpere_2.csv +41 -0
  81. PyMieSim/validation_data/pymiescatt/example_shpere_3.csv +401 -0
  82. PyMieSim/validation_data/pymiescatt/example_sphere_0.csv +51 -0
  83. PyMieSim/validation_data/pymiescatt/example_sphere_1.csv +801 -0
  84. PyMieSim/validation_data/pymiescatt/example_sphere_2.csv +41 -0
  85. PyMieSim/validation_data/pymiescatt/example_sphere_3.csv +401 -0
  86. PyMieSim/validation_data/pymiescatt/validation_Qsca.csv +800 -0
  87. PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_1.csv +400 -0
  88. PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_2.csv +400 -0
  89. PyMieSim/validation_data/pymiescatt/validation_Qsca_medium.csv +800 -0
  90. PyMieSim/validation_data/pymiescatt/validation_coreshell.csv +81 -0
  91. PyMieSim/validation_data/pymiescatt/validation_sphere.csv +801 -0
  92. lib/libZBessel.a +0 -0
  93. lib/lib_ZBessel.a +0 -0
  94. lib/libcpp_base_scatterer.a +0 -0
  95. lib/libcpp_coreshell.a +0 -0
  96. lib/libcpp_cylinder.a +0 -0
  97. lib/libcpp_sphere.a +0 -0
  98. pymiesim-3.6.0.dist-info/METADATA +246 -0
  99. pymiesim-3.6.0.dist-info/RECORD +101 -0
  100. pymiesim-3.6.0.dist-info/WHEEL +5 -0
  101. pymiesim-3.6.0.dist-info/licenses/LICENSE +21 -0
PyMieSim/mesh.py ADDED
@@ -0,0 +1,368 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from typing import Optional
5
+ import numpy
6
+
7
+ from PyMieSim.binary.interface_detector import FIBONACCIMESH
8
+ from PyMieSim import units
9
+
10
+ class FibonacciMesh(FIBONACCIMESH):
11
+ """
12
+ Represents an angular mesh using a Fibonacci sphere distribution, where each point
13
+ covers an equivalent solid angle. This type of mesh is useful for simulating angular
14
+ distributions in light scattering experiments.
15
+
16
+ The mesh is defined by a numerical aperture and a number of sampling points, with
17
+ additional control over the orientation and rotation of the mesh.
18
+
19
+ Parameters
20
+ ----------
21
+ max_angle : float
22
+ The maximum angle in radians, typically defined by the numerical aperture of the imaging system.
23
+ sampling : int
24
+ The number of points distributed across the mesh. Higher values result in finer resolution.
25
+ phi_offset : float
26
+ Angle offset in the azimuthal (parallel to incident light polarization) direction in degrees.
27
+ gamma_offset : float
28
+ Angle offset in the polar (perpendicular to incident light polarization) direction in degrees.
29
+ rotation_angle : Optional[float], default=0.0
30
+ Rotation of the entire mesh around its principal axis, in degrees.
31
+
32
+ Attributes
33
+ ----------
34
+ cartesian_coordinates : numpy.ndarray
35
+ Cartesian coordinates of the mesh points, calculated during initialization.
36
+ d_omega_radian : float
37
+ Differential solid angle element in radians for each point on the mesh.
38
+ d_omega_degree : float
39
+ Differential solid angle element in degrees for each point on the mesh.
40
+ omega_radian : float
41
+ Total solid angle covered by the mesh in radians.
42
+ omega_degree : float
43
+ Total solid angle covered by the mesh in degrees.
44
+
45
+ Methods
46
+ -------
47
+ projection_HV_vector() -> tuple:
48
+ Returns the parallel and perpendicular projections of the mesh on the horizontal and vertical base vectors as vectors.
49
+ projection_HV_scalar() -> tuple:
50
+ Returns the parallel and perpendicular projections of the mesh on the horizontal and vertical base vectors as scalar values.
51
+ projection_on_base_scalar(vector, base_vector) -> numpy.ndarray:
52
+ Computes the scalar projection of a given vector onto a base vector.
53
+ projection_on_base_vector(vector, base_vector) -> numpy.ndarray:
54
+ Computes the vector projection of a given vector onto a base vector.
55
+ get_axis_vector() -> numpy.ndarray:
56
+ Returns the axis vector corresponding to the phi and gamma offsets.
57
+ rotate_around_axis(rotation_angle) -> None:
58
+ Rotates the mesh around its principal axis by a specified angle.
59
+
60
+ """
61
+
62
+ # ---------------------- Validation Methods ----------------------
63
+ def _validate_angle_units(cls, value):
64
+ """
65
+ Ensures that diameter is Quantity objects with length units.
66
+ """
67
+ if not isinstance(value, units.Quantity) or not value.check(units.degree):
68
+ raise ValueError(f"{value} must be a Quantity with angle units [degree/radian].")
69
+
70
+ return value
71
+
72
+ def _validate_AU_units(cls, value):
73
+ """
74
+ Ensures that diameter is Quantity objects with length units.
75
+ """
76
+ if not isinstance(value, units.Quantity) or not value.check(units.AU):
77
+ raise ValueError(f"{value} must be a Quantity with arbitrary units [AU].")
78
+
79
+ return value
80
+
81
+ # ---------------------- Initialization Methods ----------------------
82
+ def __init__(self,
83
+ max_angle: units.Quantity,
84
+ sampling: int,
85
+ phi_offset: units.Quantity,
86
+ gamma_offset: units.Quantity,
87
+ min_angle: units.Quantity = 0 * units.degree,
88
+ rotation_angle: Optional[units.Quantity] = 0.0):
89
+ """
90
+ Initializes the FibonacciMesh with the specified parameters.
91
+
92
+ Parameters
93
+ ----------
94
+ max_angle : units.Quantity
95
+ The maximum angle in radians, typically defined by the numerical aperture of the imaging system.
96
+ sampling : int
97
+ The number of points distributed across the mesh. Higher values result in finer resolution.
98
+ phi_offset : units.Quantity
99
+ Angle offset in the azimuthal (parallel to incident light polarization) direction in degrees.
100
+ gamma_offset : units.Quantity
101
+ Angle offset in the polar (perpendicular to incident light polarization) direction in degrees.
102
+ rotation_angle : Optional[units.Quantity], default=0.0
103
+ Rotation of the entire mesh around its principal axis, in degrees.
104
+ """
105
+ self.sampling = sampling
106
+ self.max_angle = self._validate_angle_units(max_angle)
107
+ self.min_angle = self._validate_angle_units(min_angle)
108
+ phi_offset = self._validate_angle_units(phi_offset)
109
+ gamma_offset = self._validate_angle_units(gamma_offset)
110
+ rotation = self._validate_angle_units(rotation_angle)
111
+
112
+ super().__init__(
113
+ sampling=self.sampling,
114
+ max_angle=self.max_angle,
115
+ min_angle=self.min_angle,
116
+ rotation_angle=rotation,
117
+ phi_offset=numpy.deg2rad(phi_offset),
118
+ gamma_offset=numpy.deg2rad(gamma_offset),
119
+ )
120
+
121
+ self.compute_vector_field()
122
+
123
+ # ---------------------- Property Methods ----------------------
124
+ @property
125
+ def theta(self) -> units.Quantity:
126
+ """
127
+ Returns the polar angles (theta) of the mesh in radians.
128
+
129
+ Returns
130
+ -------
131
+ units.Quantity
132
+ The polar angles in radians.
133
+ """
134
+ return self.spherical.theta * units.radian
135
+
136
+ @property
137
+ def phi(self) -> units.Quantity:
138
+ """
139
+ Returns the azimuthal angles (phi) of the mesh in radians.
140
+
141
+ Returns
142
+ -------
143
+ units.Quantity
144
+ The azimuthal angles in radians.
145
+ """
146
+ return self.spherical.phi * units.radian
147
+
148
+ @property
149
+ def d_omega(self) -> units.Quantity:
150
+ """
151
+ Returns the solid angle (d_omega) of the mesh in steradians.
152
+
153
+ Returns
154
+ -------
155
+ units.Quantity
156
+ The solid angle in steradians.
157
+ """
158
+ return self.spherical._cpp_d_omega * units.steradian
159
+
160
+ @property
161
+ def omega(self) -> units.Quantity:
162
+ """
163
+ Returns the solid angle (omega) of the mesh in steradians.
164
+
165
+ Returns
166
+ -------
167
+ units.Quantity
168
+ The solid angle in steradians.
169
+ """
170
+ return self.spherical._cpp_omega * units.steradian
171
+
172
+ @property
173
+ def phi_offset(self) -> units.Quantity:
174
+ """
175
+ Returns the azimuthal angle offset (phi_offset) in radians.
176
+
177
+ Returns
178
+ -------
179
+ units.Quantity
180
+ The azimuthal angle offset in radians.
181
+ """
182
+ return self._cpp_phi_offset * units.radian
183
+
184
+ @property
185
+ def gamma_offset(self) -> units.Quantity:
186
+ """
187
+ Returns the polar angle offset (gamma_offset) in radians.
188
+
189
+ Returns
190
+ -------
191
+ units.Quantity
192
+ The polar angle offset in radians.
193
+ """
194
+ return self._cpp_gamma_offset * units.radian
195
+
196
+ @property
197
+ def rotation(self) -> units.Quantity:
198
+ """
199
+ Returns the rotation angle of the mesh around its principal axis in radians.
200
+
201
+ Returns
202
+ -------
203
+ units.Quantity
204
+ The rotation angle in radians.
205
+ """
206
+ return self._cpp_rotation * units.radian
207
+
208
+ # ---------------------- Additional Methods ----------------------
209
+ def get_axis_vector(self) -> numpy.ndarray:
210
+ """
211
+ Returns the axis vector corresponding to the phi and gamma offsets.
212
+
213
+ Returns
214
+ -------
215
+ numpy.ndarray
216
+ The axis vector normalized to unit length.
217
+ """
218
+ axis_vector = numpy.array([self.cartesian.x[0], self.cartesian.y[0], self.cartesian.z[0]])
219
+
220
+ norm = numpy.sqrt(numpy.square(axis_vector).sum())
221
+
222
+ return axis_vector / norm
223
+
224
+ def projection_HV_vector(self) -> tuple:
225
+ """
226
+ Projects the vertical and horizontal base vectors onto the parallel and perpendicular components
227
+ of the mesh, returning these projections as vectors.
228
+
229
+ The projections help visualize the relationship between the mesh's angular distribution and
230
+ the incident light polarization directions.
231
+
232
+ Returns
233
+ -------
234
+ tuple of numpy.ndarray
235
+ A tuple where the first element is the projection of the vertical and horizontal base vectors
236
+ onto the parallel vector field, and the second element is the projection onto the perpendicular
237
+ vector field.
238
+ Each of these projections is a 2D array of shape (2, 3), where:
239
+ - The first row corresponds to the projection onto the vertical base vector.
240
+ - The second row corresponds to the projection onto the horizontal base vector.
241
+ """
242
+ parallel_projection = [
243
+ self.projection_on_base_vector(
244
+ vector=self.parallel,
245
+ base_vector=X
246
+ ) for X in [self._cpp_vertical_base, self._cpp_horizontal_base]
247
+ ]
248
+
249
+ perpendicular_projection = [
250
+ self.projection_on_base_vector(
251
+ vector=self.perpendicular,
252
+ base_vector=X
253
+ ) for X in [self._cpp_vertical_base, self._cpp_horizontal_base]
254
+ ]
255
+
256
+ return numpy.array(parallel_projection), numpy.array(perpendicular_projection)
257
+
258
+ def projection_HV_scalar(self) -> tuple:
259
+ """
260
+ Projects the vertical and horizontal base vectors onto the parallel and perpendicular components
261
+ of the mesh, returning these projections as scalar values (dot products).
262
+
263
+ This method calculates the scalar projection, which represents the magnitude of the component
264
+ of the vector field along the base vectors.
265
+
266
+ Returns
267
+ -------
268
+ tuple of numpy.ndarray
269
+ A tuple where the first element is the scalar projection of the vertical and horizontal base vectors
270
+ onto the parallel vector field, and the second element is the scalar projection onto the perpendicular
271
+ vector field.
272
+ Each of these projections is a 1D array of length 2, where:
273
+ - The first element corresponds to the projection onto the vertical base vector.
274
+ - The second element corresponds to the projection onto the horizontal base vector.
275
+
276
+ """
277
+ parallel_projection = [
278
+ self.projection_on_base_scalar(
279
+ vector=self.parallel,
280
+ base_vector=X
281
+ ) for X in [self._cpp_vertical_base, self._cpp_horizontal_base]
282
+ ]
283
+
284
+ perpendicular_projection = [
285
+ self.projection_on_base_scalar(
286
+ vector=self.perpendicular,
287
+ base_vector=X
288
+ ) for X in [self._cpp_vertical_base, self._cpp_horizontal_base]
289
+ ]
290
+
291
+ return numpy.array(parallel_projection), numpy.array(perpendicular_projection)
292
+
293
+ def projection_on_base_scalar(self, vector: numpy.ndarray, base_vector: numpy.ndarray) -> numpy.ndarray:
294
+ r"""
295
+ Computes the scalar projection (dot product) of a given vector onto a base vector.
296
+
297
+ This method calculates the magnitude of the projection of `vector` along the direction
298
+ of `base_vector`. The scalar projection is useful for understanding how much of
299
+ `vector` is aligned with `base_vector`.
300
+
301
+ Parameters
302
+ ----------
303
+ vector : numpy.ndarray
304
+ The vector to be projected. It should be a 1D array representing the vector field component
305
+ (either parallel or perpendicular) of the mesh.
306
+ base_vector : numpy.ndarray
307
+ The base vector onto which the projection is made. It should be a 1D array, typically
308
+ representing the horizontal or vertical base direction.
309
+
310
+ Returns
311
+ -------
312
+ numpy.ndarray
313
+ The scalar projection of `vector` onto `base_vector`, which is a single numeric value.
314
+ This value represents the magnitude of the component of `vector` that is aligned with
315
+ `base_vector`.
316
+
317
+ Notes
318
+ -----
319
+ The scalar projection is calculated as the dot product of `vector` and `base_vector`:
320
+
321
+ .. math::
322
+ \text{scalar projection} = \vec{v} \cdot \vec{b}
323
+
324
+ where :math:`\vec{v}` is the input vector and :math:`\vec{b}` is the base vector.
325
+ """
326
+ return vector._cpp_get_scalar_product(base_vector)
327
+
328
+ def projection_on_base_vector(self, vector: numpy.ndarray, base_vector: numpy.ndarray) -> numpy.ndarray:
329
+ r"""
330
+ Computes the vector projection of a given vector onto a base vector.
331
+
332
+ This method calculates the projection of `vector` along the direction of `base_vector`
333
+ and returns a new vector that represents the component of `vector` aligned with
334
+ `base_vector`. This is useful for decomposing vectors in terms of directional components.
335
+
336
+ Parameters
337
+ ----------
338
+ vector : numpy.ndarray
339
+ The vector to be projected. It should be a 1D array representing the vector field component
340
+ (either parallel or perpendicular) of the mesh.
341
+ base_vector : numpy.ndarray
342
+ The base vector onto which the projection is made. It should be a 1D array, typically
343
+ representing the horizontal or vertical base direction.
344
+
345
+ Returns
346
+ -------
347
+ numpy.ndarray
348
+ The vector projection of `vector` onto `base_vector`, which is a vector in the direction
349
+ of `base_vector`. The resulting vector represents the part of `vector` that lies along
350
+ `base_vector`.
351
+
352
+ Notes
353
+ -----
354
+ The vector projection is calculated as:
355
+
356
+ .. math::
357
+ \text{vector projection} = \left( \frac{\vec{v} \cdot \vec{b}}{\vec{b} \cdot \vec{b}} \right) \vec{b}
358
+
359
+ where :math:`\vec{v}` is the input vector and :math:`\vec{b}` is the base vector. This formula gives the projection
360
+ of `vector` along the direction of `base_vector`.
361
+
362
+ The method first computes the scalar projection of `vector` onto `base_vector` using the dot product, and
363
+ then scales `base_vector` by this scalar to produce the projected vector.
364
+ """
365
+ projection = self.projection_on_base_scalar(vector, base_vector)
366
+ base_projection = numpy.outer(projection, base_vector.data)
367
+
368
+ return base_projection
@@ -0,0 +1,174 @@
1
+ import numpy as np
2
+ from pydantic.dataclasses import dataclass
3
+ from PyMieSim.units import degree, Quantity
4
+ from copy import copy
5
+
6
+ config_dict = dict(
7
+ kw_only=False,
8
+ slots=True,
9
+ extra='forbid',
10
+ arbitrary_types_allowed=True
11
+ )
12
+
13
+
14
+ class BasePolarization:
15
+ """
16
+ Base class for polarization types.
17
+
18
+ This class serves as a parent class for different types of polarizations such as Jones vectors
19
+ and specific polarization states like right circular and left circular polarizations.
20
+
21
+ It provides a foundation for shared functionality across all polarization types.
22
+ """
23
+ pass
24
+
25
+
26
+ @dataclass(config=config_dict, unsafe_hash=True)
27
+ class JonesVector(BasePolarization):
28
+ """
29
+ Represents a general Jones vector for polarization.
30
+
31
+ A Jones vector is a two-dimensional complex vector used to describe the state of polarization
32
+ of electromagnetic waves. This class handles general forms of linear and circular polarization.
33
+
34
+ Parameters
35
+ ----------
36
+ element : list
37
+ A list representing the Jones vector. The list will be converted to a 2D NumPy array
38
+ of complex numbers upon initialization.
39
+
40
+ Attributes
41
+ ----------
42
+ element : numpy.ndarray
43
+ A 2D NumPy array of complex numbers representing the Jones vector.
44
+
45
+ Methods
46
+ -------
47
+ __post_init__():
48
+ Converts `element` to a 2D NumPy array with complex data type after initialization.
49
+
50
+ __iter__():
51
+ Returns an iterator over polarization angles if available, otherwise over the Jones vector elements.
52
+
53
+ __add__(other: BasePolarization) -> BasePolarization:
54
+ Combines two Jones vectors (or derived polarizations) by stacking their elements.
55
+
56
+ __repr__() -> str:
57
+ Returns a string representation of the polarization vector or angle.
58
+
59
+ __str__() -> str:
60
+ Converts the polarization vector or angle to a string format for easy display.
61
+ """
62
+ element: list
63
+
64
+ def __post_init__(self):
65
+ self.element = np.atleast_2d(self.element).astype(complex)
66
+
67
+ def __iter__(self):
68
+ if hasattr(self, 'angle'):
69
+ return iter(self.angle)
70
+ else:
71
+ return iter(self.element)
72
+
73
+ def __add__(self, other: BasePolarization) -> BasePolarization:
74
+ output = copy(self)
75
+
76
+ if hasattr(output, 'angle'):
77
+ del output.angle
78
+
79
+ output.element = np.vstack([self.element, other.element])
80
+
81
+ if hasattr(other, 'angle') and hasattr(self, 'angle'):
82
+ output.angle = np.hstack([self.angle, other.angle])
83
+
84
+ return output
85
+
86
+ def __repr__(self) -> str:
87
+ return self.__str__()
88
+
89
+ def __str__(self) -> str:
90
+ if hasattr(self, 'angle'):
91
+ return self.angle.__str__()
92
+
93
+ return self.element.__str__()
94
+
95
+
96
+ class RightCircular(JonesVector):
97
+ """
98
+ Represents right circular polarization in the Jones formalism.
99
+
100
+ Right circular polarization is a specific form of circular polarization where the electric
101
+ field vector rotates in a right-handed sense around the direction of propagation.
102
+
103
+ Attributes
104
+ ----------
105
+ element : numpy.ndarray
106
+ A Jones vector corresponding to right circular polarization, set to `[1, 1j]`.
107
+
108
+ Methods
109
+ -------
110
+ __init__():
111
+ Initializes the Jones vector for right circular polarization.
112
+ """
113
+ def __init__(self) -> None:
114
+ super().__init__(element=[1, 1j])
115
+
116
+
117
+ class LeftCircular(JonesVector):
118
+ """
119
+ Represents left circular polarization in the Jones formalism.
120
+
121
+ Left circular polarization is a specific form of circular polarization where the electric
122
+ field vector rotates in a left-handed sense around the direction of propagation.
123
+
124
+ Attributes
125
+ ----------
126
+ element : numpy.ndarray
127
+ A Jones vector corresponding to left circular polarization, set to `[1, -1j]`.
128
+
129
+ Methods
130
+ -------
131
+ __init__():
132
+ Initializes the Jones vector for left circular polarization.
133
+ """
134
+ def __init__(self) -> None:
135
+ super().__init__(element=[1, -1j])
136
+
137
+
138
+ @dataclass(config=config_dict, unsafe_hash=True)
139
+ class Linear(JonesVector):
140
+ """
141
+ Represents linear polarization for a given angle or set of angles.
142
+
143
+ Linear polarization occurs when the electric field vector oscillates in a straight line
144
+ along the propagation direction. This class allows for defining linear polarization at any angle
145
+ in the plane perpendicular to the propagation direction.
146
+
147
+ Parameters
148
+ ----------
149
+ element : Quantity
150
+ A `Quantity` object representing the angle(s) of linear polarization. This is converted into
151
+ a Jones vector based on the provided angle in degrees.
152
+
153
+ Attributes
154
+ ----------
155
+ element : numpy.ndarray
156
+ A 2D NumPy array representing the Jones vector for linear polarization.
157
+ angle : numpy.ndarray
158
+ Array of polarization angles in the appropriate units (degrees).
159
+
160
+ Methods
161
+ -------
162
+ __post_init__():
163
+ Converts the input `element` (polarization angle) to a Jones vector representation
164
+ and stores it in `element`. It also stores the polarization angle in degrees.
165
+ """
166
+ element: Quantity
167
+
168
+ def __post_init__(self):
169
+ self.angle = np.atleast_1d(self.element.magnitude) * self.element.units
170
+ angle = self.element.to(degree).magnitude
171
+
172
+ self.element = np.empty([len(self.angle), 2]).astype(complex)
173
+ self.element[:, 0] = np.cos(np.deg2rad(angle))
174
+ self.element[:, 1] = np.sin(np.deg2rad(angle))
@@ -0,0 +1,48 @@
1
+ from .scatterer import Sphere, CoreShell, Cylinder # noqa: F401
2
+ from .source import PlaneWave, Gaussian # noqa: F401
3
+ from .detector import Photodiode, CoherentMode # noqa: F401
4
+
5
+ import pyvista
6
+ from typing import NoReturn, Any
7
+ from MPSPlots.colormaps import blue_black_red
8
+
9
+
10
+ def plot_system(*objects: Any, data: Any = None, colormap: str = blue_black_red, show_axis_label: bool = True) -> NoReturn:
11
+ """
12
+ Plots a 3D visualization of a system of objects, each of which must have a `_add_to_3d_ax` method for adding itself to the scene.
13
+
14
+ This function creates a 3D plot of multiple objects, adding each one to a shared PyVista scene.
15
+ It ensures that each object has the required `_add_to_3d_ax` method for adding itself to the scene.
16
+ Additionally, it includes an optional translucent sphere and the option to display axis labels.
17
+
18
+ Args:
19
+ objects (Any): Objects to be plotted in the 3D scene. Each object must have a `_add_to_3d_ax` method.
20
+ show_axis_label (bool): If True, axis labels will be shown. Default is False.
21
+
22
+ Returns:
23
+ NoReturn: This function does not return a value. It displays the 3D visualization.
24
+ """
25
+ # Create a PyVista plotting scene
26
+ # scene = pyvista.Plotter(theme=pyvista.themes.DarkTheme())
27
+ scene = pyvista.Plotter()
28
+
29
+ # Add each object to the scene by calling its `_add_to_3d_ax` method
30
+ for obj in objects:
31
+ if not hasattr(obj, '_add_to_3d_ax'):
32
+ raise AttributeError(f'Object {obj} cannot be added to system plotting because it lacks a `_add_to_3d_ax` method.')
33
+ obj._add_to_3d_ax(scene=scene)
34
+
35
+ if data is not None:
36
+ if not hasattr(data, '_add_to_3d_ax'):
37
+ raise AttributeError(f'Data {obj} cannot be added to system plotting because it lacks a `_add_to_3d_ax` method.')
38
+ data._add_to_3d_ax(scene=scene, colormap=colormap)
39
+
40
+ # Add a translucent sphere to the scene to provide context or reference
41
+ sphere = pyvista.Sphere(radius=1)
42
+ scene.add_mesh(sphere, opacity=0.3)
43
+
44
+ # Optionally add axis labels to the scene
45
+ scene.add_axes_at_origin(zlabel='', xlabel='', ylabel='', labels_off=not show_axis_label)
46
+
47
+ # Display the scene
48
+ scene.show()
@@ -0,0 +1,2 @@
1
+ from .coherent import CoherentMode
2
+ from .uncoherent import Photodiode, IntegratingSphere