subroutine nsatmos (ear,near,param,ifl,photar,photer) implicit none INTEGER near, ifl REAL ear(0:near),param(4),photar(near),photer(near) c Subroutine for use with XSPEC c Outputs a model NS atmosphere spectrum as calculated by George c Rybicki for given input parameters c The array ear(0:near) contains the energy values of the bins c The array param(4) gives the input parameter values: c param(1) = log(effective temperature) at the surface c param(2) = Mass of the NS in solar mass units c param(3) = Radius in km c param(4) = Distance to the NS in kpc c The output spectrum, in counts per bin, is given in photar (near) c The subroutine reads in a grid of model spectra from the datafile c nsatmos.dat in the first pass. It then saves the information for c later passes. INTEGER nparT, nparg, nparm, nfreq parameter (nparT=11,nparg=26,nparm=8,nfreq=64) REAL Tlog(nparT),gravlog(nparg),critmu(nparm), & EkeV(nfreq),fluxl(nparT,nparg,nparm,nfreq) REAL flTg(2,2,nfreq),flT(2,nfreq),fl(nfreq),Eshft(nfreq), & countsl(nfreq),countsl2(nfreq) REAL Tl, eMsun, RS, radius, RoverRS, Dkpc, RoverDsql REAL zred1, gravity, glog, sinthetac, cmu, wTlo, wThi REAL wglo, wghi, wclo, wchi, Tfactor, fluxshift REAL fluxfactorlog, countsfactorlog, deriv1, derivN REAL e1, e2, e3, e4, e5 REAL c1, c2, c3, c4, c5 INTEGER ilun, nread, nmu, nf, ngrav, ntemp INTEGER n, l, i, j, k, iT, ig, ic, ios, lfil character datdir*255, filenm*255, outstr*255, pname*128 integer lenact character fgmodf*255, fgmstr*255 external lenact, fgmodf, fgmstr save Tlog,gravlog,critmu,EkeV,fluxl,nTemp,ngrav,nmu,nf,nread data nread/0/ c Open and read nsatmos.dat the first time round. The information is saved if (nread.ne.99) then pname = 'NSATMOS_FILE' filenm = fgmstr(pname) lfil = lenact(filenm) IF ( lfil .EQ. 0 ) THEN datdir = fgmodf() filenm = datdir(:lenact(datdir))//'nsatmos.dat' ENDIF CALL getlun(ilun) CALL openwr(ilun, filenm, 'old', ' ', ' ', 0, 0, ios) IF ( ios .NE. 0 ) THEN outstr = 'NSATMOS : failed to open '// & filenm(:lenact(filenm)) CALL xwrite(outstr, 10) RETURN ENDIF outstr = 'Opened and reading '// & filenm(:lenact(filenm)) CALL xwrite(outstr, 10) rewind (ilun) c nTemp is the number of temperatures covered by the grid and c Tlog(n) are the corresponding log(temperature) values read (ilun,*,iostat=ios) nTemp IF ( ios .NE. 0 ) THEN outstr = 'NSATMOS : failed to read '// & filenm(:lenact(filenm)) CALL xwrite(outstr, 10) RETURN ENDIF if (nTemp.gt.nparT) then CALL xwrite(' Too many temperatures !! ', 10) RETURN endif read (ilun,*) (Tlog(n),n=1,nTemp) c ngrav is the number of gravities covered by the grid and c gravlog(n) are the corresponding log(gravity) values read (ilun,*) ngrav if (ngrav.gt.nparg) then CALL xwrite(' Too many gravities !! ', 10) RETURN endif read (ilun,*) (gravlog(n),n=1,ngrav) c nmu is the number of mu_crit values covered by the grid and c critmu(n) are the corresponding mu_crit values read (ilun,*) nmu if (nmu.gt.nparm) then CALL xwrite(' Too many mu_crits !! ', 10) RETURN endif read (ilun,*) (critmu(n),n=1,nmu) c nf is the number of frequencies, or photon energies, in the model c spectra and EkeV(l) are the photon energies in keV read (ilun,*) nf if (nf.gt.nfreq) then CALL xwrite (' Too many frequencies !! ', 10) RETURN endif read (ilun,*) (EkeV(l),l=1,nf) c Read in model spectra [fluxl(i,j,k,l), l=1,nf] for the entire grid c of models. Note that all the spectra have been artifically scaled c to a nominal temperature of 10^6 K to enable more accurate c interpolation. do 10 i=1,nTemp do 10 j=1,ngrav do 10 k=1,nmu read (ilun,*) (fluxl(i,j,k,l),l=1,nf) 10 continue close (ilun) CALL frelun(ilun) nread=99 endif c Identify parameter values and calculate various related quantities c RoverRS is the stellar radius in Schwarzschild units c zred1 = 1+z (redshift) c glog = log(gravity) in cgs units c cmu = mu_crit for the current model Tl=param(1) eMsun=param(2) RS=2.95e5*eMsun radius=1.e5*param(3) RoverRS=radius/RS if (RoverRS.lt.1.125) go to 170 Dkpc=param(4) RoverDsql=2.*log10(radius/(3.09e21*Dkpc)) zred1=1./sqrt(1.-(1./RoverRS)) gravity=(6.67e-8*1.99e33*eMsun/radius**2)*zred1 glog=log10(gravity) if (RoverRS.lt.1.5e0) then sinthetac=2.598/(RoverRS*zred1) cmu=sqrt(1.-sinthetac**2) cmu=sqrt(1.-6.75/RoverRS**2+6.75/RoverRS**3) else cmu=0.d0 endif c write (*,*) ' radius, 1+z, glog, mucrit = ',radius,zred1,glog,cmu c Find location of the current parameters within the grid of models, c and compute weights for linear interpolation in the grid. The c model lies between iT and iT+1 along the temperature axis, between c ig and ig+1 along the gravity axis, and between ic and ic+1 along c the mu_crit axis. if (Tl.lt.Tlog(1)) then iT=1 elseif (Tl.ge.Tlog(nTemp)) then iT=nTemp-1 else do 20 i=1,nTemp-1 if (Tl.ge.Tlog(i).and.Tl.lt.Tlog(i+1)) then iT=i go to 30 endif 20 continue endif 30 continue wTlo=(Tlog(iT+1)-Tl)/(Tlog(iT+1)-Tlog(iT)) wThi=1.-wTlo if (glog.lt.gravlog(1)) then ig=1 elseif (glog.ge.gravlog(ngrav)) then ig=ngrav-1 else do 40 i=1,ngrav-1 if (glog.ge.gravlog(i).and.glog.lt.gravlog(i+1)) then ig=i go to 50 endif 40 continue endif 50 continue wglo=(gravlog(ig+1)-glog)/(gravlog(ig+1)-gravlog(ig)) wghi=1.-wglo if (RoverRS.ge.1.5) then ic=1 elseif (cmu.ge.critmu(nmu)) then ic=nmu-1 else do 60 i=1,nmu-1 if (cmu.ge.critmu(i).and.cmu.lt.critmu(i+1)) then ic=i go to 70 endif 60 continue endif 70 continue wclo=(critmu(ic+1)-cmu)/(critmu(ic+1)-critmu(ic)) wchi=1.-wclo c Interpolate first on mu_crit. Since most often we expect c mu_crit=0 (corresponding to R/RS >= 1.5), we check for this first. if (RoverRS.ge.1.5) then do 80 l=1,nf flTg(1,1,l)=fluxl(iT,ig,1,l) flTg(1,2,l)=fluxl(iT,ig+1,1,l) flTg(2,1,l)=fluxl(iT+1,ig,1,l) flTg(2,2,l)=fluxl(iT+1,ig+1,1,l) 80 continue else do 90 l=1,nf flTg(1,1,l)=wclo*fluxl(iT,ig,ic,l)+ & wchi*fluxl(iT,ig,ic+1,l) flTg(1,2,l)=wclo*fluxl(iT,ig+1,ic,l)+ & wchi*fluxl(iT,ig+1,ic+1,l) flTg(2,1,l)=wclo*fluxl(iT+1,ig,ic,l)+ & wchi*fluxl(iT+1,ig,ic+1,l) flTg(2,2,l)=wclo*fluxl(iT+1,ig+1,ic,l)+ & wchi*fluxl(iT+1,ig+1,ic+1,l) 90 continue endif c Interpolate next on glog do 100 l=1,nf flT(1,l)=wglo*flTg(1,1,l)+wghi*flTg(1,2,l) flT(2,l)=wglo*flTg(2,1,l)+wghi*flTg(2,2,l) 100 continue c Finally interpolate on Tlog do 110 l=1,nf fl(l)=wTlo*flT(1,l)+wThi*flT(2,l) 110 continue c The spectrum fl(l) calculated above gives flux/keV scaled c artifically to log(T)=6.0. Shift the photon energies and fluxes c back to the correct local temperature Tfactor=10.**Tl/1.e6 fluxshift=3.*log10(Tfactor) do 120 l=1,nf Eshft(l)=EkeV(l)*Tfactor fl(l)=fl(l)+fluxshift 120 continue c Next shift to infinity by applying the appropriate redshift c factor. The photon energy is divided by (1+z). The flux is also c divided by (1+z) because the luminosity goes down by (1+z)^2. fluxshift=-log10(zred1) do 130 l=1,nf Eshft(l)=Eshft(l)/zred1 fl(l)=fl(l)+fluxshift 130 continue c Convert to counts/keV (which corresponds to dividing by c 1.602e-9*EkeV), multiply by the area of the star, and calculate c the count rate at the observer fluxfactorlog=RoverDsql countsfactorlog=-log10(1.602e-9) do 140 l=1,nf fl(l)=fl(l)+fluxfactorlog countsl(l)=fl(l)+countsfactorlog-log10(Eshft(l)) 140 continue c Calculate the spline coefficients countsl2 for the array countsl deriv1=1.e32 derivN=1.e32 call NRspline (Eshft,countsl,nf,deriv1,derivN,countsl2) c We are finally ready to calculate the counts per bin corresponding c to the input energy array ear(0:near). We use Simpson's c five-point formula. This is probably overkill --- trapozoidal c rule should be enough --- but we do it just to be safe. do 160 i=1,near c Calculate the energies and count rates at five equally spaced c points within the current energy bin e1=ear(i-1) if (e1.lt.Eshft(1).or.e1.gt.Eshft(nf)) then c1=0. else call NRsplint(Eshft,countsl,countsl2,nf,e1,c1) c1=10.**c1 endif e2=0.75*ear(i-1)+0.25*ear(i) if (e2.lt.Eshft(1).or.e2.gt.Eshft(nf)) then c2=0. else call NRsplint(Eshft,countsl,countsl2,nf,e2,c2) c2=10.**c2 endif e3=0.5*ear(i-1)+0.5*ear(i) if (e3.lt.Eshft(1).or.e3.gt.Eshft(nf)) then c3=0. else call NRsplint(Eshft,countsl,countsl2,nf,e3,c3) c3=10.**c3 endif e4=0.25*ear(i-1)+0.75*ear(i) if (e4.lt.Eshft(1).or.e4.gt.Eshft(nf)) then c4=0. else call NRsplint(Eshft,countsl,countsl2,nf,e4,c4) c4=10.**c4 endif e5=ear(i) if (e5.lt.Eshft(1).or.e5.gt.Eshft(nf)) then c5=0. else call NRsplint(Eshft,countsl,countsl2,nf,e5,c5) c5=10.**c5 endif c Simpson's five-point formula for the integral within the bin photar(i)=(14.*(c1+c5)+64.*(c2+c4)+24.*c3)* & (ear(i)-ear(i-1))/180. 160 continue return 170 do 180 i=1,near photar(i)=1.e10 180 continue return end c Compute spline interpolation coefficients: from Numerical Recipes, c by Press, Flannery, Teukolsky, Vetterling SUBROUTINE NRspline(x,y,n,yp1,ypn,y2) IMPLICIT NONE INTEGER n,NMAX REAL yp1,ypn,x(n),y(n),y2(n) PARAMETER (NMAX=500) INTEGER i,k REAL p,qn,sig,un,u(NMAX) if (yp1.gt..99e30) then y2(1)=0. u(1)=0. else y2(1)=-0.5 u(1)=(3./(x(2)-x(1)))*((y(2)-y(1))/(x(2)-x(1))-yp1) endif do 11 i=2,n-1 sig=(x(i)-x(i-1))/(x(i+1)-x(i-1)) p=sig*y2(i-1)+2. y2(i)=(sig-1.)/p u(i)=(6.*((y(i+1)-y(i))/(x(i+ *1)-x(i))-(y(i)-y(i-1))/(x(i)-x(i-1)))/(x(i+1)-x(i-1))-sig* *u(i-1))/p 11 continue if (ypn.gt..99e30) then qn=0. un=0. else qn=0.5 un=(3./(x(n)-x(n-1)))*(ypn-(y(n)-y(n-1))/(x(n)-x(n-1))) endif y2(n)=(un-qn*u(n-1))/(qn*y2(n-1)+1.) do 12 k=n-1,1,-1 y2(k)=y2(k)*y2(k+1)+u(k) 12 continue return END c Carry out spline interpolation using previously computed c coefficients: from Numerical Recipes, by Press, Flannery, c Teukolsky, Vetterling SUBROUTINE NRsplint(xa,ya,y2a,n,x,y) INTEGER n REAL x,y,xa(n),y2a(n),ya(n) INTEGER k,khi,klo REAL a,b,h klo=1 khi=n 1 if (khi-klo.gt.1) then k=(khi+klo)/2 if(xa(k).gt.x)then khi=k else klo=k endif goto 1 endif h=xa(khi)-xa(klo) if (h.eq.0.) pause 'bad xa input in splint' a=(xa(khi)-x)/h b=(x-xa(klo))/h y=a*ya(klo)+b*ya(khi)+((a**3-a)*y2a(klo)+(b**3-b)*y2a(khi))*(h** *2)/6. return END