pygeodesy 25.12.12__py2.py3-none-any.whl → 26.1.16__py2.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.
- pygeodesy/__init__.py +33 -25
- pygeodesy/albers.py +5 -5
- pygeodesy/cartesianBase.py +2 -2
- pygeodesy/constants.py +8 -1
- pygeodesy/datums.py +5 -8
- pygeodesy/ellipsoids.py +32 -19
- pygeodesy/elliptic.py +532 -139
- pygeodesy/fmath.py +6 -8
- pygeodesy/formy.py +6 -106
- pygeodesy/fsums.py +48 -30
- pygeodesy/geod3solve.py +26 -10
- pygeodesy/geodesici.py +5 -4
- pygeodesy/geodesicx/__init__.py +1 -1
- pygeodesy/geodesicx/gxarea.py +53 -23
- pygeodesy/geodsolve.py +4 -3
- pygeodesy/internals.py +10 -3
- pygeodesy/karney.py +21 -36
- pygeodesy/lazily.py +12 -5
- pygeodesy/lcc.py +3 -7
- pygeodesy/named.py +14 -10
- pygeodesy/props.py +5 -3
- pygeodesy/trf.py +2 -4
- pygeodesy/triaxials/bases.py +111 -58
- pygeodesy/triaxials/triaxial3.py +50 -54
- pygeodesy/triaxials/triaxial5.py +17 -49
- pygeodesy/utily.py +7 -7
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/METADATA +26 -19
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/RECORD +30 -30
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/WHEEL +0 -0
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/top_level.txt +0 -0
pygeodesy/elliptic.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
-
u'''I{Karney}'s elliptic
|
|
4
|
+
u'''I{Karney}'s elliptic integrals and elliptic and ellipse functions.
|
|
5
5
|
|
|
6
6
|
Class L{Elliptic} transcoded from I{Charles Karney}'s C++ class U{EllipticFunction
|
|
7
7
|
<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1EllipticFunction.html>}
|
|
@@ -76,36 +76,38 @@ U{22<https://DLMF.NIST.gov/22>}.
|
|
|
76
76
|
from __future__ import division as _; del _ # noqa: E702 ;
|
|
77
77
|
|
|
78
78
|
from pygeodesy.basics import copysign0, map2, neg, neg_
|
|
79
|
-
from pygeodesy.constants import EPS, INF, NAN, PI, PI_2, PI_4, \
|
|
80
|
-
_EPStol as _TolJAC, _0_0, _0_25, \
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
from pygeodesy.constants import _EPSjam as _TolJAM # PYCHOK used!
|
|
79
|
+
from pygeodesy.constants import EPS, EPS_2, INF, NAN, PI, PI_2, PI_4, PI2, \
|
|
80
|
+
_EPStol as _TolJAC, _0_0, _0_25, _0_5, _1_0, \
|
|
81
|
+
_2_0, _3_0, _4_0, _6_0, _8_0, _64_0, _180_0, \
|
|
82
|
+
_360_0, _flipsign, _over, _1_over
|
|
83
|
+
from pygeodesy.constants import _10_0, _EPSjam as _TolJAM, MANT_DIG as _DIG53 # PYCHOK used!
|
|
85
84
|
# from pygeodesy.errors import _ValueError # from .fsums
|
|
86
|
-
from pygeodesy.fmath import favg, Fdot_,
|
|
85
|
+
from pygeodesy.fmath import favg, Fdot, Fdot_, fhorner, hypot, hypot1, \
|
|
86
|
+
zqrt, _operator
|
|
87
87
|
from pygeodesy.fsums import Fsum, _fsum, _ValueError
|
|
88
88
|
from pygeodesy.internals import _Enum, typename
|
|
89
|
-
from pygeodesy.interns import NN, _delta_, _DOT_, _f_, _invalid_, \
|
|
90
|
-
|
|
89
|
+
from pygeodesy.interns import NN, _delta_, _DOT_, _f_, _invalid_, _invokation_, \
|
|
90
|
+
_negative_, _SPACE_
|
|
91
91
|
from pygeodesy.karney import _K_2_0, _norm180, _signBit, _sincos2
|
|
92
|
-
|
|
93
|
-
from pygeodesy.named import _Named, _NamedTuple,
|
|
94
|
-
from pygeodesy.props import _allPropertiesOf_n, Property_RO,
|
|
92
|
+
from pygeodesy.lazily import _ALL_LAZY, _FOR_DOCS
|
|
93
|
+
from pygeodesy.named import callername, _Named, _NamedTuple, Fmt, unstr
|
|
94
|
+
from pygeodesy.props import _allPropertiesOf_n, Property_RO, property_ROnce, \
|
|
95
|
+
_update_all
|
|
95
96
|
# from pygeodesy.streprs import Fmt, unstr # from .named
|
|
96
97
|
from pygeodesy.units import Scalar, Scalar_
|
|
97
98
|
from pygeodesy.utily import atan2 # sincos2 as _sincos2
|
|
98
99
|
|
|
99
100
|
from math import asin, asinh, atan, ceil, cosh, fabs, floor, radians, \
|
|
100
101
|
sin, sinh, sqrt, tan, tanh # tan as _tan
|
|
102
|
+
# import operator as _operator # from .fmath
|
|
101
103
|
|
|
102
104
|
__all__ = _ALL_LAZY.elliptic
|
|
103
|
-
__version__ = '
|
|
105
|
+
__version__ = '26.01.16'
|
|
104
106
|
|
|
105
107
|
_TolRD = zqrt(EPS * 0.002)
|
|
106
108
|
_TolRF = zqrt(EPS * 0.030)
|
|
107
109
|
_TolRG0 = _TolJAC * 2.7
|
|
108
|
-
|
|
110
|
+
_MAXIT = 32 # Max depth, 6-18 sufficient
|
|
109
111
|
|
|
110
112
|
|
|
111
113
|
class _Cs(_Enum):
|
|
@@ -114,6 +116,314 @@ class _Cs(_Enum):
|
|
|
114
116
|
pass
|
|
115
117
|
|
|
116
118
|
|
|
119
|
+
class Elliperim(object):
|
|
120
|
+
'''Singleton with various methods to compute the perimeter of an ellipse.
|
|
121
|
+
'''
|
|
122
|
+
_Ek = None
|
|
123
|
+
_k = _2_0 # 0 < _k < 1
|
|
124
|
+
_TOL53 = sqrt(EPS_2) # sqrt(pow(_0_5, _DIG53))
|
|
125
|
+
_TOL53_53 = _TOL53 / _DIG53 # "flat" b/a tolerance, 1.9e-10
|
|
126
|
+
# assert _DIG53 == 53
|
|
127
|
+
|
|
128
|
+
def AGM(self, a, b, maxit=_DIG53):
|
|
129
|
+
'''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using the U{AGM
|
|
130
|
+
<https://PaulBourke.net/geometry/ellipsecirc>} (Arithmetic-Geometric Mean) method.
|
|
131
|
+
|
|
132
|
+
@kwarg maxit: Number of iterations (C{int}).
|
|
133
|
+
|
|
134
|
+
@return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
135
|
+
|
|
136
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}} or no convergence for B{C{maxit}} iterations.
|
|
137
|
+
'''
|
|
138
|
+
_, p, a, b = self._pab4(a, b)
|
|
139
|
+
if p is None:
|
|
140
|
+
t = self._TOL53
|
|
141
|
+
m = -1
|
|
142
|
+
c = a + b
|
|
143
|
+
ds = [c**2]
|
|
144
|
+
_d = ds.append
|
|
145
|
+
for _ in range(maxit): # 4..5 trips
|
|
146
|
+
b = sqrt(a * b)
|
|
147
|
+
a = c * _0_5
|
|
148
|
+
c = a + b
|
|
149
|
+
d = a - b
|
|
150
|
+
m *= 2
|
|
151
|
+
_d(d**2 * m)
|
|
152
|
+
if d <= (b * t):
|
|
153
|
+
break
|
|
154
|
+
else: # PYCHOK no cover
|
|
155
|
+
raise _convergenceError(maxit, _over(d, b), t)
|
|
156
|
+
p = _over(_fsum(ds) * PI, c) # nonfinites=True
|
|
157
|
+
return p
|
|
158
|
+
|
|
159
|
+
def arc(self, a, b, deg2, deg1=0):
|
|
160
|
+
'''Compute the length of U{elliptic arc<https://www.JohnDCook.com/blog/2022/11/02/elliptic-arc-length/>}
|
|
161
|
+
C{(B{deg2} - B{deg1})}, both counter-clockwise from semi-axis B{C{a}} to B{C{b}} of the ellipse.
|
|
162
|
+
|
|
163
|
+
@arg deg2: End angle of the elliptic arc (C{degrees}).
|
|
164
|
+
@kwarg deg1: Start angle of the elliptic arc (C{degrees}).
|
|
165
|
+
|
|
166
|
+
@return: Arc length, signed (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
167
|
+
|
|
168
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
169
|
+
'''
|
|
170
|
+
return self.arc_(a, b, radians(deg2), (radians(deg1) if deg1 else _0_0))
|
|
171
|
+
|
|
172
|
+
def arc_(self, a, b, rad2, rad1=0):
|
|
173
|
+
'''Compute the length of U{elliptic arc<https://www.JohnDCook.com/blog/2022/11/02/elliptic-arc-length/>}
|
|
174
|
+
C{(B{rad2} - B{rad1})}, both counter-clockwise from semi-axis B{C{a}} to B{C{b}} of the ellipse.
|
|
175
|
+
|
|
176
|
+
@arg rad2: End angle of the elliptic arc (C{radians}).
|
|
177
|
+
@kwarg rad1: Start angle of the elliptic arc (C{radians}).
|
|
178
|
+
|
|
179
|
+
@return: Arc length, signed (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
180
|
+
|
|
181
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
182
|
+
'''
|
|
183
|
+
r, p, a, b = self._pab4(a, b)
|
|
184
|
+
if p is None:
|
|
185
|
+
_e = self._ellipe or self._ellipE
|
|
186
|
+
k = (b / a)**2
|
|
187
|
+
r = PI_2 if r else _0_0
|
|
188
|
+
p = self._arc(_e, k, rad2 + r)
|
|
189
|
+
r += rad1
|
|
190
|
+
if r:
|
|
191
|
+
p -= self._arc(_e, k, r)
|
|
192
|
+
p *= a
|
|
193
|
+
else:
|
|
194
|
+
p *= (rad2 - rad1) / PI2
|
|
195
|
+
return p
|
|
196
|
+
|
|
197
|
+
def _arc(self, _e, k, r):
|
|
198
|
+
t, r = divmod(r, PI2)
|
|
199
|
+
p = _e(k, r)
|
|
200
|
+
if t: # + t * perimeter
|
|
201
|
+
t *= _e(k) * _4_0
|
|
202
|
+
p += t
|
|
203
|
+
return p
|
|
204
|
+
|
|
205
|
+
def Arc43(self, a, b):
|
|
206
|
+
'''Compute the perimeter (and arcs) of an ellipse with semi-axes B{C{a}} and B{C{b}}
|
|
207
|
+
using the U{4-Arc<https://PaulBourke.net/geometry/ellipsecirc>} approximation.
|
|
208
|
+
|
|
209
|
+
@return: 3-Tuple C{(p, Ra, Rb)} with perimeter C{p}, arc radius C{Ra} at the
|
|
210
|
+
major and arc radius C{Rb} at the minor semi-axis (C{meter}, same
|
|
211
|
+
units as semi-axes B{C{a}} and B{C{b}}.
|
|
212
|
+
|
|
213
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
214
|
+
'''
|
|
215
|
+
r, p, a, b = self._pab4(a, b)
|
|
216
|
+
if p is None:
|
|
217
|
+
h = hypot(a, b)
|
|
218
|
+
p = atan2(b, a)
|
|
219
|
+
s, c = _sincos2(p)
|
|
220
|
+
L = (h - (a - b)) * _0_5
|
|
221
|
+
Ra = _over(L, c)
|
|
222
|
+
Rb = _over(h - L, s)
|
|
223
|
+
p = (p * Rb + (PI_2 - p) * Ra) * _4_0
|
|
224
|
+
elif a > b: # flat
|
|
225
|
+
Ra, Rb = _0_0, _1_over(b) # INF
|
|
226
|
+
else: # circle
|
|
227
|
+
Ra, Rb = a, b
|
|
228
|
+
return (p, Rb, Ra) if r else (p, Ra, Rb)
|
|
229
|
+
|
|
230
|
+
# def CR(self, a, b):
|
|
231
|
+
# '''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using U{Rackauckas'
|
|
232
|
+
# <https://www.ChrisRackauckas.com/assets/Papers/ChrisRackauckas-The_Circumference_of_an_Ellipse.pdf>}
|
|
233
|
+
# approximation, also U{here<https://ExtremeLearning.com.AU/a-formula-for-the-perimeter-of-an-ellipse>}.
|
|
234
|
+
#
|
|
235
|
+
# @return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
236
|
+
#
|
|
237
|
+
# @raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
238
|
+
# '''
|
|
239
|
+
# _, p, a, b = self._pab4(a, b)
|
|
240
|
+
# if p is None:
|
|
241
|
+
# p = a + b
|
|
242
|
+
# h = ((a - b) / p)**2
|
|
243
|
+
# p *= (fhorner(h, 135168, -85760, -5568, 3867) /
|
|
244
|
+
# fhorner(h, 135168, -119552, 22208, 345)) * PI
|
|
245
|
+
# return p
|
|
246
|
+
|
|
247
|
+
def E2k(self, a, b):
|
|
248
|
+
'''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using L{E(k)
|
|
249
|
+
<Elliptic.cE>}, the complete C{elliptic} integral of the 2nd kind.
|
|
250
|
+
|
|
251
|
+
@return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
252
|
+
|
|
253
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
254
|
+
'''
|
|
255
|
+
return self._ellip2k(a, b, self._ellipE)
|
|
256
|
+
|
|
257
|
+
def e2k(self, a, b, E_ab=None):
|
|
258
|
+
'''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using U{SciPy's
|
|
259
|
+
ellipe<https://www.JohnDCook.com/perimeter_ellipse.html>} function or method
|
|
260
|
+
C{E_ab}, otherwise C{None}.
|
|
261
|
+
|
|
262
|
+
@kwarg E_ab: Alternate C{Elliperim}C{(a, b)} method to use in case C{SciPy's
|
|
263
|
+
ellipe} is not available.
|
|
264
|
+
|
|
265
|
+
@return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
266
|
+
|
|
267
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
268
|
+
'''
|
|
269
|
+
p = self._ellipe
|
|
270
|
+
if callable(p): # p is not None
|
|
271
|
+
p = self._ellip2k(a, b, p)
|
|
272
|
+
elif callable(E_ab): # and E_ab is not Elliperim.e2k
|
|
273
|
+
p = E_ab(a, b)
|
|
274
|
+
return p
|
|
275
|
+
|
|
276
|
+
def _ellipE(self, k, phi=None):
|
|
277
|
+
'''(INTERNAL) Get the in- C{fE(phi, k)} or complete C{cE(k)} integral of the 2nd kind.
|
|
278
|
+
'''
|
|
279
|
+
if self._k != k: # or self._Ek is None
|
|
280
|
+
self._k = k
|
|
281
|
+
self._Ek = _Elliptic(k)
|
|
282
|
+
return self._Ek.cE if phi is None else self._Ek.fE(phi)
|
|
283
|
+
|
|
284
|
+
@property_ROnce
|
|
285
|
+
def _ellipe(self):
|
|
286
|
+
'''(INTERNAL) Wrap functions C{scipy.special.ellipe} and C{-.ellipeinc}, I{once}.
|
|
287
|
+
'''
|
|
288
|
+
try:
|
|
289
|
+
from scipy.special import ellipe, ellipeinc
|
|
290
|
+
|
|
291
|
+
def _ellipe(k, phi=None):
|
|
292
|
+
m = _1_0 - k
|
|
293
|
+
p = ellipe(m) if phi is None else ellipeinc(phi, m)
|
|
294
|
+
return float(p)
|
|
295
|
+
|
|
296
|
+
except (AttributeError, ImportError):
|
|
297
|
+
_ellipe = None
|
|
298
|
+
return _ellipe # overwrite property_ROnce
|
|
299
|
+
|
|
300
|
+
def _ellip2k(self, a, b, _ellip):
|
|
301
|
+
'''(INTERNAL) Helper for methods C{E2k} and C{e2k}.
|
|
302
|
+
'''
|
|
303
|
+
_, p, a, b = self._pab4(a, b)
|
|
304
|
+
if p is None: # see .ellipsoids.Ellipsoid.L
|
|
305
|
+
k = (b / a)**2
|
|
306
|
+
p = _ellip(k) * a * _4_0
|
|
307
|
+
return p
|
|
308
|
+
|
|
309
|
+
def GK(self, a, b):
|
|
310
|
+
'''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using the U{Gauss-Kummer
|
|
311
|
+
<https://www.JohnDCook.com/blog/2023/05/28/approximate-ellipse-perimeter>} series, and U{here
|
|
312
|
+
<https://MathWorld.Wolfram.com/Gauss-KummerSeries.html>}, C{B{b / a} > 0.75}.
|
|
313
|
+
|
|
314
|
+
@return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
315
|
+
|
|
316
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
317
|
+
'''
|
|
318
|
+
p, h = self._ph2(a, b)
|
|
319
|
+
if h:
|
|
320
|
+
p *= fhorner(h**2, *self._GKs) * PI
|
|
321
|
+
return p
|
|
322
|
+
|
|
323
|
+
@property_ROnce
|
|
324
|
+
def _GKs(self):
|
|
325
|
+
'''(INTERNAL) Compute the Gauss-Kummer coefficients, I{once} from U{numerators
|
|
326
|
+
<https://OEIS.org/A056981>} and U{denominators<https://OEIS.org/A056982>}.
|
|
327
|
+
'''
|
|
328
|
+
return (1, 1 / 4, 1 / 64, 1 / 256, 25 / 16384, 49 / 65536,
|
|
329
|
+
441 / 1048576, 1089 / 4194304) # overwrite property_ROnce
|
|
330
|
+
|
|
331
|
+
def HG(self, a, b, maxit=_DIG53):
|
|
332
|
+
'''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using the U{HG
|
|
333
|
+
<https://web.Tecnico.ULisboa.PT/~mcasquilho/compute/com/,ellips/PerimeterOfEllipse.pdf>}
|
|
334
|
+
(HyperGeometric Gauss-Kummer) series.
|
|
335
|
+
|
|
336
|
+
@kwarg maxit: Number of iterations (C{int}), sufficient for C{B{b / a} > 0.125}.
|
|
337
|
+
|
|
338
|
+
@return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
339
|
+
|
|
340
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}} or no convergence for B{C{maxit}} iterations.
|
|
341
|
+
'''
|
|
342
|
+
p, h = self._ph2(a, b)
|
|
343
|
+
if h:
|
|
344
|
+
hs = self._HGs(h, max(maxit, _DIG53))
|
|
345
|
+
p *= _fsum(hs) * PI # nonfinites=True
|
|
346
|
+
return p
|
|
347
|
+
|
|
348
|
+
def _HGs(self, h, maxit):
|
|
349
|
+
'''(INTERNAL) Yield the C{HG} terms.
|
|
350
|
+
'''
|
|
351
|
+
s = _1_0
|
|
352
|
+
yield s
|
|
353
|
+
p = t = -1
|
|
354
|
+
for u in range(-1, maxit * 2, 2):
|
|
355
|
+
t *= u / (u + 3) * h
|
|
356
|
+
t2 = t**2
|
|
357
|
+
s += t2
|
|
358
|
+
yield t2
|
|
359
|
+
if s == p: # 44 trips
|
|
360
|
+
break
|
|
361
|
+
p = s
|
|
362
|
+
else: # PYCHOK no cover
|
|
363
|
+
raise _convergenceError(maxit, s, s - t2)
|
|
364
|
+
|
|
365
|
+
# def LS(self, a, b):
|
|
366
|
+
# '''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using the U{Linderholm-Segal
|
|
367
|
+
# <https://www.JohnDCook.com/blog/2021/03/24/perimeter-of-an-ellipse>} formula, aka C{3/2 norm}.
|
|
368
|
+
#
|
|
369
|
+
# @return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
370
|
+
#
|
|
371
|
+
# @raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
372
|
+
# '''
|
|
373
|
+
# _, p, a, b = self._pab4(a, b)
|
|
374
|
+
# if p is None:
|
|
375
|
+
# n = pow(a, _1_5) + pow(b, _1_5)
|
|
376
|
+
# p = pow(n * _0_5, _2_3rd) * PI2
|
|
377
|
+
# return p
|
|
378
|
+
|
|
379
|
+
def _pab4(self, a, b):
|
|
380
|
+
r = a < b
|
|
381
|
+
if r:
|
|
382
|
+
a, b = b, a
|
|
383
|
+
if a > b:
|
|
384
|
+
if b > (a * self._TOL53_53):
|
|
385
|
+
p = None
|
|
386
|
+
elif b < 0: # PYCHOK no cover
|
|
387
|
+
t = callername() # underOK=True
|
|
388
|
+
t = _DOT_(typename(self), t)
|
|
389
|
+
t = unstr(t, b, a) if r else \
|
|
390
|
+
unstr(t, a, b)
|
|
391
|
+
raise _ValueError(t)
|
|
392
|
+
else: # "flat"
|
|
393
|
+
p = a * _4_0
|
|
394
|
+
else: # circle
|
|
395
|
+
p = a * PI2
|
|
396
|
+
return r, p, a, b
|
|
397
|
+
|
|
398
|
+
def _ph2(self, a, b):
|
|
399
|
+
_, p, a, b = self._pab4(a, b)
|
|
400
|
+
if p is None:
|
|
401
|
+
p = a + b
|
|
402
|
+
h = (a - b) / p
|
|
403
|
+
else:
|
|
404
|
+
h = None
|
|
405
|
+
return p, h
|
|
406
|
+
|
|
407
|
+
def R2(self, a, b):
|
|
408
|
+
'''Compute the perimeter of an ellipse with semi-axes B{C{a}} and B{C{b}} using U{Ramanujan's
|
|
409
|
+
2nd<https://PaulBourke.net/geometry/ellipsecirc>} approximation, C{B{b / a} > 0.9}.
|
|
410
|
+
|
|
411
|
+
@return: Perimeter (C{meter}, same units as semi-axes B{C{a}} and B{C{b}}).
|
|
412
|
+
|
|
413
|
+
@raise ValueError: Invalid B{C{a}} or B{C{b}}.
|
|
414
|
+
'''
|
|
415
|
+
p, h = self._ph2(a, b)
|
|
416
|
+
if h:
|
|
417
|
+
h *= _3_0 * h
|
|
418
|
+
h /= sqrt(_4_0 - h) + _10_0 # /= chokes PyChecker?
|
|
419
|
+
p *= (h + _1_0) * PI
|
|
420
|
+
return p
|
|
421
|
+
|
|
422
|
+
if not _FOR_DOCS: # PYCHOK force epydoc
|
|
423
|
+
Elliperim = Elliperim() # singleton
|
|
424
|
+
del _FOR_DOCS
|
|
425
|
+
|
|
426
|
+
|
|
117
427
|
class Elliptic(_Named):
|
|
118
428
|
'''Elliptic integrals and functions.
|
|
119
429
|
|
|
@@ -154,31 +464,55 @@ class Elliptic(_Named):
|
|
|
154
464
|
'''
|
|
155
465
|
return self._alphap2
|
|
156
466
|
|
|
467
|
+
def _B3(self, x):
|
|
468
|
+
'''(INTERNAL) Bulirsch' sncndn routine.
|
|
469
|
+
'''
|
|
470
|
+
# Numerische Mathematik 7, 1965, p 89
|
|
471
|
+
# implements DLMF Eqs 22.17.2 - 22.17.4
|
|
472
|
+
c, d, cd, mn = self._B4
|
|
473
|
+
sn, cn = _sincos2(x * cd)
|
|
474
|
+
dn = _1_0
|
|
475
|
+
if sn:
|
|
476
|
+
a = cn / sn
|
|
477
|
+
c *= a
|
|
478
|
+
for m, n in mn:
|
|
479
|
+
a *= c
|
|
480
|
+
c *= dn
|
|
481
|
+
dn = (n + a) / (m + a)
|
|
482
|
+
a = c / m
|
|
483
|
+
a = _1_over(hypot1(c))
|
|
484
|
+
sn = _flipsign(a, sn)
|
|
485
|
+
cn = sn * c
|
|
486
|
+
if d: # and _signBit(self.kp2): # implied
|
|
487
|
+
cn, dn = dn, cn
|
|
488
|
+
sn = sn / d # /= chokes PyChecker
|
|
489
|
+
return sn, cn, dn
|
|
490
|
+
|
|
157
491
|
@Property_RO
|
|
158
|
-
def
|
|
492
|
+
def _B4(self):
|
|
159
493
|
'''(INTERNAL) Get Bulirsch' 4-tuple C{(c, d, cd, mn)}.
|
|
160
494
|
'''
|
|
161
|
-
|
|
495
|
+
a = p = _1_0
|
|
496
|
+
b, d = self.kp2, 0 # kp2 >= 0 always here
|
|
162
497
|
if _signBit(b): # PYCHOK no cover
|
|
163
|
-
d =
|
|
164
|
-
b =
|
|
165
|
-
d =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
498
|
+
d = a - b
|
|
499
|
+
b = neg(b / d)
|
|
500
|
+
d = sqrt(d)
|
|
501
|
+
mn = []
|
|
502
|
+
_mn = mn.append
|
|
503
|
+
for i in range(1, _MAXIT): # GEOGRAPHICLIB_PANIC
|
|
504
|
+
b = sqrt(p * b)
|
|
505
|
+
_mn((a, b))
|
|
170
506
|
c = favg(a, b)
|
|
171
507
|
r = fabs(a - b)
|
|
172
|
-
if r <= (a * _TolJAC): # 6 trips, quadratic
|
|
508
|
+
if r <= (a * _TolJAC): # 4..6 trips, quadratic
|
|
173
509
|
self._iteration += i
|
|
174
510
|
break
|
|
175
|
-
|
|
176
|
-
b *= a
|
|
177
|
-
a = c
|
|
511
|
+
p, a = a, c
|
|
178
512
|
else: # PYCHOK no cover
|
|
179
|
-
raise _convergenceError(r
|
|
513
|
+
raise _convergenceError(_MAXIT, _over(r, p), _TolJAC)
|
|
180
514
|
cd = (c * d) if d else c
|
|
181
|
-
return c, d, cd, tuple(reversed(
|
|
515
|
+
return c, d, cd, tuple(reversed(mn))
|
|
182
516
|
|
|
183
517
|
@Property_RO
|
|
184
518
|
def cD(self):
|
|
@@ -202,15 +536,15 @@ class Elliptic(_Named):
|
|
|
202
536
|
cD = float(D)
|
|
203
537
|
# Complete elliptic integral E(k), Carlson eq. 4.2
|
|
204
538
|
# <https://DLMF.NIST.gov/19.25.E1>
|
|
205
|
-
cE =
|
|
539
|
+
cE = self._cE(kp2)
|
|
206
540
|
# Complete elliptic integral K(k), Carlson eq. 4.1
|
|
207
541
|
# <https://DLMF.NIST.gov/19.25.E1>
|
|
208
|
-
cK =
|
|
542
|
+
cK = self._cK(kp2)
|
|
209
543
|
cKE = float(D.fmul(k2))
|
|
210
544
|
eps = k2 / (sqrt(kp2) + _1_0)**2
|
|
211
545
|
|
|
212
546
|
except Exception as X:
|
|
213
|
-
raise _ellipticError(self
|
|
547
|
+
raise _ellipticError(self, k2=k2, kp2=kp2, cause=X)
|
|
214
548
|
else:
|
|
215
549
|
cD = cK = cKE = INF
|
|
216
550
|
cE = _1_0
|
|
@@ -230,6 +564,9 @@ class Elliptic(_Named):
|
|
|
230
564
|
'''
|
|
231
565
|
return self._cDEKEeps.cE
|
|
232
566
|
|
|
567
|
+
def _cE(self, kp2): # compl integr 2nd kind
|
|
568
|
+
return _rG2(kp2, _1_0, self, PI_=PI_2)
|
|
569
|
+
|
|
233
570
|
@Property_RO
|
|
234
571
|
def cG(self):
|
|
235
572
|
'''Get Legendre's complete geodesic longitude integral
|
|
@@ -274,8 +611,8 @@ class Elliptic(_Named):
|
|
|
274
611
|
cH = float(_RD(_0_0, _1_0, kp2, _3_0 / kp2, self)) if kp2 else _1_0
|
|
275
612
|
|
|
276
613
|
except Exception as X:
|
|
277
|
-
raise _ellipticError(self
|
|
278
|
-
|
|
614
|
+
raise _ellipticError(self, kp2=kp2, alpha2 =alpha2,
|
|
615
|
+
alphap2=alphap2, cause=X)
|
|
279
616
|
return _Cs(cG=cG, cH=cH, cPi=cPi)
|
|
280
617
|
|
|
281
618
|
@Property_RO
|
|
@@ -292,6 +629,9 @@ class Elliptic(_Named):
|
|
|
292
629
|
'''
|
|
293
630
|
return self._cDEKEeps.cK
|
|
294
631
|
|
|
632
|
+
def _cK(self, kp2): # compl integr 1st kind
|
|
633
|
+
return _rF2(kp2, _1_0, self)
|
|
634
|
+
|
|
295
635
|
@Property_RO
|
|
296
636
|
def cKE(self):
|
|
297
637
|
'''Get the difference between the complete integrals of the
|
|
@@ -414,24 +754,24 @@ class Elliptic(_Named):
|
|
|
414
754
|
phi = PI * r / E2 # phi in [-PI_2, PI_2)
|
|
415
755
|
Phi = Fsum(phi)
|
|
416
756
|
# first order correction
|
|
417
|
-
phi = Phi.fsum_(
|
|
757
|
+
phi = Phi.fsum_(-sin(phi * _2_0) * self.eps * _0_5)
|
|
418
758
|
# self._iteration = 0
|
|
419
759
|
# For kp2 close to zero use asin(r / cE) or J. P. Boyd,
|
|
420
760
|
# Applied Math. and Computation 218, 7005-7013 (2012)
|
|
421
761
|
# <https://DOI.org/10.1016/j.amc.2011.12.021>
|
|
422
762
|
_Phi2 = Phi.fsum2f_ # aggregate
|
|
423
|
-
for i in range(1,
|
|
763
|
+
for i in range(1, _MAXIT): # GEOGRAPHICLIB_PANIC
|
|
424
764
|
sn, cn, dn = self._sncndn3(phi)
|
|
425
765
|
if dn:
|
|
426
766
|
sn = self.fE(sn, cn, dn)
|
|
427
767
|
phi, d = _Phi2((r - sn) / dn)
|
|
428
768
|
else: # PYCHOK no cover
|
|
429
|
-
d = _0_0 # XXX
|
|
769
|
+
d = _0_0 # XXX continue?
|
|
430
770
|
if fabs(d) < _TolJAC: # 3-4 trips
|
|
431
771
|
self._iteration += i
|
|
432
772
|
break
|
|
433
773
|
else: # PYCHOK no cover
|
|
434
|
-
raise _convergenceError(d, _TolJAC)
|
|
774
|
+
raise _convergenceError(_MAXIT, d, _TolJAC)
|
|
435
775
|
return Phi.fsum_(n * PI) if n else phi
|
|
436
776
|
|
|
437
777
|
@Property_RO
|
|
@@ -698,7 +1038,8 @@ class Elliptic(_Named):
|
|
|
698
1038
|
x *= r # phi
|
|
699
1039
|
for a, c in ac:
|
|
700
1040
|
p = x
|
|
701
|
-
|
|
1041
|
+
a = asin(c * sin(x) / a)
|
|
1042
|
+
x = favg(a, x)
|
|
702
1043
|
if self.k2 < 0: # Sala Eq. 5.8
|
|
703
1044
|
x = p - x
|
|
704
1045
|
else: # PYCHOK no cover
|
|
@@ -714,20 +1055,22 @@ class Elliptic(_Named):
|
|
|
714
1055
|
# assert b and c
|
|
715
1056
|
if c < 0: # Sala Eq. 5.8
|
|
716
1057
|
r = sqrt(b)
|
|
717
|
-
b =
|
|
1058
|
+
b = _1_over(b)
|
|
718
1059
|
# c *= b # unused
|
|
719
|
-
ac
|
|
720
|
-
|
|
1060
|
+
ac = [] # [(a, sqrt(c))] unused
|
|
1061
|
+
_ac = ac.append
|
|
1062
|
+
for _ in range(_MAXIT): # GEOGRAPHICLIB_PANIC
|
|
721
1063
|
b = sqrt(a * b)
|
|
722
1064
|
c = favg(a, -b)
|
|
723
1065
|
a = favg(a, b) # == PI_2 / K
|
|
724
|
-
|
|
725
|
-
if c <= (a * _TolJAM): # 7
|
|
726
|
-
self._iteration += i
|
|
1066
|
+
_ac((a, c))
|
|
1067
|
+
if c <= (a * _TolJAM): # 7..18 trips, quadratic
|
|
727
1068
|
break
|
|
728
1069
|
else: # PYCHOK no cover
|
|
729
|
-
raise _convergenceError(c
|
|
730
|
-
|
|
1070
|
+
raise _convergenceError(_MAXIT, _over(c, a), _TolJAM)
|
|
1071
|
+
i = len(ac)
|
|
1072
|
+
r *= 2**i * a
|
|
1073
|
+
self._iteration += i
|
|
731
1074
|
return r, tuple(reversed(ac))
|
|
732
1075
|
|
|
733
1076
|
@Property_RO
|
|
@@ -811,30 +1154,11 @@ class Elliptic(_Named):
|
|
|
811
1154
|
if self.kp2:
|
|
812
1155
|
if jam: # Jacobi amplitude, C++ v 2.4
|
|
813
1156
|
sn, cn, dn = self._sncndn3(self._jam(x))
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
# Numerische Mathematik 7, 78-90 (1965).
|
|
817
|
-
# Implements DLMF Eqs 22.17.2 - 22.17.4
|
|
818
|
-
c, d, cd, mn = self._b4
|
|
819
|
-
sn, cn = _sincos2(x * cd)
|
|
820
|
-
dn = _1_0
|
|
821
|
-
if sn:
|
|
822
|
-
a = cn / sn
|
|
823
|
-
c *= a
|
|
824
|
-
for m, n in mn:
|
|
825
|
-
a *= c
|
|
826
|
-
c *= dn
|
|
827
|
-
dn = (n + a) / (m + a)
|
|
828
|
-
a = c / m
|
|
829
|
-
a = _1_0 / hypot1(c)
|
|
830
|
-
sn = neg(a) if _signBit(sn) else a
|
|
831
|
-
cn = sn * c
|
|
832
|
-
if d: # and _signBit(self.kp2): # implied
|
|
833
|
-
cn, dn = dn, cn
|
|
834
|
-
sn = sn / d # /= chokes PyChecker
|
|
1157
|
+
else:
|
|
1158
|
+
sn, cn, dn = self._B3(x)
|
|
835
1159
|
else:
|
|
836
1160
|
sn = tanh(x) # accurate for large abs(x)
|
|
837
|
-
cn = dn =
|
|
1161
|
+
cn = dn = _1_over(cosh(x))
|
|
838
1162
|
|
|
839
1163
|
except Exception as X:
|
|
840
1164
|
raise _ellipticError(self.sncndn, x, kp2=self.kp2, cause=X)
|
|
@@ -935,6 +1259,22 @@ class Elliptic(_Named):
|
|
|
935
1259
|
_allPropertiesOf_n(16, Elliptic) # PYCHOK assert, see Elliptic.reset
|
|
936
1260
|
|
|
937
1261
|
|
|
1262
|
+
class _Elliptic(Elliptic):
|
|
1263
|
+
'''(INTERNAL) Low overhead C{Elliptic} for C{Elliperim._ellipE}.
|
|
1264
|
+
'''
|
|
1265
|
+
_alpha2 = _0_0
|
|
1266
|
+
_alphap2 = _1_0
|
|
1267
|
+
cE = _0_0 # overide Property_RO
|
|
1268
|
+
_iteration = 0
|
|
1269
|
+
|
|
1270
|
+
def __init__(self, k): # k == kp2
|
|
1271
|
+
self._k2 = _1_0 - k
|
|
1272
|
+
self._kp2 = k # 0 < k < 1
|
|
1273
|
+
# assert self.kp2 and self.k2
|
|
1274
|
+
self.cE = self._cE(k) # override Property_RO
|
|
1275
|
+
# self.cK = self._cK(k) # ditto
|
|
1276
|
+
|
|
1277
|
+
|
|
938
1278
|
class EllipticError(_ValueError):
|
|
939
1279
|
'''Elliptic function, integral, convergence or other L{Elliptic} issue.
|
|
940
1280
|
'''
|
|
@@ -948,6 +1288,24 @@ class Elliptic3Tuple(_NamedTuple):
|
|
|
948
1288
|
_Units_ = ( Scalar, Scalar, Scalar)
|
|
949
1289
|
|
|
950
1290
|
|
|
1291
|
+
class _Hdot(dict):
|
|
1292
|
+
'''(INTERNAL) Caching helper for C{_Horner} and C{_RF3}.
|
|
1293
|
+
'''
|
|
1294
|
+
def __call__(self, F, h, *Xys):
|
|
1295
|
+
k = Xys[1] # unique key
|
|
1296
|
+
ys = self.get(k, None)
|
|
1297
|
+
if ys is None:
|
|
1298
|
+
self[k] = ys = tuple((y / h) for y in Xys[1::2])
|
|
1299
|
+
try:
|
|
1300
|
+
D = Fdot(Xys[0::2], *ys, f2product=True)
|
|
1301
|
+
except (OverflowError, TypeError, ValueError):
|
|
1302
|
+
ts = map(_operator.mul, Xys[0::2], ys)
|
|
1303
|
+
D = Fsum(*ts, nonfinites=True) # _Ksum(0, 0, *ts)
|
|
1304
|
+
return D.fmul(F) # Fsum
|
|
1305
|
+
|
|
1306
|
+
_Hdot = _Hdot() # PYCHOK singleton
|
|
1307
|
+
|
|
1308
|
+
|
|
951
1309
|
class _List(list):
|
|
952
1310
|
'''(INTERNAL) Helper for C{_RD}, C{_RF3} and C{_RJ}.
|
|
953
1311
|
'''
|
|
@@ -974,12 +1332,12 @@ class _List(list):
|
|
|
974
1332
|
a = L.a0(5 if y else 3)
|
|
975
1333
|
t = L.threshold(Tol)
|
|
976
1334
|
m = 1
|
|
977
|
-
for i in range(
|
|
1335
|
+
for i in range(_MAXIT):
|
|
978
1336
|
d = fabs(a * m)
|
|
979
1337
|
if d > t: # 3-6 trips
|
|
980
1338
|
break
|
|
981
1339
|
s = map2(sqrt, L) # sqrt(x), sqrt(y), sqrt(z) [, sqrt(p)]
|
|
982
|
-
Q =
|
|
1340
|
+
Q = _Qdot3(*s) # (s[0] * s[1], s[1] * s[2], s[2] * s[0])
|
|
983
1341
|
a = Q(a) # An = sum(An, *Q)) / 4
|
|
984
1342
|
L[:] = map(Q, L) # x = sum(x, *Q) / 4, ...
|
|
985
1343
|
if y: # yield only if used
|
|
@@ -987,7 +1345,7 @@ class _List(list):
|
|
|
987
1345
|
yield a, m, r, s # L[2] is next z
|
|
988
1346
|
m *= 4
|
|
989
1347
|
else: # PYCHOK no cover
|
|
990
|
-
raise _convergenceError(d, t, thresh=True)
|
|
1348
|
+
raise _convergenceError(_MAXIT, d, t, thresh=True)
|
|
991
1349
|
yield a, m, None, () # sentinel: same a, next m, no r and s
|
|
992
1350
|
if inst:
|
|
993
1351
|
inst._iteration += i
|
|
@@ -997,7 +1355,7 @@ class _List(list):
|
|
|
997
1355
|
'''
|
|
998
1356
|
# assert am
|
|
999
1357
|
a0 = self._a0
|
|
1000
|
-
_am =
|
|
1358
|
+
_am = _1_over(am)
|
|
1001
1359
|
for x in xs:
|
|
1002
1360
|
yield (a0 - x) * _am
|
|
1003
1361
|
|
|
@@ -1008,7 +1366,7 @@ class _List(list):
|
|
|
1008
1366
|
return max(fabs(x - a0) for x in self) / Tol
|
|
1009
1367
|
|
|
1010
1368
|
|
|
1011
|
-
# class
|
|
1369
|
+
# class _Qdot3(Fsum):
|
|
1012
1370
|
# '''(INTERNAL) "Quarter" 3-dot product.
|
|
1013
1371
|
# '''
|
|
1014
1372
|
# def __init__(self, x, y, z, *unused): # PYCHOK signature
|
|
@@ -1018,15 +1376,12 @@ class _List(list):
|
|
|
1018
1376
|
# return (self + a).fover(_4_0)
|
|
1019
1377
|
|
|
1020
1378
|
|
|
1021
|
-
class
|
|
1379
|
+
class _Qdot3(list):
|
|
1022
1380
|
'''(INTERNAL) "Quarter" 3-dot product.
|
|
1023
1381
|
'''
|
|
1024
1382
|
def __init__(self, x, y, z, *unused): # PYCHOK signature
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
except (OverflowError, TypeError, ValueError):
|
|
1028
|
-
D = Fsum(x * y, y * z, z * x, nonfinites=True)
|
|
1029
|
-
list.__init__(self, (0,) + D.partials) # NOT D.fsum2()!
|
|
1383
|
+
R = _Rdot(x, y, z, _0_0).partials
|
|
1384
|
+
list.__init__(self, (0,) + R) # NOT R.fsum2()!
|
|
1030
1385
|
|
|
1031
1386
|
def __call__(self, a):
|
|
1032
1387
|
try:
|
|
@@ -1040,31 +1395,35 @@ class _Qot3(list):
|
|
|
1040
1395
|
return _fsum(self) # nonfinites=True
|
|
1041
1396
|
|
|
1042
1397
|
|
|
1043
|
-
def
|
|
1044
|
-
'''(INTERNAL) Yield Carlson 3-tuples C{(xn, yn,
|
|
1398
|
+
def _abm3(x, y, inst=None):
|
|
1399
|
+
'''(INTERNAL) Yield Carlson 3-tuples C{(xn, yn, m)}.
|
|
1045
1400
|
'''
|
|
1046
|
-
a, b = sqrt(x), sqrt(y)
|
|
1401
|
+
a, b = sqrt(x), (sqrt(y) if y != _1_0 else y)
|
|
1047
1402
|
if b > a:
|
|
1048
1403
|
b, a = a, b
|
|
1049
|
-
|
|
1050
|
-
|
|
1404
|
+
yield a, -b, _0_5 # (x0 + y0)**2 * _0_5
|
|
1405
|
+
m = -1
|
|
1406
|
+
for i in range(_MAXIT):
|
|
1051
1407
|
d = fabs(a - b)
|
|
1052
|
-
if d <= (a * _TolRG0): #
|
|
1408
|
+
if d <= (a * _TolRG0): # 2..4 trips
|
|
1053
1409
|
break
|
|
1054
|
-
|
|
1055
|
-
a = favg(
|
|
1056
|
-
b = sqrt(
|
|
1410
|
+
p = a
|
|
1411
|
+
a = favg(p, b)
|
|
1412
|
+
b = sqrt(p * b)
|
|
1413
|
+
yield a, b, m # (xi - yi)**2 * m
|
|
1414
|
+
m *= 2
|
|
1057
1415
|
else: # PYCHOK no cover
|
|
1058
|
-
raise _convergenceError(d
|
|
1416
|
+
raise _convergenceError(_MAXIT, _over(d, p), _TolRG0)
|
|
1059
1417
|
if inst:
|
|
1060
1418
|
inst._iteration += i
|
|
1419
|
+
yield a, b, 0 # sentinel: m = 0
|
|
1061
1420
|
|
|
1062
1421
|
|
|
1063
|
-
def _convergenceError(d, tol, **thresh):
|
|
1422
|
+
def _convergenceError(maxit, d, tol, **thresh):
|
|
1064
1423
|
'''(INTERNAL) Format a no-convergence Error.
|
|
1065
1424
|
'''
|
|
1066
1425
|
t = Fmt.no_convergence(d, tol, **thresh)
|
|
1067
|
-
return
|
|
1426
|
+
return _ValueError(maxit=maxit, txt=t)
|
|
1068
1427
|
|
|
1069
1428
|
|
|
1070
1429
|
def _deltaX(sn, cn, dn, cX, fX):
|
|
@@ -1076,7 +1435,7 @@ def _deltaX(sn, cn, dn, cX, fX):
|
|
|
1076
1435
|
|
|
1077
1436
|
if _signBit(cn):
|
|
1078
1437
|
sn, cn = neg_(sn, cn)
|
|
1079
|
-
r = fX(sn, cn, dn) * PI_2
|
|
1438
|
+
r = fX(sn, cn, dn) * _over(PI_2, cX)
|
|
1080
1439
|
return r - atan2(sn, cn)
|
|
1081
1440
|
|
|
1082
1441
|
except Exception as X:
|
|
@@ -1084,6 +1443,28 @@ def _deltaX(sn, cn, dn, cX, fX):
|
|
|
1084
1443
|
raise _ellipticError(n, sn, cn, dn, cause=X)
|
|
1085
1444
|
|
|
1086
1445
|
|
|
1446
|
+
def elliperim(a, b, *deg2_1):
|
|
1447
|
+
'''Compute the perimeter or an arc of an ellipse with semi-axes C{a} and C{b} using
|
|
1448
|
+
method L{Elliperim.e2k}, L{Elliperim.E2k} or L{Elliperim.arc}.
|
|
1449
|
+
|
|
1450
|
+
@arg deg2_1: Optional, arc end and start angle (C{degrees}), see method L{Elliperim.arc}.
|
|
1451
|
+
|
|
1452
|
+
@return: The perimeter or arc length (C{scalar}, same units as C{a} and C{b}).
|
|
1453
|
+
'''
|
|
1454
|
+
return Elliperim.arc(a, b, *deg2_1) if deg2_1 else Elliperim.e2k(a, b, Elliperim.E2k)
|
|
1455
|
+
|
|
1456
|
+
|
|
1457
|
+
def elliperim_(a, b, *rad2_1):
|
|
1458
|
+
'''Compute the perimeter or an arc of an ellipse with semi-axes C{a} and C{b} using
|
|
1459
|
+
method L{Elliperim.e2k}, L{Elliperim.E2k} or L{Elliperim.arc_}.
|
|
1460
|
+
|
|
1461
|
+
@arg rad2_1: Optional, arc end and start angle (C{radians}), see method L{Elliperim.arc_}.
|
|
1462
|
+
|
|
1463
|
+
@return: The perimeter or arc length (C{scalar}, same units as C{a} and C{b}).
|
|
1464
|
+
'''
|
|
1465
|
+
return Elliperim.arc_(a, b, *rad2_1) if rad2_1 else Elliperim.e2k(a, b, Elliperim.E2k)
|
|
1466
|
+
|
|
1467
|
+
|
|
1087
1468
|
def _ellipticError(where, *args, **kwds_cause_txt):
|
|
1088
1469
|
'''(INTERNAL) Format an L{EllipticError}.
|
|
1089
1470
|
'''
|
|
@@ -1099,7 +1480,7 @@ def _ellipticError(where, *args, **kwds_cause_txt):
|
|
|
1099
1480
|
return EllipticError(u, cause=x, txt=t)
|
|
1100
1481
|
|
|
1101
1482
|
|
|
1102
|
-
def
|
|
1483
|
+
def _Hsum(S, e1, E2, E3, E4, E5, over):
|
|
1103
1484
|
'''(INTERNAL) Horner-like form for C{_RD} and C{_RJ} below.
|
|
1104
1485
|
'''
|
|
1105
1486
|
E22 = E2**2
|
|
@@ -1108,16 +1489,17 @@ def _Horner(S, e1, E2, E3, E4, E5, over):
|
|
|
1108
1489
|
# + 3*E5/26 - E2**3/16 + 3*E3**2/40 + 3*E2*E4/20
|
|
1109
1490
|
# + 45*E2**2*E3/272 - 9*(E3*E4+E2*E5)/68)
|
|
1110
1491
|
# converted to Horner-like form ...
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
S
|
|
1114
|
-
S +=
|
|
1115
|
-
S +=
|
|
1116
|
-
S +=
|
|
1117
|
-
S +=
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1492
|
+
_1 = _1_0
|
|
1493
|
+
h = 4084080
|
|
1494
|
+
S *= e1
|
|
1495
|
+
S += _Hdot(E5, h, E2, -540540, _1, 471240)
|
|
1496
|
+
S += _Hdot(E4, h, E2, 612612, E3, -540540, _1, -556920)
|
|
1497
|
+
S += _Hdot(E3, h, E2, -706860, E22, 675675, E3, 306306, _1, 680680)
|
|
1498
|
+
S += _Hdot(E2, h, E2, 417690, E22, -255255, _1, -875160)
|
|
1499
|
+
S += _1
|
|
1500
|
+
if over:
|
|
1501
|
+
e1 *= over
|
|
1502
|
+
return S.fdiv(e1) # Fsum
|
|
1121
1503
|
|
|
1122
1504
|
|
|
1123
1505
|
def _3over(a, b):
|
|
@@ -1148,7 +1530,7 @@ def _rC(x, y):
|
|
|
1148
1530
|
raise _ellipticError(Elliptic.fRC, x, y)
|
|
1149
1531
|
|
|
1150
1532
|
|
|
1151
|
-
def _RD(x, y, z, over=
|
|
1533
|
+
def _RD(x, y, z, over=_0_0, inst=None):
|
|
1152
1534
|
'''(INTERNAL) Carlson, eqs 2.28 - 2.34.
|
|
1153
1535
|
'''
|
|
1154
1536
|
L = _List(x, y, z)
|
|
@@ -1157,21 +1539,32 @@ def _RD(x, y, z, over=_1_0, inst=None):
|
|
|
1157
1539
|
if s:
|
|
1158
1540
|
S += _over(_3_0, (r + z) * s[2] * m)
|
|
1159
1541
|
z = L[2] # s[2] = sqrt(z)
|
|
1160
|
-
|
|
1542
|
+
m *= a
|
|
1543
|
+
x, y = L.rescale(-m, x, y)
|
|
1161
1544
|
xy = x * y
|
|
1162
1545
|
z = (x + y) / _3_0
|
|
1163
1546
|
z2 = z**2
|
|
1164
|
-
return
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1547
|
+
return _Hsum(S, sqrt(a) * m,
|
|
1548
|
+
(xy - z2 * _6_0),
|
|
1549
|
+
(xy * _3_0 - z2 * _8_0) * z,
|
|
1550
|
+
(xy - z2) * z2 * _3_0,
|
|
1551
|
+
(xy * z2 * z), over) # Fsum
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
def _Rdot(x, y, z, start3):
|
|
1555
|
+
'''(INTERNAL) "Rotated" C{dot}.
|
|
1556
|
+
'''
|
|
1557
|
+
try:
|
|
1558
|
+
R = Fdot_(x, y, y, z, z, x, start3, _3_0, f2product=True)
|
|
1559
|
+
except (OverflowError, TypeError, ValueError):
|
|
1560
|
+
R = Fsum(x * y, y * z, z * x, start3 * _3_0, nonfinites=True)
|
|
1561
|
+
return R
|
|
1169
1562
|
|
|
1170
1563
|
|
|
1171
1564
|
def _rF2(x, y, inst=None): # 2-arg version, z=0
|
|
1172
1565
|
'''(INTERNAL) Carlson, eqs 2.36 - 2.38.
|
|
1173
1566
|
'''
|
|
1174
|
-
for a, b,
|
|
1567
|
+
for a, b, m in _abm3(x, y, inst): # PYCHOK yield
|
|
1175
1568
|
pass
|
|
1176
1569
|
return _over(PI, a + b) # float
|
|
1177
1570
|
|
|
@@ -1192,24 +1585,22 @@ def _RF3(x, y, z, inst=None): # 3-arg version
|
|
|
1192
1585
|
# (1 - E2/10 + E3/14 + E2**2/24 - 3*E2*E3/44
|
|
1193
1586
|
# - 5*E2**3/208 + 3*E3**2/104 + E2**2*E3/16)
|
|
1194
1587
|
# converted to Horner-like form ...
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
S
|
|
1198
|
-
|
|
1588
|
+
_1 = _1_0
|
|
1589
|
+
h = 240240
|
|
1590
|
+
S = _Hdot(e3, h, e4, 15015, e3, 6930, e2, -16380, _1, 17160)
|
|
1591
|
+
S += _Hdot(e2, h, e4, -5775, e2, 10010, _1, -24024)
|
|
1592
|
+
S += _1
|
|
1593
|
+
return S.fdiv(sqrt(a)) # Fsum
|
|
1199
1594
|
|
|
1200
1595
|
|
|
1201
1596
|
def _rG2(x, y, inst=None, PI_=PI_4): # 2-args
|
|
1202
1597
|
'''(INTERNAL) Carlson, eqs 2.36 - 2.39.
|
|
1203
1598
|
'''
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
for a, b,
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
m *= 2
|
|
1210
|
-
else:
|
|
1211
|
-
S += (a + b)**2 * _0_5
|
|
1212
|
-
return S.fmul(PI_).fover(a + b) # float
|
|
1599
|
+
rs = [] # len 2..7, incl sentinel
|
|
1600
|
+
_r = rs.append
|
|
1601
|
+
for a, b, m in _abm3(x, y, inst): # PYCHOK yield
|
|
1602
|
+
_r((a - b)**2 * m)
|
|
1603
|
+
return _over(_fsum(rs) * PI_, a + b) # nonfinites=True, float
|
|
1213
1604
|
|
|
1214
1605
|
|
|
1215
1606
|
def _rG3(x, y, z): # 3-arg version
|
|
@@ -1223,7 +1614,7 @@ def _rG3(x, y, z): # 3-arg version
|
|
|
1223
1614
|
return R.fover(_2_0) # float
|
|
1224
1615
|
|
|
1225
1616
|
|
|
1226
|
-
def _RJ(x, y, z, p, over=
|
|
1617
|
+
def _RJ(x, y, z, p, over=_0_0, inst=None):
|
|
1227
1618
|
'''(INTERNAL) Carlson, eqs 2.17 - 2.25.
|
|
1228
1619
|
'''
|
|
1229
1620
|
def _xyzp(x, y, z, p):
|
|
@@ -1244,17 +1635,18 @@ def _RJ(x, y, z, p, over=_1_0, inst=None):
|
|
|
1244
1635
|
S += r / (d * m)
|
|
1245
1636
|
else: # PYCHOK no cover
|
|
1246
1637
|
return NAN
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1638
|
+
m *= a
|
|
1639
|
+
x, y, z = L.rescale(m, x, y, z)
|
|
1640
|
+
p = neg(Fsum(x, y, z).fover(_2_0))
|
|
1641
|
+
p2 = p**2
|
|
1642
|
+
p3 = p2 * p
|
|
1643
|
+
E2 = _Rdot(x, y, z, -p2)
|
|
1644
|
+
E2p = E2 * p
|
|
1645
|
+
xyz = x * y * z
|
|
1646
|
+
return _Hsum(S.fmul(_6_0), sqrt(a) * m, E2,
|
|
1647
|
+
Fsum(p3 * _4_0, xyz, E2p, E2p),
|
|
1648
|
+
Fsum(p3 * _3_0, E2p, xyz, xyz).fmul(p),
|
|
1649
|
+
p2 * xyz, over) # Fsum
|
|
1258
1650
|
|
|
1259
1651
|
|
|
1260
1652
|
class _RJfma(object):
|
|
@@ -1264,7 +1656,8 @@ class _RJfma(object):
|
|
|
1264
1656
|
self._Rj = _RJ(*args)
|
|
1265
1657
|
|
|
1266
1658
|
def ma(self, b, c):
|
|
1267
|
-
r =
|
|
1659
|
+
r = self._Rj._fma(b, c, nonfinites=True)
|
|
1660
|
+
# assert r is not self._Rj
|
|
1268
1661
|
return float(r)
|
|
1269
1662
|
|
|
1270
1663
|
# **) MIT License
|