PyMieSim 3.6.0__cp313-cp313-macosx_14_0_arm64.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 (106) hide show
  1. PyMieSim/.dylibs/libgcc_s.1.1.dylib +0 -0
  2. PyMieSim/.dylibs/libgfortran.5.dylib +0 -0
  3. PyMieSim/.dylibs/libgomp.1.dylib +0 -0
  4. PyMieSim/.dylibs/libquadmath.0.dylib +0 -0
  5. PyMieSim/.dylibs/libstdc++.6.dylib +0 -0
  6. PyMieSim/__init__.py +16 -0
  7. PyMieSim/__main__.py +9 -0
  8. PyMieSim/_version.py +21 -0
  9. PyMieSim/binary/__init__.py +0 -0
  10. PyMieSim/binary/interface_detector.cpython-310-darwin.so +0 -0
  11. PyMieSim/binary/interface_detector.cpython-311-darwin.so +0 -0
  12. PyMieSim/binary/interface_detector.cpython-312-darwin.so +0 -0
  13. PyMieSim/binary/interface_detector.cpython-313-darwin.so +0 -0
  14. PyMieSim/binary/interface_experiment.cpython-310-darwin.so +0 -0
  15. PyMieSim/binary/interface_experiment.cpython-311-darwin.so +0 -0
  16. PyMieSim/binary/interface_experiment.cpython-312-darwin.so +0 -0
  17. PyMieSim/binary/interface_experiment.cpython-313-darwin.so +0 -0
  18. PyMieSim/binary/interface_scatterer.cpython-310-darwin.so +0 -0
  19. PyMieSim/binary/interface_scatterer.cpython-311-darwin.so +0 -0
  20. PyMieSim/binary/interface_scatterer.cpython-312-darwin.so +0 -0
  21. PyMieSim/binary/interface_scatterer.cpython-313-darwin.so +0 -0
  22. PyMieSim/binary/interface_sets.cpython-310-darwin.so +0 -0
  23. PyMieSim/binary/interface_sets.cpython-311-darwin.so +0 -0
  24. PyMieSim/binary/interface_sets.cpython-312-darwin.so +0 -0
  25. PyMieSim/binary/interface_sets.cpython-313-darwin.so +0 -0
  26. PyMieSim/binary/interface_source.cpython-310-darwin.so +0 -0
  27. PyMieSim/binary/interface_source.cpython-311-darwin.so +0 -0
  28. PyMieSim/binary/interface_source.cpython-312-darwin.so +0 -0
  29. PyMieSim/binary/interface_source.cpython-313-darwin.so +0 -0
  30. PyMieSim/binary/libcpp_coordinates.a +0 -0
  31. PyMieSim/binary/libcpp_detector.a +0 -0
  32. PyMieSim/binary/libcpp_experiment.a +0 -0
  33. PyMieSim/binary/libcpp_fibonacci.a +0 -0
  34. PyMieSim/binary/libcpp_mode_field.a +0 -0
  35. PyMieSim/binary/libcpp_sets.a +0 -0
  36. PyMieSim/binary/libcpp_source.a +0 -0
  37. PyMieSim/directories.py +31 -0
  38. PyMieSim/experiment/__init__.py +1 -0
  39. PyMieSim/experiment/dataframe_subclass.py +220 -0
  40. PyMieSim/experiment/detector/__init__.py +2 -0
  41. PyMieSim/experiment/detector/base.py +169 -0
  42. PyMieSim/experiment/detector/coherent_mode.py +50 -0
  43. PyMieSim/experiment/detector/photodiode.py +52 -0
  44. PyMieSim/experiment/scatterer/__init__.py +4 -0
  45. PyMieSim/experiment/scatterer/base.py +98 -0
  46. PyMieSim/experiment/scatterer/core_shell.py +82 -0
  47. PyMieSim/experiment/scatterer/cylinder.py +63 -0
  48. PyMieSim/experiment/scatterer/sphere.py +66 -0
  49. PyMieSim/experiment/setup.py +356 -0
  50. PyMieSim/experiment/source/__init__.py +2 -0
  51. PyMieSim/experiment/source/base.py +85 -0
  52. PyMieSim/experiment/source/gaussian.py +60 -0
  53. PyMieSim/experiment/source/planewave.py +69 -0
  54. PyMieSim/experiment/utils.py +132 -0
  55. PyMieSim/gui/__init__.py +0 -0
  56. PyMieSim/gui/helper.py +60 -0
  57. PyMieSim/gui/interface.py +136 -0
  58. PyMieSim/gui/section.py +606 -0
  59. PyMieSim/mesh.py +368 -0
  60. PyMieSim/polarization.py +174 -0
  61. PyMieSim/single/__init__.py +48 -0
  62. PyMieSim/single/detector/__init__.py +2 -0
  63. PyMieSim/single/detector/base.py +271 -0
  64. PyMieSim/single/detector/coherent.py +99 -0
  65. PyMieSim/single/detector/uncoherent.py +105 -0
  66. PyMieSim/single/representations.py +734 -0
  67. PyMieSim/single/scatterer/__init__.py +4 -0
  68. PyMieSim/single/scatterer/base.py +405 -0
  69. PyMieSim/single/scatterer/core_shell.py +126 -0
  70. PyMieSim/single/scatterer/cylinder.py +113 -0
  71. PyMieSim/single/scatterer/sphere.py +108 -0
  72. PyMieSim/single/source/__init__.py +3 -0
  73. PyMieSim/single/source/base.py +7 -0
  74. PyMieSim/single/source/gaussian.py +137 -0
  75. PyMieSim/single/source/planewave.py +97 -0
  76. PyMieSim/special_functions.py +81 -0
  77. PyMieSim/units.py +130 -0
  78. PyMieSim/validation_data/bohren_huffman/figure_810.csv +245 -0
  79. PyMieSim/validation_data/bohren_huffman/figure_87.csv +2 -0
  80. PyMieSim/validation_data/bohren_huffman/figure_88.csv +2 -0
  81. PyMieSim/validation_data/pymiescatt/example_coreshell_0.csv +41 -0
  82. PyMieSim/validation_data/pymiescatt/example_coreshell_1.csv +401 -0
  83. PyMieSim/validation_data/pymiescatt/example_shpere_0.csv +51 -0
  84. PyMieSim/validation_data/pymiescatt/example_shpere_1.csv +801 -0
  85. PyMieSim/validation_data/pymiescatt/example_shpere_2.csv +41 -0
  86. PyMieSim/validation_data/pymiescatt/example_shpere_3.csv +401 -0
  87. PyMieSim/validation_data/pymiescatt/example_sphere_0.csv +51 -0
  88. PyMieSim/validation_data/pymiescatt/example_sphere_1.csv +801 -0
  89. PyMieSim/validation_data/pymiescatt/example_sphere_2.csv +41 -0
  90. PyMieSim/validation_data/pymiescatt/example_sphere_3.csv +401 -0
  91. PyMieSim/validation_data/pymiescatt/validation_Qsca.csv +800 -0
  92. PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_1.csv +400 -0
  93. PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_2.csv +400 -0
  94. PyMieSim/validation_data/pymiescatt/validation_Qsca_medium.csv +800 -0
  95. PyMieSim/validation_data/pymiescatt/validation_coreshell.csv +81 -0
  96. PyMieSim/validation_data/pymiescatt/validation_sphere.csv +801 -0
  97. lib/libZBessel.a +0 -0
  98. lib/lib_ZBessel.a +0 -0
  99. lib/libcpp_base_scatterer.a +0 -0
  100. lib/libcpp_coreshell.a +0 -0
  101. lib/libcpp_cylinder.a +0 -0
  102. lib/libcpp_sphere.a +0 -0
  103. pymiesim-3.6.0.dist-info/METADATA +246 -0
  104. pymiesim-3.6.0.dist-info/RECORD +106 -0
  105. pymiesim-3.6.0.dist-info/WHEEL +5 -0
  106. pymiesim-3.6.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,4 @@
1
+ from .base import BaseScatterer
2
+ from .sphere import Sphere
3
+ from .cylinder import Cylinder
4
+ from .core_shell import CoreShell
@@ -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)