miepython 3.0.5__py3-none-any.whl → 3.2.0__py3-none-any.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.
- miepython/__init__.py +79 -33
- miepython/_backend.py +49 -0
- miepython/core.py +3 -4
- miepython/field.py +676 -143
- miepython/mie_jit.py +55 -22
- miepython/mie_nojit.py +73 -34
- miepython/vsh.py +3 -1
- {miepython-3.0.5.dist-info → miepython-3.2.0.dist-info}/METADATA +43 -35
- miepython-3.2.0.dist-info/RECORD +18 -0
- {miepython-3.0.5.dist-info → miepython-3.2.0.dist-info}/WHEEL +1 -1
- miepython-3.0.5.dist-info/RECORD +0 -17
- {miepython-3.0.5.dist-info → miepython-3.2.0.dist-info}/licenses/LICENSE.txt +0 -0
- {miepython-3.0.5.dist-info → miepython-3.2.0.dist-info}/top_level.txt +0 -0
miepython/field.py
CHANGED
|
@@ -1,38 +1,188 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
1
|
+
"""Near- and far-field electromagnetic fields for a homogeneous sphere.
|
|
2
|
+
|
|
3
|
+
This module evaluates complex phasor electric and magnetic fields for a sphere
|
|
4
|
+
embedded in a uniform medium.
|
|
5
|
+
|
|
6
|
+
Main entry points
|
|
7
|
+
-----------------
|
|
8
|
+
Use these for most workflows:
|
|
9
|
+
|
|
10
|
+
- ``e_near(...)``: electric field in spherical components ``[E_r, E_theta, E_phi]``.
|
|
11
|
+
- ``h_near(...)``: magnetic field in spherical components ``[H_r, H_theta, H_phi]``.
|
|
12
|
+
- ``eh_near(...)``: both electric and magnetic fields in one call.
|
|
13
|
+
- ``e_near_cartesian(...)`` / ``h_near_cartesian(...)`` / ``eh_near_cartesian(...)``:
|
|
14
|
+
same near-field calculations in Cartesian components ``[Fx, Fy, Fz]``.
|
|
15
|
+
- ``e_far(...)``: scattered far-field electric components.
|
|
16
|
+
|
|
17
|
+
Quick examples
|
|
18
|
+
--------------
|
|
19
|
+
Evaluate the near electric field on a ring of points:
|
|
20
|
+
|
|
21
|
+
>>> import numpy as np
|
|
22
|
+
>>> import miepython.field as fields
|
|
23
|
+
>>> theta = np.linspace(0.0, np.pi, 181)
|
|
24
|
+
>>> phi = np.zeros_like(theta)
|
|
25
|
+
>>> r = np.full_like(theta, 1.5) # same length units as lambda0 and d_sphere
|
|
26
|
+
>>> E = fields.e_near(
|
|
27
|
+
... lambda0=1.0,
|
|
28
|
+
... d_sphere=1.0,
|
|
29
|
+
... m_sphere=1.5 + 0.0j,
|
|
30
|
+
... n_env=1.0,
|
|
31
|
+
... r=r,
|
|
32
|
+
... theta=theta,
|
|
33
|
+
... phi=phi,
|
|
34
|
+
... )
|
|
35
|
+
>>> E.shape
|
|
36
|
+
(3, 181)
|
|
37
|
+
|
|
38
|
+
Evaluate both fields in Cartesian coordinates on a 2D x-z slice:
|
|
39
|
+
|
|
40
|
+
>>> u = np.linspace(-1.5, 1.5, 101)
|
|
41
|
+
>>> X, Z = np.meshgrid(u, u, indexing="xy")
|
|
42
|
+
>>> E_xyz, H_xyz = fields.eh_near_cartesian(
|
|
43
|
+
... lambda0=1.0,
|
|
44
|
+
... d_sphere=1.0,
|
|
45
|
+
... m_sphere=1.5 + 0.0j,
|
|
46
|
+
... n_env=1.0,
|
|
47
|
+
... x=X,
|
|
48
|
+
... y=np.zeros_like(X),
|
|
49
|
+
... z=Z,
|
|
50
|
+
... )
|
|
51
|
+
>>> E_xyz.shape, H_xyz.shape
|
|
52
|
+
((3, 101, 101), (3, 101, 101))
|
|
53
|
+
|
|
54
|
+
Reuse precomputed Mie coefficients for repeated field evaluations:
|
|
55
|
+
|
|
56
|
+
>>> import miepython as mie
|
|
57
|
+
>>> m_rel = (1.5 + 0.0j) / 1.0
|
|
58
|
+
>>> x_size = np.pi * 1.0 * 1.0 / 1.0
|
|
59
|
+
>>> a, b, c, d = mie.coefficients(m_rel, x_size, internal=True)
|
|
60
|
+
>>> abcd = np.array([a, b, c, d])
|
|
61
|
+
>>> E2, H2 = fields.eh_near(
|
|
62
|
+
... 1.0, 1.0, 1.5 + 0.0j, 1.0, r, theta, phi, abcd=abcd
|
|
63
|
+
... )
|
|
3
64
|
|
|
4
|
-
|
|
65
|
+
Conventions
|
|
66
|
+
-----------
|
|
67
|
+
- Phasor time dependence: ``exp(-i * omega * t)``.
|
|
68
|
+
- Incident plane wave: propagation along ``+z``, ``E`` along ``+x``, ``H`` along ``+y``,
|
|
69
|
+
incident amplitude ``E0 = 1``.
|
|
70
|
+
- Spherical coordinates: ``theta`` from ``+z`` and ``phi`` from ``+x`` toward ``+y``.
|
|
71
|
+
- ``lambda0`` is vacuum wavelength.
|
|
72
|
+
- ``n_env`` is the surrounding-medium index and ``m_sphere`` is the sphere index;
|
|
73
|
+
relative index is ``m_rel = m_sphere / n_env``.
|
|
74
|
+
- Size parameter is ``x = pi * d_sphere * n_env / lambda0``.
|
|
75
|
+
- Near field definition:
|
|
76
|
+
outside the sphere, returned field is incident plus scattered;
|
|
77
|
+
inside the sphere, returned field is the internal field.
|
|
78
|
+
- Non-magnetic materials are assumed (relative permeability ``mu_r = 1``).
|
|
5
79
|
"""
|
|
6
80
|
|
|
7
81
|
import numpy as np
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
import
|
|
82
|
+
from scipy.special import factorial2, spherical_jn
|
|
83
|
+
from ._backend import D_calc, pi_tau
|
|
84
|
+
from .bessel import d_riccati_bessel_h1, spherical_h1
|
|
85
|
+
from .core import S1_S2, coefficients
|
|
86
|
+
from .util import spherical_vector_to_cartesian
|
|
11
87
|
|
|
12
|
-
__all__ = (
|
|
88
|
+
__all__ = (
|
|
89
|
+
"e_near",
|
|
90
|
+
"h_near",
|
|
91
|
+
"eh_near",
|
|
92
|
+
"e_near_cartesian",
|
|
93
|
+
"h_near_cartesian",
|
|
94
|
+
"eh_near_cartesian",
|
|
95
|
+
"e_far",
|
|
96
|
+
)
|
|
13
97
|
|
|
14
98
|
|
|
15
|
-
def
|
|
99
|
+
def _sum_two_scaled_terms(scale, coeff1, values1, scale1, coeff2, values2, scale2):
|
|
100
|
+
"""Return ``sum(scale * (scale1*coeff1*values1 + scale2*coeff2*values2))``."""
|
|
101
|
+
return np.sum(scale * (scale1 * coeff1 * values1 + scale2 * coeff2 * values2))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _vsh_components_base(n_terms, lambda0, d_sphere, m_index, r, theta):
|
|
105
|
+
"""Compute shared VSH base components for one spatial point.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
n_terms (int): Number of multipole terms.
|
|
109
|
+
lambda0 (float): Vacuum wavelength.
|
|
110
|
+
d_sphere (float): Sphere diameter.
|
|
111
|
+
m_index (complex): Refractive index at the evaluation point.
|
|
112
|
+
r (float): Radial coordinate.
|
|
113
|
+
theta (float): Polar angle in radians.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
tuple[ndarray, ndarray, ndarray, ndarray, ndarray]:
|
|
117
|
+
``(M_theta_base, M_phi_base, N_r_base, N_theta_base, N_phi_base)``
|
|
118
|
+
for multipole orders ``1..n_terms``.
|
|
16
119
|
"""
|
|
17
|
-
|
|
120
|
+
mu = float(np.cos(theta))
|
|
121
|
+
if mu >= 1.0:
|
|
122
|
+
mu = 0.999999
|
|
123
|
+
elif mu <= -1.0:
|
|
124
|
+
mu = -0.999999
|
|
125
|
+
|
|
126
|
+
pi = np.empty(n_terms)
|
|
127
|
+
tau = np.empty(n_terms)
|
|
128
|
+
pi_tau(mu, pi, tau)
|
|
129
|
+
|
|
130
|
+
n_int = np.arange(1, n_terms + 1, dtype=np.int64)
|
|
131
|
+
n_arr = n_int.astype(np.float64)
|
|
132
|
+
rho = 2 * np.pi * m_index * r / lambda0
|
|
133
|
+
kr = 2 * np.pi * r / lambda0
|
|
134
|
+
inside = r < d_sphere / 2
|
|
135
|
+
|
|
136
|
+
if inside:
|
|
137
|
+
jn = spherical_jn(n_int, rho)
|
|
138
|
+
m_factor = jn
|
|
139
|
+
|
|
140
|
+
if np.abs(rho) < 0.01:
|
|
141
|
+
rho_pow = rho ** np.arange(0, n_terms)
|
|
142
|
+
denom = factorial2(2 * n_int + 1)
|
|
143
|
+
n_factor1 = rho_pow / denom
|
|
144
|
+
n_factor2 = (n_arr + 1.0) * rho_pow / denom
|
|
145
|
+
else:
|
|
146
|
+
d_vals = D_calc(np.complex128(m_index), float(kr), n_terms + 1)[:n_terms]
|
|
147
|
+
n_factor1 = jn / rho
|
|
148
|
+
n_factor2 = jn * d_vals
|
|
149
|
+
else:
|
|
150
|
+
h1 = spherical_h1(n_int, rho)
|
|
151
|
+
m_factor = h1
|
|
152
|
+
n_factor1 = h1 / rho
|
|
153
|
+
n_factor2 = d_riccati_bessel_h1(n_int, rho) / rho
|
|
154
|
+
|
|
155
|
+
sin_theta = np.sin(theta)
|
|
156
|
+
M_theta_base = pi * m_factor
|
|
157
|
+
M_phi_base = tau * m_factor
|
|
158
|
+
N_r_base = n_arr * (n_arr + 1.0) * sin_theta * pi * n_factor1
|
|
159
|
+
N_theta_base = tau * n_factor2
|
|
160
|
+
N_phi_base = pi * n_factor2
|
|
161
|
+
return M_theta_base, M_phi_base, N_r_base, N_theta_base, N_phi_base
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def e_far(lambda0, d_sphere, m_sphere, n_env, r, theta, phi):
|
|
165
|
+
"""Evaluate the scattered electric far field.
|
|
18
166
|
|
|
19
167
|
Args:
|
|
20
|
-
lambda0 (float):
|
|
21
|
-
d_sphere (float):
|
|
22
|
-
m_sphere (complex):
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
168
|
+
lambda0 (float): Vacuum wavelength.
|
|
169
|
+
d_sphere (float): Sphere diameter.
|
|
170
|
+
m_sphere (complex): Sphere refractive index.
|
|
171
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
172
|
+
r (float or ndarray): Radial observation distance.
|
|
173
|
+
theta (float or ndarray): Polar angle in radians.
|
|
174
|
+
phi (float or ndarray): Azimuth angle in radians.
|
|
27
175
|
|
|
28
176
|
Returns:
|
|
29
|
-
|
|
177
|
+
ndarray: Complex spherical components ``[E_r, E_theta, E_phi]`` with
|
|
178
|
+
shape ``(3, ...)`` following broadcasted input shape.
|
|
30
179
|
"""
|
|
31
|
-
x = np.pi * d_sphere / lambda0
|
|
32
|
-
|
|
180
|
+
x = np.pi * d_sphere * n_env / lambda0
|
|
181
|
+
m_rel = m_sphere / n_env
|
|
182
|
+
jkr = 1j * 2 * np.pi * n_env * r / lambda0
|
|
33
183
|
amp = np.exp(jkr) / (-jkr)
|
|
34
184
|
|
|
35
|
-
S1, S2 =
|
|
185
|
+
S1, S2 = S1_S2(m_rel, x, np.cos(theta), norm="wiscombe")
|
|
36
186
|
|
|
37
187
|
E_r = np.zeros_like(S1, dtype=complex)
|
|
38
188
|
E_theta = S2 * amp * np.cos(phi)
|
|
@@ -42,102 +192,178 @@ def e_far(lambda0, d_sphere, m_sphere, r, theta, phi):
|
|
|
42
192
|
return np.array([E_r, E_theta, E_phi])
|
|
43
193
|
|
|
44
194
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
# m_sphere (complex): Rrefractive index of the sphere
|
|
53
|
-
# r (float): Radial distance at which the field is evaluated.
|
|
54
|
-
# theta (float): Scattering angle (from z-axis) in radians.
|
|
55
|
-
# phi (float): Azimuthal angle (from x-axis) in radians.
|
|
56
|
-
# norm: type of normalization to use for scattering function
|
|
57
|
-
#
|
|
58
|
-
# Returns:
|
|
59
|
-
# tuple: Electric field components (E_r, E_theta, E_phi).
|
|
60
|
-
# """
|
|
61
|
-
# x = np.pi * d_sphere / lambda0
|
|
62
|
-
# jkr = 1j * 2 * np.pi * r / lambda0
|
|
63
|
-
# amp = np.exp(jkr) / (-jkr)
|
|
64
|
-
# mu = np.cos(theta)
|
|
65
|
-
#
|
|
66
|
-
# a, b = mie.an_bn(m_sphere, x, 0)
|
|
67
|
-
# N = len(a)
|
|
68
|
-
# pi = np.zeros(N)
|
|
69
|
-
# tau = np.zeros(N)
|
|
70
|
-
#
|
|
71
|
-
# n = np.arange(1, N + 1)
|
|
72
|
-
# scale = (2 * n + 1) / ((n + 1) * n)
|
|
73
|
-
#
|
|
74
|
-
# mie._pi_tau(mu, pi, tau)
|
|
75
|
-
#
|
|
76
|
-
# E_r = complex(0)
|
|
77
|
-
# E_theta = np.sum(scale * (tau * a + pi * b)) * amp * np.cos(phi)
|
|
78
|
-
# E_phi = np.sum(scale * (pi * a + tau * b)) * amp * np.sin(phi)
|
|
79
|
-
# return np.array([E_r, E_theta, E_phi])
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
# def e_near_old(abcd, lambda0, d_sphere, m_index, r, theta, phi):
|
|
83
|
-
# """
|
|
84
|
-
# Calculate the electric field in and around a sphere.
|
|
85
|
-
#
|
|
86
|
-
# Args:
|
|
87
|
-
# abcd (array): Mie coefficients [a, b, c, d]
|
|
88
|
-
# lambda0 (float): Wavelength of the incident wave in vacuum.
|
|
89
|
-
# d_sphere (float): Diameter of the sphere.
|
|
90
|
-
# m_index (complex): refractive index at r
|
|
91
|
-
# r (float): Radial distance at which the field is evaluated.
|
|
92
|
-
# theta (float): Polar angle in radians.
|
|
93
|
-
# phi (float): Azimuthal angle in radians.
|
|
94
|
-
#
|
|
95
|
-
# Returns:
|
|
96
|
-
# tuple: Electric field components (E_r, E_theta, E_phi).
|
|
97
|
-
# """
|
|
98
|
-
# a, b, c, d = abcd
|
|
99
|
-
# E_r = np.complex128(0)
|
|
100
|
-
# E_theta = np.complex128(0)
|
|
101
|
-
# E_phi = np.complex128(0)
|
|
102
|
-
#
|
|
103
|
-
# N = len(a)
|
|
104
|
-
# nn = np.arange(1, N + 1)
|
|
105
|
-
# scale = 1j**nn * (2 * nn + 1) / ((nn + 1) * nn)
|
|
106
|
-
#
|
|
107
|
-
# inside = r < d_sphere / 2
|
|
108
|
-
#
|
|
109
|
-
# for n in nn:
|
|
110
|
-
# Mn = M_odd(n, lambda0, d_sphere, m_index, r, theta, phi)
|
|
111
|
-
# Nn = N_even(n, lambda0, d_sphere, m_index, r, theta, phi)
|
|
112
|
-
#
|
|
113
|
-
# if inside:
|
|
114
|
-
# E_r += scale[n - 1] * (c[n - 1] * Mn[0] - 1j * d[n - 1] * Nn[0])
|
|
115
|
-
# E_theta += scale[n - 1] * (c[n - 1] * Mn[1] - 1j * d[n - 1] * Nn[1])
|
|
116
|
-
# E_phi += scale[n - 1] * (c[n - 1] * Mn[2] - 1j * d[n - 1] * Nn[2])
|
|
117
|
-
# else:
|
|
118
|
-
# E_r += scale[n - 1] * (1j * a[n - 1] * Nn[0] - b[n - 1] * Mn[0])
|
|
119
|
-
# th_part = scale[n - 1] * (1j * a[n - 1] * Nn[1] - b[n - 1] * Mn[1])
|
|
120
|
-
# E_theta += th_part
|
|
121
|
-
# E_phi += -scale[n - 1] * (1j * a[n - 1] * Nn[2] - b[n - 1] * Mn[2])
|
|
122
|
-
#
|
|
123
|
-
# return np.array([E_r, E_theta, E_phi])
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def e_near(abcd, lambda0, d_sphere, m_index, r, theta, phi):
|
|
127
|
-
"""
|
|
128
|
-
Calculate the electric field in and around a sphere.
|
|
129
|
-
|
|
130
|
-
Args:
|
|
131
|
-
abcd (array): Mie coefficients [a, b, c, d]
|
|
132
|
-
lambda0 (float): Wavelength of the incident wave in vacuum.
|
|
133
|
-
d_sphere (float): Diameter of the sphere.
|
|
134
|
-
m_index (complex): refractive index at r
|
|
135
|
-
r (float): Radial distance at which the field is evaluated.
|
|
195
|
+
def _incident_e_spherical(lambda0, n_env, r, theta, phi, amplitude=1.0):
|
|
196
|
+
"""Return incident electric plane-wave components in spherical coordinates.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
lambda0 (float): Vacuum wavelength.
|
|
200
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
201
|
+
r (float): Radial coordinate.
|
|
136
202
|
theta (float): Polar angle in radians.
|
|
137
|
-
phi (float):
|
|
203
|
+
phi (float): Azimuth angle in radians.
|
|
204
|
+
amplitude (complex): Incident electric field amplitude.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
ndarray: Complex spherical vector ``[E_r, E_theta, E_phi]``.
|
|
208
|
+
"""
|
|
209
|
+
k = 2 * np.pi * n_env / lambda0
|
|
210
|
+
phase = np.exp(1j * k * r * np.cos(theta))
|
|
211
|
+
amp = amplitude * phase
|
|
212
|
+
|
|
213
|
+
e_r = amp * np.sin(theta) * np.cos(phi)
|
|
214
|
+
e_theta = amp * np.cos(theta) * np.cos(phi)
|
|
215
|
+
e_phi = -amp * np.sin(phi)
|
|
216
|
+
return np.array([e_r, e_theta, e_phi])
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _incident_h_spherical(lambda0, n_env, r, theta, phi, amplitude=1.0):
|
|
220
|
+
"""Return incident magnetic plane-wave components in spherical coordinates.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
lambda0 (float): Vacuum wavelength.
|
|
224
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
225
|
+
r (float): Radial coordinate.
|
|
226
|
+
theta (float): Polar angle in radians.
|
|
227
|
+
phi (float): Azimuth angle in radians.
|
|
228
|
+
amplitude (complex): Incident magnetic field amplitude in normalized units.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
ndarray: Complex spherical vector ``[H_r, H_theta, H_phi]``.
|
|
232
|
+
"""
|
|
233
|
+
k = 2 * np.pi * n_env / lambda0
|
|
234
|
+
phase = np.exp(1j * k * r * np.cos(theta))
|
|
235
|
+
amp = amplitude * phase
|
|
236
|
+
|
|
237
|
+
h_r = amp * np.sin(theta) * np.sin(phi)
|
|
238
|
+
h_theta = amp * np.cos(theta) * np.sin(phi)
|
|
239
|
+
h_phi = amp * np.cos(phi)
|
|
240
|
+
return np.array([h_r, h_theta, h_phi])
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole):
|
|
244
|
+
"""Compute Mie coefficients with consistent medium scaling.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
lambda0 (float): Vacuum wavelength.
|
|
248
|
+
d_sphere (float): Sphere diameter.
|
|
249
|
+
m_sphere (complex): Sphere refractive index.
|
|
250
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
251
|
+
n_pole (int): Requested multipole order. ``0`` means automatic truncation.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
ndarray: Coefficients packed as ``[a, b, c, d]``.
|
|
255
|
+
"""
|
|
256
|
+
x = np.pi * d_sphere * n_env / lambda0
|
|
257
|
+
m_rel = m_sphere / n_env
|
|
258
|
+
a, b, c, d = coefficients(m_rel, x, n_pole=n_pole, internal=True)
|
|
259
|
+
return np.array([a, b, c, d])
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _vectorized_field_eval(evaluator, r, theta, phi):
|
|
263
|
+
"""Evaluate a spherical-field callback on scalar or broadcasted inputs.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
evaluator (callable): Callable that accepts scalar ``(r, theta, phi)``
|
|
267
|
+
and returns a complex 3-vector.
|
|
268
|
+
r (float or ndarray): Radial coordinate(s).
|
|
269
|
+
theta (float or ndarray): Polar angle(s) in radians.
|
|
270
|
+
phi (float or ndarray): Azimuth angle(s) in radians.
|
|
138
271
|
|
|
139
272
|
Returns:
|
|
140
|
-
|
|
273
|
+
ndarray: Complex array with shape ``(3, ...)``.
|
|
274
|
+
"""
|
|
275
|
+
rr, tt, pp = np.broadcast_arrays(np.asarray(r), np.asarray(theta), np.asarray(phi))
|
|
276
|
+
|
|
277
|
+
if rr.ndim == 0:
|
|
278
|
+
return evaluator(float(rr), float(tt), float(pp))
|
|
279
|
+
|
|
280
|
+
out = np.empty((3,) + rr.shape, dtype=complex)
|
|
281
|
+
for idx in np.ndindex(rr.shape):
|
|
282
|
+
out[(slice(None),) + idx] = evaluator(float(rr[idx]), float(tt[idx]), float(pp[idx]))
|
|
283
|
+
return out
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _vectorized_field_pair_eval(evaluator, r, theta, phi):
|
|
287
|
+
"""Evaluate an ``(E, H)`` callback on scalar or broadcasted inputs.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
evaluator (callable): Callable that accepts scalar ``(r, theta, phi)``
|
|
291
|
+
and returns a tuple ``(E, H)``, each a complex 3-vector.
|
|
292
|
+
r (float or ndarray): Radial coordinate(s).
|
|
293
|
+
theta (float or ndarray): Polar angle(s) in radians.
|
|
294
|
+
phi (float or ndarray): Azimuth angle(s) in radians.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
tuple[ndarray, ndarray]: Tuple ``(E, H)`` with each array shaped
|
|
298
|
+
``(3, ...)``.
|
|
299
|
+
"""
|
|
300
|
+
rr, tt, pp = np.broadcast_arrays(np.asarray(r), np.asarray(theta), np.asarray(phi))
|
|
301
|
+
|
|
302
|
+
if rr.ndim == 0:
|
|
303
|
+
return evaluator(float(rr), float(tt), float(pp))
|
|
304
|
+
|
|
305
|
+
e_out = np.empty((3,) + rr.shape, dtype=complex)
|
|
306
|
+
h_out = np.empty((3,) + rr.shape, dtype=complex)
|
|
307
|
+
for idx in np.ndindex(rr.shape):
|
|
308
|
+
e_val, h_val = evaluator(float(rr[idx]), float(tt[idx]), float(pp[idx]))
|
|
309
|
+
e_out[(slice(None),) + idx] = e_val
|
|
310
|
+
h_out[(slice(None),) + idx] = h_val
|
|
311
|
+
return e_out, h_out
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _cartesian_to_spherical_safe(x, y, z):
|
|
315
|
+
"""Convert Cartesian coordinates to spherical coordinates safely.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
x (float or ndarray): Cartesian x coordinate(s).
|
|
319
|
+
y (float or ndarray): Cartesian y coordinate(s).
|
|
320
|
+
z (float or ndarray): Cartesian z coordinate(s).
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
tuple[ndarray, ndarray, ndarray]: ``(r, theta, phi)`` arrays after
|
|
324
|
+
broadcasting the inputs.
|
|
325
|
+
"""
|
|
326
|
+
xx, yy, zz = np.broadcast_arrays(np.asarray(x), np.asarray(y), np.asarray(z))
|
|
327
|
+
rr = np.sqrt(xx**2 + yy**2 + zz**2)
|
|
328
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
329
|
+
cos_theta = np.where(rr == 0, 1.0, np.clip(zz / rr, -1.0, 1.0))
|
|
330
|
+
theta = np.arccos(cos_theta)
|
|
331
|
+
phi = np.arctan2(yy, xx)
|
|
332
|
+
return rr, theta, phi
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _spherical_components_to_cartesian(field_sph, r, theta, phi):
|
|
336
|
+
"""Convert spherical vector components to Cartesian components.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
field_sph (ndarray): Spherical components ``[F_r, F_theta, F_phi]``.
|
|
340
|
+
r (float or ndarray): Radial coordinate(s).
|
|
341
|
+
theta (float or ndarray): Polar angle(s) in radians.
|
|
342
|
+
phi (float or ndarray): Azimuth angle(s) in radians.
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
ndarray: Cartesian components ``[F_x, F_y, F_z]``.
|
|
346
|
+
"""
|
|
347
|
+
fx, fy, fz = spherical_vector_to_cartesian(field_sph[0], field_sph[1], field_sph[2], r, theta, phi)
|
|
348
|
+
return np.array([fx, fy, fz])
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _e_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident):
|
|
352
|
+
"""Evaluate near-field electric components with precomputed coefficients.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
abcd (ndarray): Mie coefficients ``[a, b, c, d]``.
|
|
356
|
+
lambda0 (float): Vacuum wavelength.
|
|
357
|
+
d_sphere (float): Sphere diameter.
|
|
358
|
+
m_sphere (complex): Sphere refractive index.
|
|
359
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
360
|
+
r (float): Radial coordinate.
|
|
361
|
+
theta (float): Polar angle in radians.
|
|
362
|
+
phi (float): Azimuth angle in radians.
|
|
363
|
+
include_incident (bool): Include incident field outside the sphere.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
ndarray: Spherical electric components ``[E_r, E_theta, E_phi]``.
|
|
141
367
|
"""
|
|
142
368
|
a, b, c, d = abcd
|
|
143
369
|
|
|
@@ -145,49 +371,356 @@ def e_near(abcd, lambda0, d_sphere, m_index, r, theta, phi):
|
|
|
145
371
|
nn = np.arange(1, N + 1)
|
|
146
372
|
scale = 1j**nn * (2 * nn + 1) / ((nn + 1) * nn)
|
|
147
373
|
|
|
148
|
-
|
|
149
|
-
|
|
374
|
+
inside = r < d_sphere / 2
|
|
375
|
+
# miepython coefficients follow the n-ik convention and are conjugated
|
|
376
|
+
# internally; use conjugated sphere index so internal fields are consistent.
|
|
377
|
+
m_index = np.conjugate(m_sphere) if inside else n_env
|
|
378
|
+
|
|
379
|
+
M_the_base, M_phi_base, N_r_base, N_the_base, N_phi_base = _vsh_components_base(
|
|
380
|
+
N, lambda0, d_sphere, m_index, r, theta
|
|
381
|
+
)
|
|
382
|
+
cos_phi = np.cos(phi)
|
|
383
|
+
sin_phi = np.sin(phi)
|
|
384
|
+
M_rad = np.zeros(N, dtype=np.complex128)
|
|
385
|
+
M_the = cos_phi * M_the_base
|
|
386
|
+
M_phi = -sin_phi * M_phi_base
|
|
387
|
+
N_rad = cos_phi * N_r_base
|
|
388
|
+
N_the = cos_phi * N_the_base
|
|
389
|
+
N_phi = -sin_phi * N_phi_base
|
|
150
390
|
|
|
151
|
-
if
|
|
152
|
-
E_rad =
|
|
153
|
-
E_the =
|
|
154
|
-
E_phi =
|
|
391
|
+
if inside:
|
|
392
|
+
E_rad = _sum_two_scaled_terms(scale, c, M_rad, 1.0 + 0.0j, d, N_rad, -1.0j)
|
|
393
|
+
E_the = _sum_two_scaled_terms(scale, c, M_the, 1.0 + 0.0j, d, N_the, -1.0j)
|
|
394
|
+
E_phi = _sum_two_scaled_terms(scale, c, M_phi, 1.0 + 0.0j, d, N_phi, -1.0j)
|
|
155
395
|
else:
|
|
156
|
-
E_rad =
|
|
157
|
-
E_the =
|
|
158
|
-
E_phi =
|
|
396
|
+
E_rad = _sum_two_scaled_terms(scale, a, N_rad, 1.0j, b, M_rad, -1.0 + 0.0j)
|
|
397
|
+
E_the = _sum_two_scaled_terms(scale, a, N_the, 1.0j, b, M_the, -1.0 + 0.0j)
|
|
398
|
+
E_phi = _sum_two_scaled_terms(scale, a, N_phi, 1.0j, b, M_phi, -1.0 + 0.0j)
|
|
399
|
+
|
|
400
|
+
if include_incident:
|
|
401
|
+
Ei_rad, Ei_the, Ei_phi = _incident_e_spherical(lambda0, n_env, r, theta, phi)
|
|
402
|
+
E_rad += Ei_rad
|
|
403
|
+
E_the += Ei_the
|
|
404
|
+
E_phi += Ei_phi
|
|
405
|
+
|
|
406
|
+
return np.array([E_rad, E_the, E_phi])
|
|
407
|
+
|
|
159
408
|
|
|
160
|
-
|
|
409
|
+
def _h_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident):
|
|
410
|
+
"""Evaluate near-field magnetic components with precomputed coefficients.
|
|
161
411
|
|
|
412
|
+
Args:
|
|
413
|
+
abcd (ndarray): Mie coefficients ``[a, b, c, d]``.
|
|
414
|
+
lambda0 (float): Vacuum wavelength.
|
|
415
|
+
d_sphere (float): Sphere diameter.
|
|
416
|
+
m_sphere (complex): Sphere refractive index.
|
|
417
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
418
|
+
r (float): Radial coordinate.
|
|
419
|
+
theta (float): Polar angle in radians.
|
|
420
|
+
phi (float): Azimuth angle in radians.
|
|
421
|
+
include_incident (bool): Include incident field outside the sphere.
|
|
162
422
|
|
|
163
|
-
|
|
423
|
+
Returns:
|
|
424
|
+
ndarray: Spherical magnetic components ``[H_r, H_theta, H_phi]``.
|
|
164
425
|
"""
|
|
165
|
-
|
|
426
|
+
a, b, c, d = abcd
|
|
427
|
+
|
|
428
|
+
N = len(a)
|
|
429
|
+
nn = np.arange(1, N + 1)
|
|
430
|
+
scale = 1j**nn * (2 * nn + 1) / ((nn + 1) * nn)
|
|
431
|
+
|
|
432
|
+
inside = r < d_sphere / 2
|
|
433
|
+
m_index = np.conjugate(m_sphere) if inside else n_env
|
|
434
|
+
|
|
435
|
+
M_the_base, M_phi_base, N_r_base, N_the_base, N_phi_base = _vsh_components_base(
|
|
436
|
+
N, lambda0, d_sphere, m_index, r, theta
|
|
437
|
+
)
|
|
438
|
+
cos_phi = np.cos(phi)
|
|
439
|
+
sin_phi = np.sin(phi)
|
|
440
|
+
M_rad = np.zeros(N, dtype=np.complex128)
|
|
441
|
+
M_the = -sin_phi * M_the_base
|
|
442
|
+
M_phi = -cos_phi * M_phi_base
|
|
443
|
+
N_rad = sin_phi * N_r_base
|
|
444
|
+
N_the = sin_phi * N_the_base
|
|
445
|
+
N_phi = cos_phi * N_phi_base
|
|
446
|
+
|
|
447
|
+
if inside:
|
|
448
|
+
m_rel = np.conjugate(m_sphere / n_env)
|
|
449
|
+
H_rad = m_rel * _sum_two_scaled_terms(scale, d, M_rad, -1.0 + 0.0j, c, N_rad, -1.0j)
|
|
450
|
+
H_the = m_rel * _sum_two_scaled_terms(scale, d, M_the, -1.0 + 0.0j, c, N_the, -1.0j)
|
|
451
|
+
H_phi = m_rel * _sum_two_scaled_terms(scale, d, M_phi, -1.0 + 0.0j, c, N_phi, -1.0j)
|
|
452
|
+
else:
|
|
453
|
+
H_rad = _sum_two_scaled_terms(scale, b, N_rad, 1.0j, a, M_rad, 1.0 + 0.0j)
|
|
454
|
+
H_the = _sum_two_scaled_terms(scale, b, N_the, 1.0j, a, M_the, 1.0 + 0.0j)
|
|
455
|
+
H_phi = _sum_two_scaled_terms(scale, b, N_phi, 1.0j, a, M_phi, 1.0 + 0.0j)
|
|
456
|
+
|
|
457
|
+
if include_incident:
|
|
458
|
+
Hi_rad, Hi_the, Hi_phi = _incident_h_spherical(lambda0, n_env, r, theta, phi)
|
|
459
|
+
H_rad += Hi_rad
|
|
460
|
+
H_the += Hi_the
|
|
461
|
+
H_phi += Hi_phi
|
|
462
|
+
|
|
463
|
+
return np.array([H_rad, H_the, H_phi])
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def _eh_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident):
|
|
467
|
+
"""Evaluate electric and magnetic near fields with shared VSH work."""
|
|
468
|
+
a, b, c, d = abcd
|
|
469
|
+
|
|
470
|
+
N = len(a)
|
|
471
|
+
nn = np.arange(1, N + 1)
|
|
472
|
+
scale = 1j**nn * (2 * nn + 1) / ((nn + 1) * nn)
|
|
473
|
+
|
|
474
|
+
inside = r < d_sphere / 2
|
|
475
|
+
m_index = np.conjugate(m_sphere) if inside else n_env
|
|
476
|
+
M_the_base, M_phi_base, N_r_base, N_the_base, N_phi_base = _vsh_components_base(
|
|
477
|
+
N, lambda0, d_sphere, m_index, r, theta
|
|
478
|
+
)
|
|
479
|
+
cos_phi = np.cos(phi)
|
|
480
|
+
sin_phi = np.sin(phi)
|
|
481
|
+
|
|
482
|
+
M_odd_rad = np.zeros(N, dtype=np.complex128)
|
|
483
|
+
M_odd_the = cos_phi * M_the_base
|
|
484
|
+
M_odd_phi = -sin_phi * M_phi_base
|
|
485
|
+
N_even_rad = cos_phi * N_r_base
|
|
486
|
+
N_even_the = cos_phi * N_the_base
|
|
487
|
+
N_even_phi = -sin_phi * N_phi_base
|
|
488
|
+
|
|
489
|
+
M_even_rad = np.zeros(N, dtype=np.complex128)
|
|
490
|
+
M_even_the = -sin_phi * M_the_base
|
|
491
|
+
M_even_phi = -cos_phi * M_phi_base
|
|
492
|
+
N_odd_rad = sin_phi * N_r_base
|
|
493
|
+
N_odd_the = sin_phi * N_the_base
|
|
494
|
+
N_odd_phi = cos_phi * N_phi_base
|
|
495
|
+
|
|
496
|
+
if inside:
|
|
497
|
+
e_rad = _sum_two_scaled_terms(scale, c, M_odd_rad, 1.0 + 0.0j, d, N_even_rad, -1.0j)
|
|
498
|
+
e_the = _sum_two_scaled_terms(scale, c, M_odd_the, 1.0 + 0.0j, d, N_even_the, -1.0j)
|
|
499
|
+
e_phi = _sum_two_scaled_terms(scale, c, M_odd_phi, 1.0 + 0.0j, d, N_even_phi, -1.0j)
|
|
500
|
+
|
|
501
|
+
m_rel = np.conjugate(m_sphere / n_env)
|
|
502
|
+
h_rad = m_rel * _sum_two_scaled_terms(scale, d, M_even_rad, -1.0 + 0.0j, c, N_odd_rad, -1.0j)
|
|
503
|
+
h_the = m_rel * _sum_two_scaled_terms(scale, d, M_even_the, -1.0 + 0.0j, c, N_odd_the, -1.0j)
|
|
504
|
+
h_phi = m_rel * _sum_two_scaled_terms(scale, d, M_even_phi, -1.0 + 0.0j, c, N_odd_phi, -1.0j)
|
|
505
|
+
else:
|
|
506
|
+
e_rad = _sum_two_scaled_terms(scale, a, N_even_rad, 1.0j, b, M_odd_rad, -1.0 + 0.0j)
|
|
507
|
+
e_the = _sum_two_scaled_terms(scale, a, N_even_the, 1.0j, b, M_odd_the, -1.0 + 0.0j)
|
|
508
|
+
e_phi = _sum_two_scaled_terms(scale, a, N_even_phi, 1.0j, b, M_odd_phi, -1.0 + 0.0j)
|
|
509
|
+
h_rad = _sum_two_scaled_terms(scale, b, N_odd_rad, 1.0j, a, M_even_rad, 1.0 + 0.0j)
|
|
510
|
+
h_the = _sum_two_scaled_terms(scale, b, N_odd_the, 1.0j, a, M_even_the, 1.0 + 0.0j)
|
|
511
|
+
h_phi = _sum_two_scaled_terms(scale, b, N_odd_phi, 1.0j, a, M_even_phi, 1.0 + 0.0j)
|
|
512
|
+
|
|
513
|
+
if include_incident:
|
|
514
|
+
e_i = _incident_e_spherical(lambda0, n_env, r, theta, phi)
|
|
515
|
+
h_i = _incident_h_spherical(lambda0, n_env, r, theta, phi)
|
|
516
|
+
e_rad += e_i[0]
|
|
517
|
+
e_the += e_i[1]
|
|
518
|
+
e_phi += e_i[2]
|
|
519
|
+
h_rad += h_i[0]
|
|
520
|
+
h_the += h_i[1]
|
|
521
|
+
h_phi += h_i[2]
|
|
522
|
+
|
|
523
|
+
return np.array([e_rad, e_the, e_phi]), np.array([h_rad, h_the, h_phi])
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def e_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident=True, n_pole=0, abcd=None):
|
|
527
|
+
"""Calculate the electric field in and around a sphere.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
lambda0 (float): Vacuum wavelength.
|
|
531
|
+
d_sphere (float): Sphere diameter.
|
|
532
|
+
m_sphere (complex): Sphere refractive index.
|
|
533
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
534
|
+
r (float or ndarray): Radial coordinate(s).
|
|
535
|
+
theta (float or ndarray): Polar angle(s) in radians.
|
|
536
|
+
phi (float or ndarray): Azimuth angle(s) in radians.
|
|
537
|
+
include_incident (bool): Include incident field for points outside sphere.
|
|
538
|
+
n_pole (int): Requested multipole order. ``0`` means automatic truncation.
|
|
539
|
+
abcd (ndarray or None): Optional precomputed coefficients ``[a, b, c, d]``.
|
|
540
|
+
If provided, ``n_pole`` is ignored.
|
|
541
|
+
|
|
542
|
+
Returns:
|
|
543
|
+
ndarray: Spherical electric components ``[E_r, E_theta, E_phi]`` with
|
|
544
|
+
shape ``(3, ...)``.
|
|
545
|
+
"""
|
|
546
|
+
if abcd is None:
|
|
547
|
+
abcd = _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole)
|
|
548
|
+
|
|
549
|
+
def evaluator(rr, tt, pp):
|
|
550
|
+
return _e_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident)
|
|
551
|
+
|
|
552
|
+
return _vectorized_field_eval(evaluator, r, theta, phi)
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def h_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident=True, n_pole=0, abcd=None):
|
|
556
|
+
"""Calculate the magnetic field in and around a sphere.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
lambda0 (float): Vacuum wavelength.
|
|
560
|
+
d_sphere (float): Sphere diameter.
|
|
561
|
+
m_sphere (complex): Sphere refractive index.
|
|
562
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
563
|
+
r (float or ndarray): Radial coordinate(s).
|
|
564
|
+
theta (float or ndarray): Polar angle(s) in radians.
|
|
565
|
+
phi (float or ndarray): Azimuth angle(s) in radians.
|
|
566
|
+
include_incident (bool): Include incident field for points outside sphere.
|
|
567
|
+
n_pole (int): Requested multipole order. ``0`` means automatic truncation.
|
|
568
|
+
abcd (ndarray or None): Optional precomputed coefficients ``[a, b, c, d]``.
|
|
569
|
+
If provided, ``n_pole`` is ignored.
|
|
570
|
+
|
|
571
|
+
Returns:
|
|
572
|
+
ndarray: Spherical magnetic components ``[H_r, H_theta, H_phi]`` with
|
|
573
|
+
shape ``(3, ...)``.
|
|
574
|
+
"""
|
|
575
|
+
if abcd is None:
|
|
576
|
+
abcd = _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole)
|
|
577
|
+
|
|
578
|
+
def evaluator(rr, tt, pp):
|
|
579
|
+
return _h_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident)
|
|
580
|
+
|
|
581
|
+
return _vectorized_field_eval(evaluator, r, theta, phi)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def eh_near(
|
|
585
|
+
lambda0,
|
|
586
|
+
d_sphere,
|
|
587
|
+
m_sphere,
|
|
588
|
+
n_env,
|
|
589
|
+
r,
|
|
590
|
+
theta,
|
|
591
|
+
phi,
|
|
592
|
+
include_incident=True,
|
|
593
|
+
n_pole=0,
|
|
594
|
+
abcd=None,
|
|
595
|
+
):
|
|
596
|
+
"""Calculate electric and magnetic fields in and around a sphere.
|
|
166
597
|
|
|
167
598
|
Args:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
599
|
+
lambda0 (float): Vacuum wavelength.
|
|
600
|
+
d_sphere (float): Sphere diameter.
|
|
601
|
+
m_sphere (complex): Sphere refractive index.
|
|
602
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
603
|
+
r (float or ndarray): Radial coordinate(s).
|
|
604
|
+
theta (float or ndarray): Polar angle(s) in radians.
|
|
605
|
+
phi (float or ndarray): Azimuth angle(s) in radians.
|
|
606
|
+
include_incident (bool): Include incident field for points outside sphere.
|
|
607
|
+
n_pole (int): Requested multipole order. ``0`` means automatic truncation.
|
|
608
|
+
abcd (ndarray or None): Optional precomputed coefficients ``[a, b, c, d]``.
|
|
609
|
+
If provided, ``n_pole`` is ignored.
|
|
172
610
|
|
|
173
611
|
Returns:
|
|
174
|
-
tuple:
|
|
612
|
+
tuple[ndarray, ndarray]: Tuple ``(E, H)`` in spherical components,
|
|
613
|
+
each with shape ``(3, ...)``.
|
|
175
614
|
"""
|
|
176
|
-
|
|
177
|
-
|
|
615
|
+
if abcd is None:
|
|
616
|
+
abcd = _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole)
|
|
617
|
+
|
|
618
|
+
def evaluator(rr, tt, pp):
|
|
619
|
+
return _eh_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident)
|
|
620
|
+
|
|
621
|
+
return _vectorized_field_pair_eval(evaluator, r, theta, phi)
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def e_near_cartesian(
|
|
625
|
+
lambda0,
|
|
626
|
+
d_sphere,
|
|
627
|
+
m_sphere,
|
|
628
|
+
n_env,
|
|
629
|
+
x,
|
|
630
|
+
y,
|
|
631
|
+
z,
|
|
632
|
+
include_incident=True,
|
|
633
|
+
n_pole=0,
|
|
634
|
+
abcd=None,
|
|
635
|
+
):
|
|
636
|
+
"""Calculate electric near field in Cartesian coordinates.
|
|
178
637
|
|
|
179
|
-
|
|
180
|
-
|
|
638
|
+
Args:
|
|
639
|
+
lambda0 (float): Vacuum wavelength.
|
|
640
|
+
d_sphere (float): Sphere diameter.
|
|
641
|
+
m_sphere (complex): Sphere refractive index.
|
|
642
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
643
|
+
x (float or ndarray): Cartesian x coordinate(s).
|
|
644
|
+
y (float or ndarray): Cartesian y coordinate(s).
|
|
645
|
+
z (float or ndarray): Cartesian z coordinate(s).
|
|
646
|
+
include_incident (bool): Include incident field for points outside sphere.
|
|
647
|
+
n_pole (int): Requested multipole order. ``0`` means automatic truncation.
|
|
648
|
+
abcd (ndarray or None): Optional precomputed coefficients ``[a, b, c, d]``.
|
|
181
649
|
|
|
182
|
-
|
|
183
|
-
|
|
650
|
+
Returns:
|
|
651
|
+
ndarray: Cartesian electric components ``[E_x, E_y, E_z]``.
|
|
652
|
+
"""
|
|
653
|
+
r, theta, phi = _cartesian_to_spherical_safe(x, y, z)
|
|
654
|
+
e_sph = e_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident, n_pole, abcd)
|
|
655
|
+
return _spherical_components_to_cartesian(e_sph, r, theta, phi)
|
|
184
656
|
|
|
185
|
-
M_r, M_theta, M_phi = M_odd_array(N, lambda0, d_sphere, m_index, r, theta, phi)
|
|
186
|
-
N_r, N_theta, N_phi = N_even_array(N, lambda0, d_sphere, m_index, r, theta, phi)
|
|
187
657
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
658
|
+
def h_near_cartesian(
|
|
659
|
+
lambda0,
|
|
660
|
+
d_sphere,
|
|
661
|
+
m_sphere,
|
|
662
|
+
n_env,
|
|
663
|
+
x,
|
|
664
|
+
y,
|
|
665
|
+
z,
|
|
666
|
+
include_incident=True,
|
|
667
|
+
n_pole=0,
|
|
668
|
+
abcd=None,
|
|
669
|
+
):
|
|
670
|
+
"""Calculate magnetic near field in Cartesian coordinates.
|
|
671
|
+
|
|
672
|
+
Args:
|
|
673
|
+
lambda0 (float): Vacuum wavelength.
|
|
674
|
+
d_sphere (float): Sphere diameter.
|
|
675
|
+
m_sphere (complex): Sphere refractive index.
|
|
676
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
677
|
+
x (float or ndarray): Cartesian x coordinate(s).
|
|
678
|
+
y (float or ndarray): Cartesian y coordinate(s).
|
|
679
|
+
z (float or ndarray): Cartesian z coordinate(s).
|
|
680
|
+
include_incident (bool): Include incident field for points outside sphere.
|
|
681
|
+
n_pole (int): Requested multipole order. ``0`` means automatic truncation.
|
|
682
|
+
abcd (ndarray or None): Optional precomputed coefficients ``[a, b, c, d]``.
|
|
191
683
|
|
|
192
|
-
|
|
193
|
-
|
|
684
|
+
Returns:
|
|
685
|
+
ndarray: Cartesian magnetic components ``[H_x, H_y, H_z]``.
|
|
686
|
+
"""
|
|
687
|
+
r, theta, phi = _cartesian_to_spherical_safe(x, y, z)
|
|
688
|
+
h_sph = h_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident, n_pole, abcd)
|
|
689
|
+
return _spherical_components_to_cartesian(h_sph, r, theta, phi)
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def eh_near_cartesian(
|
|
693
|
+
lambda0,
|
|
694
|
+
d_sphere,
|
|
695
|
+
m_sphere,
|
|
696
|
+
n_env,
|
|
697
|
+
x,
|
|
698
|
+
y,
|
|
699
|
+
z,
|
|
700
|
+
include_incident=True,
|
|
701
|
+
n_pole=0,
|
|
702
|
+
abcd=None,
|
|
703
|
+
):
|
|
704
|
+
"""Calculate electric and magnetic near fields in Cartesian coordinates.
|
|
705
|
+
|
|
706
|
+
Args:
|
|
707
|
+
lambda0 (float): Vacuum wavelength.
|
|
708
|
+
d_sphere (float): Sphere diameter.
|
|
709
|
+
m_sphere (complex): Sphere refractive index.
|
|
710
|
+
n_env (float): Refractive index of the surrounding medium.
|
|
711
|
+
x (float or ndarray): Cartesian x coordinate(s).
|
|
712
|
+
y (float or ndarray): Cartesian y coordinate(s).
|
|
713
|
+
z (float or ndarray): Cartesian z coordinate(s).
|
|
714
|
+
include_incident (bool): Include incident field for points outside sphere.
|
|
715
|
+
n_pole (int): Requested multipole order. ``0`` means automatic truncation.
|
|
716
|
+
abcd (ndarray or None): Optional precomputed coefficients ``[a, b, c, d]``.
|
|
717
|
+
|
|
718
|
+
Returns:
|
|
719
|
+
tuple[ndarray, ndarray]: Tuple ``(E_xyz, H_xyz)`` where each array is
|
|
720
|
+
``[x, y, z]`` components.
|
|
721
|
+
"""
|
|
722
|
+
r, theta, phi = _cartesian_to_spherical_safe(x, y, z)
|
|
723
|
+
e_sph, h_sph = eh_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident, n_pole, abcd)
|
|
724
|
+
e_xyz = _spherical_components_to_cartesian(e_sph, r, theta, phi)
|
|
725
|
+
h_xyz = _spherical_components_to_cartesian(h_sph, r, theta, phi)
|
|
726
|
+
return e_xyz, h_xyz
|