/* GRASP: Copyright 1997,1998 Bruce Allen */ /* This code written by Jolien Creighton, jolien@tapir.caltech.edu */ #include "grasp.h" extern double log(double); static char *rcsid="$Id: qn_ringdown.c,v 1.4 1998/01/23 17:59:48 ballen Exp $\ \n$Name: $"; /* preprocessor macros */ #define CZERO Complex(0,0) #define CUNIT Complex(1,0) #define CIMAG Complex(0,1) #define CRE(x) Complex((x),0) #define CIM(y) Complex(0,(y)) #define CNEG(z) RCmul(-1,(z)) #define CINV(z) Cdiv(CUNIT,(z)) #define TWOPI (2.0*M_PI) #define TINY 1.e-30 #define EPS 1.e-7 #define SMALL 1.e-6 #define MAXIT 500 #define NEIGEN 50 #define CTINY Complex(TINY,0) static float sqrarg; #define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg) static int imaxarg1,imaxarg2; #define IMAX(a,b) (imaxarg1=(a),imaxarg2=(b),(imaxarg1) > (imaxarg2) ?\ (imaxarg1) : (imaxarg2)) static int iminarg1,iminarg2; #define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ?\ (iminarg1) : (iminarg2)) /* type definitions */ typedef struct FCOMPLEX {float r,i;} fcomplex; typedef struct QNMODE {int l,m,n,s,sign; float a;} qnmode; typedef struct QNEIGEN {fcomplex A,omega;} qneigen; typedef struct QNEIGENTAB {int l,m,s,num; qneigen *pos,*neg;} qneigentab; /* complex function declarations */ fcomplex Cprod(int n, ...); fcomplex Csum(int n, ...); fcomplex Cexp(fcomplex z); float Csqmod(fcomplex z); float Carg(fcomplex z); /* complex function declarations */ fcomplex Cadd(fcomplex a, fcomplex b); fcomplex Csub(fcomplex a, fcomplex b); fcomplex Cmul(fcomplex a, fcomplex b); fcomplex Complex(float re, float im); fcomplex Conjg(fcomplex z); fcomplex Cdiv(fcomplex a, fcomplex b); float Cabs(fcomplex z); fcomplex Csqrt(fcomplex z); fcomplex RCmul(float x, fcomplex a); /* qn function declarations */ fcomplex qn_sphwf(float mu, qnmode mode, qneigen eigen); fcomplex qn_sphwf0(float mu, fcomplex norm, qnmode mode, qneigen eigen); float qn_sphwf0_sqmod(float mu); fcomplex qn_sphwf_norm(qnmode mode, qneigen eigen); fcomplex Ccfrac(void (*coef)(int, fcomplex *, fcomplex *)); void qn_sph_Ccfrac_coef(int n, fcomplex *a, fcomplex *b); void qn_ang_Ccfrac_coef(int n, fcomplex *a, fcomplex *b); void qn_rad_Ccfrac_coef(int n, fcomplex *a, fcomplex *b); void qn_sph_coef(int n, qnmode mode, qneigen eigen, fcomplex *alp, fcomplex *bet, fcomplex *gam); void qn_ang_coef(int n, qnmode mode, qneigen eigen, fcomplex *alp, fcomplex *bet, fcomplex *gam); void qn_rad_coef(int n, qnmode mode, qneigen eigen, fcomplex *alp, fcomplex *bet, fcomplex *gam); void qn_axi_vecfunc(int n, float x[], float fvec[]); void qn_sph_vecfunc(int n, float x[], float fvec[]); qneigen qn_eigen_solve(qneigen guess, qnmode mode); void qn_make_eigentab(qneigentab *eigentab); void qn_print_eigentab(qneigentab eigentab); qneigen qn_eigen_seek(qneigentab eigentab, qnmode mode); qneigen qn_eigen(qnmode mode); qnmode qn_mode(float a, int s, int l, int m); /* metric routines */ int qn_set_grid(float dl, struct qnScope grid, int flag); /* other routines */ void error(char *fmt, ...); int warning(char *fmt, ...); /* global variables */ qnmode _mode; qneigen _eigen; /* complex function definitions */ fcomplex Cprod(int n, ...) { int i; va_list ap; fcomplex prod,fact; va_start(ap,n); prod = CUNIT; for (i=0;i= fabs(b.i)) { r=b.i/b.r; den=b.r+r*b.i; c.r=(a.r+r*a.i)/den; c.i=(a.i-r*a.r)/den; } else { r=b.r/b.i; den=b.i+r*b.r; c.r=(a.r*r+a.i)/den; c.i=(a.i*r-a.r)/den; } return c; } float Cabs(fcomplex z) { float x,y,ans,temp; x=fabs(z.r); y=fabs(z.i); if (x == 0.0) ans=y; else if (y == 0.0) ans=x; else if (x > y) { temp=y/x; ans=x*sqrt(1.0+temp*temp); } else { temp=x/y; ans=y*sqrt(1.0+temp*temp); } return ans; } fcomplex Csqrt(fcomplex z) { fcomplex c; float x,y,w,r; if ((z.r == 0.0) && (z.i == 0.0)) { c.r=0.0; c.i=0.0; return c; } else { x=fabs(z.r); y=fabs(z.i); if (x >= y) { r=y/x; w=sqrt(x)*sqrt(0.5*(1.0+sqrt(1.0+r*r))); } else { r=x/y; w=sqrt(y)*sqrt(0.5*(r+sqrt(1.0+r*r))); } if (z.r >= 0.0) { c.r=w; c.i=z.i/(2.0*w); } else { c.i=(z.i >= 0) ? w : -w; c.r=z.i/(2.0*c.i); } return c; } } fcomplex RCmul(float x, fcomplex a) { fcomplex c; c.r=x*a.r; c.i=x*a.i; return c; } /* qn functions */ fcomplex qn_sphwf(float mu, qnmode mode, qneigen eigen) { fcomplex norm; norm = qn_sphwf_norm(mode,eigen); return qn_sphwf0(mu,norm,mode,eigen); } fcomplex qn_sphwf0(float mu, fcomplex norm, qnmode mode, qneigen eigen) { int n=0,max=MAXIT,s=mode.s,m=mode.m; float k1=0.5*abs(m-s),k2=0.5*abs(m+s); float fact=1+mu,prod=fact; fcomplex sum,alp,bet,gam,a,aa,a_,Delta; sum = a_ = norm; qn_ang_coef(n,mode,eigen,&alp,&bet,&gam); a = Cdiv(Cmul(bet,a_),CNEG(alp)); while (n++=max) warning("qn_sphwf0(): maximum iterations=%d exceeded. mu=%f",max,mu); sum = RCmul(pow(1+mu,k1)*pow(1-mu,k2),sum); sum = Cmul(Cexp(RCmul(mu*mode.a,eigen.omega)),sum); return sum; } float qn_sphwf0_sqmod(float mu) { return Csqmod(qn_sphwf0(mu,CUNIT,_mode,_eigen)); } fcomplex qn_sphwf_norm(qnmode mode, qneigen eigen) { float qromb(float (*func)(float), float, float); fcomplex norm,S0; int sign,q; _mode = mode; _eigen = eigen; S0 = qn_sphwf0(0.0,CUNIT,mode,eigen); norm = Cexp(CIM(-Carg(S0))); S0 = qn_sphwf0(-1.0+EPS,norm,mode,eigen); (S0.r < 0.0) ? (sign = -1) : (sign = 1); (mode.m > mode.s) ? (q = mode.m) : (q = mode.s); (((mode.l - q)%2)==0) ? (sign *= 1) : (sign *= -1); norm = RCmul(sign/sqrt(qromb(qn_sphwf0_sqmod,-1.0,1.0)),norm); return norm; } fcomplex Ccfrac(void (*coef)(int, fcomplex *, fcomplex *)) { int n=0,max=MAXIT; fcomplex a,b,f,f_,C,C_,D,D_,Delta; (*coef)(n,&a,&b); if (Cabs(f=b)0) into array */ x[0] = guess.omega.r; x[1] = guess.omega.i; x[2] = guess.A.r; x[3] = guess.A.i; /* solve for the root */ mode.a == 0.0 ? newt(x-1,2,&check,qn_sph_vecfunc) : newt(x-1,4,&check,qn_axi_vecfunc); if (check) warning("qn_eigen_solve(): local minimum found."); /* store array values in eigen */ if (mode.m<0) { eigen.omega = Complex(-x[0],x[1]); eigen.A = Complex(x[2],-x[3]); } else { eigen.omega = Complex(x[0],x[1]); eigen.A = Complex(x[2],x[3]); } return eigen; } void qn_make_eigentab(qneigentab *eigentab) { qnmode mode; float fact=1.0/sqrt(27),da=0.5/(*eigentab).num; int i,l=(*eigentab).l,m=(*eigentab).m,s=(*eigentab).s,num=(*eigentab).num; /* allocate memory if necessary */ if ((*eigentab).pos==NULL) if (((*eigentab).pos=(qneigen *)malloc(num*sizeof(qneigen)))==NULL) error("qn_make_eigentab(): could not allocate %d bytes of memory.",num*sizeof(qneigen)); if ((*eigentab).neg==NULL) if (((*eigentab).neg=(qneigen *)malloc(num*sizeof(qneigen)))==NULL) error("qn_make_eigentab(): could not allocate %d bytes of memory.",num*sizeof(qneigen)); /* guesses for the eigenvalues for a=0 */ (*eigentab).pos[0].omega = RCmul(fact,Complex(2*l+1,-1)); (*eigentab).neg[0].omega = RCmul(fact,Complex(-2*l+1,-1)); (*eigentab).pos[0].A = (*eigentab).neg[0].A = Complex(l*(l+1)-s*(s+1),0); /* assign the mode fields */ mode.s = s; mode.l = l; mode.m = m; mode.n = 1; /* n=1: fundamental */ mode.a = 0; /* construct the table */ (*eigentab).pos[0] = qn_eigen_solve((*eigentab).pos[0],mode); (*eigentab).neg[0] = qn_eigen_solve((*eigentab).neg[0],mode); for (i=1;i 0) ? (guess = eigentab.pos[i]) : (guess = eigentab.neg[i]); return qn_eigen_solve(guess,mode); } qneigen qn_eigen(qnmode mode) { static qneigentab eigentab; static int first=1; if (first) { /* create eigen table */ first = 0; eigentab.s = mode.s; eigentab.l = mode.l; eigentab.m = abs(mode.m); eigentab.num = NEIGEN; eigentab.pos = NULL; eigentab.neg = NULL; qn_make_eigentab(&eigentab); } if ((mode.s != eigentab.s) || (mode.l != eigentab.l) || (abs(mode.m) != eigentab.m) || (mode.n != 1)) { /* reset eigen table */ eigentab.s = mode.s; eigentab.l = mode.l; eigentab.m = abs(mode.m); qn_make_eigentab(&eigentab); } return qn_eigen_seek(eigentab,mode); } qnmode qn_mode(float a, int s, int l, int m) { qnmode mode; mode.l = l; mode.m = m; mode.n = 1; /* fundamental mode */ mode.s = s; (a<0) ? (mode.sign=-1) : (mode.sign=1); mode.a = 0.5*fabs(a); /* factor of 0.5 to convert to Leaver */ return mode; } void qn_eigenvalues(float eigenvalues[], float a, int s, int l, int m) { qneigen eigen; qnmode mode; mode = qn_mode(a,s,l,m); eigen = qn_eigen(mode); eigenvalues[0] = 0.5*eigen.omega.r;/* factor of 0.5 to convert from Leaver */ eigenvalues[1] = 0.5*eigen.omega.i;/* factor of 0.5 to convert from Leaver */ eigenvalues[2] = eigen.A.r; eigenvalues[3] = eigen.A.i; } void sw_spheroid(float *re, float *im, float mu, int reset, float a, int s, int l, int m, float eigenvalues[]) { static fcomplex norm; static qneigen eigen; static qnmode mode; fcomplex spher; if (reset) { mode = qn_mode(a,s,l,m); /* Leaver eigenvalues: factor of two to convert to Leaver's convention */ eigen.omega = RCmul(2,Complex(eigenvalues[0],eigenvalues[1])); eigen.A = Complex(eigenvalues[2],eigenvalues[3]); norm = qn_sphwf_norm(mode,eigen); } spher = qn_sphwf0(mu,norm,mode,eigen); *re = spher.r; *im = spher.i; return; } int qn_ring(float theta, float phi, float eps, float M, float a, int l, int m, float du, float atten, int max, float **plusPtr, float **crossPtr) { qnmode mode; qneigen eigen; fcomplex omega,strain,S,c1,c2; int i,ndat=0,s=-2; float u,mu=cos(theta),efold=0.1*atten*log(10); if (ll) error("qn_ring(): abs(m=%d) > l=%d",m,l); if (fabs(a)>=1) error("qn_ring(): abs(a=%f) >= 1",a); if ((*plusPtr)==NULL) if (((*plusPtr)=(float *)malloc((size_t)(max*sizeof(float))))==NULL) error("qn_ring(): could not allocate %d bytes of memory.",max*sizeof(float)); if ((*crossPtr)==NULL) if (((*crossPtr)=(float *)malloc((size_t)(max*sizeof(float))))==NULL) error("qn_ring(): could not allocate %d bytes of memory.",max*sizeof(float)); mode = qn_mode(a,s,l,m); eigen = qn_eigen(mode); omega = RCmul(0.5,eigen.omega); S = qn_sphwf(mu,mode,eigen); c1 = RCmul(-4.0*sqrt(-omega.i*eps)*M/Cabs(omega),Cmul(S,Cexp(CIM(m*phi)))); c2 = RCmul(-1.0/(M*TSOLAR),Cmul(CIMAG,omega)); ndat = IMIN(max,1+(int)(efold*M*TSOLAR/(-omega.i*du))); for (i=0,u=0;i>1; float sum; sum = q[0]*q[0]*r[0]; for (j=1;j