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.
- PyMieSim/__init__.py +16 -0
- PyMieSim/__main__.py +9 -0
- PyMieSim/_version.py +21 -0
- PyMieSim/binary/__init__.py +0 -0
- PyMieSim/binary/interface_detector.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_detector.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_detector.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_detector.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/libcpp_coordinates.a +0 -0
- PyMieSim/binary/libcpp_detector.a +0 -0
- PyMieSim/binary/libcpp_experiment.a +0 -0
- PyMieSim/binary/libcpp_fibonacci.a +0 -0
- PyMieSim/binary/libcpp_mode_field.a +0 -0
- PyMieSim/binary/libcpp_sets.a +0 -0
- PyMieSim/binary/libcpp_source.a +0 -0
- PyMieSim/directories.py +31 -0
- PyMieSim/experiment/__init__.py +1 -0
- PyMieSim/experiment/dataframe_subclass.py +220 -0
- PyMieSim/experiment/detector/__init__.py +2 -0
- PyMieSim/experiment/detector/base.py +169 -0
- PyMieSim/experiment/detector/coherent_mode.py +50 -0
- PyMieSim/experiment/detector/photodiode.py +52 -0
- PyMieSim/experiment/scatterer/__init__.py +4 -0
- PyMieSim/experiment/scatterer/base.py +98 -0
- PyMieSim/experiment/scatterer/core_shell.py +82 -0
- PyMieSim/experiment/scatterer/cylinder.py +63 -0
- PyMieSim/experiment/scatterer/sphere.py +66 -0
- PyMieSim/experiment/setup.py +356 -0
- PyMieSim/experiment/source/__init__.py +2 -0
- PyMieSim/experiment/source/base.py +85 -0
- PyMieSim/experiment/source/gaussian.py +60 -0
- PyMieSim/experiment/source/planewave.py +69 -0
- PyMieSim/experiment/utils.py +132 -0
- PyMieSim/gui/__init__.py +0 -0
- PyMieSim/gui/helper.py +60 -0
- PyMieSim/gui/interface.py +136 -0
- PyMieSim/gui/section.py +606 -0
- PyMieSim/mesh.py +368 -0
- PyMieSim/polarization.py +174 -0
- PyMieSim/single/__init__.py +48 -0
- PyMieSim/single/detector/__init__.py +2 -0
- PyMieSim/single/detector/base.py +271 -0
- PyMieSim/single/detector/coherent.py +99 -0
- PyMieSim/single/detector/uncoherent.py +105 -0
- PyMieSim/single/representations.py +734 -0
- PyMieSim/single/scatterer/__init__.py +4 -0
- PyMieSim/single/scatterer/base.py +405 -0
- PyMieSim/single/scatterer/core_shell.py +126 -0
- PyMieSim/single/scatterer/cylinder.py +113 -0
- PyMieSim/single/scatterer/sphere.py +108 -0
- PyMieSim/single/source/__init__.py +3 -0
- PyMieSim/single/source/base.py +7 -0
- PyMieSim/single/source/gaussian.py +137 -0
- PyMieSim/single/source/planewave.py +97 -0
- PyMieSim/special_functions.py +81 -0
- PyMieSim/units.py +130 -0
- PyMieSim/validation_data/bohren_huffman/figure_810.csv +245 -0
- PyMieSim/validation_data/bohren_huffman/figure_87.csv +2 -0
- PyMieSim/validation_data/bohren_huffman/figure_88.csv +2 -0
- PyMieSim/validation_data/pymiescatt/example_coreshell_0.csv +41 -0
- PyMieSim/validation_data/pymiescatt/example_coreshell_1.csv +401 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_0.csv +51 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_1.csv +801 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_2.csv +41 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_3.csv +401 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_0.csv +51 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_1.csv +801 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_2.csv +41 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_3.csv +401 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca.csv +800 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_1.csv +400 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_2.csv +400 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca_medium.csv +800 -0
- PyMieSim/validation_data/pymiescatt/validation_coreshell.csv +81 -0
- PyMieSim/validation_data/pymiescatt/validation_sphere.csv +801 -0
- lib/libZBessel.a +0 -0
- lib/lib_ZBessel.a +0 -0
- lib/libcpp_base_scatterer.a +0 -0
- lib/libcpp_coreshell.a +0 -0
- lib/libcpp_cylinder.a +0 -0
- lib/libcpp_sphere.a +0 -0
- pymiesim-3.6.0.dist-info/METADATA +246 -0
- pymiesim-3.6.0.dist-info/RECORD +101 -0
- pymiesim-3.6.0.dist-info/WHEEL +5 -0
- 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
|
PyMieSim/polarization.py
ADDED
@@ -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()
|