PyFiberModes 0.2.1__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.
- PyFiberModes/.DS_Store +0 -0
- PyFiberModes/VERSION +1 -0
- PyFiberModes/__init__.py +38 -0
- PyFiberModes/constants.py +32 -0
- PyFiberModes/fiber/.DS_Store +0 -0
- PyFiberModes/fiber/__init__.py +0 -0
- PyFiberModes/fiber/factory.py +448 -0
- PyFiberModes/fiber/fiber.py +317 -0
- PyFiberModes/fiber/geometry/__init__.py +10 -0
- PyFiberModes/fiber/geometry/geometry.py +33 -0
- PyFiberModes/fiber/geometry/stepindex.py +181 -0
- PyFiberModes/fiber/geometry/supergaussian.py +193 -0
- PyFiberModes/fiber/material/__init__.py +13 -0
- PyFiberModes/fiber/material/air.py +20 -0
- PyFiberModes/fiber/material/claussiusmossotti.py +40 -0
- PyFiberModes/fiber/material/compmaterial.py +60 -0
- PyFiberModes/fiber/material/fixed.py +36 -0
- PyFiberModes/fiber/material/germania.py +14 -0
- PyFiberModes/fiber/material/material.py +107 -0
- PyFiberModes/fiber/material/sellmeier.py +47 -0
- PyFiberModes/fiber/material/sellmeiercomp.py +42 -0
- PyFiberModes/fiber/material/silica.py +15 -0
- PyFiberModes/fiber/material/sio2f.py +34 -0
- PyFiberModes/fiber/material/sio2geo2.py +36 -0
- PyFiberModes/fiber/material/sio2geo2cm.py +43 -0
- PyFiberModes/fiber/solver/__init__.py +12 -0
- PyFiberModes/fiber/solver/cuda.py +124 -0
- PyFiberModes/fiber/solver/cudasrc/besseldiff.c +28 -0
- PyFiberModes/fiber/solver/cudasrc/chareq.c +314 -0
- PyFiberModes/fiber/solver/cudasrc/constf.c +20 -0
- PyFiberModes/fiber/solver/cudasrc/hypergf.c +264 -0
- PyFiberModes/fiber/solver/cudasrc/ivf.c +49 -0
- PyFiberModes/fiber/solver/cudasrc/knf.c +166 -0
- PyFiberModes/fiber/solver/mlsif.py +289 -0
- PyFiberModes/fiber/solver/solver.py +119 -0
- PyFiberModes/fiber/solver/ssif.py +275 -0
- PyFiberModes/fiber/solver/tlsif.py +266 -0
- PyFiberModes/field.py +405 -0
- PyFiberModes/functions.py +137 -0
- PyFiberModes/mode.py +186 -0
- PyFiberModes/simulator/.DS_Store +0 -0
- PyFiberModes/simulator/__init__.py +15 -0
- PyFiberModes/simulator/psimulator.py +41 -0
- PyFiberModes/simulator/simulator.py +288 -0
- PyFiberModes/slrc.py +291 -0
- PyFiberModes/tools/__init__.py +0 -0
- PyFiberModes/tools/directories.py +41 -0
- PyFiberModes/wavelength.py +109 -0
- PyFiberModes-0.2.1.dist-info/METADATA +158 -0
- PyFiberModes-0.2.1.dist-info/RECORD +52 -0
- PyFiberModes-0.2.1.dist-info/WHEEL +5 -0
- PyFiberModes-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# This file is part of FiberModes.
|
|
2
|
+
#
|
|
3
|
+
# FiberModes is free software: you can redistribute it and/or modify
|
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
6
|
+
# (at your option) any later version.
|
|
7
|
+
#
|
|
8
|
+
from . import geometry
|
|
9
|
+
from . import solver
|
|
10
|
+
from .solver.solver import FiberSolver
|
|
11
|
+
from math import sqrt, isnan, isinf
|
|
12
|
+
from PyFiberModes import Wavelength, Mode, ModeFamily
|
|
13
|
+
from PyFiberModes import constants
|
|
14
|
+
from PyFiberModes.functions import derivative
|
|
15
|
+
from PyFiberModes.field import Field
|
|
16
|
+
from itertools import count
|
|
17
|
+
import logging
|
|
18
|
+
from scipy.optimize import fixed_point
|
|
19
|
+
from functools import lru_cache
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Fiber(object):
|
|
23
|
+
|
|
24
|
+
"""The Fiber object usually is build using
|
|
25
|
+
:py:class:`~PyFiberModes.fiber.factory.FiberFactory`.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
def __init__(self, r, f, fp, m, mp, names, Cutoff=None, Neff=None):
|
|
32
|
+
|
|
33
|
+
self._r = r
|
|
34
|
+
self._names = names
|
|
35
|
+
|
|
36
|
+
self.layers = []
|
|
37
|
+
for i, (f_, fp_, m_, mp_) in enumerate(zip(f, fp, m, mp)):
|
|
38
|
+
ri = self._r[i - 1] if i else 0
|
|
39
|
+
ro = self._r[i] if i < len(r) else float("inf")
|
|
40
|
+
layer = geometry.__dict__[f_](ri, ro, *fp_,
|
|
41
|
+
m=m_, mp=mp_, cm=m[-1], cmp=mp[-1])
|
|
42
|
+
self.layers.append(layer)
|
|
43
|
+
|
|
44
|
+
self.co_cache = {Mode("HE", 1, 1): 0,
|
|
45
|
+
Mode("LP", 0, 1): 0}
|
|
46
|
+
self.ne_cache = {}
|
|
47
|
+
|
|
48
|
+
self.setSolvers(Cutoff, Neff)
|
|
49
|
+
|
|
50
|
+
def __len__(self):
|
|
51
|
+
return len(self.layers)
|
|
52
|
+
|
|
53
|
+
def __str__(self):
|
|
54
|
+
s = "Fiber {\n"
|
|
55
|
+
for i, layer in enumerate(self.layers):
|
|
56
|
+
geom = str(layer)
|
|
57
|
+
radius = self.outerRadius(i)
|
|
58
|
+
radius = '' if isinf(radius) else ' {:.3f} µm'.format(radius*1e6)
|
|
59
|
+
name = self.name(i)
|
|
60
|
+
name = ' "{}"'.format(name) if name else ''
|
|
61
|
+
s += " {}{}{}\n".format(geom, radius, name)
|
|
62
|
+
s += "}"
|
|
63
|
+
return s
|
|
64
|
+
|
|
65
|
+
def fixedMatFiber(self, wl):
|
|
66
|
+
f = []
|
|
67
|
+
fp = []
|
|
68
|
+
m = []
|
|
69
|
+
mp = []
|
|
70
|
+
for layer in self.layers:
|
|
71
|
+
f.append(layer.__class__.__name__)
|
|
72
|
+
fp.append(layer._fp)
|
|
73
|
+
m.append("Fixed")
|
|
74
|
+
mp.append([layer._m.n(wl, *layer._mp)])
|
|
75
|
+
return Fiber(self._r, f, fp, m, mp, self._names,
|
|
76
|
+
self._cutoff.__class__, self._neff.__class__)
|
|
77
|
+
|
|
78
|
+
def name(self, layer):
|
|
79
|
+
return self._names[layer]
|
|
80
|
+
|
|
81
|
+
def _layer(self, r):
|
|
82
|
+
r = abs(r)
|
|
83
|
+
for i, r_ in enumerate(self._r):
|
|
84
|
+
if r < r_:
|
|
85
|
+
return self.layers[i]
|
|
86
|
+
return self.layers[-1]
|
|
87
|
+
|
|
88
|
+
def innerRadius(self, layer):
|
|
89
|
+
if layer < 0:
|
|
90
|
+
layer = len(self._r) + layer + 1
|
|
91
|
+
return self._r[layer-1] if layer else 0
|
|
92
|
+
|
|
93
|
+
def outerRadius(self, layer):
|
|
94
|
+
return self._r[layer] if layer < len(self._r) else float("inf")
|
|
95
|
+
|
|
96
|
+
def thickness(self, layer):
|
|
97
|
+
return self.outerRadius(layer) - self.innerRadius(layer)
|
|
98
|
+
|
|
99
|
+
def index(self, r, wl):
|
|
100
|
+
return self._layer(r).index(r, wl)
|
|
101
|
+
|
|
102
|
+
def minIndex(self, layer, wl):
|
|
103
|
+
return self.layers[layer].minIndex(wl)
|
|
104
|
+
|
|
105
|
+
def maxIndex(self, layer, wl):
|
|
106
|
+
return self.layers[layer].maxIndex(wl)
|
|
107
|
+
|
|
108
|
+
def _findCutoffSolver(self):
|
|
109
|
+
cutoff = FiberSolver
|
|
110
|
+
if all(isinstance(layer, geometry.StepIndex)
|
|
111
|
+
for layer in self.layers):
|
|
112
|
+
nlayers = len(self)
|
|
113
|
+
if nlayers == 2: # SSIF
|
|
114
|
+
cutoff = solver.ssif.Cutoff
|
|
115
|
+
elif nlayers == 3:
|
|
116
|
+
cutoff = solver.tlsif.Cutoff
|
|
117
|
+
return cutoff
|
|
118
|
+
|
|
119
|
+
def _findNeffSolver(self):
|
|
120
|
+
neff = solver.mlsif.Neff
|
|
121
|
+
if all(isinstance(layer, geometry.StepIndex)
|
|
122
|
+
for layer in self.layers):
|
|
123
|
+
nlayers = len(self)
|
|
124
|
+
if nlayers == 2: # SSIF
|
|
125
|
+
neff = solver.ssif.Neff
|
|
126
|
+
return neff
|
|
127
|
+
|
|
128
|
+
def setSolvers(self, Cutoff=None, Neff=None):
|
|
129
|
+
assert Cutoff is None or issubclass(Cutoff, FiberSolver)
|
|
130
|
+
assert Neff is None or issubclass(Neff, FiberSolver)
|
|
131
|
+
if Cutoff is None:
|
|
132
|
+
Cutoff = self._findCutoffSolver()
|
|
133
|
+
self._cutoff = Cutoff(self)
|
|
134
|
+
if Neff is None:
|
|
135
|
+
Neff = self._findNeffSolver()
|
|
136
|
+
self._neff = Neff(self)
|
|
137
|
+
|
|
138
|
+
def set_ne_cache(self, wl, mode, neff):
|
|
139
|
+
try:
|
|
140
|
+
self.ne_cache[wl][mode] = neff
|
|
141
|
+
except KeyError:
|
|
142
|
+
self.ne_cache[wl] = {mode: neff}
|
|
143
|
+
|
|
144
|
+
def NA(self, wl):
|
|
145
|
+
n1 = max(layer.maxIndex(wl) for layer in self.layers)
|
|
146
|
+
n2 = self.minIndex(-1, wl)
|
|
147
|
+
return sqrt(n1*n1 - n2*n2)
|
|
148
|
+
|
|
149
|
+
def V0(self, wl):
|
|
150
|
+
return Wavelength(wl).k0 * self.innerRadius(-1) * self.NA(wl)
|
|
151
|
+
|
|
152
|
+
def toWl(self, V0, maxiter=500, tol=1e-15):
|
|
153
|
+
"""Convert V0 number to wavelength.
|
|
154
|
+
|
|
155
|
+
An iterative method is used, since the index can be wavelength
|
|
156
|
+
dependant.
|
|
157
|
+
|
|
158
|
+
"""
|
|
159
|
+
if V0 == 0:
|
|
160
|
+
return float("inf")
|
|
161
|
+
if isinf(V0):
|
|
162
|
+
return 0
|
|
163
|
+
|
|
164
|
+
def f(x):
|
|
165
|
+
return constants.tpi / V0 * b * self.NA(x)
|
|
166
|
+
|
|
167
|
+
b = self.innerRadius(-1)
|
|
168
|
+
|
|
169
|
+
wl = f(1.55e-6)
|
|
170
|
+
if abs(wl - f(wl)) > tol:
|
|
171
|
+
for w in (1.55e-6, 5e-6, 10e-6):
|
|
172
|
+
try:
|
|
173
|
+
wl = fixed_point(f, w, xtol=tol, maxiter=maxiter)
|
|
174
|
+
except RuntimeError:
|
|
175
|
+
# FIXME: What should we do if it does not converge?
|
|
176
|
+
self.logger.info(
|
|
177
|
+
"toWl: did not converged from {}µm "
|
|
178
|
+
"for V0 = {} (wl={})".format(w * 1e6, V0, wl))
|
|
179
|
+
if wl > 0:
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
if wl == 0:
|
|
183
|
+
self.logger.error("toWl: did not converged for "
|
|
184
|
+
"V0 = {} (wl={})".format(V0, wl))
|
|
185
|
+
|
|
186
|
+
return Wavelength(wl)
|
|
187
|
+
|
|
188
|
+
def cutoff(self, mode):
|
|
189
|
+
try:
|
|
190
|
+
return self.co_cache[mode]
|
|
191
|
+
except KeyError:
|
|
192
|
+
co = self._cutoff(mode)
|
|
193
|
+
self.co_cache[mode] = co
|
|
194
|
+
return co
|
|
195
|
+
|
|
196
|
+
def cutoffWl(self, mode):
|
|
197
|
+
return self.toWl(self.cutoff(mode))
|
|
198
|
+
|
|
199
|
+
def neff(self, mode, wl, delta=1e-6, lowbound=None):
|
|
200
|
+
try:
|
|
201
|
+
return self.ne_cache[wl][mode]
|
|
202
|
+
except KeyError:
|
|
203
|
+
neff = self._neff(Wavelength(wl), mode, delta, lowbound)
|
|
204
|
+
self.set_ne_cache(wl, mode, neff)
|
|
205
|
+
return neff
|
|
206
|
+
|
|
207
|
+
def beta(self, omega, mode, p=0, delta=1e-6, lowbound=None):
|
|
208
|
+
wl = Wavelength(omega=omega)
|
|
209
|
+
if p == 0:
|
|
210
|
+
neff = self.neff(mode, wl, delta, lowbound)
|
|
211
|
+
return neff * wl.k0
|
|
212
|
+
|
|
213
|
+
m = 5
|
|
214
|
+
j = (m - 1) // 2
|
|
215
|
+
h = 1e12 # This value is critical for accurate computation
|
|
216
|
+
lb = lowbound
|
|
217
|
+
for i in range(m-1, -1, -1):
|
|
218
|
+
# Precompute neff using previous wavelength
|
|
219
|
+
o = omega + (i-j) * h
|
|
220
|
+
wl = Wavelength(omega=o)
|
|
221
|
+
lb = self.neff(mode, wl, delta, lb) + delta * 1.1
|
|
222
|
+
|
|
223
|
+
return derivative(
|
|
224
|
+
self.beta, omega, p, m, j, h, mode, 0, delta, lowbound)
|
|
225
|
+
|
|
226
|
+
def b(self, mode, wl, delta=1e-6, lowbound=None):
|
|
227
|
+
"""Normalized propagation constant"""
|
|
228
|
+
neff = self.neff(mode, wl, delta, lowbound)
|
|
229
|
+
nmax = max(layer.maxIndex(wl) for layer in self.layers)
|
|
230
|
+
ncl = self.minIndex(-1, wl)
|
|
231
|
+
ncl2 = ncl*ncl
|
|
232
|
+
return (neff*neff - ncl2) / (nmax*nmax - ncl2)
|
|
233
|
+
|
|
234
|
+
def vp(self, mode, wl, delta=1e-6, lowbound=None):
|
|
235
|
+
return constants.c / self.neff(mode, wl, delta, lowbound)
|
|
236
|
+
|
|
237
|
+
def ng(self, mode, wl, delta=1e-6, lowbound=None):
|
|
238
|
+
return self.beta(Wavelength(wl).omega,
|
|
239
|
+
mode, 1, delta, lowbound) * constants.c
|
|
240
|
+
|
|
241
|
+
def vg(self, mode, wl, delta=1e-6, lowbound=None):
|
|
242
|
+
return 1 / self.beta(
|
|
243
|
+
Wavelength(wl).omega, mode, 1, delta, lowbound)
|
|
244
|
+
|
|
245
|
+
def D(self, mode, wl, delta=1e-6, lowbound=None):
|
|
246
|
+
return -(self.beta(
|
|
247
|
+
Wavelength(wl).omega, mode, 2, delta, lowbound) *
|
|
248
|
+
constants.tpi * constants.c * 1e6 / (wl * wl))
|
|
249
|
+
|
|
250
|
+
def S(self, mode, wl, delta=1e-6, lowbound=None):
|
|
251
|
+
return (self.beta(
|
|
252
|
+
Wavelength(wl).omega, mode, 3, delta, lowbound) *
|
|
253
|
+
(constants.tpi * constants.c / (wl * wl))**2 * 1e-3)
|
|
254
|
+
|
|
255
|
+
def findVmodes(self, wl, numax=None, mmax=None, delta=1e-6):
|
|
256
|
+
families = (ModeFamily.HE, ModeFamily.EH, ModeFamily.TE, ModeFamily.TM)
|
|
257
|
+
return self.findModes(families, wl, numax, mmax, delta)
|
|
258
|
+
|
|
259
|
+
def findLPmodes(self, wl, ellmax=None, mmax=None, delta=1e-6):
|
|
260
|
+
families = (ModeFamily.LP,)
|
|
261
|
+
return self.findModes(families, wl, ellmax, mmax, delta)
|
|
262
|
+
|
|
263
|
+
def findModes(self, families, wl, numax=None, mmax=None, delta=1e-6):
|
|
264
|
+
"""Find all modes of given families, within given constraints
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
"""
|
|
269
|
+
modes = set()
|
|
270
|
+
v0 = self.V0(wl)
|
|
271
|
+
for fam in families:
|
|
272
|
+
for nu in count(0):
|
|
273
|
+
try:
|
|
274
|
+
_mmax = mmax[nu]
|
|
275
|
+
except IndexError:
|
|
276
|
+
_mmax = mmax[-1]
|
|
277
|
+
except TypeError:
|
|
278
|
+
_mmax = mmax
|
|
279
|
+
|
|
280
|
+
if (fam is ModeFamily.TE or fam is ModeFamily.TM) and nu > 0:
|
|
281
|
+
break
|
|
282
|
+
if (fam is ModeFamily.HE or fam is ModeFamily.EH) and nu == 0:
|
|
283
|
+
continue
|
|
284
|
+
if numax is not None and nu > numax:
|
|
285
|
+
break
|
|
286
|
+
for m in count(1):
|
|
287
|
+
if _mmax is not None and m > _mmax:
|
|
288
|
+
break
|
|
289
|
+
mode = Mode(fam, nu, m)
|
|
290
|
+
try:
|
|
291
|
+
co = self.cutoff(mode)
|
|
292
|
+
if co > v0:
|
|
293
|
+
break
|
|
294
|
+
except (NotImplementedError, ValueError):
|
|
295
|
+
neff = self.neff(mode, wl, delta)
|
|
296
|
+
if isnan(neff):
|
|
297
|
+
break
|
|
298
|
+
modes.add(mode)
|
|
299
|
+
if m == 1:
|
|
300
|
+
break
|
|
301
|
+
return modes
|
|
302
|
+
|
|
303
|
+
def field(self, mode, wl, r, np=101):
|
|
304
|
+
"""Return electro-magnetic field.
|
|
305
|
+
|
|
306
|
+
"""
|
|
307
|
+
return Field(self, mode, wl, r, np)
|
|
308
|
+
|
|
309
|
+
@lru_cache(maxsize=None)
|
|
310
|
+
def _rfield(self, mode, wl, r):
|
|
311
|
+
neff = self.neff(mode, wl)
|
|
312
|
+
fct = {ModeFamily.LP: self._neff._lpfield,
|
|
313
|
+
ModeFamily.TE: self._neff._tefield,
|
|
314
|
+
ModeFamily.TM: self._neff._tmfield,
|
|
315
|
+
ModeFamily.EH: self._neff._ehfield,
|
|
316
|
+
ModeFamily.HE: self._neff._hefield}
|
|
317
|
+
return fct[mode.family](wl, mode.nu, neff, r)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This file is part of FiberModes.
|
|
2
|
+
#
|
|
3
|
+
# FiberModes is free software: you can redistribute it and/or modify
|
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
6
|
+
# (at your option) any later version.
|
|
7
|
+
#
|
|
8
|
+
# FiberModes is distributed in the hope that it will be useful,
|
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11
|
+
# GNU General Public License for more details.
|
|
12
|
+
#
|
|
13
|
+
# You should have received a copy of the GNU General Public License
|
|
14
|
+
# along with FiberModes. If not, see <http://www.gnu.org/licenses/>.
|
|
15
|
+
|
|
16
|
+
from PyFiberModes.fiber import material
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Geometry(object):
|
|
20
|
+
|
|
21
|
+
def __init__(self, ri, ro, *fp, m, mp, **kwargs):
|
|
22
|
+
self._m = material.__dict__[m]() # instantiate material object
|
|
23
|
+
self._mp = mp
|
|
24
|
+
cm = kwargs.get("cm", None)
|
|
25
|
+
if cm:
|
|
26
|
+
self._cm = material.__dict__[cm]()
|
|
27
|
+
self._cmp = kwargs.get("cmp")
|
|
28
|
+
self._fp = fp
|
|
29
|
+
self.ri = ri
|
|
30
|
+
self.ro = ro
|
|
31
|
+
|
|
32
|
+
def __str__(self):
|
|
33
|
+
return self.__class__.__name__ + ' ' + self._m.str(*self._mp)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from PyFiberModes.fiber.geometry.geometry import Geometry
|
|
2
|
+
from PyFiberModes import constants
|
|
3
|
+
from math import sqrt
|
|
4
|
+
import numpy
|
|
5
|
+
from scipy.special import jn, yn, iv, kn
|
|
6
|
+
from scipy.special import j0, y0, i0, k0
|
|
7
|
+
from scipy.special import j1, y1, i1, k1
|
|
8
|
+
from scipy.special import jvp, yvp, ivp, kvp
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StepIndex(Geometry):
|
|
12
|
+
|
|
13
|
+
DEFAULT_PARAMS = []
|
|
14
|
+
|
|
15
|
+
def index(self, r, wl):
|
|
16
|
+
if self.ri <= abs(r) <= self.ro:
|
|
17
|
+
return self._m.n(wl, *self._mp)
|
|
18
|
+
else:
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
def minIndex(self, wl):
|
|
22
|
+
return self._m.n(wl, *self._mp)
|
|
23
|
+
|
|
24
|
+
def maxIndex(self, wl):
|
|
25
|
+
return self._m.n(wl, *self._mp)
|
|
26
|
+
|
|
27
|
+
def u(self, r, neff, wl):
|
|
28
|
+
return wl.k0 * r * sqrt(abs(self.index(r, wl)**2 - neff**2))
|
|
29
|
+
|
|
30
|
+
def Psi(self, r, neff, wl, nu, C):
|
|
31
|
+
u = self.u(r, neff, wl)
|
|
32
|
+
if neff < self.maxIndex(wl):
|
|
33
|
+
psi = (C[0] * jn(nu, u) + C[1] * yn(nu, u) if C[1] else
|
|
34
|
+
C[0] * jn(nu, u))
|
|
35
|
+
psip = u * (C[0] * jvp(nu, u) + C[1] * yvp(nu, u) if C[1] else
|
|
36
|
+
C[0] * jvp(nu, u))
|
|
37
|
+
else:
|
|
38
|
+
psi = (C[0] * iv(nu, u) + C[1] * kn(nu, u) if C[1] else
|
|
39
|
+
C[0] * iv(nu, u))
|
|
40
|
+
psip = u * (C[0] * ivp(nu, u) + C[1] * kvp(nu, u) if C[1] else
|
|
41
|
+
C[0] * ivp(nu, u))
|
|
42
|
+
# if numpy.isnan(psi):
|
|
43
|
+
# print(neff, self.maxIndex(wl), C, r)
|
|
44
|
+
return psi, psip
|
|
45
|
+
|
|
46
|
+
def lpConstants(self, r, neff, wl, nu, A):
|
|
47
|
+
u = self.u(r, neff, wl)
|
|
48
|
+
if neff < self.maxIndex(wl):
|
|
49
|
+
W = constants.pi / 2
|
|
50
|
+
return (W * (u * yvp(nu, u) * A[0] - yn(nu, u) * A[1]),
|
|
51
|
+
W * (jn(nu, u) * A[1] - u * jvp(nu, u) * A[0]))
|
|
52
|
+
else:
|
|
53
|
+
return ((u * kvp(nu, u) * A[0] - kn(nu, u) * A[1]),
|
|
54
|
+
(iv(nu, u) * A[1] - u * ivp(nu, u) * A[0]))
|
|
55
|
+
|
|
56
|
+
def EH_fields(self, ri, ro, nu, neff, wl, EH, tm=True):
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
modify EH in-place (for speed)
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
n = self.maxIndex(wl)
|
|
63
|
+
u = self.u(ro, neff, wl)
|
|
64
|
+
|
|
65
|
+
if ri == 0:
|
|
66
|
+
if nu == 0:
|
|
67
|
+
if tm:
|
|
68
|
+
self.C = numpy.array([1., 0., 0., 0.])
|
|
69
|
+
else:
|
|
70
|
+
self.C = numpy.array([0., 0., 1., 0.])
|
|
71
|
+
else:
|
|
72
|
+
self.C = numpy.zeros((4, 2))
|
|
73
|
+
self.C[0, 0] = 1 # Ez = 1
|
|
74
|
+
self.C[2, 1] = 1 # Hz = alpha
|
|
75
|
+
elif nu == 0:
|
|
76
|
+
self.C = numpy.zeros(4)
|
|
77
|
+
if tm:
|
|
78
|
+
c = constants.Y0 * n * n
|
|
79
|
+
idx = (0, 3)
|
|
80
|
+
self.C[:2] = self.tetmConstants(ri, ro, neff, wl, EH, c, idx)
|
|
81
|
+
else:
|
|
82
|
+
c = -constants.eta0
|
|
83
|
+
idx = (1, 2)
|
|
84
|
+
self.C[2:] = self.tetmConstants(ri, ro, neff, wl, EH, c, idx)
|
|
85
|
+
else:
|
|
86
|
+
self.C = self.vConstants(ri, ro, neff, wl, nu, EH)
|
|
87
|
+
|
|
88
|
+
# Compute EH fields
|
|
89
|
+
if neff < n:
|
|
90
|
+
c1 = wl.k0 * ro / u
|
|
91
|
+
F3 = jvp(nu, u) / jn(nu, u)
|
|
92
|
+
F4 = yvp(nu, u) / yn(nu, u)
|
|
93
|
+
else:
|
|
94
|
+
c1 = -wl.k0 * ro / u
|
|
95
|
+
F3 = ivp(nu, u) / iv(nu, u)
|
|
96
|
+
F4 = kvp(nu, u) / kn(nu, u)
|
|
97
|
+
|
|
98
|
+
c2 = neff * nu / u * c1
|
|
99
|
+
c3 = constants.eta0 * c1
|
|
100
|
+
c4 = constants.Y0 * n * n * c1
|
|
101
|
+
|
|
102
|
+
EH[0] = self.C[0] + self.C[1]
|
|
103
|
+
EH[1] = self.C[2] + self.C[3]
|
|
104
|
+
EH[2] = (c2 * (self.C[0] + self.C[1]) -
|
|
105
|
+
c3 * (F3 * self.C[2] + F4 * self.C[3]))
|
|
106
|
+
EH[3] = (c4 * (F3 * self.C[0] + F4 * self.C[1]) -
|
|
107
|
+
c2 * (self.C[2] + self.C[3]))
|
|
108
|
+
|
|
109
|
+
return EH
|
|
110
|
+
|
|
111
|
+
def vConstants(self, ri, ro, neff, wl, nu, EH):
|
|
112
|
+
a = numpy.zeros((4, 4))
|
|
113
|
+
n = self.maxIndex(wl)
|
|
114
|
+
u = self.u(ro, neff, wl)
|
|
115
|
+
urp = self.u(ri, neff, wl)
|
|
116
|
+
|
|
117
|
+
if neff < n:
|
|
118
|
+
B1 = jn(nu, u)
|
|
119
|
+
B2 = yn(nu, u)
|
|
120
|
+
F1 = jn(nu, urp) / B1
|
|
121
|
+
F2 = yn(nu, urp) / B2
|
|
122
|
+
F3 = jvp(nu, urp) / B1
|
|
123
|
+
F4 = yvp(nu, urp) / B2
|
|
124
|
+
c1 = wl.k0 * ro / u
|
|
125
|
+
else:
|
|
126
|
+
B1 = iv(nu, u)
|
|
127
|
+
B2 = kn(nu, u)
|
|
128
|
+
F1 = iv(nu, urp) / B1 if u else 1
|
|
129
|
+
F2 = kn(nu, urp) / B2
|
|
130
|
+
F3 = ivp(nu, urp) / B1 if u else 1
|
|
131
|
+
F4 = kvp(nu, urp) / B2
|
|
132
|
+
c1 = -wl.k0 * ro / u
|
|
133
|
+
c2 = neff * nu / urp * c1
|
|
134
|
+
c3 = constants.eta0 * c1
|
|
135
|
+
c4 = constants.Y0 * n * n * c1
|
|
136
|
+
|
|
137
|
+
a[0, 0] = F1
|
|
138
|
+
a[0, 1] = F2
|
|
139
|
+
a[1, 2] = F1
|
|
140
|
+
a[1, 3] = F2
|
|
141
|
+
a[2, 0] = F1 * c2
|
|
142
|
+
a[2, 1] = F2 * c2
|
|
143
|
+
a[2, 2] = -F3 * c3
|
|
144
|
+
a[2, 3] = -F4 * c3
|
|
145
|
+
a[3, 0] = F3 * c4
|
|
146
|
+
a[3, 1] = F4 * c4
|
|
147
|
+
a[3, 2] = -F1 * c2
|
|
148
|
+
a[3, 3] = -F2 * c2
|
|
149
|
+
|
|
150
|
+
return numpy.linalg.solve(a, EH)
|
|
151
|
+
|
|
152
|
+
def tetmConstants(self, ri, ro, neff, wl, EH, c, idx):
|
|
153
|
+
a = numpy.empty((2, 2))
|
|
154
|
+
n = self.maxIndex(wl)
|
|
155
|
+
u = self.u(ro, neff, wl)
|
|
156
|
+
urp = self.u(ri, neff, wl)
|
|
157
|
+
|
|
158
|
+
if neff < n:
|
|
159
|
+
B1 = j0(u)
|
|
160
|
+
B2 = y0(u)
|
|
161
|
+
F1 = j0(urp) / B1
|
|
162
|
+
F2 = y0(urp) / B2
|
|
163
|
+
F3 = -j1(urp) / B1
|
|
164
|
+
F4 = -y1(urp) / B2
|
|
165
|
+
c1 = wl.k0 * ro / u
|
|
166
|
+
else:
|
|
167
|
+
B1 = i0(u)
|
|
168
|
+
B2 = k0(u)
|
|
169
|
+
F1 = i0(urp) / B1
|
|
170
|
+
F2 = k0(urp) / B2
|
|
171
|
+
F3 = i1(urp) / B1
|
|
172
|
+
F4 = -k1(urp) / B2
|
|
173
|
+
c1 = -wl.k0 * ro / u
|
|
174
|
+
c3 = c * c1
|
|
175
|
+
|
|
176
|
+
a[0, 0] = F1
|
|
177
|
+
a[0, 1] = F2
|
|
178
|
+
a[1, 0] = F3 * c3
|
|
179
|
+
a[1, 1] = F4 * c3
|
|
180
|
+
|
|
181
|
+
return numpy.linalg.solve(a, EH.take(idx))
|