Home
Manual
Packages
Global Index
Keywords
Quick Reference
|
/*
* elliptic.i -- $Id$
* elliptic functions
*
* Copyright (c) 1999. See accompanying LEGAL file for details.
*/
/* Abramowitz and Stegun, sections 16.4, 17.2.17-19, 17.6 */
local elliptic ;
/* DOCUMENT elliptic, ell_am, ell_f, ell_e, dn_, ellip_k, ellip_e
The elliptic integral of the first kind is:
u = integral[0 to phi]( dt / sqrt(1-m*sin(t)^2) )
The functions ell_f and ell_am compute this integral and its
inverse:
u = ell_f(phi, m)
phi = ell_am(u, m)
The Jacobian elliptic functions can be computed from the
"amplitude" ell_am by means of:
sn(u|m) = sin(ell_am(u,m))
cn(u|m) = cos(ell_am(u,m))
dn(u|m) = dn_(ell_am(u,m)) = sqrt(1-m*sn(u|m)^2)
The other nine functions are sc=sn/cn, cs=cn/sn, nd=1/dn,
cd=cn/dn, dc=dn/cn, ns=1/sn, sd=sn/dn, nc=1/cn, and ds=dn/sn.
(The notation u|m does not means yorick's | operator; it is
the mathematical notation, not valid yorick code!)
The parameter M is given in three different notations:
as M, the "parameter",
as k, the "modulus", or
as alpha, the "modular angle",
which are related by: M = k^2 = sin(alpha)^2. The yorick elliptic
functions in terms of M may need to be written
ell_am(u,k^2) or ell_am(u,sin(alpha)^2)
in order to agree with the definitions in other references.
Sections 17.2.17-19 of Abramowitz and Stegun explains these notations,
and chapters 16 and 17 present a compact overview of the subject of
elliptic functions in general.
The parameter M must be a scalar; U may be an array. The
exceptions are the complete elliptic integrals ellip_k and
ellip_e which accept an array of M values.
The ell_am function uses the external variable ell_m if M is
omitted, otherwise stores M in ell_m. Hence, you may set ell_m,
then simply call ell_am(u) if you have a series of calls with
the same value of M; this also allows the dn_ function to work
without a second specification of M.
The elliptic integral of the second kind is:
u = integral[0 to phi]( dt * sqrt(1-m*sin(t)^2) )
The function ell_e computes this integral:
u = ell_e(phi, m)
The special values ell_f(pi/2,m) and ell_e(pi/2,m) are the complete
elliptic integrals of the first and second kinds; separate functions
ellip_k and ellip_e are provided to compute them.
Note that the function ellip_k is infinite for M=1 and for large
negative M. The "natural" range for M is 0<=M<=1; all other real
values can be "reduced" to this range by various transformations;
the logarithmic singularity of ellip_k is actually very mild, and
other functions such as ell_am are perfectly well-defined there.
Here are the sum formulas for elliptic functions:
sn(u+v) = ( sn(u)*cn(v)*dn(v) + sn(v)*cn(u)*dn(u) ) /
( 1 - m*sn(u)^2*sn(v)^2 )
cn(u+v) = ( cn(u)*cn(v) - sn(u)*dn(u)*sn(v)*dn(v) ) /
( 1 - m*sn(u)^2*sn(v)^2 )
dn(u+v) = ( dn(u)*dn(v) - m*sn(u)*cn(u)*sn(v)*cn(v) ) /
( 1 - m*sn(u)^2*sn(v)^2 )
And the formulas for pure imaginary values:
sn(1i*u,m) = 1i * sc(u,1-m)
cn(1i*u,m) = nc(u,1-m)
dn(1i*u,m) = dc(u,1-m)
SEE ALSO: ell_am, ell_f, ell_e, dn_, ellip_k, ellip_e
*/
func ell_am (u,m)
/* DOCUMENT ell_am(u)
or ell_am(u,m)
returns the "amplitude" (an angle in radians) for the Jacobi
elliptic functions at U, with parameter M. That is,
phi = ell_am(u,m)
means that
u = integral[0 to phi]( dt / sqrt(1-m*sin(t)^2) )
Thus ell_am is the inverse of the incomplete elliptic function
of the first kind ell_f. See help,elliptic for more.
SEE ALSO: elliptic
*/
{
/* set up the arithmetic-geometric mean scale */
extern ell_m, _agm_m, _agm_n, _agm_coa, _agm_a, _agm_sn;
if (is_void(m)) m= ell_m;
if (m != ell_m) {
ell_m= m= double(m);
if (m<0.) {
_agm_sn= 1./(1.-m);
m*= -_agm_sn;
_agm_sn= sqrt(_agm_sn);
} else if (m>1.) {
m= 1./m;
_agm_sn= sqrt(m);
}
_agm_m= m;
_agm_coa()= 0.;
_agm_n= 0;
_agm_a= 1.;
if (m!=1.) {
b= sqrt(1.-m);
for (;;) { /* maximum of 8 passes for 64-bit double */
c= 0.5*(_agm_a-b);
if (!c) break;
am= _agm_a-c; /* arithmetic mean */
_agm_coa(++_agm_n)= c/am;
if (am==_agm_a) break;
b= sqrt(_agm_a*b); /* geometric mean */
_agm_a= am;
}
_agm_a*= 2.^_agm_n;
}
}
phi= _agm_a*u;
if (m<0. || m>1.) phi/= _agm_sn;
if (m!=1.)
for (n=_agm_n ; n>0 ; n--) phi= 0.5*(phi+asin(_agm_coa(n)*sin(phi)));
else
phi= atan(tanh(phi), sech(phi));
if (m<0.) {
cn= cos(phi);
phi= sin(phi);
nd= 1./sqrt(1.-m*phi*phi);
phi= atan(_agm_sn*phi*nd, cn*nd);
} else if (m>1.) {
phi= sin(phi);
phi= atan(_agm_sn*phi, sqrt(1.-_agm_m*phi*phi));
}
return phi;
}
_agm_coa= array(0.,16);
_agm_n= 0;
_agm_m= 0.0;
_agm_a= 1.0;
_agm_sn= 1.0;
ell_m= 0.0;
func dn_ (phi, m)
/* DOCUMENT dn_(ell_am(u,m))
return the Jacobian elliptic function dn(u|m). The external
variable ell_m must be set properly before calling dn_.
SEE ALSO: elliptic, ell_am
*/
{
if (is_void(m)) m= ell_m;
phi= sin(phi);
return sqrt(1.-m*phi*phi);
}
func ell_f (phi,m)
/* DOCUMENT ell_f(phi,m)
returns the incomplete elliptic integral of the first kind F(phi|M).
That is,
u = ell_f(phi,m)
means that
u = integral[0 to phi]( dt / sqrt(1-m*sin(t)^2) )
See help,elliptic for more.
SEE ALSO: elliptic, ell_e
*/
{
orig_m= m= double(m);
if (m>1.) {
scale= sqrt(m);
phi= asin(scale*sin(phi));
m= 1./m;
} else if (m<0.) {
scale= sqrt(1.-m);
phi= 0.5*pi - phi;
m/= m-1.;
}
if (m==1.) {
phi= atanh(sin(phi));
a= 0.;
} else { /* compute using arithmetic-geometric mean */
sgn= sign(phi);
phi= abs(phi);
a= 1.;
b= sqrt(1.-m);
twon= 1.0;
pi2= 0.5*pi;
for (;;) { /* maximum of 8 passes for 64-bit double */
c= 0.5*(a-b);
if (!c) break;
phase= (phi+pi2)/pi;
cycle= floor(phase);
phi*= 1. + 1.e-15*(cycle==phase);
phi+= atan((b/a)*tan(phi)) + pi*cycle;
twon*= 2.0;
am= a-c;
if (am==a) break;
b= sqrt(a*b);
a= am;
}
phi/= twon*a*sgn;
}
if (orig_m>1.) {
phi/= scale;
} else if (orig_m<0.) {
phi= (0.5*pi/a - phi)/scale;
}
return phi;
}
func ell_e (phi,m)
/* DOCUMENT ell_e(phi,m)
returns the incomplete elliptic integral of the second kind E(phi|M).
That is,
u = ell_e(phi,m)
means that
u = integral[0 to phi]( dt * sqrt(1-m*sin(t)^2) )
See help,elliptic for more.
SEE ALSO: elliptic, ell_f
*/
{
orig_m= m= double(m);
if (m>1.) {
scale= sqrt(m);
phi= asin(scale*sin(phi));
m= 1./m;
} else if (m<0.) {
scale= sqrt(1.-m);
phi= 0.5*pi - phi;
m/= m-1.;
}
if (m==1.) {
per= floor((phi+0.5*pi)/pi);
phi= sin(phi)*sign(0.5-abs(per%2.)) + 2.*per;
a= 0.;
} else { /* compute using arithmetic-geometric mean */
sgn= sign(phi);
phi= abs(phi);
a= 1.;
b= sqrt(1.-m);
eok= 1.-0.5*m;
cs= 0.;
twon= 1.0;
pi2= 0.5*pi;
for (;;) { /* maximum of 8 passes for 64-bit double */
c= 0.5*(a-b);
if (!c) break;
phi+= atan((b/a)*tan(phi)) + pi*floor((phi+pi2)/pi);
cs+= c*sin(phi);
eok-= twon*c*c;
twon*= 2.0;
am= a-c;
if (am==a) break;
b= sqrt(a*b);
a= am;
}
f= sgn*phi/(twon*a);
phi= eok*f + sgn*cs;
}
if (orig_m>1.) {
phi= (phi-(1.-m)*f)/scale;
} else if (orig_m<0.) {
phi= (eok*0.5*pi/a - phi)*scale;
}
return phi;
}
func ellip_k (m)
/* DOCUMENT ellip_k(m)
returns the complete elliptic integral of the first kind K(M):
K(M) = integral[0 to pi/2]( dt / sqrt(1-M*sin(t)^2) )
See help,elliptic for more.
SEE ALSO: elliptic, ellip_e, ell_f
*/
{
if (anyof(m>=1.)) error, "ellip_k(m) not computed for m>=1";
m= double(m);
mask= (m>=0.);
list= where(mask);
if (numberof(list)) scale= array(0.5*pi,numberof(list));
list= where(!mask);
if (numberof(list)) {
sm= 1./(1.-m(list));
m(list)*= -sm;
sm= 0.5*pi*sqrt(sm);
}
scale= merge(scale,sm,mask);
a= array(1.,dimsof(m));
b= sqrt(1.-m);
for (;;) {
c= 0.5*(a-b);
am= a-c;
if (allof(am==a)) break;
b= sqrt(a*b);
a= am;
}
return scale/a;
}
func ellip_e (m)
/* DOCUMENT ellip_e(m)
returns the complete elliptic integral of the second kind E(M):
E(M) = integral[0 to pi/2]( dt * sqrt(1-M*sin(t)^2) )
See help,elliptic for more.
SEE ALSO: elliptic, ellip_k, ell_e
*/
{
if (anyof(m>1.)) error, "ellip_e(m) not computed for m>1";
m= double(m);
mask= (m>=0.);
list= where(mask);
if (numberof(list)) scale= array(0.5*pi,numberof(list));
list= where(!mask);
if (numberof(list)) {
sm= 1.-m(list);
m(list)/= -sm;
sm= 0.5*pi*sqrt(sm);
}
scale= merge(scale,sm,mask);
mask= (m!=1.);
list= where(mask);
if (numberof(list)) {
m= m(list);
a= array(1.,numberof(m));
b= sqrt(1.-m);
e= 1.-0.5*m;
twon= 1.0;
for (n=0 ;;) { /* maximum of 8 passes for 64-bit double */
c= 0.5*(a-b);
am= a-c;
if (allof(am==a)) break;
e-= twon*c*c;
twon*= 2.0;
b= sqrt(a*b);
a= am;
}
e/= a;
}
list= where(!mask);
if (numberof(list)) em= array(2./pi,numberof(list));
return scale*merge(e,em,mask);
}
func ellip2_k (m)
/* DOCUMENT ellip2_k(m)
returns the complete elliptic integral of the first kind K(M):
K(M) = integral[0 to pi/2]( dt / sqrt(1-M*sin(t)^2) )
accurate to 2e-8 for 0<=M<1
SEE ALSO: elliptic, ellip_e, ell_f
*/
{
m= 1.-m;
return poly(m,1.38629436112,0.09666344259,0.03590092383,
0.03742563713,0.01451196212) +
log(1./m)*poly(m,0.5,0.12498593597,0.06880248576,
0.03328355346,0.00441787012);
}
func ellip2_e (m)
/* DOCUMENT ellip2_e(m)
returns the complete elliptic integral of the second kind E(M):
E(M) = integral[0 to pi/2]( dt * sqrt(1-M*sin(t)^2) )
accurate to 2e-8 for 0<=M<=1
SEE ALSO: elliptic, ellip_k, ell_e
*/
{
m= 1.-m;
return poly(m,1.,0.44325141463,0.06260601220,
0.04757383546,0.01736506451) +
log(1./m)*poly(m,0.,0.24998368310,0.09200180037,
0.04069697526,0.00526449639);
}
|