PyMieSim 3.6.0__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.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.cpython-310-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_detector.cpython-311-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_detector.cpython-312-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_detector.cpython-313-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_experiment.cpython-310-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_experiment.cpython-311-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_experiment.cpython-312-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_experiment.cpython-313-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_scatterer.cpython-310-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_scatterer.cpython-311-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_scatterer.cpython-312-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_scatterer.cpython-313-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_sets.cpython-310-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_sets.cpython-311-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_sets.cpython-312-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_sets.cpython-313-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_source.cpython-310-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_source.cpython-311-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_source.cpython-312-x86_64-linux-gnu.so +0 -0
- PyMieSim/binary/interface_source.cpython-313-x86_64-linux-gnu.so +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 +104 -0
- pymiesim-3.6.0.dist-info/WHEEL +6 -0
- pymiesim-3.6.0.dist-info/licenses/LICENSE +21 -0
- pymiesim.libs/libgfortran-040039e1.so.5.0.0 +0 -0
- pymiesim.libs/libgomp-a34b3233.so.1.0.0 +0 -0
- pymiesim.libs/libquadmath-96973f99.so.0.0.0 +0 -0
@@ -0,0 +1,405 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
from typing import Tuple
|
5
|
+
import numpy
|
6
|
+
from PyOptik.material.base_class import BaseMaterial
|
7
|
+
from tabulate import tabulate
|
8
|
+
from PyMieSim.single.representations import S1S2, FarField, Stokes, SPF, Footprint
|
9
|
+
from PyMieSim import units
|
10
|
+
|
11
|
+
class BaseScatterer(units.UnitsValidation):
|
12
|
+
"""
|
13
|
+
A generic class for a scatterer, providing properties and methods to compute various scattering-related quantities.
|
14
|
+
|
15
|
+
"""
|
16
|
+
def print_properties(self) -> None:
|
17
|
+
"""Prints a table of the scatterer's properties and their values."""
|
18
|
+
data = [getattr(self, name) for name in self.property_names]
|
19
|
+
property_dict = {"Property": self.property_names, "Value": data}
|
20
|
+
|
21
|
+
table = tabulate(property_dict, headers="keys")
|
22
|
+
print(table)
|
23
|
+
|
24
|
+
@property
|
25
|
+
def size_parameter(self) -> units.Quantity:
|
26
|
+
"""Returns the size parameter of the scatterer."""
|
27
|
+
return self._cpp_size_parameter * units.AU
|
28
|
+
|
29
|
+
@property
|
30
|
+
def cross_section(self) -> units.Quantity:
|
31
|
+
"""Returns the cross-section of the scatterer."""
|
32
|
+
return (self._cpp_cross_section * units.meter**2).to_compact()
|
33
|
+
|
34
|
+
@property
|
35
|
+
def Qsca(self) -> units.Quantity:
|
36
|
+
"""Returns the scattering efficiency."""
|
37
|
+
return self._cpp_Qsca * units.AU
|
38
|
+
|
39
|
+
@property
|
40
|
+
def Qext(self) -> units.Quantity:
|
41
|
+
"""Returns the extinction efficiency."""
|
42
|
+
return self._cpp_Qext * units.AU
|
43
|
+
|
44
|
+
@property
|
45
|
+
def Qabs(self) -> units.Quantity:
|
46
|
+
"""Returns the absorption efficiency."""
|
47
|
+
return self._cpp_Qabs * units.AU
|
48
|
+
|
49
|
+
@property
|
50
|
+
def Qback(self) -> units.Quantity:
|
51
|
+
"""Returns the backscattering efficiency."""
|
52
|
+
return self._cpp_Qback * units.AU
|
53
|
+
|
54
|
+
@property
|
55
|
+
def Qforward(self) -> units.Quantity:
|
56
|
+
"""Returns the forward-scattering efficiency."""
|
57
|
+
return self._cpp_Qforward * units.AU
|
58
|
+
|
59
|
+
@property
|
60
|
+
def Qratio(self) -> units.Quantity:
|
61
|
+
"""Returns the efficiency ratio of backscattering over total scattering."""
|
62
|
+
return self._cpp_Qratio * units.AU
|
63
|
+
|
64
|
+
@property
|
65
|
+
def g(self) -> units.Quantity:
|
66
|
+
"""Returns the anisotropy factor."""
|
67
|
+
return self._cpp_g * units.AU
|
68
|
+
|
69
|
+
@property
|
70
|
+
def Qpr(self) -> units.Quantity:
|
71
|
+
"""Returns the radiation pressure efficiency."""
|
72
|
+
return self._cpp_Qpr * units.AU
|
73
|
+
|
74
|
+
@property
|
75
|
+
def Csca(self) -> units.Quantity:
|
76
|
+
"""Returns the scattering cross-section."""
|
77
|
+
return (self._cpp_Csca * units.meter ** 2).to_compact()
|
78
|
+
|
79
|
+
@property
|
80
|
+
def Cext(self) -> units.Quantity:
|
81
|
+
"""Returns the extinction cross-section."""
|
82
|
+
return (self._cpp_Cext * units.meter ** 2).to_compact()
|
83
|
+
|
84
|
+
@property
|
85
|
+
def Cabs(self) -> units.Quantity:
|
86
|
+
"""Returns the absorption cross-section."""
|
87
|
+
return (self._cpp_Cabs * units.meter ** 2).to_compact()
|
88
|
+
|
89
|
+
@property
|
90
|
+
def Cpr(self) -> units.Quantity:
|
91
|
+
"""Returns the radiation pressure cross-section."""
|
92
|
+
return (self._cpp_Cpr * units.meter ** 2).to_compact()
|
93
|
+
|
94
|
+
@property
|
95
|
+
def Cback(self) -> units.Quantity:
|
96
|
+
"""Returns the backscattering cross-section."""
|
97
|
+
return (self._cpp_Cback * units.meter ** 2).to_compact()
|
98
|
+
|
99
|
+
@property
|
100
|
+
def Cforward(self) -> units.Quantity:
|
101
|
+
"""Returns the forward-scattering cross-section."""
|
102
|
+
return (self._cpp_Cforward * units.meter ** 2).to_compact()
|
103
|
+
|
104
|
+
@property
|
105
|
+
def Cratio(self) -> units.Quantity:
|
106
|
+
"""Returns the ratio of backscattering cross-section over total scattering."""
|
107
|
+
return (self._cpp_Cratio * units.meter ** 2).to_compact()
|
108
|
+
|
109
|
+
def get_farfields_array(self, phi: numpy.ndarray, theta: numpy.ndarray, r: units.Quantity) -> Tuple[numpy.ndarray, numpy.ndarray]:
|
110
|
+
"""
|
111
|
+
Computes the scattering far field for unstructured coordinates.
|
112
|
+
|
113
|
+
The method computes the fields up to a constant phase value.
|
114
|
+
|
115
|
+
Parameters
|
116
|
+
----------
|
117
|
+
phi : numpy.ndarray
|
118
|
+
The phi angles in radians.
|
119
|
+
theta : numpy.ndarray
|
120
|
+
The theta angles in radians.
|
121
|
+
r : numpy.ndarray
|
122
|
+
The radial distances.
|
123
|
+
|
124
|
+
Returns:
|
125
|
+
Tuple[numpy.ndarray, numpy.ndarray]: The computed far fields.
|
126
|
+
"""
|
127
|
+
return self._cpp_get_fields(phi=phi, theta=theta, r=r.to_base_units().magnitude)
|
128
|
+
|
129
|
+
def get_s1s2(self, sampling: int = 200, distance: units.Quantity = 1 * units.meter) -> S1S2:
|
130
|
+
r"""
|
131
|
+
Compute the S1 and S2 scattering amplitude functions for a spherical scatterer.
|
132
|
+
|
133
|
+
The S1 and S2 parameters represent the scattering amplitudes for perpendicular and parallel polarizations of light, respectively. These parameters are fundamental in Mie theory, which describes the scattering of electromagnetic waves by spherical particles.
|
134
|
+
|
135
|
+
The formulas for \( S_1 \) and \( S_2 \) are:
|
136
|
+
|
137
|
+
.. math::
|
138
|
+
S_1 = \sum\limits_{n=1}^{n_{\text{max}}} \frac{2n+1}{n(n+1)} \left( a_n \pi_n + b_n \tau_n \right) \\
|
139
|
+
S_2 = \sum\limits_{n=1}^{n_{\text{max}}} \frac{2n+1}{n(n+1)} \left( a_n \tau_n + b_n \pi_n \right)
|
140
|
+
|
141
|
+
Where:
|
142
|
+
|
143
|
+
- :math:`a_n` and :math:`b_n`: Mie coefficients, which depend on the size, shape, and refractive index of the scatterer.
|
144
|
+
- :math:`\pi_n` and :math:`\tau_n` \)`: Angular functions related to the angular components of the incident and scattered fields.
|
145
|
+
- :math:`n_{\text{max}}`: Maximum number of terms in the series, determined by the size parameter of the scatterer.
|
146
|
+
|
147
|
+
These scattering amplitude functions are essential for calculating properties such as scattering phase functions, efficiencies, and angular distribution of scattered light.
|
148
|
+
|
149
|
+
Parameters
|
150
|
+
----------
|
151
|
+
sampling : int
|
152
|
+
The number of angular points used to sample the S1 and S2 functions. Higher sampling improves the resolution of the scattering pattern but increases computation time.
|
153
|
+
distance : units.Quantity, optional
|
154
|
+
The distance from the scatterer at which the S1 and S2 parameters are evaluated. This is typically set to 1 meter by default, but can be adjusted for specific setups.
|
155
|
+
|
156
|
+
Returns
|
157
|
+
-------
|
158
|
+
S1S2
|
159
|
+
An object containing the computed S1 and S2 parameters, representing the scattering amplitudes for the two polarization components.
|
160
|
+
|
161
|
+
Notes
|
162
|
+
-----
|
163
|
+
- The S1 and S2 parameters are central to Mie scattering theory and are used to derive many important scattering properties, such as intensity distributions and polarization effects.
|
164
|
+
- The `sampling` parameter controls how finely the angular distribution is resolved. A higher value of `sampling` provides more detailed scattering information, which can be critical for accurately modeling the far-field pattern.
|
165
|
+
|
166
|
+
Example
|
167
|
+
-------
|
168
|
+
You can use this method to compute the scattering properties of spherical particles, particularly in experiments where the polarization and scattering pattern of the light are important.
|
169
|
+
|
170
|
+
Example usage:
|
171
|
+
|
172
|
+
>>> s1s2 = scatterer.get_s1s2(sampling=500)
|
173
|
+
>>> print(s1s2.S1, s1s2.S2)
|
174
|
+
|
175
|
+
"""
|
176
|
+
return S1S2(scatterer=self, sampling=sampling, distance=distance)
|
177
|
+
|
178
|
+
def get_stokes(self, sampling: int = 200, distance: units.Quantity = 1 * units.meter) -> Stokes:
|
179
|
+
r"""
|
180
|
+
Compute the four Stokes parameters: I, Q, U, and V, which describe the polarization state of scattered light.
|
181
|
+
|
182
|
+
The Stokes parameters provide a complete description of the polarization state of electromagnetic radiation, and are defined as follows:
|
183
|
+
|
184
|
+
.. math::
|
185
|
+
I &= \big| E_x \big|^2 + \big| E_y \big|^2 \quad &\text{(total intensity)} \\
|
186
|
+
Q &= \big| E_x \big|^2 - \big| E_y \big|^2 \quad &\text{(linear polarization along x and y)} \\
|
187
|
+
U &= 2 \mathcal{Re} \big\{ E_x E_y^* \big\} \quad &\text{(linear polarization at +45° and -45°)} \\
|
188
|
+
V &= 2 \mathcal{Im} \big\{ E_x E_y^* \big\} \quad &\text{(circular polarization)}
|
189
|
+
|
190
|
+
Where:
|
191
|
+
|
192
|
+
- :math:`I`: Total intensity of the scattered light.
|
193
|
+
- :math:`Q`: The degree of linear polarization along the x and y axes.
|
194
|
+
- :math:`U`: The degree of linear polarization at +45° and -45° to the axes.
|
195
|
+
- :math:`V`: The degree of circular polarization.
|
196
|
+
|
197
|
+
These parameters provide a powerful way to represent the full polarization state of the scattered light.
|
198
|
+
|
199
|
+
Parameters
|
200
|
+
----------
|
201
|
+
sampling : int
|
202
|
+
The number of angular points used to sample the Stokes parameters. A higher sampling value increases the resolution of the computed polarization pattern.
|
203
|
+
distance : units.Quantity, optional
|
204
|
+
The distance from the scatterer at which the Stokes parameters are evaluated. The default is 1 meter, but this can be adjusted based on the experimental setup.
|
205
|
+
|
206
|
+
Returns
|
207
|
+
-------
|
208
|
+
Stokes
|
209
|
+
An object containing the computed Stokes parameters (I, Q, U, V), which describe the polarization state of the scattered light.
|
210
|
+
|
211
|
+
Notes
|
212
|
+
-----
|
213
|
+
- The Stokes parameters are essential for understanding and characterizing the polarization of scattered light. They are used in a wide variety of applications, including atmospheric optics, remote sensing, and optical communication.
|
214
|
+
- The `sampling` parameter controls the angular resolution of the calculated Stokes parameters. Higher values provide finer detail, which can be important in accurately modeling polarization effects in scattering experiments.
|
215
|
+
|
216
|
+
Example
|
217
|
+
-------
|
218
|
+
You can use this method to compute the Stokes parameters for a spherical scatterer, helping you analyze how the scatterer affects the polarization state of light.
|
219
|
+
|
220
|
+
Example usage:
|
221
|
+
|
222
|
+
>>> stokes_params = scatterer.get_stokes(sampling=500)
|
223
|
+
>>> print(stokes_params.I, stokes_params.Q, stokes_params.U, stokes_params.V)
|
224
|
+
|
225
|
+
"""
|
226
|
+
return Stokes(scatterer=self, sampling=sampling, distance=distance)
|
227
|
+
|
228
|
+
def get_far_field(self, sampling: int = 200, distance: units.Quantity = 1 * units.meter) -> FarField:
|
229
|
+
r"""
|
230
|
+
Compute the far-field scattering pattern for the scatterer.
|
231
|
+
|
232
|
+
The far fields describe the behavior of the scattered electromagnetic waves at a large distance from the scatterer, where the waves can be approximated as planar.
|
233
|
+
The computed far fields represent the scattered electric field components in the directions parallel and perpendicular to the plane of incidence.
|
234
|
+
|
235
|
+
The far fields are computed as:
|
236
|
+
|
237
|
+
.. math::
|
238
|
+
\text{Fields} = E_{||}(\phi, \theta)^2, \, E_{\perp}(\phi, \theta)^2
|
239
|
+
|
240
|
+
These components represent the intensities of the scattered electric field in the parallel (\( E_{||} \)) and perpendicular (\( E_{\perp} \)) directions as functions of the spherical angles \( \phi \) (azimuthal) and \( \theta \) (polar).
|
241
|
+
|
242
|
+
The fields are expressed up to a constant phase factor:
|
243
|
+
|
244
|
+
.. math::
|
245
|
+
\exp{\left(-i k r \right)}
|
246
|
+
|
247
|
+
Where:
|
248
|
+
|
249
|
+
- :math:`k`: The wave number, related to the wavelength of the incident light.
|
250
|
+
- :math:`r`: The distance from the scatterer (assumed to be large in the far-field approximation).
|
251
|
+
|
252
|
+
Parameters
|
253
|
+
----------
|
254
|
+
sampling : int
|
255
|
+
The number of angular points used to sample the far-field pattern. A higher sampling value increases the angular resolution of the computed far-field intensities.
|
256
|
+
distance : units.Quantity, optional
|
257
|
+
The distance from the scatterer at which the far fields are evaluated. By default, this is set to 1 meter, but it can be adjusted to suit the specific experimental configuration.
|
258
|
+
|
259
|
+
Returns
|
260
|
+
-------
|
261
|
+
FarField
|
262
|
+
An object containing the computed far-field components (parallel and perpendicular intensities), which represent the angular distribution of the scattered light in the far field.
|
263
|
+
|
264
|
+
Notes
|
265
|
+
-----
|
266
|
+
- The far-field approximation assumes that the distance from the scatterer is large enough that the curvature of the wavefronts can be neglected, and the fields can be treated as planar.
|
267
|
+
- This method is useful for determining the angular distribution of the scattered light, which is critical in applications such as radar cross-section analysis, optical scattering experiments, and remote sensing.
|
268
|
+
|
269
|
+
Example
|
270
|
+
-------
|
271
|
+
You can use this method to compute the far-field scattering pattern of a spherical scatterer, which is essential in understanding how the scatterer redirects light in the far field.
|
272
|
+
|
273
|
+
Example usage:
|
274
|
+
|
275
|
+
>>> far_field = scatterer.get_far_field(sampling=500)
|
276
|
+
>>> print(far_field.E_phi, far_field.E_theta)
|
277
|
+
|
278
|
+
"""
|
279
|
+
return FarField(scatterer=self, sampling=sampling, distance=distance)
|
280
|
+
|
281
|
+
def get_spf(self, sampling: int = 200, distance: units.Quantity = 1 * units.meter) -> SPF:
|
282
|
+
r"""
|
283
|
+
Compute the scattering phase function (SPF) for the scatterer.
|
284
|
+
|
285
|
+
The scattering phase function describes how the intensity of scattered light varies as a function of the scattering angles \( \theta \) (polar) and \( \phi \) (azimuthal). It is a key quantity in light scattering, as it characterizes the angular distribution of scattered light.
|
286
|
+
|
287
|
+
The scattering phase function is computed as:
|
288
|
+
|
289
|
+
.. math::
|
290
|
+
\text{SPF} = \sqrt{ E_{\parallel}(\phi, \theta)^2 + E_{\perp}(\phi, \theta)^2 }
|
291
|
+
|
292
|
+
Where:
|
293
|
+
|
294
|
+
- :math:`E_{\parallel}`: The parallel component of the scattered electric field.
|
295
|
+
- :math:`E_{\perp}`: The perpendicular component of the scattered electric field.
|
296
|
+
- :math:`\phi` and :math:`\theta`: The azimuthal and polar angles, respectively, which describe the angular position of the scattered light.
|
297
|
+
|
298
|
+
The SPF combines the intensity contributions from both polarization components to describe the total scattered intensity in different directions.
|
299
|
+
|
300
|
+
Parameters
|
301
|
+
----------
|
302
|
+
sampling : int
|
303
|
+
The number of angular points used to sample the scattering phase function. A higher sampling value increases the angular resolution of the computed SPF.
|
304
|
+
distance : units.Quantity, optional
|
305
|
+
The distance from the scatterer at which the scattering phase function is evaluated. By default, this is set to 1 meter, but it can be adjusted depending on the specific experimental configuration.
|
306
|
+
|
307
|
+
Returns
|
308
|
+
-------
|
309
|
+
SPF
|
310
|
+
An object containing the computed scattering phase function (SPF), representing the angular distribution of scattered light intensity.
|
311
|
+
|
312
|
+
Notes
|
313
|
+
-----
|
314
|
+
- The scattering phase function is a critical quantity in the study of light scattering, as it provides insight into the directionality of the scattered light. It is commonly used in fields like atmospheric optics, biomedical imaging, and remote sensing.
|
315
|
+
- The `sampling` parameter controls the angular resolution of the SPF. Higher sampling values yield more detailed scattering patterns, especially important when capturing small angular features.
|
316
|
+
|
317
|
+
Example
|
318
|
+
-------
|
319
|
+
You can use this method to compute the scattering phase function of a spherical scatterer, helping to understand the angular distribution of light scattered by the particle.
|
320
|
+
|
321
|
+
Example usage:
|
322
|
+
|
323
|
+
>>> spf = scatterer.get_spf(sampling=500)
|
324
|
+
>>> print(spf)
|
325
|
+
|
326
|
+
"""
|
327
|
+
return SPF(scatterer=self, sampling=sampling, distance=distance)
|
328
|
+
|
329
|
+
def get_footprint(self, detector) -> Footprint:
|
330
|
+
r"""
|
331
|
+
Compute the footprint of the scattered light coupling with the detector.
|
332
|
+
|
333
|
+
The footprint represents the spatial distribution of the scattered light as it couples with the detector.
|
334
|
+
This is important for understanding how the scattered light interacts with the detector's field of view and is influenced by both the scatterer's properties and the detector configuration.
|
335
|
+
|
336
|
+
The footprint is computed using an inverse Fourier transform:
|
337
|
+
|
338
|
+
.. math::
|
339
|
+
\big| \mathscr{F}^{-1} \big\{ \tilde{ \psi } (\xi, \nu),\
|
340
|
+
\tilde{ \phi}_{l,m}(\xi, \nu) \big\}
|
341
|
+
(\delta_x, \delta_y) \big|^2
|
342
|
+
|
343
|
+
Where:
|
344
|
+
|
345
|
+
- :math:`\tilde{ \psi } (\xi, \nu)`: The Fourier transform of the scattered field.
|
346
|
+
- :math:`\tilde{ \phi}_{l,m} (\xi, \nu)`: The Fourier transform of the detector's capturing field.
|
347
|
+
- :math:`\mathscr{F}^{-1}`: The inverse Fourier transform operator.
|
348
|
+
- :math:`(\delta_x, \delta_y)`: Spatial coordinates describing the footprint in the detector plane.
|
349
|
+
|
350
|
+
The inverse Fourier transform yields the spatial pattern of the scattered light's interaction with the detector, which is then squared to compute the intensity of the footprint.
|
351
|
+
|
352
|
+
Parameters
|
353
|
+
----------
|
354
|
+
detector : GenericDetector
|
355
|
+
The detector object that defines the capturing field and geometry. This object provides information about the detector's configuration, including its numerical aperture, position, and sensitivity.
|
356
|
+
|
357
|
+
Returns
|
358
|
+
-------
|
359
|
+
Footprint
|
360
|
+
An object containing the computed scatterer footprint, representing the spatial distribution of the scattered light on the detector plane.
|
361
|
+
|
362
|
+
Notes
|
363
|
+
-----
|
364
|
+
- The footprint provides valuable information about the coupling efficiency between the scattered light and the detector. This is useful in designing experiments where precise control of light detection is important, such as in microscopy, remote sensing, or optical measurements.
|
365
|
+
- The interaction between the scattered field and the detector is influenced by factors such as the size, shape, and refractive index of the scatterer, as well as the detector's aperture and positioning.
|
366
|
+
|
367
|
+
Example
|
368
|
+
-------
|
369
|
+
You can use this method to compute the footprint of the scattered light on a detector, helping to visualize how light from the scatterer is spatially distributed on the detector.
|
370
|
+
|
371
|
+
Example usage:
|
372
|
+
|
373
|
+
>>> footprint = scatterer.get_footprint(detector=my_detector)
|
374
|
+
>>> print(footprint)
|
375
|
+
|
376
|
+
"""
|
377
|
+
return Footprint(scatterer=self, detector=detector)
|
378
|
+
|
379
|
+
def _assign_index_or_material(self, property: units.Quantity | BaseMaterial) -> tuple[units.Quantity | None, BaseMaterial | None]:
|
380
|
+
"""
|
381
|
+
Determines whether the provided property is a refractive index (Quantity) or a material (BaseMaterial),
|
382
|
+
and returns the corresponding values.
|
383
|
+
|
384
|
+
Parameters:
|
385
|
+
----------
|
386
|
+
property : units.Quantity or BaseMaterial
|
387
|
+
The core property to be assigned, which can either be a refractive index (Quantity) or a material (BaseMaterial).
|
388
|
+
|
389
|
+
Returns:
|
390
|
+
-------
|
391
|
+
tuple[units.Quantity | None, BaseMaterial | None]
|
392
|
+
A tuple where the first element is the refractive index (Quantity) if provided, otherwise None.
|
393
|
+
The second element is the material (BaseMaterial) if provided, otherwise None.
|
394
|
+
|
395
|
+
Raises:
|
396
|
+
------
|
397
|
+
ValueError:
|
398
|
+
If the provided property is neither a Quantity (refractive index) nor a BaseMaterial.
|
399
|
+
"""
|
400
|
+
if isinstance(property, units.Quantity):
|
401
|
+
return property, None
|
402
|
+
if isinstance(property, BaseMaterial):
|
403
|
+
return numpy.atleast_1d(property.compute_refractive_index(self.source.wavelength.to_base_units().magnitude))[0] * units.RIU, property
|
404
|
+
|
405
|
+
raise ValueError(f"Invalid material property: {property}. Expected a BaseMaterial or Quantity (RIU).")
|
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
import numpy
|
5
|
+
import pyvista
|
6
|
+
from PyOptik.material.base_class import BaseMaterial
|
7
|
+
|
8
|
+
from PyMieSim import units
|
9
|
+
from PyMieSim.single.scatterer.base import BaseScatterer
|
10
|
+
from PyMieSim.binary.interface_scatterer import CORESHELL
|
11
|
+
from PyMieSim.single.source.base import BaseSource
|
12
|
+
|
13
|
+
|
14
|
+
class CoreShell(CORESHELL, BaseScatterer):
|
15
|
+
"""
|
16
|
+
Class representing a core/shell spherical scatterer.
|
17
|
+
|
18
|
+
Parameters
|
19
|
+
----------
|
20
|
+
core_diameter : units.Quantity
|
21
|
+
Diameter of the core of the scatterer.
|
22
|
+
shell_thickness : units.Quantity
|
23
|
+
Thickness of the shell surrounding the core.
|
24
|
+
core_property : units.Quantity | BaseMaterial
|
25
|
+
Defines either the refractive index (`Quantity`) or material (`BaseMaterial`) of the scatterer's core. Only one can be provided.
|
26
|
+
shell_property : units.Quantity | BaseMaterial
|
27
|
+
Defines either the refractive index (`Quantity`) or material (`BaseMaterial`) of the scatterer's shell. Only one can be provided.
|
28
|
+
|
29
|
+
"""
|
30
|
+
|
31
|
+
core_diameter: units.Quantity
|
32
|
+
shell_thickness: units.Quantity
|
33
|
+
core_property: units.Quantity | BaseMaterial
|
34
|
+
shell_property: units.Quantity | BaseMaterial
|
35
|
+
medium_property: units.Quantity | BaseMaterial
|
36
|
+
source: BaseSource
|
37
|
+
|
38
|
+
property_names = [
|
39
|
+
"size_parameter", "radius", "volume", "cross_section", "g",
|
40
|
+
"Qsca", "Qext", "Qabs", "Qback", "Qratio", "Qpr",
|
41
|
+
"Csca", "Cext", "Cabs", "Cback", "Cratio", "Cpr"
|
42
|
+
]
|
43
|
+
|
44
|
+
def __init__(self,
|
45
|
+
core_diameter: units.Quantity,
|
46
|
+
shell_thickness: units.Quantity,
|
47
|
+
core_property: units.Quantity | BaseMaterial,
|
48
|
+
shell_property: units.Quantity | BaseMaterial,
|
49
|
+
medium_property: units.Quantity | BaseMaterial,
|
50
|
+
source: BaseSource):
|
51
|
+
"""
|
52
|
+
Initialize the CoreShell scatterer with its core and shell properties.
|
53
|
+
|
54
|
+
Parameters
|
55
|
+
----------
|
56
|
+
core_diameter : units.Quantity
|
57
|
+
Diameter of the core in meters.
|
58
|
+
shell_thickness : units.Quantity
|
59
|
+
Thickness of the shell in meters.
|
60
|
+
core_property : units.Quantity or BaseMaterial
|
61
|
+
Refractive index or material of the core.
|
62
|
+
shell_property : units.Quantity or BaseMaterial
|
63
|
+
Refractive index or material of the shell.
|
64
|
+
medium_property : units.Quantity or BaseMaterial
|
65
|
+
Refractive index or material of the surrounding medium.
|
66
|
+
source : BaseSource
|
67
|
+
Source object associated with the scatterer.
|
68
|
+
"""
|
69
|
+
self.core_diameter = self._validate_units(core_diameter, dimension='distance', units=units.meter)
|
70
|
+
self.shell_thickness = self._validate_units(shell_thickness, dimension='distance', units=units.meter)
|
71
|
+
self.core_property = self._validate_property(core_property)
|
72
|
+
self.shell_property = self._validate_property(shell_property)
|
73
|
+
self.medium_property = self._validate_property(medium_property)
|
74
|
+
|
75
|
+
self.source = source
|
76
|
+
self.core_index, self.core_material = self._assign_index_or_material(self.core_property)
|
77
|
+
self.shell_index, self.shell_material = self._assign_index_or_material(self.shell_property)
|
78
|
+
self.medium_index, self.medium_material = self._assign_index_or_material(self.medium_property)
|
79
|
+
|
80
|
+
super().__init__(
|
81
|
+
core_diameter=core_diameter.to(units.meter).magnitude,
|
82
|
+
shell_thickness=shell_thickness.to(units.meter).magnitude,
|
83
|
+
core_refractive_index=self.core_index.to(units.RIU).magnitude,
|
84
|
+
shell_refractive_index=self.shell_index.to(units.RIU).magnitude,
|
85
|
+
medium_refractive_index=self.medium_index.to(units.RIU).magnitude,
|
86
|
+
source=self.source
|
87
|
+
)
|
88
|
+
|
89
|
+
@property
|
90
|
+
def radius(self) -> units.Quantity:
|
91
|
+
"""Return the outer radius of the scatterer."""
|
92
|
+
return self.core_diameter / 2 + self.shell_thickness
|
93
|
+
|
94
|
+
@property
|
95
|
+
def volume(self) -> units.Quantity:
|
96
|
+
"""Return the volume of the scatterer."""
|
97
|
+
vol = (4/3) * numpy.pi * (self.radius ** 3)
|
98
|
+
return vol.to(units.meter ** 3)
|
99
|
+
|
100
|
+
def _add_to_3d_ax(self, scene: pyvista.Plotter, color: str = 'black', opacity: float = 1.0) -> None:
|
101
|
+
"""
|
102
|
+
Adds a 3D cone representation to the given PyVista plotting scene.
|
103
|
+
|
104
|
+
The cone represents the acceptance angle determined by the numerical aperture (NA) of the system.
|
105
|
+
The cone is positioned at the origin and points downward along the z-axis.
|
106
|
+
|
107
|
+
Parameters
|
108
|
+
----------
|
109
|
+
scene : pyvista.Plotter
|
110
|
+
The 3D plotting scene to which the cone will be added.
|
111
|
+
color : str
|
112
|
+
The color of the cone mesh. Default is 'red'.
|
113
|
+
opacity : float
|
114
|
+
The opacity of the cone mesh. Default is 0.8.
|
115
|
+
|
116
|
+
"""
|
117
|
+
# Create the cone mesh
|
118
|
+
sphape = pyvista.Sphere(
|
119
|
+
center=(0.0, 0.0, 0.0),
|
120
|
+
radius=0.1,
|
121
|
+
theta_resolution=100,
|
122
|
+
phi_resolution=100
|
123
|
+
)
|
124
|
+
|
125
|
+
# Add the cone mesh to the scene
|
126
|
+
scene.add_mesh(sphape, color=color, opacity=opacity)
|
@@ -0,0 +1,113 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
import pyvista
|
5
|
+
from PyOptik.material.base_class import BaseMaterial
|
6
|
+
|
7
|
+
from PyMieSim import units
|
8
|
+
from PyMieSim.single.scatterer.base import BaseScatterer
|
9
|
+
from PyMieSim.binary.interface_scatterer import CYLINDER
|
10
|
+
from PyMieSim.single.source.base import BaseSource
|
11
|
+
|
12
|
+
class Cylinder(CYLINDER, BaseScatterer):
|
13
|
+
"""
|
14
|
+
Represents a cylindrical scatterer used for scattering simulations in optical systems.
|
15
|
+
|
16
|
+
Parameters
|
17
|
+
----------
|
18
|
+
diameter : units.Quantity
|
19
|
+
Diameter of the cylindrical scatterer, given in meters.
|
20
|
+
property : units.Quantity | BaseMaterial
|
21
|
+
Defines either the refractive index (`Quantity`) or material (`BaseMaterial`) of the scatterer. Only one of these should be provided at a time to specify the core characteristic.
|
22
|
+
|
23
|
+
"""
|
24
|
+
diameter: units.Quantity
|
25
|
+
property: units.Quantity | BaseMaterial
|
26
|
+
medium_property: units.Quantity | BaseMaterial
|
27
|
+
source: BaseSource
|
28
|
+
|
29
|
+
property_names = [
|
30
|
+
"size_parameter", "radius", "cross_section", "g",
|
31
|
+
"Qsca", "Qext", "Qabs",
|
32
|
+
"Csca", "Cext", "Cabs"
|
33
|
+
]
|
34
|
+
|
35
|
+
def __init__(self, diameter: units.Quantity, property: units.Quantity | BaseMaterial, medium_property: units.Quantity | BaseMaterial, source: BaseSource):
|
36
|
+
"""
|
37
|
+
Initialize the Cylinder scatterer with its diameter and material properties.
|
38
|
+
|
39
|
+
Parameters
|
40
|
+
----------
|
41
|
+
diameter : units.Quantity
|
42
|
+
Diameter of the sphere in meters.
|
43
|
+
property : units.Quantity or BaseMaterial
|
44
|
+
Refractive index or material of the sphere.
|
45
|
+
medium_property : units.Quantity
|
46
|
+
Refractive index of the surrounding medium.
|
47
|
+
source : Source
|
48
|
+
Source object associated with the scatterer.
|
49
|
+
"""
|
50
|
+
self.diameter = self._validate_units(diameter, dimension='distance', units=units.meter)
|
51
|
+
self.property = self._validate_property(property)
|
52
|
+
self.medium_property = self._validate_property(medium_property)
|
53
|
+
self.source = source
|
54
|
+
|
55
|
+
self.index, self.material = self._assign_index_or_material(self.property)
|
56
|
+
self.medium_index, self.medium_material = self._assign_index_or_material(self.medium_property)
|
57
|
+
|
58
|
+
super().__init__(
|
59
|
+
diameter=diameter.to(units.meter).magnitude,
|
60
|
+
refractive_index=self.index.to(units.RIU).magnitude,
|
61
|
+
medium_refractive_index=self.medium_index.to(units.RIU).magnitude,
|
62
|
+
source=self.source
|
63
|
+
)
|
64
|
+
|
65
|
+
@property
|
66
|
+
def radius(self) -> units.Quantity:
|
67
|
+
"""Return the radius of the cylinder."""
|
68
|
+
return self.diameter / 2
|
69
|
+
|
70
|
+
@property
|
71
|
+
def Cback(self) -> None:
|
72
|
+
raise NotImplementedError
|
73
|
+
|
74
|
+
@property
|
75
|
+
def Qback(self) -> None:
|
76
|
+
raise NotImplementedError
|
77
|
+
|
78
|
+
@property
|
79
|
+
def Cratio(self) -> None:
|
80
|
+
raise NotImplementedError
|
81
|
+
|
82
|
+
@property
|
83
|
+
def Qratio(self) -> None:
|
84
|
+
raise NotImplementedError
|
85
|
+
|
86
|
+
def _add_to_3d_ax(self, scene: pyvista.Plotter, color: str = 'black', opacity: float = 1.0) -> None:
|
87
|
+
"""
|
88
|
+
Adds a 3D cone representation to the given PyVista plotting scene.
|
89
|
+
|
90
|
+
The cone represents the acceptance angle determined by the numerical aperture (NA) of the system.
|
91
|
+
The cone is positioned at the origin and points downward along the z-axis.
|
92
|
+
|
93
|
+
Parameters
|
94
|
+
----------
|
95
|
+
scene : pyvista.Plotter
|
96
|
+
The 3D plotting scene to which the cone will be added.
|
97
|
+
color : str
|
98
|
+
The color of the cone mesh. Default is 'red'.
|
99
|
+
opacity : float
|
100
|
+
The opacity of the cone mesh. Default is 0.8.
|
101
|
+
|
102
|
+
"""
|
103
|
+
# Create the cone mesh
|
104
|
+
shape = pyvista.Cylinder(
|
105
|
+
center=(0.0, 0.0, 0.0),
|
106
|
+
radius=0.1,
|
107
|
+
height=2.0, # Height of the cylinder
|
108
|
+
direction=(0, -1, 0), # Pointing downwards along the z-axis
|
109
|
+
resolution=100 # Number of sides for the cylinder
|
110
|
+
)
|
111
|
+
|
112
|
+
# Add the cone mesh to the scene
|
113
|
+
scene.add_mesh(shape, color=color, opacity=opacity)
|