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,289 @@
|
|
|
1
|
+
from .solver import FiberSolver
|
|
2
|
+
from PyFiberModes import Wavelength, Mode, ModeFamily
|
|
3
|
+
from PyFiberModes import constants
|
|
4
|
+
from math import isnan
|
|
5
|
+
import numpy
|
|
6
|
+
from scipy.special import kn, kvp, k0, k1, jn, jvp, yn, yvp, iv, ivp
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Neff(FiberSolver):
|
|
10
|
+
|
|
11
|
+
def __call__(self, wl, mode, delta, lowbound):
|
|
12
|
+
wl = Wavelength(wl)
|
|
13
|
+
if lowbound is None or isnan(lowbound):
|
|
14
|
+
pm = None
|
|
15
|
+
if mode.family is ModeFamily.HE:
|
|
16
|
+
if mode.m > 1:
|
|
17
|
+
pm = Mode(ModeFamily.EH, mode.nu, mode.m - 1)
|
|
18
|
+
elif mode.family is ModeFamily.EH:
|
|
19
|
+
pm = Mode(ModeFamily.HE, mode.nu, mode.m)
|
|
20
|
+
elif mode.m > 1:
|
|
21
|
+
pm = Mode(mode.family, mode.nu, mode.m - 1)
|
|
22
|
+
|
|
23
|
+
if pm:
|
|
24
|
+
lowbound = self.fiber.neff(pm, wl, delta)
|
|
25
|
+
if isnan(lowbound):
|
|
26
|
+
return lowbound
|
|
27
|
+
else:
|
|
28
|
+
lowbound = max(layer.maxIndex(wl)
|
|
29
|
+
for layer in self.fiber.layers)
|
|
30
|
+
|
|
31
|
+
if mode.family is ModeFamily.LP and mode.nu > 0:
|
|
32
|
+
pm = Mode(mode.family, mode.nu - 1, mode.m)
|
|
33
|
+
lb = self.fiber.neff(pm, wl, delta)
|
|
34
|
+
if isnan(lb):
|
|
35
|
+
return lb
|
|
36
|
+
lowbound = min(lowbound, lb)
|
|
37
|
+
# try:
|
|
38
|
+
# # Use cutoff information if available
|
|
39
|
+
# co = self.fiber.cutoff(mode)
|
|
40
|
+
# if self.fiber.V0(wl) < co:
|
|
41
|
+
# return float("nan")
|
|
42
|
+
|
|
43
|
+
# nco = max(layer.maxIndex(wl) for layer in self.fiber.layers)
|
|
44
|
+
# r = self.fiber.innerRadius(-1)
|
|
45
|
+
|
|
46
|
+
# lowbound = min(lowbound, sqrt(nco**2 - (co / (r * wl.k0))**2))
|
|
47
|
+
# except (NotImplementedError):
|
|
48
|
+
# pass
|
|
49
|
+
# if mode == Mode(ModeFamily.HE, 3, 1):
|
|
50
|
+
# print('3', lowbound)
|
|
51
|
+
|
|
52
|
+
highbound = self.fiber.minIndex(-1, wl)
|
|
53
|
+
|
|
54
|
+
fct = {ModeFamily.LP: self._lpceq,
|
|
55
|
+
ModeFamily.TE: self._teceq,
|
|
56
|
+
ModeFamily.TM: self._tmceq,
|
|
57
|
+
ModeFamily.HE: self._heceq,
|
|
58
|
+
ModeFamily.EH: self._heceq
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if lowbound <= highbound:
|
|
62
|
+
print("impossible bound")
|
|
63
|
+
return float("nan")
|
|
64
|
+
|
|
65
|
+
if (lowbound - highbound) < 10 * delta:
|
|
66
|
+
delta = (lowbound - highbound) / 10
|
|
67
|
+
|
|
68
|
+
return self._findFirstRoot(fct[mode.family], args=(wl, mode.nu),
|
|
69
|
+
lowbound=lowbound-1e-15,
|
|
70
|
+
highbound=highbound+1e-15,
|
|
71
|
+
delta=-delta)
|
|
72
|
+
|
|
73
|
+
def _lpfield(self, wl, nu, neff, r):
|
|
74
|
+
N = len(self.fiber)
|
|
75
|
+
C = numpy.array((1, 0))
|
|
76
|
+
|
|
77
|
+
for i in range(1, N):
|
|
78
|
+
rho = self.fiber.innerRadius(i)
|
|
79
|
+
if r < rho:
|
|
80
|
+
layer = self.fiber.layers[i-1]
|
|
81
|
+
break
|
|
82
|
+
A = self.fiber.layers[i-1].Psi(rho, neff, wl, nu, C)
|
|
83
|
+
C = self.fiber.layers[i].lpConstants(rho, neff, wl, nu, A)
|
|
84
|
+
else:
|
|
85
|
+
layer = self.fiber.layers[-1]
|
|
86
|
+
u = layer.u(rho, neff, wl)
|
|
87
|
+
C = (0, A[0] / kn(nu, u))
|
|
88
|
+
|
|
89
|
+
ex, _ = layer.Psi(r, neff, wl, nu, C)
|
|
90
|
+
hy = neff * constants.Y0 * ex
|
|
91
|
+
|
|
92
|
+
return numpy.array((ex, 0, 0)), numpy.array((0, hy, 0))
|
|
93
|
+
|
|
94
|
+
def _tefield(self, wl, nu, neff, r):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def _tmfield(self, wl, nu, neff, r):
|
|
98
|
+
N = len(self.fiber)
|
|
99
|
+
C = numpy.array((1, 0))
|
|
100
|
+
EH = numpy.zeros(4)
|
|
101
|
+
ri = 0
|
|
102
|
+
|
|
103
|
+
for i in range(N-1):
|
|
104
|
+
ro = self.fiber.outerRadius(i)
|
|
105
|
+
layer = self.fiber.layers[i]
|
|
106
|
+
n = layer.maxIndex(wl)
|
|
107
|
+
u = layer.u(ro, neff, wl)
|
|
108
|
+
|
|
109
|
+
if i > 0:
|
|
110
|
+
C = layer.tetmConstants(ri, ro, neff, wl, EH,
|
|
111
|
+
constants.Y0 * n * n, (0, 3))
|
|
112
|
+
|
|
113
|
+
if r < ro:
|
|
114
|
+
break
|
|
115
|
+
|
|
116
|
+
if neff < n:
|
|
117
|
+
c1 = wl.k0 * ro / u
|
|
118
|
+
F3 = jvp(nu, u) / jn(nu, u)
|
|
119
|
+
F4 = yvp(nu, u) / yn(nu, u)
|
|
120
|
+
else:
|
|
121
|
+
c1 = -wl.k0 * ro / u
|
|
122
|
+
F3 = ivp(nu, u) / iv(nu, u)
|
|
123
|
+
F4 = kvp(nu, u) / kn(nu, u)
|
|
124
|
+
|
|
125
|
+
c4 = constants.Y0 * n * n * c1
|
|
126
|
+
|
|
127
|
+
EH[0] = C[0] + C[1]
|
|
128
|
+
EH[3] = c4 * (F3 * C[0] + F4 * C[1])
|
|
129
|
+
|
|
130
|
+
ri = ro
|
|
131
|
+
else:
|
|
132
|
+
layer = self.fiber.layers[-1]
|
|
133
|
+
u = layer.u(ro, neff, wl)
|
|
134
|
+
# C =
|
|
135
|
+
|
|
136
|
+
return numpy.array((0, ephi, 0)), numpy.array((hr, 0, hz))
|
|
137
|
+
|
|
138
|
+
def _hefield(self, wl, nu, neff, r):
|
|
139
|
+
self._heceq(neff, wl, nu)
|
|
140
|
+
for i, rho in enumerate(self.fiber._r):
|
|
141
|
+
if r < rho:
|
|
142
|
+
break
|
|
143
|
+
else:
|
|
144
|
+
i += 1
|
|
145
|
+
layer = self.fiber.layers[i]
|
|
146
|
+
n = layer.maxIndex(wl)
|
|
147
|
+
u = layer.u(rho, neff, wl)
|
|
148
|
+
urp = u * r / rho
|
|
149
|
+
|
|
150
|
+
c1 = rho / u
|
|
151
|
+
c2 = wl.k0 * c1
|
|
152
|
+
c3 = nu * c1 / r if r else 0 # To avoid div by 0
|
|
153
|
+
c6 = constants.Y0 * n * n
|
|
154
|
+
|
|
155
|
+
if neff < n:
|
|
156
|
+
B1 = jn(nu, u)
|
|
157
|
+
B2 = yn(nu, u)
|
|
158
|
+
F1 = jn(nu, urp) / B1
|
|
159
|
+
F2 = yn(nu, urp) / B2 if i > 0 else 0
|
|
160
|
+
F3 = jvp(nu, urp) / B1
|
|
161
|
+
F4 = yvp(nu, urp) / B2 if i > 0 else 0
|
|
162
|
+
else:
|
|
163
|
+
c2 = -c2
|
|
164
|
+
B1 = iv(nu, u)
|
|
165
|
+
B2 = kn(nu, u)
|
|
166
|
+
F1 = iv(nu, urp) / B1
|
|
167
|
+
F2 = kn(nu, urp) / B2 if i > 0 else 0
|
|
168
|
+
F3 = ivp(nu, urp) / B1
|
|
169
|
+
F4 = kvp(nu, urp) / B2 if i > 0 else 0
|
|
170
|
+
|
|
171
|
+
A, B, Ap, Bp = layer.C[:, 0] + layer.C[:, 1] * self.alpha
|
|
172
|
+
|
|
173
|
+
Ez = A * F1 + B * F2
|
|
174
|
+
Ezp = A * F3 + B * F4
|
|
175
|
+
Hz = Ap * F1 + Bp * F2
|
|
176
|
+
Hzp = Ap * F3 + Bp * F4
|
|
177
|
+
|
|
178
|
+
if r == 0 and nu == 1:
|
|
179
|
+
# Asymptotic expansion of Ez (or Hz):
|
|
180
|
+
# J1(ur/p)/r (r->0) = u/(2p)
|
|
181
|
+
if neff < n:
|
|
182
|
+
f = 1 / (2 * jn(nu, u))
|
|
183
|
+
else:
|
|
184
|
+
f = 1 / (2 * iv(nu, u))
|
|
185
|
+
c3ez = A * f
|
|
186
|
+
c3hz = Ap * f
|
|
187
|
+
else:
|
|
188
|
+
c3ez = c3 * Ez
|
|
189
|
+
c3hz = c3 * Hz
|
|
190
|
+
|
|
191
|
+
Er = c2 * (neff * Ezp - constants.eta0 * c3hz)
|
|
192
|
+
Ep = c2 * (neff * c3ez - constants.eta0 * Hzp)
|
|
193
|
+
|
|
194
|
+
Hr = c2 * (neff * Hzp - c6 * c3ez)
|
|
195
|
+
Hp = c2 * (-neff * c3hz + c6 * Ezp)
|
|
196
|
+
|
|
197
|
+
return numpy.array((Er, Ep, Ez)), numpy.array((Hr, Hp, Hz))
|
|
198
|
+
|
|
199
|
+
_ehfield = _hefield
|
|
200
|
+
|
|
201
|
+
def _lpceq(self, neff, wl, nu):
|
|
202
|
+
N = len(self.fiber)
|
|
203
|
+
C = numpy.zeros((N-1, 2))
|
|
204
|
+
C[0, 0] = 1
|
|
205
|
+
|
|
206
|
+
for i in range(1, N-1):
|
|
207
|
+
r = self.fiber.innerRadius(i)
|
|
208
|
+
A = self.fiber.layers[i-1].Psi(r, neff, wl, nu, C[i-1, :])
|
|
209
|
+
C[i, :] = self.fiber.layers[i].lpConstants(r, neff, wl, nu, A)
|
|
210
|
+
|
|
211
|
+
r = self.fiber.innerRadius(-1)
|
|
212
|
+
A = self.fiber.layers[N-2].Psi(r, neff, wl, nu, C[-1, :])
|
|
213
|
+
u = self.fiber.layers[N-1].u(r, neff, wl)
|
|
214
|
+
return u * kvp(nu, u) * A[0] - kn(nu, u) * A[1]
|
|
215
|
+
|
|
216
|
+
def _teceq(self, neff, wl, nu):
|
|
217
|
+
N = len(self.fiber)
|
|
218
|
+
EH = numpy.empty(4)
|
|
219
|
+
ri = 0
|
|
220
|
+
|
|
221
|
+
for i in range(N-1):
|
|
222
|
+
ro = self.fiber.outerRadius(i)
|
|
223
|
+
self.fiber.layers[i].EH_fields(ri, ro, nu, neff, wl, EH, False)
|
|
224
|
+
ri = ro
|
|
225
|
+
|
|
226
|
+
# Last layer
|
|
227
|
+
_, Hz, Ep, _ = EH
|
|
228
|
+
u = self.fiber.layers[-1].u(ri, neff, wl)
|
|
229
|
+
|
|
230
|
+
F4 = k1(u) / k0(u)
|
|
231
|
+
return Ep + wl.k0 * ri / u * constants.eta0 * Hz * F4
|
|
232
|
+
|
|
233
|
+
def _tmceq(self, neff, wl, nu):
|
|
234
|
+
N = len(self.fiber)
|
|
235
|
+
EH = numpy.empty(4)
|
|
236
|
+
ri = 0
|
|
237
|
+
|
|
238
|
+
for i in range(N-1):
|
|
239
|
+
ro = self.fiber.outerRadius(i)
|
|
240
|
+
self.fiber.layers[i].EH_fields(ri, ro, nu, neff, wl, EH, True)
|
|
241
|
+
ri = ro
|
|
242
|
+
|
|
243
|
+
# Last layer
|
|
244
|
+
Ez, _, _, Hp = EH
|
|
245
|
+
u = self.fiber.layers[-1].u(ri, neff, wl)
|
|
246
|
+
n = self.fiber.maxIndex(-1, wl)
|
|
247
|
+
|
|
248
|
+
F4 = k1(u) / k0(u)
|
|
249
|
+
return Hp - wl.k0 * ri / u * constants.Y0 * n * n * Ez * F4
|
|
250
|
+
|
|
251
|
+
def _heceq(self, neff, wl, nu):
|
|
252
|
+
N = len(self.fiber)
|
|
253
|
+
EH = numpy.empty((4, 2))
|
|
254
|
+
ri = 0
|
|
255
|
+
|
|
256
|
+
for i in range(N-1):
|
|
257
|
+
ro = self.fiber.outerRadius(i)
|
|
258
|
+
try:
|
|
259
|
+
self.fiber.layers[i].EH_fields(ri, ro, nu, neff, wl, EH)
|
|
260
|
+
except ZeroDivisionError:
|
|
261
|
+
return float("inf")
|
|
262
|
+
ri = ro
|
|
263
|
+
|
|
264
|
+
# Last layer
|
|
265
|
+
C = numpy.zeros((4, 2))
|
|
266
|
+
C[1, :] = EH[0, :]
|
|
267
|
+
C[3, :] = EH[1, :]
|
|
268
|
+
self.fiber.layers[N-1].C = C
|
|
269
|
+
|
|
270
|
+
u = self.fiber.layers[N-1].u(ri, neff, wl)
|
|
271
|
+
n = self.fiber.maxIndex(-1, wl)
|
|
272
|
+
|
|
273
|
+
F4 = kvp(nu, u) / kn(nu, u)
|
|
274
|
+
c1 = -wl.k0 * ri / u
|
|
275
|
+
c2 = neff * nu / u * c1
|
|
276
|
+
c3 = constants.eta0 * c1
|
|
277
|
+
c4 = constants.Y0 * n * n * c1
|
|
278
|
+
|
|
279
|
+
E = EH[2, :] - (c2 * EH[0, :] - c3 * F4 * EH[1, :])
|
|
280
|
+
H = EH[3, :] - (c4 * F4 * EH[0, :] - c2 * EH[1, :])
|
|
281
|
+
|
|
282
|
+
if E[1] != 0:
|
|
283
|
+
self.alpha = -E[0] / E[1]
|
|
284
|
+
else:
|
|
285
|
+
self.alpha = -H[0] / H[1]
|
|
286
|
+
|
|
287
|
+
return E[0]*H[1] - E[1]*H[0]
|
|
288
|
+
|
|
289
|
+
_ehceq = _heceq
|
|
@@ -0,0 +1,119 @@
|
|
|
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 itertools import count
|
|
17
|
+
from scipy.optimize import brentq
|
|
18
|
+
import logging
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FiberSolver(object):
|
|
22
|
+
|
|
23
|
+
"""Generic abstract class for callable objects used as fiber solvers."""
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
_MCD = 0.1
|
|
27
|
+
|
|
28
|
+
def __init__(self, fiber):
|
|
29
|
+
self.fiber = fiber
|
|
30
|
+
self.log = []
|
|
31
|
+
self._logging = False
|
|
32
|
+
|
|
33
|
+
def __call__(self, *args, **kwargs):
|
|
34
|
+
raise NotImplementedError()
|
|
35
|
+
|
|
36
|
+
def start_log(self):
|
|
37
|
+
self.log = []
|
|
38
|
+
self._logging = True
|
|
39
|
+
|
|
40
|
+
def stop_log(self):
|
|
41
|
+
self._logging = False
|
|
42
|
+
|
|
43
|
+
def __record(self, fct):
|
|
44
|
+
def wrapper(z, *args):
|
|
45
|
+
r = fct(z, *args)
|
|
46
|
+
if self._logging:
|
|
47
|
+
self.log.append((z, r))
|
|
48
|
+
return r
|
|
49
|
+
return wrapper
|
|
50
|
+
|
|
51
|
+
def _findFirstRoot(self, fct, args=(), lowbound=0, highbound=None,
|
|
52
|
+
ipoints=[], delta=0.25, maxiter=None):
|
|
53
|
+
fct = self.__record(fct) # For debug purpose.
|
|
54
|
+
while True:
|
|
55
|
+
if ipoints:
|
|
56
|
+
maxiter = len(ipoints)
|
|
57
|
+
elif highbound:
|
|
58
|
+
maxiter = int((highbound - lowbound) / delta)
|
|
59
|
+
assert maxiter is not None
|
|
60
|
+
|
|
61
|
+
a = lowbound
|
|
62
|
+
fa = fct(a, *args)
|
|
63
|
+
if fa == 0:
|
|
64
|
+
return a
|
|
65
|
+
|
|
66
|
+
for i in range(1, maxiter+1):
|
|
67
|
+
b = ipoints.pop(0) if ipoints else a + delta
|
|
68
|
+
if highbound:
|
|
69
|
+
if ((b > highbound > lowbound) or
|
|
70
|
+
(b < highbound < lowbound)):
|
|
71
|
+
self.logger.info("_findFirstRoot: no root found within"
|
|
72
|
+
" allowed range")
|
|
73
|
+
return float("nan")
|
|
74
|
+
fb = fct(b, *args)
|
|
75
|
+
if fb == 0:
|
|
76
|
+
return b
|
|
77
|
+
|
|
78
|
+
if (fa > 0 and fb < 0) or (fa < 0 and fb > 0):
|
|
79
|
+
z = brentq(fct, a, b, args=args, xtol=1e-20)
|
|
80
|
+
fz = fct(z, *args)
|
|
81
|
+
if abs(fa) > abs(fz) < abs(fb): # Skip discontinuities
|
|
82
|
+
self.logger.debug("skipped ({}, {}, {})".format(
|
|
83
|
+
fa, fz, fb))
|
|
84
|
+
return z
|
|
85
|
+
|
|
86
|
+
a, fa = b, fb
|
|
87
|
+
if highbound and maxiter < 100:
|
|
88
|
+
delta /= 10
|
|
89
|
+
else:
|
|
90
|
+
break
|
|
91
|
+
self.logger.info("maxiter reached ({}, {}, {})".format(
|
|
92
|
+
maxiter, lowbound, highbound))
|
|
93
|
+
return float("nan")
|
|
94
|
+
|
|
95
|
+
def _findBetween(self, fct, lowbound, highbound, args=(), maxj=15):
|
|
96
|
+
fct = self.__record(fct) # For debug purpose.
|
|
97
|
+
v = [lowbound, highbound]
|
|
98
|
+
s = [fct(lowbound, *args), fct(highbound, *args)]
|
|
99
|
+
|
|
100
|
+
for j in count(): # probably not needed...
|
|
101
|
+
if j == maxj:
|
|
102
|
+
self.logger.warning("_findBetween: max iter reached")
|
|
103
|
+
return float("nan")
|
|
104
|
+
for i in range(len(s)-1):
|
|
105
|
+
a, b = v[i], v[i+1]
|
|
106
|
+
fa, fb = s[i], s[i+1]
|
|
107
|
+
|
|
108
|
+
if (fa > 0 and fb < 0) or (fa < 0 and fb > 0):
|
|
109
|
+
z = brentq(fct, a, b, args=args)
|
|
110
|
+
fz = fct(z, *args)
|
|
111
|
+
if abs(fa) > abs(fz) < abs(fb): # Skip discontinuities
|
|
112
|
+
return z
|
|
113
|
+
|
|
114
|
+
ls = len(s)
|
|
115
|
+
for i in range(ls-1):
|
|
116
|
+
a, b = v[2*i], v[2*i+1]
|
|
117
|
+
c = (a + b) / 2
|
|
118
|
+
v.insert(2*i+1, c)
|
|
119
|
+
s.insert(2*i+1, fct(c, *args))
|
|
@@ -0,0 +1,275 @@
|
|
|
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 .solver import FiberSolver
|
|
17
|
+
from PyFiberModes import Mode, ModeFamily
|
|
18
|
+
from math import sqrt, isnan, isinf
|
|
19
|
+
import numpy
|
|
20
|
+
from scipy.special import jn, jn_zeros, kn, j0, j1, k0, k1, jvp, kvp
|
|
21
|
+
from PyFiberModes.constants import Y0
|
|
22
|
+
import logging
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Cutoff(FiberSolver):
|
|
26
|
+
|
|
27
|
+
"""Cutoff for standard step-index fiber."""
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
def __call__(self, mode):
|
|
32
|
+
nu = mode.nu
|
|
33
|
+
m = mode.m
|
|
34
|
+
|
|
35
|
+
if mode.family is ModeFamily.LP:
|
|
36
|
+
if nu == 0:
|
|
37
|
+
nu, m = 1, -1
|
|
38
|
+
|
|
39
|
+
else:
|
|
40
|
+
nu -= 1
|
|
41
|
+
elif mode.family is ModeFamily.HE:
|
|
42
|
+
if nu == 1:
|
|
43
|
+
m -= 1
|
|
44
|
+
else:
|
|
45
|
+
return self._findHEcutoff(mode)
|
|
46
|
+
|
|
47
|
+
return jn_zeros(nu, m)[m - 1]
|
|
48
|
+
|
|
49
|
+
def _cutoffHE(self, V0, nu):
|
|
50
|
+
wl = self.fiber.toWl(V0)
|
|
51
|
+
n02 = self.fiber.maxIndex(0, wl)**2 / self.fiber.minIndex(1, wl)**2
|
|
52
|
+
return (1 + n02) * jn(nu - 2, V0) - (1 - n02) * jn(nu, V0)
|
|
53
|
+
|
|
54
|
+
def _findHEcutoff(self, mode):
|
|
55
|
+
if mode.m > 1:
|
|
56
|
+
pm = Mode(mode.family, mode.nu, mode.m - 1)
|
|
57
|
+
lowbound = self.fiber.cutoff(pm)
|
|
58
|
+
if isnan(lowbound) or isinf(lowbound):
|
|
59
|
+
raise AssertionError("_findHEcutoff: no previous cutoff for"
|
|
60
|
+
"{} mode".format(str(mode)))
|
|
61
|
+
delta = 1 / lowbound if lowbound else self._MCD
|
|
62
|
+
lowbound += delta
|
|
63
|
+
else:
|
|
64
|
+
lowbound = delta = self._MCD
|
|
65
|
+
ipoints = numpy.concatenate((jn_zeros(mode.nu, mode.m),
|
|
66
|
+
jn_zeros(mode.nu - 2, mode.m)))
|
|
67
|
+
ipoints.sort()
|
|
68
|
+
ipoints = list(ipoints[ipoints > lowbound])
|
|
69
|
+
co = self._findFirstRoot(self._cutoffHE,
|
|
70
|
+
args=(mode.nu,),
|
|
71
|
+
lowbound=lowbound,
|
|
72
|
+
ipoints=ipoints,
|
|
73
|
+
delta=delta)
|
|
74
|
+
if isnan(co):
|
|
75
|
+
self.logger.error("_findHEcutoff: no cutoff found for "
|
|
76
|
+
"{} mode".format(str(mode)))
|
|
77
|
+
return 0
|
|
78
|
+
return co
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Neff(FiberSolver):
|
|
82
|
+
|
|
83
|
+
"""neff for standard step-index fiber"""
|
|
84
|
+
|
|
85
|
+
def __call__(self, wl, mode, delta, lowbound):
|
|
86
|
+
epsilon = 1e-12
|
|
87
|
+
|
|
88
|
+
co = self.fiber.cutoff(mode)
|
|
89
|
+
if self.fiber.V0(wl) < co:
|
|
90
|
+
return float("nan")
|
|
91
|
+
|
|
92
|
+
nco = self.fiber.maxIndex(0, wl)
|
|
93
|
+
r = self.fiber.outerRadius(0)
|
|
94
|
+
highbound = sqrt(nco**2 - (co / (r * wl.k0))**2) - epsilon
|
|
95
|
+
|
|
96
|
+
if mode.family is ModeFamily.LP:
|
|
97
|
+
nm = Mode(ModeFamily.LP, mode.nu+1, mode.m)
|
|
98
|
+
elif mode.family is ModeFamily.HE:
|
|
99
|
+
nm = Mode(ModeFamily.LP, mode.nu, mode.m)
|
|
100
|
+
elif mode.family is ModeFamily.EH:
|
|
101
|
+
nm = Mode(ModeFamily.LP, mode.nu+2, mode.m)
|
|
102
|
+
else:
|
|
103
|
+
nm = Mode(ModeFamily.LP, 1, mode.m+1)
|
|
104
|
+
co = self.fiber.cutoff(nm)
|
|
105
|
+
try:
|
|
106
|
+
lowbound = max(sqrt(nco**2 - (co / (r * wl.k0))**2) + epsilon,
|
|
107
|
+
self.fiber.minIndex(-1, wl) + epsilon)
|
|
108
|
+
except ValueError:
|
|
109
|
+
lowbound = nco
|
|
110
|
+
|
|
111
|
+
fct = {ModeFamily.LP: self._lpceq,
|
|
112
|
+
ModeFamily.TE: self._teceq,
|
|
113
|
+
ModeFamily.TM: self._tmceq,
|
|
114
|
+
ModeFamily.HE: self._heceq,
|
|
115
|
+
ModeFamily.EH: self._ehceq
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return self._findBetween(fct[mode.family], lowbound, highbound,
|
|
119
|
+
args=(wl, mode.nu))
|
|
120
|
+
|
|
121
|
+
def _lpfield(self, wl, nu, neff, r):
|
|
122
|
+
rho = self.fiber.outerRadius(0)
|
|
123
|
+
k = wl.k0
|
|
124
|
+
nco2 = self.fiber.maxIndex(0, wl)**2
|
|
125
|
+
ncl2 = self.fiber.minIndex(1, wl)**2
|
|
126
|
+
u = rho * k * sqrt(nco2 - neff**2)
|
|
127
|
+
w = rho * k * sqrt(neff**2 - ncl2)
|
|
128
|
+
|
|
129
|
+
if r < rho:
|
|
130
|
+
ex = j0(u * r / rho) / j0(u)
|
|
131
|
+
else:
|
|
132
|
+
ex = k0(w * r / rho) / k0(w)
|
|
133
|
+
hy = neff * Y0 * ex # Snyder & Love uses nco, but Bures uses neff
|
|
134
|
+
|
|
135
|
+
return numpy.array((ex, 0, 0)), numpy.array((0, hy, 0))
|
|
136
|
+
|
|
137
|
+
def _tefield(self, wl, nu, neff, r):
|
|
138
|
+
rho = self.fiber.outerRadius(0)
|
|
139
|
+
k = wl.k0
|
|
140
|
+
nco2 = self.fiber.maxIndex(0, wl)**2
|
|
141
|
+
ncl2 = self.fiber.minIndex(1, wl)**2
|
|
142
|
+
u = rho * k * sqrt(nco2 - neff**2)
|
|
143
|
+
w = rho * k * sqrt(neff**2 - ncl2)
|
|
144
|
+
|
|
145
|
+
if r < rho:
|
|
146
|
+
hz = -Y0 * u / (k * rho) * j0(u * r / rho) / j1(u)
|
|
147
|
+
ephi = -j1(u * r / rho) / j1(u)
|
|
148
|
+
else:
|
|
149
|
+
hz = Y0 * w / (k * rho) * k0(w * r / rho) / k1(w)
|
|
150
|
+
ephi = -k1(w * r / rho) / k1(w)
|
|
151
|
+
hr = -neff * Y0 * ephi
|
|
152
|
+
|
|
153
|
+
return numpy.array((0, ephi, 0)), numpy.array((hr, 0, hz))
|
|
154
|
+
|
|
155
|
+
def _tmfield(self, wl, nu, neff, r):
|
|
156
|
+
rho = self.fiber.outerRadius(0)
|
|
157
|
+
k = wl.k0
|
|
158
|
+
nco2 = self.fiber.maxIndex(0, wl)**2
|
|
159
|
+
ncl2 = self.fiber.minIndex(1, wl)**2
|
|
160
|
+
u = rho * k * sqrt(nco2 - neff**2)
|
|
161
|
+
w = rho * k * sqrt(neff**2 - ncl2)
|
|
162
|
+
|
|
163
|
+
if r < rho:
|
|
164
|
+
ez = -u / (k * neff * rho) * j0(u * r / rho) / j1(u)
|
|
165
|
+
er = j1(u * r / rho) / j1(u)
|
|
166
|
+
hphi = Y0 * nco2 / neff * er
|
|
167
|
+
else:
|
|
168
|
+
ez = nco2 / ncl2 * w / (k * neff * rho) * k0(w * r / rho) / k1(w)
|
|
169
|
+
er = nco2 / ncl2 * k1(w * r / rho) / k1(w)
|
|
170
|
+
hphi = Y0 * nco2 / ncl2 * k1(w * r / rho) / k1(w)
|
|
171
|
+
|
|
172
|
+
return numpy.array((er, 0, ez)), numpy.array((0, hphi, 0))
|
|
173
|
+
|
|
174
|
+
def _hefield(self, wl, nu, neff, r):
|
|
175
|
+
rho = self.fiber.outerRadius(0)
|
|
176
|
+
k = wl.k0
|
|
177
|
+
nco2 = self.fiber.maxIndex(0, wl)**2
|
|
178
|
+
ncl2 = self.fiber.minIndex(1, wl)**2
|
|
179
|
+
u = rho * k * sqrt(nco2 - neff**2)
|
|
180
|
+
w = rho * k * sqrt(neff**2 - ncl2)
|
|
181
|
+
v = rho * k * sqrt(nco2 - ncl2)
|
|
182
|
+
|
|
183
|
+
jnu = jn(nu, u)
|
|
184
|
+
knw = kn(nu, w)
|
|
185
|
+
|
|
186
|
+
Delta = (1 - ncl2/nco2)/2
|
|
187
|
+
b1 = jvp(nu, u) / (u * jnu)
|
|
188
|
+
b2 = kvp(nu, w) / (w * knw)
|
|
189
|
+
F1 = (u * w / v)**2 * (b1 + (1 - 2 * Delta)*b2) / nu
|
|
190
|
+
F2 = (v / (u * w))**2 * nu / (b1 + b2)
|
|
191
|
+
a1 = (F2 - 1) / 2
|
|
192
|
+
a2 = (F2 + 1) / 2
|
|
193
|
+
a3 = (F1 - 1) / 2
|
|
194
|
+
a4 = (F1 + 1) / 2
|
|
195
|
+
a5 = (F1 - 1 + 2 * Delta) / 2
|
|
196
|
+
a6 = (F1 + 1 - 2 * Delta) / 2
|
|
197
|
+
|
|
198
|
+
if r < rho:
|
|
199
|
+
jmur = jn(nu-1, u * r / rho)
|
|
200
|
+
jpur = jn(nu+1, u * r / rho)
|
|
201
|
+
jnur = jn(nu, u * r / rho)
|
|
202
|
+
er = -(a1 * jmur + a2 * jpur) / jnu
|
|
203
|
+
ephi = -(a1 * jmur - a2 * jpur) / jnu
|
|
204
|
+
ez = u / (k * neff * rho) * jnur / jnu
|
|
205
|
+
hr = Y0 * nco2 / neff * (a3 * jmur - a4 * jpur) / jnu
|
|
206
|
+
hphi = -Y0 * nco2 / neff * (a3 * jmur + a4 * jpur) / jnu
|
|
207
|
+
hz = Y0 * u * F2 / (k * rho) * jnur / jnu
|
|
208
|
+
else:
|
|
209
|
+
kmur = kn(nu-1, w * r / rho)
|
|
210
|
+
kpur = kn(nu+1, w * r / rho)
|
|
211
|
+
knur = kn(nu, w * r / rho)
|
|
212
|
+
er = -u / w * (a1 * kmur - a2 * kpur) / knw
|
|
213
|
+
ephi = -u / w * (a1 * kmur + a2 * kpur) / knw
|
|
214
|
+
ez = u / (k * neff * rho) * knur / knw
|
|
215
|
+
hr = Y0 * nco2 / neff * u / w * (a5 * kmur + a6 * kpur) / knw
|
|
216
|
+
hphi = -Y0 * nco2 / neff * u / w * (a5 * kmur - a6 * kpur) / knw
|
|
217
|
+
hz = Y0 * u * F2 / (k * rho) * knur / knw
|
|
218
|
+
|
|
219
|
+
return numpy.array((er, ephi, ez)), numpy.array((hr, hphi, hz))
|
|
220
|
+
|
|
221
|
+
_ehfield = _hefield
|
|
222
|
+
|
|
223
|
+
def _uw(self, wl, neff):
|
|
224
|
+
r = self.fiber.outerRadius(0)
|
|
225
|
+
rk0 = r * wl.k0
|
|
226
|
+
return (rk0 * sqrt(self.fiber.maxIndex(0, wl)**2 - neff**2),
|
|
227
|
+
rk0 * sqrt(neff**2 - self.fiber.minIndex(1, wl)**2))
|
|
228
|
+
|
|
229
|
+
def _lpceq(self, neff, wl, nu):
|
|
230
|
+
u, w = self._uw(wl, neff)
|
|
231
|
+
return (u * jn(nu - 1, u) * kn(nu, w) +
|
|
232
|
+
w * jn(nu, u) * kn(nu - 1, w))
|
|
233
|
+
|
|
234
|
+
def _teceq(self, neff, wl, nu):
|
|
235
|
+
u, w = self._uw(wl, neff)
|
|
236
|
+
return u * j0(u) * k1(w) + w * j1(u) * k0(w)
|
|
237
|
+
|
|
238
|
+
def _tmceq(self, neff, wl, nu):
|
|
239
|
+
u, w = self._uw(wl, neff)
|
|
240
|
+
nco = self.fiber.maxIndex(0, wl)
|
|
241
|
+
ncl = self.fiber.minIndex(1, wl)
|
|
242
|
+
return (u * j0(u) * k1(w) * ncl**2 +
|
|
243
|
+
w * j1(u) * k0(w) * nco**2)
|
|
244
|
+
|
|
245
|
+
def _heceq(self, neff, wl, nu):
|
|
246
|
+
u, w = self._uw(wl, neff)
|
|
247
|
+
v2 = u*u + w*w
|
|
248
|
+
nco = self.fiber.maxIndex(0, wl)
|
|
249
|
+
ncl = self.fiber.minIndex(1, wl)
|
|
250
|
+
delta = (1 - ncl**2 / nco**2) / 2
|
|
251
|
+
jnu = jn(nu, u)
|
|
252
|
+
knu = kn(nu, w)
|
|
253
|
+
kp = kvp(nu, w)
|
|
254
|
+
|
|
255
|
+
return (jvp(nu, u) * w * knu +
|
|
256
|
+
kp * u * jnu * (1 - delta) +
|
|
257
|
+
jnu * sqrt((u * kp * delta)**2 +
|
|
258
|
+
((nu * neff * v2 * knu) /
|
|
259
|
+
(nco * u * w))**2))
|
|
260
|
+
|
|
261
|
+
def _ehceq(self, neff, wl, nu):
|
|
262
|
+
u, w = self._uw(wl, neff)
|
|
263
|
+
v2 = u*u + w*w
|
|
264
|
+
nco = self.fiber.maxIndex(0, wl)
|
|
265
|
+
ncl = self.fiber.minIndex(1, wl)
|
|
266
|
+
delta = (1 - ncl**2 / nco**2) / 2
|
|
267
|
+
jnu = jn(nu, u)
|
|
268
|
+
knu = kn(nu, w)
|
|
269
|
+
kp = kvp(nu, w)
|
|
270
|
+
|
|
271
|
+
return (jvp(nu, u) * w * knu +
|
|
272
|
+
kp * u * jnu * (1 - delta) -
|
|
273
|
+
jnu * sqrt((u * kp * delta)**2 +
|
|
274
|
+
((nu * neff * v2 * knu) /
|
|
275
|
+
(nco * u * w))**2))
|