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.
Files changed (52) hide show
  1. PyFiberModes/.DS_Store +0 -0
  2. PyFiberModes/VERSION +1 -0
  3. PyFiberModes/__init__.py +38 -0
  4. PyFiberModes/constants.py +32 -0
  5. PyFiberModes/fiber/.DS_Store +0 -0
  6. PyFiberModes/fiber/__init__.py +0 -0
  7. PyFiberModes/fiber/factory.py +448 -0
  8. PyFiberModes/fiber/fiber.py +317 -0
  9. PyFiberModes/fiber/geometry/__init__.py +10 -0
  10. PyFiberModes/fiber/geometry/geometry.py +33 -0
  11. PyFiberModes/fiber/geometry/stepindex.py +181 -0
  12. PyFiberModes/fiber/geometry/supergaussian.py +193 -0
  13. PyFiberModes/fiber/material/__init__.py +13 -0
  14. PyFiberModes/fiber/material/air.py +20 -0
  15. PyFiberModes/fiber/material/claussiusmossotti.py +40 -0
  16. PyFiberModes/fiber/material/compmaterial.py +60 -0
  17. PyFiberModes/fiber/material/fixed.py +36 -0
  18. PyFiberModes/fiber/material/germania.py +14 -0
  19. PyFiberModes/fiber/material/material.py +107 -0
  20. PyFiberModes/fiber/material/sellmeier.py +47 -0
  21. PyFiberModes/fiber/material/sellmeiercomp.py +42 -0
  22. PyFiberModes/fiber/material/silica.py +15 -0
  23. PyFiberModes/fiber/material/sio2f.py +34 -0
  24. PyFiberModes/fiber/material/sio2geo2.py +36 -0
  25. PyFiberModes/fiber/material/sio2geo2cm.py +43 -0
  26. PyFiberModes/fiber/solver/__init__.py +12 -0
  27. PyFiberModes/fiber/solver/cuda.py +124 -0
  28. PyFiberModes/fiber/solver/cudasrc/besseldiff.c +28 -0
  29. PyFiberModes/fiber/solver/cudasrc/chareq.c +314 -0
  30. PyFiberModes/fiber/solver/cudasrc/constf.c +20 -0
  31. PyFiberModes/fiber/solver/cudasrc/hypergf.c +264 -0
  32. PyFiberModes/fiber/solver/cudasrc/ivf.c +49 -0
  33. PyFiberModes/fiber/solver/cudasrc/knf.c +166 -0
  34. PyFiberModes/fiber/solver/mlsif.py +289 -0
  35. PyFiberModes/fiber/solver/solver.py +119 -0
  36. PyFiberModes/fiber/solver/ssif.py +275 -0
  37. PyFiberModes/fiber/solver/tlsif.py +266 -0
  38. PyFiberModes/field.py +405 -0
  39. PyFiberModes/functions.py +137 -0
  40. PyFiberModes/mode.py +186 -0
  41. PyFiberModes/simulator/.DS_Store +0 -0
  42. PyFiberModes/simulator/__init__.py +15 -0
  43. PyFiberModes/simulator/psimulator.py +41 -0
  44. PyFiberModes/simulator/simulator.py +288 -0
  45. PyFiberModes/slrc.py +291 -0
  46. PyFiberModes/tools/__init__.py +0 -0
  47. PyFiberModes/tools/directories.py +41 -0
  48. PyFiberModes/wavelength.py +109 -0
  49. PyFiberModes-0.2.1.dist-info/METADATA +158 -0
  50. PyFiberModes-0.2.1.dist-info/RECORD +52 -0
  51. PyFiberModes-0.2.1.dist-info/WHEEL +5 -0
  52. 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))