Program Code for Coulomb Wave Function (specially adapted for very small energies)
/*************************************************************************************
* This program calculates the regular Coulomb wave function (the radial part of
* the continuum wave function for a hydrogen-like atom).
* It is written in PL/1, but should be easily adaptable to other languages.
* It is based on a program that was part of the PL/1 scientific subroutine
* package at the University of Bonn, which I modified so that it can be used
* for very small energies and high angular momenta (the modification
* consists of using special subroutines for arithmetic operations that
* could otherwise not be completed within the normal numerical range).
* This routine should in principle be faster than my routine COULOMBC
* as it does not involve numerical integration but uses a recursion technique,
* and also it requires only the usual double precision.
*
* The program calculates the wave function for a given radial interval
* and stores it on file. Subsequent runs can append to this file if required.
* There is in principle no limit to how far the radial range can be extended
* this way, so for the calculation of the dipole integral with the wave
* function of a given bound state, one should know beforehand what about the
* maximum radius required is (for instance, by means of my subroutine RSMAX).
*
*
* Input parameters are:
* CO: if CO=0: output file is newly created in this run;
otherwise: output is appended to existing file
* MOD: if ='TEST': print out of recursion index
* Z1: electron charge number (<0(=-1))
* Z2: nucleus charge number (>0)
* E: energy of continuum electron (Rydberg)
* N: highest angular momentum to be computed
* DL: angular momentum step width for output
* LMAX: highest angular momentum for output
* URM: (only relevant for CO=0): maximum radius point for which the wave
* function will be stored (including subsequent extensions of the file)
* (in Bohr radii)
* LR: (only relevant if LRMOD not ='FILE') smallest radius point for this run
* (in Bohr radii)
* UR: (only relevant if LRMOD not ='FILE') largest radius point for this run
* (in Bohr radii; <URM)
* LRMOD: if ='FILE': LR is taken from external file
* DRF: (only relevant if LRMOD ='FILE'): radius step width for this run;
* if CO not=0 : if this run terminates regularly, UR=LR+DRF is stored;
if not, a negative value is stored (preventing further runs);
* if CO=0: >4.5.π.DRFAK.√E
* (in Bohr radii)
* DRFAK: multiplicator for changing the density of radius points
* D: desired relative accuracy of results
* LIMFAK: multiplicator for length of recursion array vectors (=1.5)
* NUMAX: if >0: internally computed maximum recursion index will be
* replaced by this value
* DNU: (only relevant if NUMAX>0): step width for increase of recursion index
* FNDIG: number of relevant digits after the decimal point for output
*
*
* Required subroutines: NUM, ADD, MULT, DIV
* (replacing basic arithmetic operations where these could lead
* to a numerical overflow or underflow).
*
*
* This program has worked well and correct for me in the past (using IBM PL/1 compilers),
* but I can't guarantee this for all compilers, especially if the code is translated
* into a different language.
*
* If there are any problems, please let me know and I mention the issue here.
* (for contact details see http://www.plasmaphysics.org.uk/feedback.htm ).
*
* Thomas Smid, May 2007
*********************************************************************************/
COULOMBC2: PROCEDURE OPTIONS(MAIN) REORDER ;
DEFAULT RANGE(A:Z) DEC FLOAT VALUE(DEC FLOAT(16)) ;
DCL (ETA,RHO,EPS,D1,R,S,OMEGA,RHO1,A,B,SUM,PI2) DEC FLOAT(16) ;
DCL (F(*),RR(*),FAP(*),LAM(*)) CTL DEC FLOAT(16) ;
DCL (LAMEX(*),FEX(*),RREX(*),FAPEX(*)) CTL DEC FIXED(15,0);
DCL (N,D,LIM,CTR,K,NU,DNU,K1,NU1,DL,LMAX) DEC FIXED(15,0) ;
DCL (XEX,YEX,XYEX,REX,SEX,RLEX,SLEX,S1EX,NULL,SUMEX) DEC FIXED(15,0);
DCL(AEX,R1EX,R2EX,SQBEX,KKEX,BEX,NUMAX,CO) DEC FIXED(15,0) ;
DCL MMEX DEC FIXED(15,0) ;
DCL RPOINT(*) CTL DEC FLOAT(16) ;
DCL QG(16) INIT
(4.947004674958250E-01, 1.357622970587705E-02,
4.722875115366163E-01, 3.112676196932395E-02,
4.328156011939159E-01, 4.757925584124639E-02,
3.777022041775015E-01, 6.231448562776694E-02,
3.089381222013219E-01, 7.479799440828837E-02,
2.290083888286137E-01, 8.457825969750127E-02,
1.408017753896295E-01, 9.130170752246179E-02,
4.750625491881872E-02, 9.472530522753425E-02);
DCL QGF(16) DEC FLOAT(16) ;
DCL (MOD,LRMOD) CHARACTER(4) ;
DCL (LR,UR,DR,LR1,UR1,URM,DRF,URF,LR0) DEC FIXED (12,3) ;
DCL (IR,IRA,J,JJ,I,I2,JJ1,FNDIG) DEC FIXED(15,0) ;
DCL OUTFILE FILE STREAM OUTPUT ;
DCL ILRFILE FILE STREAM INPUT ;
DCL OLRFILE FILE STREAM OUTPUT ;
DCL (NUM,ADD,MULT,DIV) ENTRY ;
ON UNDERFLOW ;
GET DATA COPY ; /* Data input */
ALLOCATE F(0:N),RR(0:N),FAP(0:N),FEX(0:N),
RREX(0:N),FAPEX(0:N) ;
KE=SQRT(E) ;
ETA=Z1*Z2/KE ;
OPEN FILE(OUTFILE) ;
MEINS=-1 ;
PI=ACOS(MEINS) ;
DR=1.5*PI/KE*DRFAK; DRR=DR;
IF LRMOD='FILE' THEN DO ;
IF CO>0|CO<0 THEN DO ;
OPEN FILE(ILRFILE); GET FILE(ILRFILE) EDIT(LR) (SKIP,E(22,15)) ;
IF LR<0 THEN GO TO ENDE ;
END ;
ELSE LR=0 ;
LR0=-1E07 ;
OPEN FILE(OLRFILE) ;
PUT FILE(OLRFILE) EDIT(LR0) (SKIP,E(22,15)) ;
CLOSE FILE(OLRFILE) ;
URF=LR+DRF; UR=URF ;
END ;
LR=CEIL(LR/DR)*DR ;
UR=CEIL(UR/DR)*DR ;UR1=URM ;
UR1=CEIL(UR1/DR)*DR ;LR1=0 ;
IF CO=0 THEN IR=FLOOR((UR-LR)/DR)+7 ;
ELSE IR=FLOOR((UR-LR)/DR)+1 ;
IRA=(IR-1)*16 ;
ALLOCATE RPOINT(IRA) ;
KVV=1E-04; IF LMAX>=N THEN LMAX=N-DL ;
DO J=1 TO IR-1 ; /* Calculation of radius points */
IF J<=9&CO=0 THEN DO ;
DRJ=DRR/3 ;
R=(J-1)*DRJ+LR ;
END ;
ELSE DO ;
DRJ=DRR ;
IF CO=0 THEN R=3*DRR+(J-10)*DRJ ;
ELSE R=LR+(J-1)*DRJ ;
END ;
DR2=0.5*DRJ ;
JJ=16*(J-1) ; JJ1=JJ+17 ;
DO I=1 TO 8 ;
I2=2*I-1 ;
RPOINT(JJ+I)=R+DR2-QG(I2)*DRJ;
RPOINT(JJ1-I)=R+DR2+QG(I2)*DRJ;
END ;
END ;
IF CO=0 THEN DO ; /* Output of file header */
PUT FILE(OUTFILE) EDIT(KVV,E,Z1,Z2,N,LMAX,DL,LR1,UR1,DR,QG)
(SKIP,26 E(FNDIG+7,FNDIG)) ;
END ;
DO J=1 TO IRA ; /* Radius point loop */
RHO=RPOINT(J)*KE ;
F=0; RR=0; FAP=0; RLEX=0; SLEX=0; S1EX=0; NULL=0;
FEX=0; RREX=0; FAPEX=0; AEX=0; R1EX=0; R2EX=0; SQBEX=0; KKEX=0;
BEX=0;
IF RHO<0|N<0 THEN GO TO ENDE ;
IF RHO=0 THEN GO TO C3 ;
IF D<2 THEN D=2; DEPS=D;
EPS=0.5/10**DEPS ;
RHO1=1/RHO ;
PI2=1.570796326794897E00 ; SQPI2=SQRT(1/PI2) ;
LRR0=1E-14; REX=0; SEX=0; EINS=1 ;
DO K=0 TO N ;
FAP(K)=0 ;
END ;
R=2*ETA*RHO1 ;
IF R<0 THEN OMEGA=0 ;
ELSE DO ;
OMEGA=PI2*R ;
IF R>1 THEN DO ;
S=ATAN(SQRT(SQRT(R*R-1))) ;
OMEGA=OMEGA-S*R+SQRT(R-1) ;
END ;
END ;
D1=2.3026E00*D+1.3863E00 ;
IF OMEGA=0 THEN DO ;
A=PI2*ABS(ETA) ;
END ;
ELSE A=EXP(-ATAN(1/OMEGA)*ETA) ;
S=SQRT(1+OMEGA*OMEGA) ; /* Calculation of maximum recursion index */
B=OMEGA+S ;
S=D1-OMEGA*RHO+A+LOG(SQRT(B/S)) ;
B=1.3591E00*RHO*B ;
S=S/B ;
IF S<-0.3679E00 THEN S=1 ;
ELSE DO ;
TS=S ;
CALL T ;
S=B*TT ;
END ;
IF N=0 THEN R=1 ;
ELSE DO ;
TS=0.5*D1/N ;
CALL T ;
R=N*TT ;
END ;
IF R<=S THEN R=S ;
NU=FLOOR(R)+2 ;
IF NUMAX>0 THEN NU=NUMAX ; ELSE NU=CEIL(NU*8E-01) ;
NU1=1 ; IF NUMAX=0 THEN DNU=CEIL(NU*1E-01) ;
LIM=CEIL(NU*LIMFAK); ALLOCATE LAM(0:LIM),LAMEX(0:LIM) ;
LAM=0; LAMEX=0; LAM(0)=1; LAM(1)=OMEGA-ETA ;
CALL NUM(LAM(1),LAMEX(1)) ;
SUM=RHO*EXP(OMEGA*RHO) ;
CALL NUM(SUM,SUMEX) ;
C1: IF NU>LIM THEN DO ; /* Iteration loop */
PUT DATA(NU); PUT SKIP;
GO TO ENDE ;
END ;
IF MOD='TEST' THEN DO ;
PUT DATA(NU); PUT SKIP; /* Print recursion index in test mode */
END ;
DO K=NU1 TO NU-1 ; /* Recursive calculation of recursion coefficient */
A=1+1E0/K ;
B=ETA/K ;
R=OMEGA*(1+1/A) ;
IF ABS(R)>0 THEN CALL NUM(R,REX) ;
S=(1+B*B)/A ;
CALL NUM(S,SEX) ;
CALL MULT(R,REX,LAM(K),LAMEX(K),RL,RLEX) ;
CALL MULT(S,SEX,LAM(K-1),LAMEX(K-1),SL,SLEX) ;
CALL ADD(RL,RLEX,SL,SLEX,LAM(K+1),LAMEX(K+1)) ;
END ;
R=0; REX=0; S=0; SEX=0;
DO K=NU TO 1 BY -1 ; /* Recursive calculation of reduced wave function */
A=ETA/(K+1) ;
B=A/K+RHO1 ;
A=(1+A*A)/(2*K+3) ;
CALL NUM(A,AEX) ;
CALL NUM(B,BEX) ;
MM=R; MMEX=REX ;
CALL MULT(A,AEX,MM,MMEX,R,REX) ;
MM=-R; MMEX=REX;
CALL ADD(B,BEX,MM,MMEX,R,REX) ;
KK=2*K-1;
CALL NUM(KK,KKEX) ;
MM=R; MMEX=REX;
CALL MULT(KK,KKEX,MM,MMEX,R,REX);
MM=R; MMEX=REX;
CALL DIV(EINS,NULL,MM,MMEX,R,REX) ;
CALL MULT(R,REX,LAM(K),LAMEX(K),RL,RLEX) ;
CALL MULT(R,REX,S,SEX,SL,SLEX) ;
CALL ADD(RL,RLEX,SL,SLEX,S,SEX) ;
IF K<=N THEN DO ;
RR(K-1)=R; RREX(K-1)=REX ;
END ;
END ;
CALL ADD(EINS,NULL,S,SEX,S1,S1EX) ;
CALL DIV(SUM,SUMEX,S1,S1EX,F(0),FEX(0)) ;
IF N>0 THEN DO ;
DO K=0 TO N-1 ;
MM=F(K); MMEX=FEX(K);
CALL MULT(RR(K),RREX(K),MM,MMEX,F(K+1),FEX(K+1)) ;
END ;
END ;
DO K=0 TO N ; /* Check for convergence and repeat recursion if required */
IF ABS(F(K)-FAP(K))<EPS*ABS(F(K))&FEX(K)=FAPEX(K) THEN GO TO CC1;
ELSE DO ;
DO K1=0 TO N ;
FAP(K1)=F(K1) ; FAPEX(K1)=FEX(K1) ;
END ;
NU1=NU; NU=NU+DNU ;
GO TO C1 ;
END ;
CC1: END ; /* End iteration loop */
A=4*PI2*ETA ; /* Calculation of wave function from reduced wave function */
EPS=EPS/10 ; D2=D+1 ;
IF ABS(A)<0.75 THEN DO ;
CALL NUM(A,AEX) ;
SUM=1; SUMEX=0; K=1; R=1; REX=0;
C2: K=K+1; KK=K ;
CALL NUM(KK,KKEX) ;
MM=R; MMEX=REX ;
CALL MULT(MM,MMEX,A,AEX,R,REX) ;
MM=R; MMEX=REX;
CALL DIV(MM,MMEX,KK,KKEX,R,REX) ;
MM=SUM; MMEX=SUMEX;
CALL ADD(MM,MMEX,R,REX,SUM,SUMEX) ;
IF REX>SUMEX-D2 THEN GO TO C2;
SUME=SUMEX ;
SUM=SUM*10**SUME ;
END ;
ELSE SUM=(EXP(A)-1)/A ;
A=SQRT(1/SUM) ;
CALL NUM(A,AEX) ;
MM=F(0); MMEX=FEX(0) ;
CALL MULT(A,AEX,MM,MMEX,F(0),FEX(0)) ;
IF N>0 THEN DO ;
DO K=1 TO N ;
R=1E0/K ;
B=ETA*R ;
R1=2-R; R2=2+R;
SQB=SQRT(1+B*B);
CALL NUM(SQB,SQBEX) ;
MM=A; MMEX=AEX;
CALL MULT(MM,MMEX,R1,R1EX,A,AEX);
MM=A; MMEX=AEX;
CALL MULT(MM,MMEX,SQB,SQBEX,A,AEX);
MM=A; MMEX=AEX;
CALL DIV(MM,MMEX,R2,R2EX,A,AEX);
MM=F(K); MMEX=FEX(K);
CALL MULT(A,AEX,MM,MMEX,F(K),FEX(K)) ;
END ;
END ;
GO TO C4 ;
C3: DO K=0 TO N ;
F(K)=0 ;
END ;
C4: DO K=0 TO LMAX+1 ;
FEXK=FEX(K) ;
F(K)=SQPI2*F(K)*10**FEXK ;
END ;
DO K=0 TO LMAX BY DL ; /* Store results for desired angular momenta */
IF K=0 THEN FK1=0; ELSE FK1=F(K-1) ;
PUT FILE(OUTFILE) EDIT(FK1,F(K+1)) (SKIP,2 E(FNDIG+7,FNDIG)) ;
END ;
FREE LAM,LAMEX ;
END ; /* End radius point loop */
IF LRMOD='FILE' THEN DO ; /* Store maximum radius point for next run */
OPEN FILE(OLRFILE); PUT FILE(OLRFILE) EDIT(URF) (SKIP,E(22,15)) ;
END ;
/* Internal Subroutines */
T: PROCEDURE ; /* For calculation of maximum recursion index */
IF TS<=0 THEN TT=0.36788+1.0422*SQRT(TS+0.36788) ;
ELSE DO ;
IF TS<=10 THEN DO ;
TP=(0.1*TS-0.00176148)*TS+0.0208645 ;
TP=(TP*TS-0.129013)*TS+0.85777 ;
TT=TP*TS+1.0125 ;
END ;
ELSE DO ;
TZ=LOG(TS)-0.775 ;
TP=(0.775-LOG(TZ))/(1+TZ) ;
TT=TS/(TZ*(1+TP)) ;
END ;
END ;
END T ;
ENDE: END COULOMBC2 ;
/***********************************************************************************
* The subroutines NUM, ADD, MULT, DIV enable basic arithmetic operations of numbers
* with exponents up to ±1015.
* NUM separates usual floating point numbers (X) into a mantissa (X) and exponent (XEX),
* and ADD, MULT, DIV provide the basic arithmetic operations of these representations,
* where XM,XEX,YM,YEX are the two input numbers and XYM, XYEX the result.
***********************************************************************************/
NUM: PROCEDURE(X,XEX) ;
DEFAULT RANGE(A:Z) DEC FLOAT VALUE(DEC FLOAT(16)) ;
DCL X DEC FLOAT(16) ;
DCL XEX DEC FIXED(15,0) ;
LAX=LOG10(ABS(X)) ;
XEX=FLOOR(LAX) ;
X=10**(LAX-XEX)*SIGN(X) ;
END NUM ;
ADD: PROCEDURE(XM,XEX,YM,YEX,XYM,XYEX) ;
DEFAULT RANGE(A:Z) DEC FLOAT VALUE(DEC FLOAT(16)) ;
DCL (XM,YM,XYM) DEC FLOAT(16) ;
DCL (XEX,YEX,XYEX) DEC FIXED(15,0) ;
IF XM=0&YM=0 THEN DO;
XYM=0; XYEX=0;
RETURN;
END ;
IF XM=0 THEN DO;
XYM=YM; XYEX=YEX;
RETURN;
END ;
IF YM=0 THEN DO;
XYM=XM; XYEX=XEX;
RETURN;
END ;
IF XEX>=YEX THEN DO ;
XYEX=XEX ;
XYM=YEX-XEX; XYME=XYM;
IF XYM<-60 THEN XYM=0; ELSE XYM=10**XYME ;
XYM=XM+YM*XYM ;
END ;
ELSE DO ;
XYEX=YEX;
XYM=XEX-YEX; XYME=XYM;
IF XYM<-60 THEN XYM=0; ELSE XYM=10**XYME ;
XYM=XM*XYM+YM ;
END ;
IF ABS(XYM)>=10 THEN DO ;
XYM=XYM/10; XYEX=XYEX+1 ;
END ;
IF ABS(XYM)<1 THEN DO;
XYM=XYM*10; XYEX=XYEX-1;
END ;
END ADD ;
MULT: PROCEDURE(XM,XEX,YM,YEX,XYM,XYEX) ;
DEFAULT RANGE(A:Z) DEC FLOAT VALUE(DEC FLOAT(16)) ;
DCL (XM,YM,XYM) DEC FLOAT(16) ;
DCL (XEX,YEX,XYEX) DEC FIXED(15,0) ;
IF XM=0|YM=0 THEN DO ;
XYM=0; XYEX=0;
RETURN;
END ;
XYM=XM*YM ;
XYEX=XEX+YEX ;
IF ABS(XYM)>=10 THEN DO ;
XYM=XYM/10; XYEX=XYEX+1 ;
END ;
END MULT ;
DIV: PROCEDURE(XM,XEX,YM,YEX,XYM,XYEX) ;
DEFAULT RANGE(A:Z) DEC FLOAT VALUE(DEC FLOAT(16)) ;
DCL (XM,YM,XYM) DEC FLOAT(16) ;
DCL (XEX,YEX,XYEX) DEC FIXED(15,0) ;
IF XM=0 THEN DO;
XYM=0; XYEX=0;
RETURN;
END ;
XYM=XM/YM ;
XYEX=XEX-YEX ;
IF ABS(XYM)<1 THEN DO ;
XYM=XYM*10; XYEX=XYEX-1 ;
END ;
END DIV ;
|