def getNz(numDeps, temp, pGas, pe, ATot, nelemAbnd, logAz): #double[][] logNz = new double[nelemAbnd][numDeps]; logNz = [ [0.0 for i in range(numDeps)] for j in range(nelemAbnd) ] logATot = math.log(ATot) #double help, logHelp, logNumerator; for i in range(numDeps): #// Initial safety check to avoid negative logNz as Pg and Pe each converge: #// maximum physical Pe is about 0.5*PGas (complete ionization of pure H): if (pe[0][i] > 0.5 * pGas[0][i]): pe[0][i] = 0.5 * pGas[0][i] pe[1][i] = math.log(pe[0][i]) #// H (Z=1) is a special case: N_H(tau) = (Pg(tau)-Pe(tau))/{kTk(tau)A_Tot} logHelp = pe[1][i] - pGas[1][i] help = 1.0 - math.exp(logHelp) logHelp = math.log(help) logNumerator = pGas[1][i] + logHelp logNz[0][i] = logNumerator - Useful.logK() - temp[1][i] - logATot #// Remaining elements: for j in range(nelemAbnd): #// N_z = A_z * N_H: logNz[j][i] = logAz[j] + logNz[0][i] return logNz
def phxSunNe(grav, numDeps, tauRos, scaleTemp, kappaScale): logE = math.log10(math.e) logEg = math.log(grav) #//base e! logEkappaScale = math.log(kappaScale); #//Theoretical radiative/convective model from Phoenix V15: phxSunPe64 = [ 1.53086468021591745e-07, 5.66518458165471424e-03, 6.72808433760886656e-03, 8.00271552708326656e-03, 9.51809762875982208e-03, 1.13117438884935648e-02, 1.34299756939525680e-02, 1.59287848014678144e-02, 1.88751877391284448e-02, 2.23491173128862976e-02, 2.64457686695698400e-02, 3.12779350532322240e-02, 3.69791374171045888e-02, 4.37078139287801024e-02, 5.16503829681397248e-02, 6.10221573903118336e-02, 7.20768505868849536e-02, 8.51123959415642752e-02, 1.00475763241309840e-01, 1.18571138726675232e-01, 1.39870552376136714e-01, 1.64923053015554560e-01, 1.94357063774820192e-01, 2.28928720249475840e-01, 2.69525262128246720e-01, 3.17192228891198592e-01, 3.73192988074577856e-01, 4.39058414038311360e-01, 5.16615873984964544e-01, 6.08066526878471680e-01, 7.16264581324812416e-01, 8.44657163125294336e-01, 9.97267452897639808e-01, 1.17915717019238848e+00, 1.39715732004723136e+00, 1.66026825646718432e+00, 1.97886823850223904e+00, 2.36716912384854112e+00, 2.84540915928013805e+00, 3.44853013665125120e+00, 4.21529199485384704e+00, 5.21488490421314560e+00, 6.56660005867586432e+00, 8.55643059606379776e+00, 1.16931723772200080e+01, 1.71629079266534368e+01, 2.75152019254691616e+01, 4.18720694941323264e+01, 7.66283674228108288e+01, 1.45995186997127872e+02, 3.04766672331673792e+02, 5.44151864837275328e+02, 8.17181982032739072e+02, 1.11216222784450608e+03, 1.43633935534913856e+03, 1.79603721463325728e+03, 2.19692608617747040e+03, 2.64548745663525184e+03, 3.14931730610757952e+03, 3.71721361233669376e+03, 4.35932065708395904e+03, 5.08736399892079296e+03, 5.91634943413070720e+03, 6.85104524590000384e+03] numPhxDeps = len(phxSunPe64) #//yeah, I know, 64, but that could change! logPhxSunPe64 = [0.0 for i in range(numPhxDeps)] for i in range(len(phxSunPe64)): logPhxSunPe64[i] = math.log(phxSunPe64[i]); logPhxSunTau64 = getLogPhxSunTau64() #// interpolate onto gS3 tauRos grid and re-scale with Teff: phxSunPe = [0.0 for i in range(numDeps)] logPhxSunPe = [0.0 for i in range(numDeps)] logScalePe = [0.0 for i in range(numDeps)] scaleNe = [ [0.0 for i in range(numDeps)] for j in range(2) ] for i in range(numDeps): logPhxSunPe[i] = ToolBox.interpol(logPhxSunTau64, logPhxSunPe64, tauRos[1][i]) logScalePe[i] = logEg + logPhxSunPe[i] - phxSunLogEg() - logEkappaScale scaleNe[1][i] = logScalePe[i] - scaleTemp[1][i] - Useful.logK() scaleNe[0][i] = math.exp(scaleNe[1][i]) #//System.out.println("scaleNe[1][i] " + logE * scaleNe[1][i]); return scaleNe
def phxRefNe(numDeps, scaleTemp, scalePe): logE = math.log10(math.e) scaleNe = [[0.0 for i in range(numDeps)] for j in range(2)] #for i in range(numDeps): # scaleNe[1][i] = scalePe[1][i] - scaleTemp[1][i] - Useful.logK() # scaleNe[0][i] = math.exp(scaleNe[1][i]) scaleNe[1] = [ scalePe[1][i] - scaleTemp[1][i] - Useful.logK() for i in range(numDeps) ] scaleNe[0] = [math.exp(x) for x in scaleNe[1]] return scaleNe
def massDensity(numDeps, temp, press, mmw, zScale): """Solves the equation of state (EOS) for the mass density (rho) given total * pressure from HSE solution, for a mixture of ideal gas particles and photons * * Need to assume a mean molecular weight structure, mu(Tau) """ logE = math.log10(math.e) #// for debug output #//press is a 4 x numDeps array: #// rows 0 & 1 are linear and log *gas* pressure, respectively #// rows 2 & 3 are linear and log *radiation* pressure #// double c = 9.9989E+10; // light speed in vaccuum in cm/s #// double sigma = 5.670373E-5; //Stefan-Boltzmann constant ergs/s/cm^2/K^4 #//Row 0 of mmwNe is Mean molecular weight in amu k = Useful.k() logK = Useful.logK() amu = Useful.amu() logAmu = Useful.logAmu() #double logMuAmu #//System.out.println("STATE: logK " + logK + " logMuAmu " + logMuAmu); rho = [[0.0 for i in range(numDeps)] for j in range(2)] #// Declare scatch variables: #// double logPrad, pRad, pGas, logPgas; for i in range(numDeps): logMuAmu = math.log(mmw[i]) + logAmu #// Compute LTE bolometric radiation contribution to total HSE pressure #//logPrad = radFac + 4.0*temp[1][i] ; #//pRad = Math.exp(logPrad); #//pGas = press[0][i] - pRad; #//logPgas = Math.log(pGas); rho[1][i] = press[1][i] - temp[1][i] + (logMuAmu - logK) rho[0][i] = math.exp(rho[1][i]) #//System.out.println("i " + i + " press[1] " + logE * press[1][i] + " mmw[i] " + mmw[i] + " rho " + logE * rho[1][i]); #//System.out.println("temp " + temp[0][i] + " rho " + rho[0][i]); return rho
def dBdT(temp, lambda2): """// Computes the first partial derivative of B(T) wrt T, dB/dT:""" #double logdBdTlam; #//double c = Useful.c; //linear logC = Useful.logC() #//log #//double k = Useful.k #//linear logK = Useful.logK() #//log #//double h = Useful.h #//linear logH = Useful.logH() #//log logPreFac = math.log(2.0) + logH + 2.0 * logC #//log logExpFac = logH + logC - logK #//log #//Declare scratch variables: #double logLam, logTemp, logPreLamFac, logExpLamFac, expon, logExpon, eTerm, denom, logDenom; //log #//lambda = lambda * 1.0E-7; // convert nm to cm logLam = math.log(lambda2) #// Do the call to log for lambda once //log logTemp = math.log(temp) logPreLamFac = logPreFac + logExpFac - 6.0 * logLam - 2.0 * logTemp #//log logExpLamFac = logExpFac - logLam #//log #//This is very subtle and dangerous! logExpon = logExpLamFac - logTemp #// log of hc/kTlambda expon = math.exp(logExpon) #// hc/kTlambda eTerm = math.exp(expon) #// e^hc/ktlambda denom = eTerm - 1.0 #// e^hc/ktlambda - 1 logDenom = math.log(denom) #// log(e^hc/ktlambda - 1) logdBdTlam = logPreLamFac + expon - 2.0 * logDenom #//log return logdBdTlam; #} //end method dBdT
def convec(numDeps, tauRos, depths, temp, press, rho, kappa, kappaSun, zScale, teff, logg, mmw): logE = math.log10(math.E) #// for debug output ln10 = math.log(10.0) #//needed to convert logg from base 10 to base e convTemp = [[0.0 for i in range(numDeps)] for j in range(2)] #//Schwarzschild criterion for convective instability: gamma = 5.0 / 3.0 #//adiabatic gamma for ideal monatomic gas - the photon gas is negligible in stars w convection gammaFac = gamma / ( gamma - 1.0 ) #// yeah, yeah - I know it's 2.5, but let's show where it comes from for the record... invGamFac = 1.0 / gammaFac #//CHEAT: Set gammaThing to value that makes convection just disappear at bottom of mid-F star (7000 K) #//double gammaThing = 1.60; #//double invGamThing = 1.0 / gammaThing; #double invGamThing; #//System.out.println("gammaThing " + gammaThing); #double deltaP, deltaT; //, dlnPdlnT; #double dlnTdlnP, dlnMudlnP, deltaMu; #double Hp, logHp; #//double HpSun = 1.2535465715411615E7; //cm, as computed by GrayStar at depth index=36 HpSun = 2.0e7 #//cm, approximately as computed by GrayStar at depth index=36 logHpSun = math.log(HpSun) #//Compute the presure scale height as a reality check: HpRefDep = 36 #//index of reference depth for computing pressure scale height logHp = press[1][HpRefDep] - rho[1][HpRefDep] - ln10 * logg Hp = math.exp(logHp) #//Try scaling gamma to "fix" the convective boundary #//invGamThing = invGamFac * HpSun/Hp; #//System.out.println("Hp/HpSun " + Hp/HpSun); #//double[] mmw = State.mmwFn(numDeps, temp, zScale); #//Search outward for top of convection zone isStable = False iBound = numDeps - 1 #//initialize index of depth where convection begins to bottom of atmosphere for i in range(numDeps - 2, 0, -1): #//System.out.println("Hp " + Hp); #//1st order finite difference - erratic? #//double deltaP = press[1][i] - press[1][i-1]; #//double deltaT = temp[1][i] - temp[1][i-1]; #//Try "2nd order" finite difference - non-uniform spacing in deltaT deltaP = press[1][i + 1] - press[1][i - 1] deltaT = temp[1][i + 1] - temp[1][i - 1] deltaMu = (mmw[i + 1] - mmw[i]) * Useful.amu #//dlnPdlnT = deltaP / deltaT; dlnTdlnP = deltaT / deltaP dlnMudlnP = deltaMu / deltaP #//System.out.format("%12.8f %12.8f%n", logE * tauRos[1][i], dlnPlndT); #// This can be finicky - let's say we have not found the radiative zone unless two consecutive layers meet the criterion #//if (dlnPdlnT > gammaThing) { if (dlnTdlnP < invGamFac + dlnMudlnP): #//Convectively stable if (isStable == False): #//The previous convectively unstable layer was an isolated anomoly - we're have NOT found the zone! Reset: isStable = true iBound = i #//System.out.println("First stable layer was found, tauRos " + logE * tauRos[1][i] + " NOW: isStable " + isStable); #} #} #} #//System.out.println("Convec: iBound " + iBound); #//Radiative zone - leave temperatures alone: for i in range(iBound): convTemp[0][i] = temp[0][i] convTemp[1][i] = temp[1][i] baseTemp = temp[0][iBound] baseLogTemp = temp[1][iBound] baseTau = tauRos[0][iBound] baseLogTau = tauRos[1][iBound] #//double baseDepth = depths[iBound] logSigma = Useful.logSigma() logK = Useful.logK() logAmu = Useful.logAmu() mixLSun = 1.0 #// convective mixing length in pressure scale heights (H_P) betaSun = 0.5 #// factor for square of convective bubble velocity (range: 0.0 - 1.0) #double Cp, logCp; //Specific heat capacity at constant pressure mixL = mixLSun #//initialization beta = betaSun #//initialization teffSun = 5778.0 loggSun = 4.44 #//Shameless fix: #//It seems mixL and beta need to be temp and press dependent: if (teff < teffSun): mixL = mixLSun * math.pow( teff / teffSun, 4.0) #//lower teff -> smaller mixL -> steeper SAdGrad beta = betaSun * math.pow( teff / teffSun, 4.0) #//lower teff -> smaller beta -> steeper SAdGrad mixL = mixL * math.pow( loggSun / logg, 2.0) #// lower logg -> larger mixL -> smaller sAdGrad beta = beta * math.pow( loggSun / logg, 2.0) #// lower logg -> larger beta -> smaller sAdGrad """/* //Shameless fix: beta = betaSun; // no fix? mixL = mixLSun * Math.pow(Hp / HpSun, 4.0); //lower teff -> smaller Hp -> smaller mixL -> steeper SAdGrad //mixL = mixL * Math.pow(logg / loggSun, 4.0); // lower logg -> smaller mixL -> larger sAdGrad */""" logMixL = math.log(mixL) logBeta = math.log(beta) logFluxSurfBol = logSigma + 4.0 * math.log(teff) #// This will get hairy when we take it super-adiabatic so let's take it *really* easy and make every factor and term clear: logInvGamFac = math.log(invGamFac) #//Get the mean molecular weight in amu from State - Row 0 is "mu" in amu: #double mu, logMu, logFctr1, logFctr2, logFctr3; #double nextTemp, lastTemp, nextTemp2; #//Adiabatic dT/dx gradients in various coordinates #//tau, logTau space #double logAdGradTauMag, logAdGradLogTauMag, adGradLogTau; #//SuperAdiabatic dT/dx gradients in various coordinates #double deltaTau, logDeltaTau, deltaLogTau, logDeltaLogTau; #double sAdGradLogTau, logSadGradR, logSadGradTau, logSadGradLogTau; #double lastLogTau; #//r space: #double logAdGradRMag, adGradR; #//SuperAdiabatic dT/dx gradients in various coordinates #double deltaR, logDeltaR; #/* # double sAdGradR; # double lastDepth; # */ lastTemp = baseTemp lastLogTau = baseLogTau #//lastDepth = baseDepth; #//System.out.println( #// "tauRos[1][i] (tauRos[1][i]-lastLogTau) adGradLogTau rho[1][i] kappa[1][i] lastTemp nextTemp"); for i in range(iBound, numDeps): mu = mmw[i] logMu = math.log(mu) logFctr1 = logMu + logAmu - logK #//System.out.println("logFactr1 " + logE*logFctr1 + " logInvGamFac " + logE*logInvGamFac + " logg " + logg); logCp = math.log( 5.0 / 2.0 ) - logFctr1 #//ideal monatomic gas - underestimate that neglects partial ionization #// ** Caution: These are log_e of the *magnitude* of the temperature gradients! #//The adiabatic dT/dTau in r space logAdGradRMag = logInvGamFac + logFctr1 + ln10 * logg #//logg is in base 10 #//This is baaad stuff - remember our tuaRos scale has *nothing* to do with our kappa values! #//The adiabatic dT/dTau in tau space - divide dT/dr by rho and kappa and make it +ve becasue we're in tau-space: #//Bad fake to fix artificially small dT/dr at low Teff - use kappaSun instead of kappa logAdGradTauMag = logAdGradRMag - rho[1][i] - kappa[1][i] #//The adiabatic dT/dLnTau in log_e(tau) space logAdGradLogTauMag = tauRos[1][i] + logAdGradTauMag #//Build the T(tau) in the convection zone: #// Work in logTau space - numerically safer?? adGradLogTau = math.exp( logAdGradLogTauMag) #//No minus sign - logTau increases inward... nextTemp = lastTemp + adGradLogTau * (tauRos[1][i] - lastLogTau) #//System.out.format("%12.8f %12.8f %12.8f %12.8f %12.8f %7.1f %7.1f%n", logE * tauRos[1][i], logE * (tauRos[1][i] - lastLogTau), adGradLogTau, logE * rho[1][i], logE * kappa[1][i], lastTemp, nextTemp); """/* // Do in geometric depth space adGradR = Math.exp(logAdGradRMag); // no minus sign - our depths *increase* inwards (they're NOT heights!) nextTemp = lastTemp + adGradR * (depths[i] - lastDepth); //System.out.format("%12.8f %12.8f %12.8f %7.1f %7.1f%n", logE*tauRos[1][i], (depths[i] - lastDepth), adGradR, lastTemp, nextTemp); */""" #//Okay - now the difference between the superadiabatic and adiabatic dT/dr: logFctr2 = rho[1][i] + logCp + 2.0 * logMixL #// ** NOTE ** Should temp in the following line be the *convective* temp of the last depth??? #// logg is in base 10 - convert to base e logFctr3 = 3.0 * (ln10 * logg - math.log(lastTemp)) / 2.0 #//Difference between SuperAdibatic dT/dr and Adiabtic dT/dr in r-space - Carroll & Ostlie 2nd Ed. p. 328 #//System.out.println("logFluxSurfBol " + logE * logFluxSurfBol + " logFctr2 " + logE * logFctr2 + " logFctr1 " + logE * logFctr1 + " logFctr3 " + logE * logFctr3 + " logBeta " + logE * logBeta); logDeltaR = logFluxSurfBol - logFctr2 + 2.0 * logFctr1 + logFctr3 - 0.5 * logBeta logDeltaR = 2.0 * logDeltaR / 3.0 #//DeltaR is above formula to the 2/3 power #//This is baaad stuff - remember our tuaRos scale has *nothing* to do with our kappa values! #//Bad fake to fix artificially small dT/dr at low Teff - use kappaSun instead of kappa logDeltaTau = logDeltaR - rho[1][i] - kappa[1][i] logDeltaLogTau = tauRos[1][i] + logDeltaTau sAdGradLogTau = adGradLogTau + math.exp(logDeltaLogTau) #//System.out.format("%12.8f %12.8f %12.8f %12.8f%n", logE*tauRos[1][i], logE*logDeltaR, logE*logDeltaTau, logE*logDeltaLogTau); nextTemp2 = lastTemp + sAdGradLogTau * (tauRos[1][i] - lastLogTau) """/* // Do in geometric depth space sAdGradR = adGradR + Math.exp(logDeltaR); nextTemp2 = lastTemp + sAdGradR * (depths[i] - lastDepth); */""" #// set everything to nextTemp2 for superadibatic dT/dr, and to nexTemp for adiabatic dT/dr convTemp[0][i] = nextTemp2 convTemp[1][i] = math.log(nextTemp2) lastTemp = nextTemp2 lastLogTau = tauRos[1][i] #//lastDepth = depths[i] #} return convTemp
def lineGridGauss(lam0In, massIn, xiTIn, numDeps, teff, numCore): c = Useful.c() logC = Useful.logC() #//double k = Useful.k; logK = Useful.logK() #//double e = Useful.e; #//double mE = Useful.mE; amu = Useful.amu() dln10 = math.log(10.0) ln2 = math.log(2.0) logE = math.log10(math.e) #// for debug output #//Put input parameters into linear cgs units: #//double gammaCol = Math.pow(10.0, logGammaCol); logTeff = math.log(teff) xiT = xiTIn * 1.0E5 #//km/s to cm/s lam0 = lam0In #// * 1.0E-7; //nm to cm logLam0 = math.log(lam0) logMass = math.log(massIn * amu) #//amu to g #// Compute depth-independent Doppler width, Delta_lambda_D: #double doppler, logDopp; #double logHelp, help; //scratch logHelp = ln2 + logK + logTeff - logMass #// M-B dist, square of v_mode help = math.exp( logHelp) + xiT * xiT #// quadratic sum of thermal v and turbulent v logHelp = 0.5 * math.log(help) logDopp = logHelp + logLam0 - logC doppler = math.exp(logDopp) #// cm #//System.out.println("LineGrid: doppler, logDopp: " + doppler + " " + logE*logDopp); #//Set up a half-profile Delta_lambda grid in Doppler width units #//from line centre to wing #//int numCore = 5; #//int numWing = 5; #//int numWing = 0; //debug numPoints = numCore #// a 2D 2 X numPoints array of Delta Lambdas #// Row 0 : Delta lambdas in cm - will need to be in nm for Planck and Rad Trans? #// Row 1 : Delta lambdas in Doppler widths linePoints = [[0.0 for i in range(numPoints)] for j in range(2)] #// Line profiel points in Doppler widths - needed for Voigt function, H(a,v): v = [0.0 for i in range(numPoints)] maxCoreV = 3.5 #//core half-width ~ in Doppler widths #//double maxWingDeltaLogV = 1.5 * ln10; //maximum base e logarithmic shift from line centre in Doppler widths minWingDeltaLogV = math.log(maxCoreV + 1.5) maxWingDeltaLogV = 9.0 + minWingDeltaLogV #double logV, ii, jj; for il in range(numPoints): ii = float(il) #// In core, space v points linearly: #// Voigt "v" parameter #// v > 0 --> This is the *red* wing: v[il] = ii * maxCoreV / (numCore - 1) linePoints[0][il] = doppler * v[il] linePoints[1][il] = v[il] #//System.out.println("LineGrid: il, lam, v: " + il + " " + #// linePoints[0][il] + " " + linePoints[1][il]); #} // il lambda loop #// Add the negative DeltaLambda half of the line: numPoints2 = (2 * numPoints) - 1 #//System.out.println("LineGrid: numpoints2: " + numPoints2); #// Return a 2D 2 X (2xnumPoints-1) array of Delta Lambdas #// Row 0 : Delta lambdas in cm - will need to be in nm for Planck and Rad Trans? #// Row 1 : Delta lambdas in Doppler widths linePoints2 = [[0.0 for i in range(numPoints2)] for j in range(2)] #//wavelengths are depth-independent - just put them in the 0th depth slot: for il2 in range(numPoints2): if (il2 < numPoints - 1): il = (numPoints - 1) - il2 linePoints2[0][il2] = -1.0 * linePoints[0][il] linePoints2[1][il2] = -1.0 * linePoints[1][il] else: #//Positive DelataLambda half: il = il2 - (numPoints - 1) linePoints2[0][il2] = linePoints[0][il] linePoints2[1][il2] = linePoints[1][il] #//System.out.println("LineGrid: il2, lam, v: " + il2 + " " + #// linePoints2[0][il2] + " " + linePoints2[1][il2]); #} //il2 loop return linePoints2
def kappas2(numDeps, pe, zScale, temp, rho, numLams, lambdas, logAHe, \ logNH1, logNH2, logNHe1, logNHe2, Ne, teff, logKapFudge): """/* Compute opacities properly from scratch with real physical cross-sections */ // *** CAUTION: // // This return's "kappa" as defined by Gray 3rd Ed. - cm^2 per *relelvant particle* where the "releveant particle" // depends on *which* kappa """ #// #// *** CAUTION: #// #// This return's "kappa" as defined by Gray 3rd Ed. - cm^2 per *relelvant particle* where the "releveant particle" #// depends on *which* kappa log10E = math.log10(math.e) #//needed for g_ff logLog10E = math.log(log10E) logE10 = math.log(10.0) logNH = [0.0 for i in range(numDeps)] #//Total H particle number density cm^-3 #double logPH1, logPH2, logPHe1, logPHe2; for i in range(numDeps): logNH[i] = math.exp(logNH1[i]) + math.exp(logNH2[i]) logNH[i] = math.log(logNH[i]) #//System.out.println("i " + i + " logNH1 " + log10E*logNH1[i] + " logNH2 " + log10E*logNH2[i] #//+ " logNHe1 " + log10E*logNHe1[i] + " logNHe2 " + log10E*logNHe2[i] + " logPe " + log10E*pe[1][i]); #// logPH1 = logNH1[i] + temp[1][i] + Useful.logK(); #// logPH2 = logNH2[i] + temp[1][i] + Useful.logK(); #// logPHe1 = logNHe1[i] + temp[1][i] + Useful.logK(); #// logPHe2 = logNHe2[i] + temp[1][i] + Useful.logK(); #//System.out.println("i " + i + " logPH1 " + log10E*logPH1 + " logPH2 " + log10E*logPH2 #//+ " logPHe1 " + log10E*logPHe1 + " logPHe2 " + log10E*logPHe2 + " logPe " + log10E*pe[1][i]); #double[][] logKappa = new double[numLams][numDeps]; logKappa = [ [0.0 for i in range(numDeps)] for j in range(numLams) ] #double kappa; //helper #double stimEm; //temperature- and wavelength-dependent stimulated emission correction #double stimHelp, logStimEm; #double ii; //useful for converting integer loop counter, i, to float #// #// #//Input data and variable declarations: #// #// #// H I b-f & f-f chiIH = 13.598433 #//eV Rydberg = 1.0968e-2 #// "R" in nm^-1 #//Generate threshold wavelengths and b-f Gaunt (g_bf) helper factors up to n=10: #double n; //principle quantum number of Bohr atom E-level numHlevs = 10 #double logChiHlev; invThresh = [0.0 for i in range(numHlevs)] #//also serves as g_bf helper factor threshLambs = [0.0 for i in range(numHlevs)] chiHlev = [0.0 for i in range(numHlevs)] for i in range(numHlevs): n = 1.0 + float(i) invThresh[i] = Rydberg / n / n #//nm^-1; also serves as g_bf helper factor threshLambs[i] = 1.0 / invThresh[i] #//nm logChiHlev = Useful.logH() + Useful.logC() + math.log(invThresh[i]) + 7.0*logE10 #// ergs chiHlev[i] = math.exp(logChiHlev - Useful.logEv()) #//eV chiHlev[i] = chiIH - chiHlev[i] #// System.out.println("i " + i + " n " + n + " invThresh " + invThresh[i] + " threshLambs[i] " + threshLambs[i] + " chiHlev " + chiHlev[i]); logGauntPrefac = math.log(0.3456) - 0.333333*math.log(Rydberg) #// **** Caution: this will require lamba in A!: a0 = 1.0449e-26 #//if lambda in A logA0 = math.log(a0) #// Boltzmann const "k" in eV/K - needed for "theta" logKeV = Useful.logK() - Useful.logEv() #//g_bf Gaunt factor - depends on lower E-level, n: loggbf = [0.0 for i in range(numHlevs)] #//initialize quantities that depend on lowest E-level contributing to opacity at current wavelength: for iThresh in range(numHlevs): loggbf[iThresh] = 0.0 #double logGauntHelp, gauntHelp; #double gbf, gbfHelp, loggbfHelp; #double gff, gffHelp, loggffHelp, logffHelp, loggff; #double help, logHelp3; #double chiLambda, logChiLambda; #double bfTerm, logbfTerm, bfSum, logKapH1bf, logKapH1ff; #//initial defaults: gbf = 1.0 gff = 1.0 loggff = 0.0 logChiFac = math.log(1.2398e3) #// eV per lambda, for lambda in nm #// Needed for kappa_ff: #double ffBracket; logffHelp = logLog10E - math.log(chiIH) - math.log(2.0) #//logHelp = logffHelp - math.log(2.0) #// #//Hminus: #// #// H^- b-f #//This is for the sixth order polynomial fit to the cross-section's wavelength dependence numHmTerms = 7 logAHm = [0.0 for i in range(numHmTerms)] signAHm = [0.0 for i in range(numHmTerms)] aHmbf = 4.158e-10 #//double logAHmbf = Math.log(aHmbf); #//Is the factor of 10^-18cm^2 from the polynomial fit to alpha_Hmbf missing in Eq. 8.12 on p. 156 of Gray 3rd Ed?? logAHmbf = math.log(aHmbf) - 18.0*logE10 #double alphaHmbf, logAlphaHmbf, logTermHmbf, logKapHmbf; #//Computing each polynomial term logarithmically logAHm[0] = math.log(1.99654) signAHm[0] = 1.0 logAHm[1] = math.log(1.18267e-5) signAHm[1] = -1.0 logAHm[2] = math.log(2.64243e-6) signAHm[2] = 1.0 logAHm[3] = math.log(4.40524e-10) signAHm[3] = -1.0 logAHm[4] = math.log(3.23992e-14) signAHm[4] = 1.0 logAHm[5] = math.log(1.39568e-18) signAHm[5] = -1.0 logAHm[6] = math.log(2.78701e-23) signAHm[6] = 1.0 alphaHmbf = math.exp(logAHm[0]) #//initialize accumulator #// H^- f-f: logAHmff = -26.0*logE10 numHmffTerms = 5 #double fPoly, logKapHmff, logLambdaAFac; fHmTerms = [ [ 0.0 for i in range(numHmffTerms) ] for j in range(3) ] fHm = [0.0 for i in range(3)] fHmTerms[0][0] = -2.2763 fHmTerms[0][1] = -1.6850 fHmTerms[0][2] = 0.76661 fHmTerms[0][3] = -0.053346 fHmTerms[0][4] = 0.0 fHmTerms[1][0] = 15.2827 fHmTerms[1][1] = -9.2846 fHmTerms[1][2] = 1.99381 fHmTerms[1][3] = -0.142631 fHmTerms[1][4] = 0.0 fHmTerms[2][0] = -197.789 fHmTerms[2][1] = 190.266 fHmTerms[2][2] = -67.9775 fHmTerms[2][3] = 10.6913 fHmTerms[2][4] = -0.625151 #// #//H_2^+ molecular opacity - cool stars #// scasles with proton density (H^+) #//This is for the third order polynomial fit to the "sigma_l(lambda)" and "U_l(lambda)" #//terms in the cross-section numH2pTerms = 4 sigmaH2pTerm = [0.0 for i in range(numH2pTerms)] UH2pTerm = [0.0 for i in range(numH2pTerms)] #double logSigmaH2p, sigmaH2p, UH2p, logKapH2p; aH2p = 2.51e-42 logAH2p = math.log(aH2p) sigmaH2pTerm[0] = -1040.54 sigmaH2pTerm[1] = 1345.71 sigmaH2pTerm[2] = -547.628 sigmaH2pTerm[3] = 71.9684 #//UH2pTerm[0] = 54.0532 #//UH2pTerm[1] = -32.713 #//UH2pTerm[2] = 6.6699 #//UH2pTerm[3] = -0.4574 #//Reverse signs on U_1 polynomial expansion co-efficients - Dave Gray private communcation #//based on Bates (1952) UH2pTerm[0] = -54.0532 UH2pTerm[1] = 32.713 UH2pTerm[2] = -6.6699 UH2pTerm[3] = 0.4574 #// He I b-f & ff: #double totalH1Kap, logTotalH1Kap, helpHe, logKapHe; #// #//He^- f-f AHe = math.exp(logAHe) #double logKapHemff, nHe, logNHe, thisTerm, thisLogTerm, alphaHemff, log10AlphaHemff; #// Gray does not have this pre-factor, but PHOENIX seems to and without it #// the He opacity is about 10^26 too high!: logAHemff = -26.0*logE10 numHemffTerms = 5 logC0HemffTerm = [0.0 for i in range(numHemffTerms)] logC1HemffTerm = [0.0 for i in range(numHemffTerms)] logC2HemffTerm = [0.0 for i in range(numHemffTerms)] logC3HemffTerm = [0.0 for i in range(numHemffTerms)] signC0HemffTerm = [0.0 for i in range(numHemffTerms)] signC1HemffTerm = [0.0 for i in range(numHemffTerms)] signC2HemffTerm = [0.0 for i in range(numHemffTerms)] signC3HemffTerm = [0.0 for i in range(numHemffTerms)] #//we'll be evaluating the polynominal in theta logarithmically by adding logarithmic terms - logC0HemffTerm[0] = math.log(9.66736) signC0HemffTerm[0] = 1.0 logC0HemffTerm[1] = math.log(71.76242) signC0HemffTerm[1] = -1.0 logC0HemffTerm[2] = math.log(105.29576) signC0HemffTerm[2] = 1.0 logC0HemffTerm[3] = math.log(56.49259) signC0HemffTerm[3] = -1.0 logC0HemffTerm[4] = math.log(10.69206) signC0HemffTerm[4] = 1.0 logC1HemffTerm[0] = math.log(10.50614) signC1HemffTerm[0] = -1.0 logC1HemffTerm[1] = math.log(48.28802) signC1HemffTerm[1] = 1.0 logC1HemffTerm[2] = math.log(70.43363) signC1HemffTerm[2] = -1.0 logC1HemffTerm[3] = math.log(37.80099) signC1HemffTerm[3] = 1.0 logC1HemffTerm[4] = math.log(7.15445) signC1HemffTerm[4] = -1.0 logC2HemffTerm[0] = math.log(2.74020) signC2HemffTerm[0] = 1.0 logC2HemffTerm[1] = math.log(10.62144) signC2HemffTerm[1] = -1.0 logC2HemffTerm[2] = math.log(15.50518) signC2HemffTerm[2] = 1.0 logC2HemffTerm[3] = math.log(8.33845) signC2HemffTerm[3] = -1.0 logC2HemffTerm[4] = math.log(1.57960) signC2HemffTerm[4] = 1.0 logC3HemffTerm[0] = math.log(0.19923) signC3HemffTerm[0] = -1.0 logC3HemffTerm[1] = math.log(0.77485) signC3HemffTerm[1] = 1.0 logC3HemffTerm[2] = math.log(1.13200) signC3HemffTerm[2] = -1.0 logC3HemffTerm[3] = math.log(0.60994) signC3HemffTerm[3] = 1.0 logC3HemffTerm[4] = math.log(0.11564) signC3HemffTerm[4] = -1.0 # //initialize accumulators: cHemff = [0.0 for i in range(4)] cHemff[0] = signC0HemffTerm[0] * math.exp(logC0HemffTerm[0]); cHemff[1] = signC1HemffTerm[0] * math.exp(logC1HemffTerm[0]); cHemff[2] = signC2HemffTerm[0] * math.exp(logC2HemffTerm[0]); cHemff[3] = signC3HemffTerm[0] * math.exp(logC3HemffTerm[0]); #// #//Should the polynomial expansion for the Cs by in 10g10Theta?? No! Doesn't help: #//double[] C0HemffTerm = new double[numHemffTerms]; #//double[] C1HemffTerm = new double[numHemffTerms]; #//double[] C2HemffTerm = new double[numHemffTerms]; #//double[] C3HemffTerm = new double[numHemffTerms]; #// #//C0HemffTerm[0] = 9.66736; #//C0HemffTerm[1] = -71.76242; #//C0HemffTerm[2] = 105.29576; #//C0HemffTerm[3] = -56.49259; #//C0HemffTerm[4] = 10.69206; #//C1HemffTerm[0] = -10.50614; #//C1HemffTerm[1] = 48.28802; #//C1HemffTerm[2] = -70.43363; #//C1HemffTerm[3] = 37.80099; #//C1HemffTerm[4] = -7.15445; #//C2HemffTerm[0] = 2.74020; #//C2HemffTerm[1] = -10.62144; #//C2HemffTerm[2] = 15.50518; #//C2HemffTerm[3] = -8.33845; #//C2HemffTerm[4] = 1.57960; #//C3HemffTerm[0] = -0.19923; #//C3HemffTerm[1] = 0.77485; #//C3HemffTerm[2] = -1.13200; #//C3HemffTerm[3] = 0.60994; #//C3HemffTerm[4] = -0.11564; #//initialize accumulators: #// double[] cHemff = new double[4]; #// cHemff[0] = C0HemffTerm[0]; #// cHemff[1] = C1HemffTerm[0]; #// cHemff[2] = C2HemffTerm[0]; #// cHemff[3] = C3HemffTerm[0]; #// #// electron (e^-1) scattering (Thomson scattering) #double kapE, logKapE; alphaE = 0.6648e-24 #//cm^2/e^-1 logAlphaE = math.log(0.6648e-24) #//Universal: #// # double theta, logTheta, log10Theta, log10ThetaFac; # double logLambda, lambdaA, logLambdaA, log10LambdaA, lambdanm, logLambdanm; #//Okay - here we go: #//Make the wavelength loop the outer loop - lots of depth-independnet lambda-dependent quantities: #// #// # //System.out.println("Kappas called..."); #// #// **** START WAVELENGTH LOOP iLam #// #// #// for iLam in range(numLams): #// #//Re-initialize all accumulators to be on safe side: kappa = 0.0 logKapH1bf = -99.0 logKapH1ff = -99.0 logKapHmbf = -99.0 logKapHmff = -99.0 logKapH2p = -99.0 logKapHe = -99.0 logKapHemff = -99.0 logKapE = -99.0 #// #//*** CAUTION: lambda MUST be in nm here for consistency with Rydbeg logLambda = math.log(lambdas[iLam]) #//log cm lambdanm = 1.0e7 * lambdas[iLam] logLambdanm = math.log(lambdanm) lambdaA = 1.0e8 * lambdas[iLam] #//Angstroms logLambdaA = math.log(lambdaA) log10LambdaA = log10E * logLambdaA logChiLambda = logChiFac - logLambdanm chiLambda = math.exp(logChiLambda) #//eV #// Needed for both g_bf AND g_ff: logGauntHelp = logGauntPrefac - 0.333333*logLambdanm #//lambda in nm here gauntHelp = math.exp(logGauntHelp) #// if (iLam == 142){ #// System.out.println("lambdaA " + lambdaA); #// } #//HI b-f depth independent factors: #//Start at largest threshold wavelength and break out of loop when next threshold lambda is less than current lambda: #for (iThresh = numHlevs-1; iThresh >= 0; iThresh--){ for iThresh in range(0, numHlevs-1, -1): if (threshLambs[iThresh] < lambdanm): break if (lambdanm <= threshLambs[iThresh]): #//this E-level contributes loggbfHelp = logLambdanm + math.log(invThresh[iThresh]) # //lambda in nm here; invThresh here as R/n^2 gbfHelp = math.exp(loggbfHelp) gbf = 1.0 - (gauntHelp * (gbfHelp - 0.5)) #// if (iLam == 1){} #// System.out.println("iThresh " + iThresh + " threshLambs " + threshLambs[iThresh] + " gbf " + gbf); #// } loggbf[iThresh] = math.log(gbf) #//end iThresh loop #//HI f-f depth independent factors: # //logChi = logLog10E + logLambdanm - logChiFac; //lambda in nm here # //chi = Math.exp(logChi); loggffHelp = logLog10E - logChiLambda #// #// #// #// ****** Start depth loop iTau ****** #// #// #// #// for iTau in range(numDeps): #// # //Re-initialize all accumulators to be on safe side: kappa = 0.0 logKapH1bf = -99.0 logKapH1ff = -99.0 logKapHmbf = -99.0 logKapHmff = -99.0 logKapH2p = -99.0 logKapHe = -99.0 logKapHemff = -99.0 logKapE = -99.0 #// #// #//if (iTau == 36 && iLam == 142){ #// System.out.println("lambdanm[142] " + lambdanm + " temp[0][iTau=36] " + temp[0][iTau=36]); #// } #//This is "theta" ~ 5040/T: logTheta = logLog10E - logKeV - temp[1][iTau] log10Theta = log10E * logTheta theta = math.exp(logTheta) #//System.out.println("theta " + theta + " logTheta " + logTheta); #// temperature- and wavelength-dependent stimulated emission coefficient: stimHelp = -1.0 * theta * chiLambda * logE10 stimEm = 1.0 - math.exp(stimHelp) logStimEm = math.log(stimEm) # // if (iTau == 36 && iLam == 142){ # // System.out.println("stimEm " + stimEm); # //} ffBracket = math.exp(loggffHelp - logTheta) + 0.5 gff = 1.0 + (gauntHelp*ffBracket) #//if (iTau == 36 && iLam == 1){ #// System.out.println("gff " + gff); #// } loggff = math.log(gff) #//H I b-f: #//Start at largest threshold wavelength and break out of loop when next threshold lambda is less than current lambda: bfSum = 0.0 #//initialize accumulator logHelp3 = logA0 + 3.0*logLambdaA #//lambda in A here #for (int iThresh = numHlevs-1; iThresh >= 0; iThresh--){ for iThresh in range(0, numHlevs-1, -1): if (threshLambs[iThresh] < lambdanm): break n = 1.0 + float(iThresh) if (lambdanm <= threshLambs[iThresh]): #//this E-level contributes logbfTerm = loggbf[iThresh] - 3.0*math.log(n) logbfTerm = logbfTerm - (theta*chiHlev[iThresh])*logE10 bfSum = bfSum + math.exp(logbfTerm) #//if (iTau == 36 && iLam == 142){ # //System.out.println("lambdanm " + lambdanm + " iThresh " + iThresh + " threshLambs[iThresh] " + threshLambs[iThresh]); # //System.out.println("loggbf " + loggbf[iThresh] + " theta " + theta + " chiHlev " + chiHlev[iThresh]); # //System.out.println("bfSum " + bfSum + " logbfTerm " + logbfTerm); #// } #//end iThresh loop #// cm^2 per *neutral* H atom logKapH1bf = logHelp3 + math.log(bfSum) #//Stimulated emission correction logKapH1bf = logKapH1bf + logStimEm #//System.out.println("lambda " + lambdas[iLam] + "iTau " + iTau + " sigma " + Math.exp(logKapH1bf)); #//Add it in to total - opacity per neutral HI atom, so multiply by logNH1 #// This is now linear opacity in cm^-1 logKapH1bf = logKapH1bf + logNH1[iTau] #//System.out.println(" aH1 " + Math.exp(logKapH1bf)); #////Nasty fix to make Balmer lines show up in A0 stars! #// if (teff > 8000){ #// logKapH1bf = logKapH1bf - logE10*1.5; #// kappa = math.exp(logKapH1bf) #//System.out.println("HIbf " + log10E*logKapH1bf); #//if (iTau == 36 && iLam == 142){ #// System.out.println("lambdaA " + lambdaA + " logKapH1bf " + log10E*(logKapH1bf)); //-rho[1][iTau])); #//} #//H I f-f: #// cm^2 per *neutral* H atom logKapH1ff = logHelp3 + loggff + logffHelp - logTheta - (theta*chiIH)*logE10 #//Stimulated emission correction logKapH1ff = logKapH1ff + logStimEm #//Add it in to total - opacity per neutral HI atom, so multiply by logNH1 #// This is now linear opacity in cm^-1 logKapH1ff = logKapH1ff + logNH1[iTau] #////Nasty fix to make Balmer lines show up in A0 stars! #// if (teff > 8000){ #// logKapH1ff = logKapH1ff - logE10*1.5; #// kappa = kappa + math.exp(logKapH1ff); #//System.out.println("HIff " + log10E*logKapH1ff); #//if (iTau == 36 && iLam == 142){ #// System.out.println("logKapH1ff " + log10E*(logKapH1ff)); //-rho[1][iTau])); #//} #// #//Hminus: #// #// H^- b-f: #//if (iTau == 36 && iLam == 142){ # // System.out.println("temp " + temp[0][iTau] + " lambdanm " + lambdanm); # // } logKapHmbf = -99.0 #//initialize default #//if ( (temp[0][iTau] > 2500.0) && (temp[0][iTau] < 10000.0) ){ #//if ( (temp[0][iTau] > 2500.0) && (temp[0][iTau] < 8000.0) ){ #//Try lowering lower Teff limit to avoid oapcity collapse in outer layers of late-type stars if ( (temp[0][iTau] > 1000.0) and (temp[0][iTau] < 10000.0) ): if ((lambdanm > 225.0) and (lambdanm < 1500.0) ): # //nm #//if (iTau == 36 && iLam == 142){ # // System.out.println("In KapHmbf condition..."); #//} ii = 0.0 alphaHmbf = signAHm[0]*math.exp(logAHm[0]) #//initialize accumulator #for (int i = 1; i < numHmTerms; i++){ for i in range(1, numHmTerms): ii = float(i) #//if (iTau == 36 && iLam == 142){ #// System.out.println("ii " + ii); #//} logTermHmbf = logAHm[i] + ii*logLambdaA alphaHmbf = alphaHmbf + signAHm[i]*math.exp(logTermHmbf) #//if (iTau == 36 && iLam == 142){ #// System.out.println("logTermHmbf " + log10E*logTermHmbf + " i " + i + " logAHm " + log10E*logAHm[i]); #//} logAlphaHmbf = math.log(alphaHmbf) #// cm^2 per neutral H atom logKapHmbf = logAHmbf + logAlphaHmbf + pe[1][iTau] + 2.5*logTheta + (0.754*theta)*logE10 #//Stimulated emission correction logKapHmbf = logKapHmbf + logStimEm #//if (iTau == 36 && iLam == 142){ #// System.out.println("alphaHmbf " + alphaHmbf); #// System.out.println("logKapHmbf " + log10E*logKapHmbf + " logAHmbf " + log10E*logAHmbf + " logAlphaHmbf " + log10E*logAlphaHmbf); #// } #//Add it in to total - opacity per neutral HI atom, so multiply by logNH1 #// This is now linear opacity in cm^-1 logKapHmbf = logKapHmbf + logNH1[iTau] kappa = kappa + math.exp(logKapHmbf) #//System.out.println("Hmbf " + log10E*logKapHmbf); #//if (iTau == 36 && iLam == 142){ #// System.out.println("logKapHmbf " + log10E*(logKapHmbf)); //-rho[1][iTau])); #//} #//wavelength condition #// temperature condition #// H^- f-f: logKapHmff = -99.0 #//initialize default #//if ( (temp[0][iTau] > 2500.0) && (temp[0][iTau] < 10000.0) ){ #//Try lowering lower Teff limit to avoid oapcity collapse in outer layers of late-type stars #//if ( (temp[0][iTau] > 2500.0) && (temp[0][iTau] < 8000.0) ){ if ( (temp[0][iTau] > 1000.0) and (temp[0][iTau] < 10000.0) ): if ((lambdanm > 260.0) and (lambdanm < 11390.0) ): #//nm #//construct "f_n" polynomials in log(lambda) for j in range(3): fHm[j] = fHmTerms[j][0] #//initialize accumulators ii = 0.0 for i in range(1, numHmffTerms): ii = float(i) logLambdaAFac = math.pow(log10LambdaA, ii) for j in range(3): fHm[j] = fHm[j] + (fHmTerms[j][i]*logLambdaAFac) #} #// i #} #// j #// fPoly = fHm[0] + fHm[1]*log10Theta + fHm[2]*log10Theta*log10Theta #// In cm^2 per neutral H atom: #// Stimulated emission alreadya ccounted for logKapHmff = logAHmff + pe[1][iTau] + fPoly*logE10 #//Add it in to total - opacity per neutral HI atom, so multiply by logNH1 #// This is now linear opacity in cm^-1 logKapHmff = logKapHmff + logNH1[iTau] kappa = kappa + math.exp(logKapHmff) #//System.out.println("Hmff " + log10E*logKapHmff); #//if (iTau == 36 && iLam == 142){ #// System.out.println("logKapHmff " + log10E*(logKapHmff)); //-rho[1][iTau])); #//} #//wavelength condition #// temperature condition #// H^+_2: #// logKapH2p = -99.0 #//initialize default if ( temp[0][iTau] < 4000.0 ): if ((lambdanm > 380.0) and (lambdanm < 2500.0) ): # //nm sigmaH2p = sigmaH2pTerm[0] #//initialize accumulator UH2p = UH2pTerm[0] #//initialize accumulator ii = 0.0# for i in range(1, numH2pTerms): ii = float(i) logLambdaAFac = math.pow(log10LambdaA, ii) #// kapH2p way too large with lambda in A - try cm: No! - leads to negative logs #//logLambdaAFac = Math.pow(logLambda, ii); sigmaH2p = sigmaH2p + sigmaH2pTerm[i] * logLambdaAFac UH2p = UH2p + UH2pTerm[i] * logLambdaAFac logSigmaH2p = math.log(sigmaH2p) logKapH2p = logAH2p + logSigmaH2p - (UH2p*theta)*logE10 + logNH2[iTau] #//Stimulated emission correction logKapH2p = logKapH2p + logStimEm #//Add it in to total - opacity per neutral HI atom, so multiply by logNH1 #// This is now linear opacity in cm^-1 logKapH2p = logKapH2p + logNH1[iTau] kappa = kappa + math.exp(logKapH2p) #//System.out.println("H2p " + log10E*logKapH2p); #//if (iTau == 16 && iLam == 142){ # //System.out.println("logKapH2p " + log10E*(logKapH2p-rho[1][iTau]) + " logAH2p " + log10E*logAH2p #// + " logSigmaH2p " + log10E*logSigmaH2p + " (UH2p*theta)*logE10 " + log10E*((UH2p*theta)*logE10) + " logNH2[iTau] " + log10E*logNH2[iTau]); #//} #//wavelength condition #// temperature condition #//He I #// #// HeI b-f + f-f #//Scale sum of He b-f and f-f with sum of HI b-f and f-f #//wavelength condition comes from requirement that lower E level be greater than n=2 (edge at 22.78 nm) logKapHe = -99.0 #//default intialization if ( temp[0][iTau] > 10000.0 ): if (lambdanm > 22.8): #//nm totalH1Kap = math.exp(logKapH1bf) + math.exp(logKapH1ff) logTotalH1Kap = math.log(totalH1Kap) helpHe = Useful.k() * temp[0][iTau] #// cm^2 per neutral H atom (after all, it's scaled wrt kappHI #// Stimulated emission already accounted for #// #// *** CAUTION: Is this *really* the right thing to do??? #// - we're re-scaling the final H I kappa in cm^2/g corrected for stim em, NOT the raw cross section logKapHe = math.log(4.0) - (10.92 / helpHe) + logTotalH1Kap #//Add it in to total - opacity per neutral HI atom, so multiply by logNH1 #// This is now linear opacity in cm^-1 logKapHe = logKapHe + logNH1[iTau] kappa = kappa + math.exp(logKapHe) #//System.out.println("He " + log10E*logKapHe); #//if (iTau == 36 && iLam == 142){ #// System.out.println("logKapHe " + log10E*(logKapHe)); //-rho[1][iTau])); #//} #//wavelength condition #// temperature condition #// #//He^- f-f: logKapHemff = -99.0 #//default initialization if ( (theta > 0.5) and (theta < 2.0) ): if ((lambdanm > 500.0) and (lambdanm < 15000.0) ): #// initialize accumulators: cHemff[0] = signC0HemffTerm[0]*math.exp(logC0HemffTerm[0]); #//System.out.println("C0HemffTerm " + signC0HemffTerm[0]*Math.exp(logC0HemffTerm[0])); cHemff[1] = signC1HemffTerm[0]*math.exp(logC1HemffTerm[0]); #//System.out.println("C1HemffTerm " + signC1HemffTerm[0]*Math.exp(logC1HemffTerm[0])); cHemff[2] = signC2HemffTerm[0]*math.exp(logC2HemffTerm[0]); #//System.out.println("C2HemffTerm " + signC2HemffTerm[0]*Math.exp(logC2HemffTerm[0])); cHemff[3] = signC3HemffTerm[0]*math.exp(logC3HemffTerm[0]); #//System.out.println("C3HemffTerm " + signC3HemffTerm[0]*Math.exp(logC3HemffTerm[0])); #//build the theta polynomial coefficients ii = 0.0 for i in range(1, numHemffTerms): ii = float(i) thisLogTerm = ii*logTheta + logC0HemffTerm[i] cHemff[0] = cHemff[0] + signC0HemffTerm[i]*math.exp(thisLogTerm) #//System.out.println("i " + i + " ii " + ii + " C0HemffTerm " + signC0HemffTerm[i]*Math.exp(logC0HemffTerm[i])); thisLogTerm = ii*logTheta + logC1HemffTerm[i] cHemff[1] = cHemff[1] + signC1HemffTerm[i]*math.exp(thisLogTerm) #//System.out.println("i " + i + " ii " + ii + " C1HemffTerm " + signC1HemffTerm[i]*Math.exp(logC1HemffTerm[i])); thisLogTerm = ii*logTheta + logC2HemffTerm[i] cHemff[2] = cHemff[2] + signC2HemffTerm[i]*math.exp(thisLogTerm) #//System.out.println("i " + i + " ii " + ii + " C2HemffTerm " + signC2HemffTerm[i]*Math.exp(logC2HemffTerm[i])); thisLogTerm = ii*logTheta + logC3HemffTerm[i] cHemff[3] = cHemff[3] + signC3HemffTerm[i]*math.exp(thisLogTerm) #//System.out.println("i " + i + " ii " + ii + " C3HemffTerm " + signC3HemffTerm[i]*Math.exp(logC3HemffTerm[i])); #//// Should polynomial expansion for Cs be in log10Theta??: - No! Doesn't help #// initialize accumulators: #// cHemff[0] = C0HemffTerm[0]; #// cHemff[1] = C1HemffTerm[0]; #// cHemff[2] = C2HemffTerm[0]; #// cHemff[3] = C3HemffTerm[0]; #// ii = 0.0; #// for (int i = 1; i < numHemffTerms; i++){ #// ii = (double) i; #// log10ThetaFac = Math.pow(log10Theta, ii); #// thisTerm = log10ThetaFac * C0HemffTerm[i]; #// cHemff[0] = cHemff[0] + thisTerm; #// thisTerm = log10ThetaFac * C1HemffTerm[i]; #// cHemff[1] = cHemff[1] + thisTerm; #// thisTerm = log10ThetaFac * C2HemffTerm[i]; #// cHemff[2] = cHemff[2] + thisTerm; #// thisTerm = log10ThetaFac * C3HemffTerm[i]; #// cHemff[3] = cHemff[3] + thisTerm; #// } #//Build polynomial in logLambda for alpha(He^1_ff): log10AlphaHemff = cHemff[0] #//initialize accumulation #//System.out.println("cHemff[0] " + cHemff[0]); ii = 0.0 for i in range(1, 3+1): #//System.out.println("i " + i + " cHemff[i] " + cHemff[i]); ii = float(i) thisTerm = cHemff[i] * math.pow(log10LambdaA, ii) log10AlphaHemff = log10AlphaHemff + thisTerm #//System.out.println("log10AlphaHemff " + log10AlphaHemff); alphaHemff = math.pow(10.0, log10AlphaHemff) #//gives infinite alphas! #// alphaHemff = log10AlphaHemff; // ?????!!!!! #//System.out.println("alphaHemff " + alphaHemff); #// Note: this is the extinction coefficient per *Hydrogen* particle (NOT He- particle!) # //nHe = Math.exp(logNHe1[iTau]) + Math.exp(logNHe2[iTau]); # //logNHe = Math.log(nHe); # //logKapHemff = Math.log(alphaHemff) + Math.log(AHe) + pe[1][iTau] + logNHe1[iTau] - logNHe; logKapHemff = logAHemff + math.log(alphaHemff) + pe[1][iTau] + logNHe1[iTau] - logNH[iTau] #//Stimulated emission already accounted for #//Add it in to total - opacity per H particle, so multiply by logNH #// This is now linear opacity in cm^-1 logKapHemff = logKapHemff + logNH[iTau] kappa = kappa + math.exp(logKapHemff) #//System.out.println("Hemff " + log10E*logKapHemff); #//if (iTau == 36 && iLam == 155){ #//if (iLam == 155){ #// System.out.println("logKapHemff " + log10E*(logKapHemff)); //-rho[1][iTau])); #//} #//wavelength condition #// temperature condition #// #// electron (e^-1) scattering (Thomson scattering) #//coefficient per *"hydrogen atom"* (NOT per e^-!!) (neutral or total H??): logKapE = logAlphaE + Ne[1][iTau] - logNH[iTau] #//Stimulated emission not relevent #//Add it in to total - opacity per H particle, so multiply by logNH #// This is now linear opacity in cm^-1 #//I know, we're adding logNH right back in after subtracting it off, but this is for dlarity and consistency for now... : logKapE = logKapE + logNH[iTau] kappa = kappa + math.exp(logKapE) #//System.out.println("E " + log10E*logKapE); #//if (iTau == 36 && iLam == 142){ #// System.out.println("logKapE " + log10E*(logKapE)); //-rho[1][iTau])); #//} #//Metal b-f #//Fig. 8.6 Gray 3rd Ed. #// #// #// This is now linear opacity in cm^-1 #// Divide by mass density #// This is now mass extinction in cm^2/g #// logKappa[iLam][iTau] = math.log(kappa) - rho[1][iTau] #// Fudge is in cm^2/g: Converto to natural log: logEKapFudge = logE10 * logKapFudge logKappa[iLam][iTau] = logKappa[iLam][iTau] + logEKapFudge #//if (iTau == 36 && iLam == 142){ #//System.out.println(" " + log10E*(logKappa[iLam][iTau]+rho[1][iTau])); #//} #// close iTau depth loop #// #//close iLam wavelength loop return logKappa
def delta(linePoints, lam0In, numDeps, tauRos, massIn, xiTIn, teff): """//delta function line profile for initiali check of line strength""" lam0 = lam0In #// * 1.0E-7; //nm to cm logLam0 = math.log(lam0) logE = math.log10(math.e) #// for debug output #//System.out.println("LineProf: doppler, logDopp: " + doppler + " " + logE*logDopp); #//Put input parameters into linear cgs units: #//System.out.println("LINEGRID: Tau1: " + tau1); #//logA = 2.0 * logLam0 + logGamma - ln4pi - logC - logDopp; #//a = Math.exp(logA); #//System.out.println("LINEGRID: logA: " + logE * logA); #//Set up a half-profile Delta_lambda grid in Doppler width units #//from line centre to wing numPoints = 1 #//System.out.println("LineProf: numPoints: " + numPoints); #// Return a 2D numPoints X numDeps array of normalized line profile points (phi) lineProf = [ [ 0.0 for i in range(numDeps) ] for j in range(1) ] c = Useful.c() logC = Useful.logC() logK = Useful.logK() amu = Useful.amu() ln10 = math.log(10.0) ln2 = math.log(2.0) lnSqRtPi = 0.5 * math.log(math.pi) logTeff = math.log(teff) xiT = xiTIn * 1.0E5 #//km/s to cm/s logMass = math.log(massIn * amu) #//amu to g #// Compute depth-independent Doppler width, Delta_lambda_D: #double doppler, logDopp; #double logHelp, help; //scratch logHelp = ln2 + logK + logTeff - logMass #// M-B dist, square of v_mode help = math.exp(logHelp) + xiT * xiT #// quadratic sum of thermal v and turbulent v logHelp = 0.5 * math.log(help) logDopp = logHelp + logLam0 - logC doppler = math.exp(logDopp) #// cm #// Line profile points in Doppler widths - needed for Voigt function, H(a,v): #double ii; #// lineProf[0][0] = 0.0; v[0] = 0.0; //Line centre - cannot do logaritmically! #double delta, core, logDelta; #//int il0 = 36; #//System.out.println("il0 " + il0 + " temp[il] " + temp[0][il0] + " press[il] " + logE*press[1][il0]); for id in range(numDeps): #//if (il <= numCore) { #// - Gaussian ONLY - at line centre Lorentzian will diverge! delta = 1.0 #//System.out.println("LINEGRID- CORE: core: " + core); #//System.out.println("LINEGRID: il, v[il]: " + il + " " + v[il] + " lineProf[0][il]: " + lineProf[0][il]); #//System.out.println("LINEGRID: il, Voigt, H(): " + il + " " + voigt); #//Convert from H(a,v) in dimensionless Voigt units to physical phi((Delta lambda) profile: logDelta = math.log(delta) + 2.0 * logLam0 - lnSqRtPi - logDopp - logC lineProf[0][id] = math.exp(logDelta) #//if (id == 36) { #// System.out.println("il " + il + " linePoints " + 1.0e7 * linePoints[0][il] + " id " + id + " lineProf[il][id] " + lineProf[il][id]); #//} #//System.out.println("LineProf: il, id, lineProf[il][id]: " + il + " " + id + " " + lineProf[il][id]); #// if (id == 20) { #// for (int il = 0; il < numPoints; il++) { #// System.out.format("Voigt: %20.16f %20.16f%n", linePoints[1][il], logE * Math.log(lineProf[il][id])); #// } #// } #} //id loop return lineProf
def lineGridDelta(lam0In, massIn, xiTIn, numDeps, teff): c = Useful.c() logC = Useful.logC() #//double k = Useful.k; logK = Useful.logK() #//double e = Useful.e; #//double mE = Useful.mE; amu = Useful.amu() ln10 = math.log(10.0) ln2 = math.log(2.0) logE = math.log10(math.e) #// for debug output #//Put input parameters into linear cgs units: #//double gammaCol = Math.pow(10.0, logGammaCol); logTeff = math.log(teff) xiT = xiTIn * 1.0E5 #//km/s to cm/s lam0 = lam0In #// * 1.0E-7; //nm to cm logLam0 = math.log(lam0) logMass = math.log(massIn * amu) #//amu to g #// Compute depth-independent Doppler width, Delta_lambda_D: #double doppler, logDopp; #double logHelp, help; //scratch logHelp = ln2 + logK + logTeff - logMass #// M-B dist, square of v_mode help = math.exp( logHelp) + xiT * xiT #// quadratic sum of thermal v and turbulent v logHelp = 0.5 * math.log(help) logDopp = logHelp + logLam0 - logC doppler = math.exp(logDopp) #// cm #//System.out.println("LineGrid: doppler, logDopp: " + doppler + " " + logE*logDopp); #//Set up a half-profile Delta_lambda grid in Doppler width units #//from line centre to wing #//int numCore = 5; #//int numWing = 5; #//int numWing = 0; //debug numPoints = 1 #// a 2D 2 X numPoints array of Delta Lambdas #// Row 0 : Delta lambdas in cm - will need to be in nm for Planck and Rad Trans? #// Row 1 : Delta lambdas in Doppler widths linePoints = [[0.0 for i in range(numPoints)] for j in range(2)] #// Line profiel points in Doppler widths - needed for Voigt function, H(a,v): v = [0.0 for i in range(numPoints)] #double logV, ii, jj; il = 0 ii = float(il) #// In core, space v points linearly: #// Voigt "v" parameter #// v > 0 --> This is the *red* wing: v[il] = ii linePoints[0][il] = doppler * v[il] linePoints[1][il] = v[il] #//System.out.println("LineGrid: il, lam, v: " + il + " " + #// linePoints[0][il] + " " + linePoints[1][il]); return linePoints
def voigt(linePoints, lam0In, logAij, logGammaCol, numDeps, teff, tauRos, temp, pGas, tempSun, pGasSun, hjertComp, dbgHandle): c = Useful.c() logC = Useful.logC() #//double k = Useful.k; logK = Useful.logK() #//double e = Useful.e; #//double mE = Useful.mE; lam0 = lam0In #// * 1.0E-7; //nm to cm logLam0 = math.log(lam0) ln10 = math.log(10.0) ln2 = math.log(2.0); ln4pi = math.log(4.0 * math.pi) #lnSqRtPi = 0.5 * math.log(math.pi) sqRtPi = math.sqrt(math.pi) sqPi = math.sqrt(math.pi) #//double ln100 = 2.0*Math.log(10.0); logE = math.log10(math.e) #// for debug output doppler = linePoints[0][1] / linePoints[1][1] logDopp = math.log(doppler) #//System.out.println("LineProf: doppler, logDopp: " + doppler + " " + logE*logDopp); #//Put input parameters into linear cgs units: #//double gammaCol = Math.pow(10.0, logGammaCol); #// Lorentzian broadening: #// Assumes Van der Waals dominates radiative damping #// log_10 Gamma_6 for van der Waals damping around Tau_Cont = 1 in Sun #// - p. 57 of Radiative Transfer in Stellar Atmospheres (Rutten) logGammaSun = 9.0 * ln10 #// Convert to base e #//double logFudge = Math.log(2.5); // Van der Waals enhancement factor tau1 = ToolBox.tauPoint(numDeps, tauRos, 1.0) #outline = ("tau1 "+ str(tau1) + "\n") #dbgHandle.write(outline) #//System.out.println("LINEGRID: Tau1: " + tau1); #//logA = 2.0 * logLam0 + logGamma - ln4pi - logC - logDopp; #//a = Math.exp(logA); #//System.out.println("LINEGRID: logA: " + logE * logA); #//Set up a half-profile Delta_lambda grid in Doppler width units #//from line centre to wing numPoints = len(linePoints[0]) #//System.out.println("LineProf: numPoints: " + numPoints); #// Return a 2D numPoints X numDeps array of normalized line profile points (phi) lineProf = [ [ 0.0 for i in range(numDeps) ] for j in range(numPoints) ] #// Line profiel points in Doppler widths - needed for Voigt function, H(a,v): v = [0.0 for i in range(numPoints)] #double logV, ii; #// lineProf[0][0] = 0.0; v[0] = 0.0; //Line centre - cannot do logaritmically! #double gamma, logGamma, a, logA, voigt, core, wing, logWing, logVoigt; Aij = math.pow(10.0, logAij) il0 = 36 #// For Hjerting function approximation: #double vSquare, vFourth, vAbs, a2, a3, a4, Hjert0, Hjert1, Hjert2, Hjert3, Hjert4, hjertFn; #//System.out.println("il0 " + il0 + " temp[il] " + temp[0][il0] + " press[il] " + logE*press[1][il0]); for id in range(numDeps): #//Formula from p. 56 of Radiative Transfer in Stellar Atmospheres (Rutten), #// logarithmically with respect to solar value: logGamma = pGas[1][id] - pGasSun[1][tau1] + 0.7 * (tempSun[1][tau1] - temp[1][id]) + logGammaSun #if (id%5 == 1): # outline = ("id "+ str(id)+ " logGamma "+ str(logGamma) + "\n") # dbgHandle.write(outline) #//logGamma = logGamma + logFudge + logGammaCol; logGamma = logGamma + logGammaCol #//Add radiation (natural) broadning: gamma = math.exp(logGamma) + Aij logGamma = math.log(gamma) #// #//if (id == 12){ #//System.out.println("LineGrid: logGamma: " + id + " " + logE * logGamma + " press[1][id] " + press[1][id] + " pressSun[1][tau1] " #// + pressSun[1][tau1] + " temp[1][id] " + temp[1][id] + " tempSun[1][tau1] " + tempSun[1][tau1]); #// } #//Voigt "a" parameter with line centre wavelength: logA = 2.0 * logLam0 + logGamma - ln4pi - logC - logDopp a = math.exp(logA) a2 = math.exp(2.0*logA) a3 = math.exp(3.0*logA) a4 = math.exp(4.0*logA) #// if (id == 12) { #//System.out.println("LineGrid: lam0: " + lam0 + " logGam " + logE * logGamma + " logA " + logE * logA); #// } #//if (id == 30) { #// //System.out.println("il v[il] iy y logNumerator logDenominator logInteg "); #// System.out.println("voigt: v logVoigt: "); #//} for il in range(numPoints): v[il] = linePoints[1][il] vAbs = abs(v[il]) vSquare = vAbs * vAbs vFourth = vSquare * vSquare #//System.out.println("LineProf: il, v[il]: " + il + " " + v[il]); #//Approximate Hjerting fn from tabulated expansion coefficients: #// Interpolate in Hjerting table to exact "v" value for each expanstion coefficient: #// Row 0 of Hjerting component table used for tabulated abscissae, Voigt "v" parameter if (vAbs <= 12.0): #//we are within abscissa domain of table Hjert0 = ToolBox.interpol(hjertComp[0], hjertComp[1], vAbs) Hjert1 = ToolBox.interpol(hjertComp[0], hjertComp[2], vAbs) Hjert2 = ToolBox.interpol(hjertComp[0], hjertComp[3], vAbs) Hjert3 = ToolBox.interpol(hjertComp[0], hjertComp[4], vAbs) Hjert4 = ToolBox.interpol(hjertComp[0], hjertComp[5], vAbs) else: #// We use the analytic expansion Hjert0 = 0.0 Hjert1 = (0.56419 / vSquare) + (0.846 / vFourth) Hjert2 = 0.0 Hjert3 = -0.56 / vFourth Hjert4 = 0.0 #//Approximate Hjerting fn with power expansion in Voigt "a" parameter #// "Observation & Analysis of Stellar Photospeheres" (D. Gray), 3rd Ed., p. 258: hjertFn = Hjert0 + a*Hjert1 + a2*Hjert2 + a3*Hjert3 + a4*Hjert4 #if ((id%5 == 1) and (il%2 == 0)): # outline = ("il "+ str(il)+ " hjertFn "+ str(hjertFn) + "\n") # dbgHandle.write(outline) """/* Gaussian + Lorentzian approximation: //if (il <= numCore) { if (v[il] <= 2.0 && v[il] >= -2.0) { // - Gaussian ONLY - at line centre Lorentzian will diverge! core = Math.exp(-1.0 * (v[il] * v[il])); voigt = core; //System.out.println("LINEGRID- CORE: core: " + core); } else { logV = Math.log(Math.abs(v[il])); //Gaussian core: core = Math.exp(-1.0 * (v[il] * v[il])); // if (id == 12) { // System.out.println("LINEGRID- WING: core: " + core); // } //Lorentzian wing: logWing = logA - lnSqRtPi - (2.0 * logV); wing = Math.exp(logWing); voigt = core + wing; // if (id == 12) { // System.out.println("LINEGRID- WING: wing: " + wing + " logV " + logV); // } } // end else */""" #//System.out.println("LINEGRID: il, v[il]: " + il + " " + v[il] + " lineProf[0][il]: " + lineProf[0][il]); #//System.out.println("LINEGRID: il, Voigt, H(): " + il + " " + voigt); #//Convert from H(a,v) in dimensionless Voigt units to physical phi((Delta lambda) profile: #//logVoigt = Math.log(voigt) + 2.0 * logLam0 - lnSqRtPi - logDopp - logC; #//System.out.println("voigt: Before log... id " + id + " il " + il + " hjertFn " + hjertFn); #logVoigt = math.log(hjertFn) + 2.0 * logLam0 - lnSqRtPi - logDopp - logC voigt = hjertFn * math.pow(lam0, 2) / sqRtPi / doppler / c #logVoigt = math.log(voigt) #lineProf[il][id] = math.exp(logVoigt) lineProf[il][id] = voigt if (lineProf[il][id] <= 0.0): lineProf[il][id] = 1.0e-49 #// if (id == 12) { #// System.out.println("il " + il + " linePoints " + 1.0e7 * linePoints[0][il] + " id " + id + " lineProf[il][id] " + lineProf[il][id]); #// } #//System.out.println("LineProf: il, id, lineProf[il][id]: " + il + " " + id + " " + lineProf[il][id]); #} // il lambda loop #// if (id == 20) { #// for (int il = 0; il < numPoints; il++) { #// System.out.format("Voigt: %20.16f %20.16f%n", linePoints[1][il], logE * Math.log(lineProf[il][id])); #// } #// } #} //id loop return lineProf
def stark(linePoints, lam0In, logAij, logGammaCol, numDeps, teff, tauRos, temp, pGas, Ne, tempSun, pGasSun, hjertComp, lineName): c = Useful.c() logC = Useful.logC() #//double k = Useful.k; logK = Useful.logK() #//double e = Useful.e; #//double mE = Useful.mE; lam0 = lam0In #// * 1.0E-7; //nm to cm logLam0 = math.log(lam0) logLam0A = math.log(lam0) + 8.0*math.log(10.0) #//cm to A ln10 = math.log(10.0) ln2 = math.log(2.0) ln4pi = math.log(4.0 * math.pi) lnSqRtPi = 0.5 * math.log(math.pi) sqRtPi = math.sqrt(math.pi) sqPi = math.sqrt(math.pi) #//double ln100 = 2.0*Math.log(10.0); logE = math.log10(math.e) #// for debug output doppler = linePoints[0][1] / linePoints[1][1] logDopp = math.log(doppler) #//System.out.println("LineProf: doppler, logDopp: " + doppler + " " + logE*logDopp); #//Put input parameters into linear cgs units: #//double gammaCol = Math.pow(10.0, logGammaCol); #// Lorentzian broadening: #// Assumes Van der Waals dominates radiative damping #// log_10 Gamma_6 for van der Waals damping around Tau_Cont = 1 in Sun #// - p. 57 of Radiative Transfer in Stellar Atmospheres (Rutten) logGammaSun = 9.0 * ln10 #// Convert to base e #//double logFudge = Math.log(2.5); // Van der Waals enhancement factor tau1 = ToolBox.tauPoint(numDeps, tauRos, 1.0) #//System.out.println("LINEGRID: Tau1: " + tau1); #//logA = 2.0 * logLam0 + logGamma - ln4pi - logC - logDopp; #//a = Math.exp(logA); #//System.out.println("LINEGRID: logA: " + logE * logA); #//Set up a half-profile Delta_lambda grid in Doppler width units #//from line centre to wing numPoints = len(linePoints[0]) #//System.out.println("LineProf: numPoints: " + numPoints); #// Return a 2D numPoints X numDeps array of normalized line profile points (phi) lineProf = [ [ 0.0 for i in range(numDeps) ] for j in range(numPoints) ] #// Line profiel points in Doppler widths - needed for Voigt function, H(a,v): v = [0.0 for i in range(numPoints)] #double logV, ii; #// lineProf[0][0] = 0.0; v[0] = 0.0; //Line centre - cannot do logaritmically! #double gamma, logGamma, a, logA, voigt, core, wing, logWing, logVoigt; Aij = math.pow(10.0, logAij) il0 = 36 #// For Hjerting function approximation: #double vSquare, vFourth, vAbs, a2, a3, a4, Hjert0, Hjert1, Hjert2, Hjert3, Hjert4, hjertFn; #//Parameters for linear Stark broadening: #//Assymptotic ("far wing") "K" parameters #//Stehle & Hutcheon, 1999, A&A Supp Ser, 140, 93 and CDS data table #//Assume K has something to do with "S" and proceed as in Observation and Analysis of #// Stellar Photosphere, 3rd Ed. (D. Gray), Eq. 11.50, #// logTuneStark = math.log(3.16e7) #//convert DeltaI K parameters to deltaS STark profile parameters logKStark = [0.0 for i in range(11)] logKStark[0] = math.log(2.56e-03) + logTuneStark #//Halpha logKStark[1] = math.log(7.06e-03) + logTuneStark #//Hbeta logKStark[2] = math.log(1.19e-02) + logTuneStark #//Hgamma logKStark[3] = math.log(1.94e-02) + logTuneStark #//Hdelta logKStark[4] = math.log(2.95e-02) + logTuneStark #//Hepsilon logKStark[5] = math.log(4.62e-02) + logTuneStark #//H8 JB logKStark[6] = math.log(6.38e-02) + logTuneStark #//H9 JB logKStark[7] = math.log(8.52e-02) + logTuneStark #//H10 JB logKStark[8] = math.log(1.12e-01) + logTuneStark #//H11 JB logKStark[9] = math.log(1.43e-01) + logTuneStark #//H12 JB logKStark[10] = math.log(1.80e-01) + logTuneStark #//H13 JB #//logKStark[11] = Math.log(2.11) + logTuneStark; //H30 JB thisLogK = [0.0 for i in range(4)] #//default initialization #//double thisLogK = logKStark[10]; //default initialization #//which Balmer line are we? crude but effective: if (lam0In > 650.0e-7): thisLogK = logKStark[0] #//Halpha #//System.out.println("Halpha") #} if ( (lam0In > 480.0e-7) and (lam0In < 650.0e-7) ): #//System.out.println("Hbeta"); thisLogK = logKStark[1] #//Hbeta #} if ( (lam0In > 420.0e-7) and (lam0In < 470.0e-7) ): #//System.out.println("Hgamma"); thisLogK = logKStark[2] #//Hgamma #} if ( (lam0In > 400.0e-7) and (lam0In < 450.0e-7) ): #//System.out.println("Hdelta"); thisLogK = logKStark[3] #//Hdelta if ( (lam0In < 400.0e-7) ): #//System.out.println("Hepsilon"); thisLogK = logKStark[4] #//Hepsilon #} #// if ((lam0In < 390.0e-7)){ #// #////This won't work here - "species" is always just "HI": #// int numberInName = (int) lineName.substring("HI".length()); #// //console.log(numberInName); #// thisLogK = logKStark[numberInName-3]; #// } #// #double F0, logF0, lamOverF0, logLamOverF0; //electrostatic field strength (e.s.u.) #double deltaAlpha, logDeltaAlpha, logStark, logStarkTerm; //reduced wavelength de-tuning parameter (Angstroms/e.s.u.) logF0Fac = math.log(1.249e-9) #// log wavelength de-tunings in A: #double logThisPoint, thisPoint; #//System.out.println("il0 " + il0 + " temp[il] " + temp[0][il0] + " press[il] " + logE*press[1][il0]); for id in range(numDeps): #//linear Stark broadening stuff: logF0 = logF0Fac + (0.666667)*Ne[1][id] logLamOverF0 = logLam0A - logF0 lamOverF0 = math.exp(logLamOverF0) #//System.out.println("id " + id + " logF0 " + logE*logF0 + " logLamOverF0 " + logE*logLamOverF0 + " lamOverF0 " + lamOverF0); #//Formula from p. 56 of Radiative Transfer in Stellar Atmospheres (Rutten), #// logarithmically with respect to solar value: logGamma = pGas[1][id] - pGasSun[1][tau1] + 0.7 * (tempSun[1][tau1] - temp[1][id]) + logGammaSun #//logGamma = logGamma + logFudge + logGammaCol logGamma = logGamma + logGammaCol #//Add radiation (natural) broadning: gamma = math.exp(logGamma) + Aij logGamma = math.log(gamma) #// #//if (id == 12){ #//System.out.println("LineGrid: logGamma: " + id + " " + logE * logGamma + " press[1][id] " + press[1][id] + " pressSun[1][tau1] " #// + pressSun[1][tau1] + " temp[1][id] " + temp[1][id] + " tempSun[1][tau1] " + tempSun[1][tau1]); #// } #//Voigt "a" parameter with line centre wavelength: logA = 2.0 * logLam0 + logGamma - ln4pi - logC - logDopp a = math.exp(logA) a2 = math.exp(2.0*logA) a3 = math.exp(3.0*logA) a4 = math.exp(4.0*logA) #// if (id == 12) { #//System.out.println("LineGrid: lam0: " + lam0 + " logGam " + logE * logGamma + " logA " + logE * logA); #// } #//if (id == 30) { #// //System.out.println("il v[il] iy y logNumerator logDenominator logInteg "); #// System.out.println("voigt: v logVoigt: "); #//} for il in range(numPoints): v[il] = linePoints[1][il] vAbs = abs(v[il]) vSquare = vAbs * vAbs vFourth = vSquare * vSquare #//System.out.println("LineProf: il, v[il]: " + il + " " + v[il]); #//Approximate Hjerting fn from tabulated expansion coefficients: #// Interpolate in Hjerting table to exact "v" value for each expanstion coefficient: #// Row 0 of Hjerting component table used for tabulated abscissae, Voigt "v" parameter if (vAbs <= 12.0): #//we are within abscissa domain of table Hjert0 = ToolBox.interpol(hjertComp[0], hjertComp[1], vAbs) Hjert1 = ToolBox.interpol(hjertComp[0], hjertComp[2], vAbs) Hjert2 = ToolBox.interpol(hjertComp[0], hjertComp[3], vAbs) Hjert3 = ToolBox.interpol(hjertComp[0], hjertComp[4], vAbs) Hjert4 = ToolBox.interpol(hjertComp[0], hjertComp[5], vAbs) else: #// We use the analytic expansion Hjert0 = 0.0 Hjert1 = (0.56419 / vSquare) + (0.846 / vFourth) Hjert2 = 0.0 Hjert3 = -0.56 / vFourth Hjert4 = 0.0 #} #//Approximate Hjerting fn with power expansion in Voigt "a" parameter #// "Observation & Analysis of Stellar Photospeheres" (D. Gray), 3rd Ed., p. 258: hjertFn = Hjert0 + a*Hjert1 + a2*Hjert2 + a3*Hjert3 + a4*Hjert4; logStark = -49.0 #//re-initialize if (vAbs > 2.0): #//System.out.println("Adding in Stark wing"); thisPoint = 1.0e8 * abs(linePoints[0][il]) #//cm to A logThisPoint = math.log(thisPoint) logDeltaAlpha = logThisPoint - logF0 deltaAlpha = math.exp(logDeltaAlpha) logStarkTerm = ( math.log(lamOverF0 + deltaAlpha) - logLamOverF0 ) logStark = thisLogK + 0.5*logStarkTerm - 2.5*logDeltaAlpha #//System.out.println("il " + il + " logDeltaAlpha " + logE*logDeltaAlpha + " logStarkTerm " + logE*logStarkTerm + " logStark " + logE*logStark); #//console.log("il " + il + " logDeltaAlpha " + logE*logDeltaAlpha + " logStarkTerm " + logE*logStarkTerm + " logStark " + logE*logStark); #//System.out.println("id " + id + " il " + il + " v[il] " + v[il] #// + " hjertFn " + hjertFn + " Math.exp(logStark) " + Math.exp(logStark)); #//not here! hjertFn = hjertFn + Math.exp(logStark); #//System.out.println("LINEGRID: il, v[il]: " + il + " " + v[il] + " lineProf[0][il]: " + lineProf[0][il]); #//System.out.println("LINEGRID: il, Voigt, H(): " + il + " " + voigt); #//Convert from H(a,v) in dimensionless Voigt units to physical phi((Delta lambda) profile: #//logVoigt = Math.log(voigt) + 2.0 * logLam0 - lnSqRtPi - logDopp - logC; #//System.out.println("stark: Before log... id " + id + " il " + il + " hjertFn " + hjertFn); #logVoigt = math.log(hjertFn) - lnSqRtPi - logDopp voigt = hjertFn / sqRtPi / doppler #//logVoigt = math.log(voigt) logStark = logStark - logF0 if (vAbs > 2.0): #//if (id == 24) { #// System.out.println("il " + il + " v[il] " + v[il] + " logVoigt " + logE*logVoigt + " logStark " + logE*logStark); #//} #//voigt = math.exp(logVoigt) + math.exp(logStark) voigt = voigt + math.exp(logStark) #//logVoigt = math.log(voigt) #logVoigt = logVoigt + 2.0 * logLam0 - logC voigt = voigt * math.pow(lam0, 2) / c #//lineProf[il][id] = math.exp(logVoigt) lineProf[il][id] = voigt if (lineProf[il][id] <= 0.0): lineProf[il][id] = 1.0e-49 #//if (id == 24) { #// System.out.println("lam0In " + lam0In); #// System.out.println("il " + il + " linePoints " + 1.0e7 * linePoints[0][il] + " id " + id + " lineProf[il][id] " + lineProf[il][id]); #//} #//System.out.println("LineProf: il, id, lineProf[il][id]: " + il + " " + id + " " + lineProf[il][id]); #} // il lambda loop #// if (id == 20) { #// for (int il = 0; il < numPoints; il++) { #// System.out.format("Voigt: %20.16f %20.16f%n", linePoints[1][il], logE * Math.log(lineProf[il][id])); #// } #// } #} //id loop return lineProf
def opacC1(numDeps, temp, lambda2, logGroundPops): """#//c****************************************************************************** #//c This routine computes the bound-free absorption due to C I. #//c******************************************************************************""" #//System.out.println("opacC1 called..."); #// include 'Atmos.com' #// include 'Kappa.com' sigma = 0.0 aC1 = [0.0 for i in range(numDeps)] #//cross-section is zero below threshold, so initialize: for i in range(numDeps): aC1[i] = 0.0 waveno = 1.0 / lambda2 #//cm^-1?? freq = Useful.c / lambda2 #double arg; c1240 = [0.0 for i in range(numDeps)] c1444 = [0.0 for i in range(numDeps)] freq1 = 0.0 #double logTkev; tkev = [0.0 for i in range(numDeps)] #// int modcount = 0; for i in range(numDeps): logTkev = temp[1][i] + Useful.logK() - Useful.logEv() tkev[i] = math.exp(logTkev) #//c initialize some quantities for each new model atmosphere for i in range(numDeps): c1240[i] = 5.0 * math.exp(-1.264 / tkev[i]) c1444[i] = math.exp(-2.683 / tkev[i]) #//c initialize some quantities for each new model atmosphere or new frequency; #//c Luo, D. and Pradhan, A.K. 1989, J.Phys. B, 22, 3377-3395. #//c Burke, P.G. and Taylor, K.T. 1979, J. Phys. B, 12, 2971-2984. #// if (modelnum.ne.modcount .or. freq.ne.freq1) then #double aa, bb, eeps; #freq1 = freq; ryd = 109732.298 #//Rydberg constant in cm^-1 #//waveno = freq/2.99792458d10 xs0 = 0.0 xs1 = 0.0 xd0 = 0.0 xd1 = 0.0 xd2 = 0.0 x1444 = 0.0 x1240 = 0.0 x1100 = 0.0 #//c P2 3P 1 #//c I AM NOT SURE WHETHER THE CALL TO SEATON IN THE NEXT STATEMENT IS #//c CORRECT, BUT IT ONLY AFFECTS THINGS BELOW 1100A if (freq >= 2.7254e15): arg = -16.80 - ((waveno - 90777.000) / 3.0 / ryd) x1100 = math.pow(10.0, arg) * seaton(2.7254e15, 1.219e-17, 2.0e0, 3.317e0, freq) #//c P2 1D 2 if (freq >= 2.4196e15): arg = -16.80 - ((waveno - 80627.760) / 3.0 / ryd) xd0 = math.pow(10.0, arg) eeps = (waveno - 93917.0) * 2.0 / 9230.0 aa = 22.0e-18 bb = 26.0e-18 xd1 = ((aa * eeps) + bb) / (math.pow(eeps, 2) + 1.0) eeps = (waveno - 111130.0) * 2.0 / 2743.0 aa = -10.5e-18 bb = 46.0e-18 xd2 = ((aa * eeps) + bb) / (math.pow(eeps, 2) + 1.0) x1240 = xd0 + xd1 + xd2 #//c P2 1S 3 if (freq >= 2.0761e15): arg = -16.80 - ((waveno - 69172.400) / 3.0 / ryd) xs0 = math.pow(10.0, arg) eeps = (waveno - 97700.0) * 2.0 / 2743.0 aa = 68.0e-18 bb = 118.0e-18 xs1 = ((aa * eeps) + bb) / (math.pow(eeps, 2) + 1.0) x1444 = xs0 + xs1 #//System.out.println("freq " + freq + " lambda " + lambda); for i in range(numDeps): if (freq >= 2.0761e15): sigma = (x1100 * 9.0 + x1240 * c1240[i] + x1444 * c1444[i]) aC1[i] = sigma * math.exp(logGroundPops[i]) #//System.out.println("i " + i + " sigma " + sigma + " aC1 " + aC1[i]); #//System.out.println("i " + i + " logPop " + logGroundPops[i] + " aC1 " + aC1[i]); return aC1
def planck(temp, lambda2): """ /** * Inputs: lambda: a single scalar wavelength in cm temp: a single scalar * temperature in K Returns log of Plank function in logBBlam - B_lambda * distribution in pure cgs units: ergs/s/cm^2/ster/cm */""" #//int numLams = (int) (( lamSetup[1] - lamSetup[0] ) / lamSetup[2]) + 1; #double logBBlam; //, BBlam; #//double c = Useful.c; //linear logC = Useful.logC() # //log #//double k = Useful.k; //linear logK = Useful.logK() #//log #//double h = Useful.h; //linear logH = Useful.logH() #//log logPreFac = math.log(2.0) + logH + 2.0 * logC #//log logExpFac = logH + logC - logK #//log #//double preFac = 2.0 * h * ( c * c ); //linear #//double expFac = ( h / k ) * c; //linear #//System.out.println("logC " + logC + " logK " + logK + " logH " + logH); #//System.out.println("logPreFac " + logPreFac + " logExpFac " + logExpFac); #//Declare scratch variables: #double logLam, logPreLamFac, logExpLamFac, expon, logExpon, eTerm, denom, logDenom; //log #//double preLamFac, expLamFac, expon, denom; //linear #//for (int il = 0; il < numLams; il++){ #//lambda = lambda[il] * 1.0E-7; // convert nm to cm #//lambda = lambda * 1.0E-7; // convert nm to cm logLam = math.log(lambda2) #// Do the call to log for lambda once //log #//System.out.println("lambda " + lambda + " logLam " + logLam); logPreLamFac = logPreFac - 5.0 * logLam #//log logExpLamFac = logExpFac - logLam #//log #//System.out.println("logPreLamFac " + logPreLamFac + " logExpLamFac " + logExpLamFac); #// Be VERY careful about how we divide by lambda^5: #//preLamFac = preFac / ( lambda * lambda ); //linear #//preLamFac = preLamFac / ( lambda * lambda ); //linear #//preLamFac = preLamFac / lambda; //linear #//expLamFac = expFac / lambda; #//for (int id = 0; id < numDeps; id++){ #//logExpon = logExpLamFac - temp[1][id]; #//This is very subtle and dangerous! logExpon = logExpLamFac - math.log(temp) #// log of hc/kTlambda #//System.out.println("temp " + temp + " logTemp " + Math.log(temp)); expon = math.exp(logExpon) #// hc/kTlambda #//System.out.println("logExpon " + logExpon + " expon " + expon + " denom " + denom); #// expon = expLamFac / temp; //linear eTerm = math.exp(expon) #// e^hc/ktlambda denom = eTerm - 1.0 #// e^hc/ktlambda - 1 logDenom = math.log(denom) #// log(e^hc/ktlambda - 1) #//BBlam[1][id][il] = logPreLamFac - logDenom; #//BBlam[0][id][il] = Math.exp(BBlam[1][id][il]); logBBlam = logPreLamFac - logDenom #//log #// Not needed? BBlam = math.exp(logBBlam) #//log #//BBlam = preLamFac / denom; //linear #// } //id loop - depths #// } //il loop - lambdas return logBBlam;
def lineKap(lam0In, logNums, logFluIn, linePoints, lineProf, numDeps, zScale, tauRos, temp, rho, logFudgeTune): logE10 = math.log(10.0) #//natural log of 10 c = Useful.c() logC = Useful.logC() k = Useful.k() logK = Useful.logK() logH = Useful.logH() logEe = Useful.logEe() logMe = Useful.logMe() ln10 = math.log(10.0) logE = math.log10(math.e) #// for debug output log2pi = math.log(2.0 * math.pi) log2 = math.log(2.0) lam0 = lam0In #// * 1.0E-7; //nm to cm logLam0 = math.log(lam0) #//double logNl = logNlIn * ln10; // Convert to base e logFlu = logFluIn * ln10 #// Convert to base e logKScale = math.log10(zScale) #//chiI = chiI * Useful.eV; // Convert lower E-level from eV to ergs #//double boltzFacI = chiI / k; // Pre-factor for exponent of excitation Boltzmann factor #//double logSahaFac = log2 + (3.0/2.0) * ( log2pi + logMe + logK - 2.0*logH); #//chiL = chiL * Useful.eV; // Convert lower E-level from eV to ergs #//double boltzFac = chiL / k; // Pre-factor for exponent of excitation Boltzmann factor numPoints = len(linePoints[0]) #//System.out.println("LineKappa: numPoints: " + numPoints); #double logPreFac; #//This converts f_lu to a volume extinction coefficient per particle - Rutten, p. 23 logPreFac = logFlu + math.log(math.pi) + 2.0 * logEe - logMe - logC #//System.out.println("LINEKAPPA: logPreFac " + logPreFac); #//Assume wavelength, lambda, is constant throughout line profile for purpose #// of computing the stimulated emission correction #double logExpFac; logExpFac = logH + logC - logK - logLam0 #//System.out.println("LINEKAPPA: logExpFac " + logExpFac); #// int refRhoIndx = TauPoint.tauPoint(numDeps, tauRos, 1.0); #// double refLogRho = rho[1][refRhoIndx]; #//System.out.println("LINEKAPPA: refRhoIndx, refRho " + refRhoIndx + " " + logE*refRho); #// return a 2D numPoints x numDeps array of monochromatic *LINE* extinction line profiles logKappaL = [[0.0 for i in range(numDeps)] for j in range(numPoints)] #double num, logNum, logExpFac2, expFac, stimEm, logStimEm, logSaha, saha, logIonFrac; #double logNe; for id in range(numDeps): logExpFac2 = logExpFac - temp[1][id] expFac = -1.0 * math.exp(logExpFac2) stimEm = 1.0 - math.exp(expFac) logStimEm = math.log(stimEm) logNum = logNums[id] #//if (id == refRhoIndx) { #// System.out.println("LINEKAPPA: logStimEm " + logE*logStimEm); #//} for il in range(numPoints): #// From Radiative Transfer in Stellar Atmospheres (Rutten), p.31 #// This is a *volume* co-efficient ("alpha_lambda") in cm^-1: logKappaL[il][id] = logPreFac + logStimEm + logNum + math.log( lineProf[il][id]) #//if (id == 36) { #// System.out.println("il " + il + " logNum " + logE*logNum + " Math.log(lineProf[il][id]) " + logE*Math.log(lineProf[il][id])); #//// //System.out.println("logPreFac " + logPreFac + " logStimEm " + logStimEm); #//} #//System.out.println("LINEKAPPA: id, il " + id + " " + il + " logKappaL " + logE * logKappaL[il][id]); #//Convert to mass co-efficient in g/cm^2: #// This direct approach won't work - is not consistent with fake Kramer's law scaling of Kapp_Ros with g instead of rho logKappaL[il][id] = logKappaL[il][id] - rho[1][id] #//Try something: #// #// ********************** #// Opacity problem #2 #// #//Line opacity needs to be enhanced by same factor as the conitnuum opacity #// - related to Opacity problem #1 (logFudgeTune in GrayStarServer3.java) - ?? #// logKappaL[il][id] = logKappaL[il][id] + logE10 * logFudgeTune #//if (id == 12) { #// System.out.println("LINEKAPPA: id, il " + id + " " + il + " logKappaL " + logE * logKappaL[il][id] #// + " logPreFac " + logE*logPreFac + " logStimEm " + logE*logStimEm + " logNum " + logE*logNum #// + " log(lineProf[il]) " + logE*Math.log(lineProf[il][id]) + " rho[1][id] " + logE * rho[1][id]); #// } #//if (id == refRhoIndx-45) { #// System.out.println("LINEKAPPA: id, il " + id + " " + il + " logKappaL " + logE*logKappaL[il][id] #// + " logPreFac " + logE*logPreFac + " logStimEm " + logE*logStimEm + " logNum " + logE*logNum + " logRho " + logE*rho[1][id] #// + " log(lineProf[1]) " + logE*Math.log(lineProf[1][il]) ); #//} #} // il - lambda loop #} // id - depth loop return logKappaL
def molPops(nmrtrLogNumB, nmrtrDissE, logUwA, nmrtrLogUwB, nmrtrLogQwAB, nmrtrLogMuAB, \ numMolsB, logNumB, dissEArr, logUwB, logQwABArr, logMuABArr, \ logGroundRatio, numDeps, temp): # line 1: //species A data - ionization equilibrium of A # //data for set of species "B" - molecular equlibrium for set {AB} """Diatomic molecular equilibrium routine that accounts for molecule formation: // Returns depth distribution of molecular population // Input parameters: // logNum - array with depth-dependent total element number densities (cm^-3) // chiI1 - ground state ionization energy of neutral stage // chiI2 - ground state ionization energy of singly ionized stage // Also needs atsmopheric structure information: // numDeps // temp structure // rho structure // // Atomic element "A" is the one kept on the LHS of the master fraction, whose ionization fractions are included // in the denominator of the master fraction // Element "B" refers to array of other sintpecies with which A forms molecules "AB" """ logE = math.log10(math.e) #// for debug output #//System.out.println("molPops: nmrtrDissE " + nmrtrDissE + " log10UwA " + log10UwA[0] + " " + log10UwA[1] + " nmrtrLog10UwB " + #// nmrtrLog10UwB[0] + " " + nmrtrLog10UwB[1] + " nmrtrLog10QwAB " + logE*nmrtrLogQwAB[2] + " nmrtrLogMuAB " + logE*nmrtrLogMuAB #// + " numMolsB " + numMolsB + " dissEArr " + dissEArr[0] + " log10UwBArr " + log10UwBArr[0][0] + " " + log10UwBArr[0][1] + " log10QwABArr " + #// logE*logQwABArr[0][2] + " logMuABArr " + logE*logMuABArr[0]); #//System.out.println("Line: nmrtrLog10UwB[0] " + logE*nmrtrLog10UwB[0] + " nmrtrLog10UwB[1] " + logE*nmrtrLog10UwB[1]); ln10 = math.log(10.0) log2pi = math.log(2.0 * math.pi) log2 = math.log(2.0) logE10 = math.log(10.0) #// Convert to natural logs: #double Ttheta, thisTemp; #//Treat at least one molecule - if there are really no molecules for an atomic species, #//there will be one phantom molecule in the denominator of the ionization fraction #//with an impossibly high dissociation energy if (numMolsB == 0): numMolsB = 1 #//This should be inherited, but let's make sure: dissEArr[0] = 29.0 #//eV #//var molPops = function(logNum, numeratorLogNumB, numeratorDissE, numeratorLog10UwA, numeratorLog10QwAB, numeratorLogMuAB, //species A data - ionization equilibrium of A #//Molecular partition functions - default initialization: thisLogUwB = [0.0 for i in range(numMolsB)] for iMol in range(numMolsB): thisLogUwB[ iMol] = 0.0 #// variable for temp-dependent computed partn fn of array element B thisLogUwA = 0.0 #// element A nmrtrThisLogUwB = 0.0 #// element A thisLogQwAB = math.log(300.0) nmrtrThisLogQwAB = math.log(300.0) #//For clarity: neutral stage of atom whose ionization equilibrium is being computed is element A #// for molecule formation: #logUwA = [0.0 for i in range(5)] #nmrtrLogUwB = [0.0 for i in range(5)] #for kk in range(len(logUwA)): #logUwA[kk] = logE10*log10UwA[kk] #nmrtrLogUwB[kk] = logE10*nmrtrLog10UwB[kk] #// lburns #// Array of elements B for all molecular species AB: #double[][] logUwB = new double[numMolsB][2]; #logUwB = [ [ 0.0 for i in range(5) ] for j in range(numMolsB) ] #//if (numMolsB > 0){ #for iMol in range(numMolsB): # for kk in range(5): # logUwB[iMol][kk] = logE10*log10UwBArr[iMol][kk] # // lburns new loop #//} #// Molecular partition functions: #// double nmrtrLogQwAB = logE10*nmrtrLog10QwAB; #// double[] logQwAB = new double[numMolsB]; #// //if (numMolsB > 0){ #// for (int iMol = 0; iMol < numMolsB; iMol++){ #// logQwAB[iMol] = logE10*log10QwABArr[iMol]; #// } # //} #//Molecular dissociation Boltzmann factors: nmrtrBoltzFacIAB = 0.0 nmrtrLogMolSahaFac = 0.0 logDissE = math.log(nmrtrDissE) + Useful.logEv() #//System.out.println("logDissE " + logE*logDissE) logBoltzFacIAB = logDissE - Useful.logK() #//System.out.println("logBoltzFacIAB " + logE*logBoltzFacIAB); nmrtrBoltzFacIAB = math.exp(logBoltzFacIAB) nmrtrLogMolSahaFac = (3.0 / 2.0) * (log2pi + nmrtrLogMuAB + Useful.logK() - 2.0 * Useful.logH()) #//System.out.println("nmrtrLogMolSahaFac " + logE*nmrtrLogMolSahaFac); #//System.out.println("nmrtrDissE " + nmrtrDissE + " logDissE " + logE*logDissE + " logBoltzFacIAB " + logE*logBoltzFacIAB + " nmrtrBoltzFacIAB " + nmrtrBoltzFacIAB + " nmrtrLogMuAB " + logE*nmrtrLogMuAB + " nmrtrLogMolSahaFac " + logE*nmrtrLogMolSahaFac); boltzFacIAB = [0.0 for i in range(numMolsB)] logMolSahaFac = [0.0 for i in range(numMolsB)] #//if (numMolsB > 0){ for iMol in range(numMolsB): logDissE = math.log(dissEArr[iMol]) + Useful.logEv() logBoltzFacIAB = logDissE - Useful.logK() boltzFacIAB[iMol] = math.exp(logBoltzFacIAB) logMolSahaFac[iMol] = (3.0 / 2.0) * ( log2pi + logMuABArr[iMol] + Useful.logK() - 2.0 * Useful.logH()) #//System.out.println("logMolSahaFac[iMol] " + logE*logMolSahaFac[iMol]); #//System.out.println("iMol " + iMol + " dissEArr[iMol] " + dissEArr[iMol] + " logDissE " + logE*logDissE + " logBoltzFacIAB " + logE*logBoltzFacIAB + " boltzFacIAB[iMol] " + boltzFacIAB[iMol] + " logMuABArr " + logE*logMuABArr[iMol] + " logMolSahaFac " + logE*logMolSahaFac[iMol]); #//double[] logNums = new double[numDeps] #//} #// For molecular species: #double nmrtrSaha, nmrtrLogSahaMol, nmrtrLogInvSahaMol; //, nmrtrInvSahaMol; logMolFrac = [0.0 for i in range(numDeps)] logSahaMol = [0.0 for i in range(numMolsB)] invSahaMol = [0.0 for i in range(numMolsB)] #// #// System.out.println("molPops: id nmrtrLogNumB logNumBArr[0] logGroundRatio"); for id in range(numDeps): #//System.out.format("%03d, %21.15f, %21.15f, %21.15f, %n", id, logE*nmrtrLogNumB[id], logE*logNumB[0][id], logE*logGroundRatio[id]); #//// reduce or enhance number density by over-all Rosseland opcity scale parameter #//Determine temparature dependent partition functions Uw: thisTemp = temp[0][id] #Ttheta = 5040.0 / thisTemp """ if (Ttheta >= 1.0): thisLogUwA = logUwA[0] nmrtrThisLogUwB = nmrtrLogUwB[0] for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][0] if (Ttheta <= 0.5): thisLogUwA = logUwA[1] nmrtrThisLogUwB = nmrtrLogUwB[1] for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][1] if (Ttheta > 0.5 and Ttheta < 1.0): thisLogUwA = ( logUwA[1] * ((Ttheta - 0.5)/(1.0 - 0.5)) ) \ + ( logUwA[0] * ((1.0 - Ttheta)/(1.0 - 0.5)) ) nmrtrThisLogUwB = ( nmrtrLogUwB[1] * ((Ttheta - 0.5)/(1.0 - 0.5)) ) \ + ( nmrtrLogUwB[0] * ((1.0 - Ttheta)/(1.0 - 0.5)) ) for iMol in range(numMolsB): thisLogUwB[iMol] = ( logUwB[iMol][1] * ((Ttheta - 0.5)/(1.0 - 0.5)) ) \ + ( logUwB[iMol][0] * ((1.0 - Ttheta)/(1.0 - 0.5)) ) """ #// NEW Determine temperature dependent partition functions Uw: lburns thisTemp = temp[0][id] if (thisTemp <= 130): thisLogUwA = logUwA[0] nmrtrThisLogUwB = nmrtrLogUwB[0] for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][0] if (thisTemp > 130 and thisTemp <= 500): thisLogUwA = logUwA[1] * (thisTemp - 130)/(500 - 130) \ + logUwA[0] * (500 - thisTemp)/(500 - 130) nmrtrThisLogUwB = nmrtrLogUwB[1] * (thisTemp - 130)/(500 - 130) \ + nmrtrLogUwB[0] * (500 - thisTemp)/(500 - 130) for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][1] * (thisTemp - 130)/(500 - 130) \ + logUwB[iMol][0] * (500 - thisTemp)/(500 - 130) if (thisTemp > 500 and thisTemp <= 3000): thisLogUwA = logUwA[2] * (thisTemp - 500)/(3000 - 500) \ + logUwA[1] * (3000 - thisTemp)/(3000 - 500) nmrtrThisLogUwB = nmrtrLogUwB[2] * (thisTemp - 500)/(3000 - 500) \ + nmrtrLogUwB[1] * (3000 - thisTemp)/(3000 - 500) for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][2] * (thisTemp - 500)/(3000 - 500) \ + logUwB[iMol][1] * (3000 - thisTemp)/(3000 - 500) if (thisTemp > 3000 and thisTemp <= 8000): thisLogUwA = logUwA[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUwA[2] * (8000 - thisTemp)/(8000 - 3000) nmrtrThisLogUwB = nmrtrLogUwB[3] * (thisTemp - 3000)/(8000 - 3000) \ + nmrtrLogUwB[2] * (8000 - thisTemp)/(8000 - 3000) for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][3] * (thisTemp - 3000)/(8000 - 3000) \ + logUwB[iMol][2] * (8000 - thisTemp)/(8000 - 3000) if (thisTemp > 8000 and thisTemp < 10000): thisLogUwA = logUwA[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUwA[3] * (10000 - thisTemp)/(10000 - 8000) nmrtrThisLogUwB = nmrtrLogUwB[4] * (thisTemp - 8000)/(10000 - 8000) \ + nmrtrLogUwB[3] * (10000 - thisTemp)/(10000 - 8000) for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][4] * (thisTemp - 8000)/(10000 - 8000) \ + logUwB[iMol][3] * (10000 - thisTemp)/(10000 - 8000) if (thisTemp >= 10000): thisLogUwA = logUwA[4] nmrtrThisLogUwB = nmrtrLogUwB[4] for iMol in range(numMolsB): thisLogUwB[iMol] = logUwB[iMol][4] for iMol in range(numMolsB): if (thisTemp < 3000.0): thisLogQwAB = ( logQwABArr[iMol][1] * (3000.0 - thisTemp)/(3000.0 - 500.0) ) \ + ( logQwABArr[iMol][2] * (thisTemp - 500.0)/(3000.0 - 500.0) ) if ((thisTemp >= 3000.0) and (thisTemp <= 8000.0)): thisLogQwAB = ( logQwABArr[iMol][2] * (8000.0 - thisTemp)/(8000.0 - 3000.0) ) \ + ( logQwABArr[iMol][3] * (thisTemp - 3000.0)/(8000.0 - 3000.0) ) if (thisTemp > 8000.0): thisLogQwAB = ( logQwABArr[iMol][3] * (10000.0 - thisTemp)/(10000.0 - 8000.0) ) \ + ( logQwABArr[iMol][4] * (thisTemp - 8000.0)/(10000.0 - 8000.0) ) if (thisTemp < 3000.0): nmrtrThisLogQwAB = ( nmrtrLogQwAB[1] * (3000.0 - thisTemp)/(3000.0 - 500.0) ) \ + ( nmrtrLogQwAB[2] * (thisTemp - 500.0)/(3000.0 - 500.0) ) if ((thisTemp >= 3000.0) and (thisTemp <= 8000.0)): nmrtrThisLogQwAB = ( nmrtrLogQwAB[2] * (8000.0 - thisTemp)/(8000.0 - 3000.0) ) \ + ( nmrtrLogQwAB[3] * (thisTemp - 3000.0)/(8000.0 - 3000.0) ) if (thisTemp > 8000.0): nmrtrThisLogQwAB = ( nmrtrLogQwAB[3] * (10000.0 - thisTemp)/(10000.0 - 8000.0) ) \ + ( nmrtrLogQwAB[4] * (thisTemp - 8000.0)/(10000.0 - 8000.0) ) #//For clarity: neutral stage of atom whose ionization equilibrium is being computed is element A #// for molecule formation: # //Ionization stage Saha factors: #//System.out.println("id " + id + " nmrtrLogNumB[id] " + logE*nmrtrLogNumB[id]); # // if (id == 16){ # // System.out.println("id " + id + " nmrtrLogNumB[id] " + logE*nmrtrLogNumB[id] + " pp nmrtB " + (logE*(nmrtrLogNumB[id]+temp[1][id]+Useful.logK())) + " nmrtrThisLogUwB " + logE*nmrtrThisLogUwB + " thisLogUwA " + logE*thisLogUwA + " nmrtrLogQwAB " + logE*nmrtrThisLogQwAB); # //System.out.println("nmrtrThisLogUwB " + logE*nmrtrThisLogUwB + " thisLogUwA " + logE*thisLogUwA + " nmrtrThisLogQwAB " + logE*nmrtrThisLogQwAB); # // } nmrtrLogSahaMol = nmrtrLogMolSahaFac - nmrtrLogNumB[id] - ( nmrtrBoltzFacIAB / temp[0][id]) + ( 3.0 * temp[1][id] / 2.0) + nmrtrThisLogUwB + thisLogUwA - nmrtrThisLogQwAB nmrtrLogInvSahaMol = -1.0 * nmrtrLogSahaMol #//System.out.println("nmrtrLogInvSahaMol " + logE*nmrtrLogInvSahaMol); #//nmrtrInvSahaMol = Math.exp(nmrtrLogSahaMol); #// if (id == 16){ #// System.out.println("nmrtrLogInvSahaMol " + logE*nmrtrLogInvSahaMol); #// } #// if (id == 16){ #// System.out.println("nmrtrBoltzFacIAB " + nmrtrBoltzFacIAB + " nmrtrThisLogUwB " + logE*nmrtrThisLogUwB + " thisLogUwA " + logE*thisLogUwA + " nmrtrThisLogQwAB " + nmrtrThisLogQwAB); #// System.out.println("nmrtrLogSahaMol " + logE*nmrtrLogSahaMol); // + " nmrtrInvSahaMol " + nmrtrInvSahaMol); #// } #//Molecular Saha factors: for iMol in range(numMolsB): #//System.out.println("iMol " + iMol + " id " + id + " logNumB[iMol][id] " + logE*nmrtrLogNumB[id]); #//System.out.println("iMol " + iMol + " thisLogUwB[iMol] " + logE*thisLogUwB[iMol] + " thisLogUwA " + logE*thisLogUwA + " thisLogQwAB " + logE*thisLogQwAB); logSahaMol[iMol] = logMolSahaFac[iMol] - logNumB[iMol][id] - ( boltzFacIAB[iMol] / temp[0][id]) + ( 3.0 * temp[1][id] / 2.0) + thisLogUwB[iMol] + thisLogUwA - thisLogQwAB #//For denominator of ionization fraction, we need *inverse* molecular Saha factors (N_AB/NI): logSahaMol[iMol] = -1.0 * logSahaMol[iMol] invSahaMol[iMol] = math.exp(logSahaMol[iMol]) #//TEST invSahaMol[iMol] = 1.0e-99; //test #// if (id == 16){ #// System.out.println("iMol " + iMol + " boltzFacIAB[iMol] " + boltzFacIAB[iMol] + " thisLogUwB[iMol] " + logE*thisLogUwB[iMol] + " logQwAB[iMol] " + logE*thisLogQwAB + " logNumB[iMol][id] " + logE*logNumB[iMol][id] + " logMolSahaFac[iMol] " + logE*logMolSahaFac[iMol]); #// System.out.println("iMol " + iMol + " logSahaMol " + logE*logSahaMol[iMol] + " invSahaMol[iMol] " + invSahaMol[iMol]); #// } #//Compute log of denominator is ionization fraction, f_stage # //default initialization # // - ratio of total atomic particles in all ionization stages to number in ground state: denominator = math.exp( logGroundRatio[id] ) #//default initialization - ratio of total atomic particles in all ionization stages to number in ground state #//molecular contribution for iMol in range(numMolsB): #// if (id == 16){ #// System.out.println("invSahaMol[iMol] " + invSahaMol[iMol] + " denominator " + denominator); #// } denominator = denominator + invSahaMol[iMol] #// logDenominator = math.log(denominator) #//System.out.println("logGroundRatio[id] " + logE*logGroundRatio[id] + " logDenominator " + logE*logDenominator); #// if (id == 16){ #// System.out.println("id " + id + " logGroundRatio " + logGroundRatio[id] + " logDenominator " + logDenominator); #// } #//if (id == 36){ #// System.out.println("logDenominator " + logE*logDenominator); #// } #//var logDenominator = Math.log( 1.0 + saha21 + (saha32 * saha21) + (saha43 * saha32 * saha21) + (saha54 * saha43 * saha32 * saha21) ); logMolFrac[id] = nmrtrLogInvSahaMol - logDenominator #// if (id == 16){ #// System.out.println("id " + id + " logMolFrac[id] " + logE*logMolFrac[id]); #// } #//logNums[id] = logNum[id] + logMolFrac; #} //id loop return logMolFrac
def sahaRHS(chiI, logUwU, logUwL, temp): """RHS of partial pressure formulation of Saha equation in standard form (N_U*P_e/N_L on LHS) // Returns depth distribution of LHS: Phi(T) === N_U*P_e/N_L (David Gray notation) // Input parameters: // chiI - ground state ionization energy of lower stage // log10UwUArr, log10UwLArr - array of temperature-dependent partition function for upper and lower ionization stage // Also needs atsmopheric structure information: // numDeps // temp structure // // Atomic element "A" is the one whose ionization fractions are being computed // Element "B" refers to array of other species with which A forms molecules "AB" """ ln10 = math.log(10.0) logE = math.log10(math.e) #// for debug output log2pi = math.log(2.0 * math.pi) log2 = math.log(2.0) #// var numMols = dissEArr.length; #// Parition functions passed in are 2-element vectore with remperature-dependent base 10 log Us #// Convert to natural logs: #double Ttheta, thisTemp; #//Default initializations: #//We need one more stage in size of saha factor than number of stages we're actualy populating thisLogUwU = 0.0 thisLogUwL = 0.0 logE10 = math.log(10.0) #//We need one more stage in size of saha factor than number of stages we're actualy populating #logUwU = [0.0 for i in range(5)] #logUwL = [0.0 for i in range(5)] for kk in range(len(logUwL)): logUwU[kk] = logUwL[kk] # logUwL[kk] = logE10*log10UwLArr[kk] #//System.out.println("chiL before: " + chiL); #// If we need to subtract chiI from chiL, do so *before* converting to tiny numbers in ergs! #//atomic ionization stage Boltzmann factors: #double logChiI, logBoltzFacI; #double boltzFacI; logChiI = math.log(chiI) + Useful.logEv() logBoltzFacI = logChiI - Useful.logK() boltzFacI = math.exp(logBoltzFacI) #//Extra factor of k to get k^5/2 in the P_e formulation of Saha Eq. logSahaFac = log2 + (3.0 / 2.0) * (log2pi + Useful.logMe() + Useful.logK() - 2.0 * Useful.logH()) + Useful.logK() #//double[] logLHS = new double[numDeps]; #double logLHS; #// For atomic ionization stages: #double logSaha, saha, expFac; #// for (int id = 0; id < numDeps; id++) { #// #//Determine temperature dependent partition functions Uw: thisTemp = temp[0] #Ttheta = 5040.0 / thisTemp """ if (Ttheta >= 1.0): thisLogUwU = logUwU[0] thisLogUwL = logUwL[0] if (Ttheta <= 0.5): thisLogUwU = logUwU[1] thisLogUwL = logUwL[1] if (Ttheta > 0.5 and Ttheta < 1.0): thisLogUwU = ( logUwU[1] * (Ttheta - 0.5)/(1.0 - 0.5) ) + ( logUwU[0] * (1.0 - Ttheta)/(1.0 - 0.5) ) thisLogUwL = ( logUwL[1] * (Ttheta - 0.5)/(1.0 - 0.5) ) + ( logUwL[0] * (1.0 - Ttheta)/(1.0 - 0.5) ) """ if (thisTemp <= 130): thisLogUwU = logUwU[0] thisLogUwL = logUwL[0] if (thisTemp > 130 and thisTemp <= 500): thisLogUwU = logUwU[1] * (thisTemp - 130)/(500 - 130) \ + logUwU[0] * (500 - thisTemp)/(500 - 130) thisLogUwL = logUwL[1] * (thisTemp - 130)/(500 - 130) \ + logUwL[0] * (500 - thisTemp)/(500 - 130) if (thisTemp > 500 and thisTemp <= 3000): thisLogUwU = logUwU[2] * (thisTemp - 500)/(3000 - 500) \ + logUwU[1] * (3000 - thisTemp)/(3000 - 500) thisLogUwL = logUwL[2] * (thisTemp - 500)/(3000 - 500) \ + logUwL[1] * (3000 - thisTemp)/(3000 - 500) if (thisTemp > 3000 and thisTemp <= 8000): thisLogUwU = logUwU[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUwU[2] * (8000 - thisTemp)/(8000 - 3000) thisLogUwL = logUwL[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUwL[2] * (8000 - thisTemp)/(8000 - 3000) if (thisTemp > 8000 and thisTemp < 10000): thisLogUwU = logUwU[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUwU[3] * (10000 - thisTemp)/(10000 - 8000) thisLogUwL = logUwL[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUwL[3] * (10000 - thisTemp)/(10000 - 8000) if (thisTemp >= 10000): thisLogUwU = logUwU[4] thisLogUwL = logUwL[4] #//Ionization stage Saha factors: #//Need T_kin^5/2 in the P_e formulation of Saha Eq. logSaha = logSahaFac - (boltzFacI / temp[0]) + ( 5.0 * temp[1] / 2.0) + thisLogUwU - thisLogUwL #// saha = Math.exp(logSaha); #//logLHS[id] = logSaha; logLHS = logSaha #// } //id loop return logLHS
def stagePops2(logNum, Ne, chiIArr, logUw, \ numMols, logNumB, dissEArr, logUwB, logQwABArr, logMuABArr, \ numDeps, temp): #line 1: //species A data - ionization equilibrium of A #line 2: //data for set of species "B" - molecular equlibrium for set {AB} """Ionization equilibrium routine that accounts for molecule formation: // Returns depth distribution of ionization stage populations // Input parameters: // logNum - array with depth-dependent total element number densities (cm^-3) // chiI1 - ground state ionization energy of neutral stage // chiI2 - ground state ionization energy of singly ionized stage // Also needs atsmopheric structure information: // numDeps // temp structure // rho structure // Atomic element A is the one whose ionization fractions are being computed // Element B refers to array of other species with which A forms molecules AB """ ln10 = math.log(10.0) logE = math.log10(math.e) #// for debug output log2pi = math.log(2.0 * math.pi) log2 = math.log(2.0) numStages = len( chiIArr ) #// + 1; //need one more stage above the highest stage to be populated #// var numMols = dissEArr.length; #// Parition functions passed in are 2-element vectore with remperature-dependent base 10 log Us #// Convert to natural logs: #double Ttheta, thisTemp; #//Default initializations: #//We need one more stage in size of saha factor than number of stages we're actualy populating thisLogUw = [0.0 for i in range(numStages + 1)] for i in range(numStages + 1): thisLogUw[i] = 0.0 logE10 = math.log(10.0) #//We need one more stage in size of saha factor than number of stages we're actualy populating #double[][] logUw = new double[numStages+1][2]; #logUw = [ [ 0.0 for i in range(5) ] for j in range(numStages+1) ] #for i in range(numStages): # for kk in range(5): # logUw[i][kk] = logE10*log10UwAArr[i][kk] #// lburns- what variable can we use instead of 5? #//Assume ground state statistical weight (or partition fn) of highest stage is 1.0; #//var logGw5 = 0.0; #for kk in range(5): # logUw[numStages][kk] = 0.0 #// lburns #//System.out.println("chiL before: " + chiL); #// If we need to subtract chiI from chiL, do so *before* converting to tiny numbers in ergs! #//atomic ionization stage Boltzmann factors: #double logChiI, logBoltzFacI; boltzFacI = [0.0 for i in range(numStages)] #print("numStages ", numStages, " Useful.logEv ", Useful.logEv()) for i in range(numStages): #print("i ", i, " chiIArr ", chiIArr[i]) logChiI = math.log(chiIArr[i]) + Useful.logEv() logBoltzFacI = logChiI - Useful.logK() boltzFacI[i] = math.exp(logBoltzFacI) logSahaFac = log2 + (3.0 / 2.0) * (log2pi + Useful.logMe() + Useful.logK() - 2.0 * Useful.logH()) #// return a 2D 5 x numDeps array of logarithmic number densities #// Row 0: neutral stage ground state population #// Row 1: singly ionized stage ground state population #// Row 2: doubly ionized stage ground state population #// Row 3: triply ionized stage ground state population #// Row 4: quadruply ionized stage ground state population #double[][] logNums = new double[numStages][numDeps]; logNums = [[0.0 for i in range(numDeps)] for j in range(numStages)] #//We need one more stage in size of saha factor than number of stages we're actualy populating #// for index accounting pirposes #// For atomic ionization stages: # double[][] logSaha = new double[numStages+1][numStages+1]; # double[][] saha = new double[numStages+1][numStages+1]; logSaha = [[0.0 for i in range(numStages + 1)] for j in range(numStages + 1)] saha = [[0.0 for i in range(numStages + 1)] for j in range(numStages + 1)] #// logIonFrac = [0.0 for i in range(numStages)] #double expFac, logNe; #// Now - molecular variables: #//Treat at least one molecule - if there are really no molecules for an atomic species, #//there will be one phantom molecule in the denominator of the ionization fraction #//with an impossibly high dissociation energy ifMols = True if (numMols == 0): ifMols = False numMols = 1 #//This should be inherited, but let's make sure: dissEArr[0] = 19.0 #//eV #//Molecular partition functions - default initialization: #double[] thisLogUwB = new double[numMols]; thisLogUwB = [0.0 for i in range(numMols)] for iMol in range(numMols): thisLogUwB[ iMol] = 0.0 #// variable for temp-dependent computed partn fn of array element B thisLogUwA = 0.0 #// element A thisLogQwAB = math.log(300.0) #//For clarity: neutral stage of atom whose ionization equilibrium is being computed is element A #// for molecule formation: logUwA = [0.0 for i in range(5)] if (numMols > 0): for kk in range(len(logUwA)): logUwA[kk] = logUw[0][kk] #// lburns #// Array of elements B for all molecular species AB: #double[][] logUwB = new double[numMols][2]; #logUwB = [ [ 0.0 for i in range(5) ] for j in range(numMols) ] #//if (numMols > 0){ #for iMol in range(numMols): # for kk in range(5): # #print("iMol ", iMol, " kk ", kk) # logUwB[iMol][kk] = logE10*log10UwBArr[iMol][kk] #// lburns new loop #//} #//// Molecular partition functions: #// double[] logQwAB = new double[numMols]; #// //if (numMols > 0){ #// for (int iMol = 0; iMol < numMols; iMol++){ #// logQwAB[iMol] = logE10*log10QwABArr[iMol]; #// } # //} #//Molecular dissociation Boltzmann factors: boltzFacIAB = [0.0 for i in range(numMols)] logMolSahaFac = [0.0 for i in range(numMols)] #//if (numMols > 0){ #double logDissE, logBoltzFacIAB; for iMol in range(numMols): logDissE = math.log(dissEArr[iMol]) + Useful.logEv() logBoltzFacIAB = logDissE - Useful.logK() boltzFacIAB[iMol] = math.exp(logBoltzFacIAB) logMolSahaFac[iMol] = (3.0 / 2.0) * ( log2pi + logMuABArr[iMol] + Useful.logK() - 2.0 * Useful.logH()) #//console.log("iMol " + iMol + " dissEArr[iMol] " + dissEArr[iMol] + " logDissE " + logE*logDissE + " logBoltzFacIAB " + logE*logBoltzFacIAB + " boltzFacIAB[iMol] " + boltzFacIAB[iMol] + " logMuABArr " + logE*logMuABArr[iMol] + " logMolSahaFac " + logE*logMolSahaFac[iMol]); #//} #// For molecular species: logSahaMol = [0.0 for i in range(numMols)] invSahaMol = [0.0 for i in range(numMols)] for id in range(numDeps): #//// reduce or enhance number density by over-all Rosseland opcity scale parameter #// #//Row 1 of Ne is log_e Ne in cm^-3 logNe = Ne[1][id] #//Determine temperature dependent partition functions Uw: thisTemp = temp[0][id] #Ttheta = 5040.0 / thisTemp """ if (Ttheta >= 1.0): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][0] for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][0] if (Ttheta <= 0.5): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][1] for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][1] if (Ttheta > 0.5 and Ttheta < 1.0): for iStg in range(numStages): thisLogUw[iStg] = ( logUw[iStg][1] * (Ttheta - 0.5)/(1.0 - 0.5) ) \ + ( logUw[iStg][0] * (1.0 - Ttheta)/(1.0 - 0.5) ) """ #// NEW Determine temperature dependent partition functions Uw: lburns if (thisTemp <= 130): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][0] for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][0] if (thisTemp > 130 and thisTemp <= 500): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][1] * (thisTemp - 130)/(500 - 130) \ + logUw[iStg][0] * (500 - thisTemp)/(500 - 130) for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][1] * (thisTemp - 130)/(500 - 130) \ + logUwB[iMol][0] * (500 - thisTemp)/(500 - 130) if (thisTemp > 500 and thisTemp <= 3000): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][2] * (thisTemp - 500)/(3000 - 500) \ + logUw[iStg][1] * (3000 - thisTemp)/(3000 - 500) for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][2] * (thisTemp - 500)/(3000 - 500) \ + logUwB[iMol][1] * (3000 - thisTemp)/(3000 - 500) if (thisTemp > 3000 and thisTemp <= 8000): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][3] * (thisTemp - 3000)/(8000 - 3000) \ + logUw[iStg][2] * (8000 - thisTemp)/(8000 - 3000) for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][3] * (thisTemp - 3000)/(8000 - 3000) \ + logUwB[iMol][2] * (8000 - thisTemp)/(8000 - 3000) if (thisTemp > 8000 and thisTemp < 10000): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][4] * (thisTemp - 8000)/(10000 - 8000) \ + logUw[iStg][3] * (10000 - thisTemp)/(10000 - 8000) for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][4] * (thisTemp - 8000)/(10000 - 8000) \ + logUwB[iMol][3] * (10000 - thisTemp)/(10000 - 8000) if (thisTemp >= 10000): for iStg in range(numStages): thisLogUw[iStg] = logUw[iStg][4] for iMol in range(numMols): thisLogUwB[iMol] = logUwB[iMol][4] thisLogUw[numStages] = 0.0 for iMol in range(numMols): if (thisTemp < 3000.0): thisLogQwAB = ( logQwABArr[iMol][1] * (3000.0 - thisTemp)/(3000.0 - 500.0) ) \ + ( logQwABArr[iMol][2] * (thisTemp - 500.0)/(3000.0 - 500.0) ) if ((thisTemp >= 3000.0) and (thisTemp <= 8000.0)): thisLogQwAB = ( logQwABArr[iMol][2] * (8000.0 - thisTemp)/(8000.0 - 3000.0) ) \ + ( logQwABArr[iMol][3] * (thisTemp - 3000.0)/(8000.0 - 3000.0) ) if (thisTemp > 8000.0): thisLogQwAB = ( logQwABArr[iMol][3] * (10000.0 - thisTemp)/(10000.0 - 8000.0) ) \ + ( logQwABArr[iMol][4] * (thisTemp - 8000.0)/(10000.0 - 8000.0) ) #// iMol loop #//For clarity: neutral stage of atom whose ionization equilibrium is being computed is element A #// for molecule formation: thisLogUwA = thisLogUw[0] #//Ionization stage Saha factors: for iStg in range(numStages): #print("iStg ", iStg) #stop logSaha[iStg + 1][iStg] = logSahaFac - logNe - ( boltzFacI[iStg] / temp[0][id]) + (3.0 * temp[1][id] / 2.0) + thisLogUw[iStg + 1] - thisLogUw[iStg] saha[iStg + 1][iStg] = math.exp(logSaha[iStg + 1][iStg]) #// if (id == 36){ #// console.log("iStg " + iStg + " boltzFacI[iStg] " + boltzFacI[iStg] + " thisLogUw[iStg] " + logE*thisLogUw[iStg] + " thisLogUw[iStg+1] " + logE*thisLogUw[iStg+1]); #// console.log("iStg+1 " + (iStg+1) + " iStg " + iStg + " logSahaji " + logE*logSaha[iStg+1][iStg] + " saha[iStg+1][iStg] " + saha[iStg+1][iStg]); #// } #//Molecular Saha factors: for iMol in range(numMols): logSahaMol[iMol] = logMolSahaFac[iMol] - logNumB[iMol][id] - ( boltzFacIAB[iMol] / temp[0][id]) + ( 3.0 * temp[1][id] / 2.0) + thisLogUwB[iMol] + thisLogUwA - thisLogQwAB #//For denominator of ionization fraction, we need *inverse* molecular Saha factors (N_AB/NI): logSahaMol[iMol] = -1.0 * logSahaMol[iMol] invSahaMol[iMol] = math.exp(logSahaMol[iMol]) #//TEST invSahaMol[iMol] = 1.0e-99; //test #// if (id == 36){ #// console.log("iMol " + iMol + " boltzFacIAB[iMol] " + boltzFacIAB[iMol] + " thisLogUwB[iMol] " + logE*thisLogUwB[iMol] + " logNumB[iMol][id] " + logE*logNumB[iMol][id] + " logMolSahaFac[iMol] " + logMolSahaFac[iMol]); #// console.log("iMol " + iMol + " logSahaMol " + logE*logSahaMol[iMol] + " invSahaMol[iMol] " + invSahaMol[iMol]); #// } #//logSaha32 = logSahaFac - logNe - (boltzFacI2 / temp[0][id]) + (3.0 * temp[1][id] / 2.0) + thisLogUw3 - thisLogUw2; // log(RHS) of standard Saha equation #//saha32 = Math.exp(logSaha32); //RHS of standard Saha equation #//Compute log of denominator is ionization fraction, f_stage denominator = 1.0 #//default initialization - leading term is always unity #//ion stage contributions: for jStg in range(1, numStages + 1): addend = 1.0 #//default initialization for product series for iStg in range(jStg): #//console.log("jStg " + jStg + " saha[][] indices " + (iStg+1) + " " + iStg); addend = addend * saha[iStg + 1][iStg] denominator = denominator + addend #//molecular contribution if (ifMols == True): for iMol in range(numMols): denominator = denominator + invSahaMol[iMol] #// logDenominator = math.log(denominator) #//if (id == 36){ #// console.log("logDenominator " + logE*logDenominator); #// } #//var logDenominator = Math.log( 1.0 + saha21 + (saha32 * saha21) + (saha43 * saha32 * saha21) + (saha54 * saha43 * saha32 * saha21) ); logIonFrac[ 0] = -1.0 * logDenominator #// log ionization fraction in stage I #//if (id == 36){ # //console.log("jStg 0 " + " logIonFrac[jStg] " + logE*logIonFrac[0]); #//} for jStg in range(1, numStages): addend = 0.0 #//default initialization for product series for iStg in range(jStg): #//console.log("jStg " + jStg + " saha[][] indices " + (iStg+1) + " " + iStg); addend = addend + logSaha[iStg + 1][iStg] logIonFrac[jStg] = addend - logDenominator #//if (id == 36){ #// console.log("jStg " + jStg + " logIonFrac[jStg] " + logE*logIonFrac[jStg]); #//} #//logIonFracI = -1.0 * logDenominator; // log ionization fraction in stage I #//logIonFracII = logSaha21 - logDenominator; // log ionization fraction in stage II #//logIonFracIII = logSaha32 + logSaha21 - logDenominator; //log ionization fraction in stage III #//logIonFracIV = logSaha43 + logSaha32 + logSaha21 - logDenominator; //log ionization fraction in stage III #//if (id == 36) { #// System.out.println("logSaha21 " + logE*logSaha21 + " logSaha32 " + logE*logSaha32); #// System.out.println("IonFracII " + Math.exp(logIonFracII) + " IonFracI " + Math.exp(logIonFracI) + " logNe " + logE*logNe); #//} #//System.out.println("LevelPops: id, ionFracI, ionFracII: " + id + " " + Math.exp(logIonFracI) + " " + Math.exp(logIonFracII) ); # //System.out.println("LevPops: ionized branch taken, ionized = " + ionized); for iStg in range(numStages): logNums[iStg][id] = logNum[id] + logIonFrac[iStg] #//id loop return logNums
def levelPops(lam0In, logNStage, chiL, logUw, gwL, numDeps, temp): """ Returns depth distribution of occupation numbers in lower level of b-b transition, // Input parameters: // lam0 - line centre wavelength in nm // logNStage - log_e density of absorbers in relevent ion stage (cm^-3) // logFlu - log_10 oscillator strength (unitless) // chiL - energy of lower atomic E-level of b-b transition in eV // Also needs atsmopheric structure information: // numDeps // temp structure """ c = Useful.c() logC = Useful.logC() k = Useful.k() logK = Useful.logK() logH = Useful.logH() logEe = Useful.logEe() logMe = Useful.logMe() ln10 = math.log(10.0) logE = math.log10(math.e) #// for debug output log2pi = math.log(2.0 * math.pi) log2 = math.log(2.0) #//double logNl = logNlIn * ln10; // Convert to base e #// Parition functions passed in are 2-element vectore with remperature-dependent base 10 log Us #// Convert to natural logs: #double thisLogUw, Ttheta; thisLogUw = 0.0 # //default initialization #logUw = [ 0.0 for i in range(5) ] logE10 = math.log(10.0) #print("log10UwStage ", log10UwStage) #for kk in range(len(logUw)): # logUw[kk] = logE10*log10UwStage[kk] #// lburns new loop logGwL = math.log(gwL) #//System.out.println("chiL before: " + chiL); #// If we need to subtract chiI from chiL, do so *before* converting to tiny numbers in ergs! #////For testing with Ca II lines using gS3 internal line list only: #//boolean ionized = true; #//if (ionized) { #// //System.out.println("ionized, doing chiL - chiI: " + ionized); #// // chiL = chiL - chiI; #// chiL = chiL - 6.113; #// } #// // #//Log of line-center wavelength in cm logLam0 = math.log(lam0In) #// * 1.0e-7); #// energy of b-b transition logTransE = logH + logC - logLam0 #//ergs if (chiL <= 0.0): chiL = 1.0e-49 logChiL = math.log( chiL) + Useful.logEv() #// Convert lower E-level from eV to ergs logBoltzFacL = logChiL - Useful.logK( ) #// Pre-factor for exponent of excitation Boltzmann factor boltzFacL = math.exp(logBoltzFacL) boltzFacGround = 0.0 / k #//I know - its zero, but let's do it this way anyway' #// return a 1D numDeps array of logarithmic number densities #// level population of lower level of bb transition (could be in either stage I or II!) logNums = [0.0 for i in range(numDeps)] #double num, logNum, expFac; for id in range(numDeps): #//Determine temperature dependenet partition functions Uw: #Ttheta = 5040.0 / temp[0][id] #//NEW Determine temperature dependent partition functions Uw: lburns thisTemp = temp[0][id] """ if (Ttheta >= 1.0): thisLogUw = logUw[0] if (Ttheta <= 0.5): thisLogUw = logUw[1] if (Ttheta > 0.5 and Ttheta < 1.0): thisLogUw = ( logUw[1] * (Ttheta - 0.5)/(1.0 - 0.5) ) \ + ( logUw[0] * (1.0 - Ttheta)/(1.0 - 0.5) ) """ if (thisTemp >= 10000): thisLogUw = logUw[4] if (thisTemp <= 130): thisLogUw = logUw[0] if (thisTemp > 130 and thisTemp <= 500): thisLogUw = logUw[1] * (thisTemp - 130)/(500 - 130) \ + logUw[0] * (500 - thisTemp)/(500 - 130) if (thisTemp > 500 and thisTemp <= 3000): thisLogUw = logUw[2] * (thisTemp - 500)/(3000 - 500) \ + logUw[1] * (3000 - thisTemp)/(3000 - 500) if (thisTemp > 3000 and thisTemp <= 8000): thisLogUw = logUw[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUw[2] * (8000 - thisTemp)/(8000 - 3000) if (thisTemp > 8000 and thisTemp < 10000): thisLogUw = logUw[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUw[3] * (10000 - thisTemp)/(10000 - 8000) #print("logUw ", logUw, " thisLogUw ", thisLogUw) #//System.out.println("LevPops: ionized branch taken, ionized = " + ionized); #// Take stat weight of ground state as partition function: logNums[id] = logNStage[id] - boltzFacL / temp[0][ id] + logGwL - thisLogUw #// lower level of b-b transition #print("LevelPopsServer.stagePops id ", id, " logNStage[id] ", logNStage[id], " boltzFacL ", boltzFacL, " temp[0][id] ", temp[0][id], " logGwL ", logGwL, " thisLogUw ", thisLogUw, " logNums[id] ", logNums[id]); #// System.out.println("LevelPops: id, logNums[0][id], logNums[1][id], logNums[2][id], logNums[3][id]: " + id + " " #// + Math.exp(logNums[0][id]) + " " #// + Math.exp(logNums[1][id]) + " " #// + Math.exp(logNums[2][id]) + " " #// + Math.exp(logNums[3][id])); #//System.out.println("LevelPops: id, logNums[0][id], logNums[1][id], logNums[2][id], logNums[3][id], logNums[4][id]: " + id + " " #// + logE * (logNums[0][id]) + " " #// + logE * (logNums[1][id]) + " " #// + logE * (logNums[2][id]) + " " # // + logE * (logNums[3][id]) + " " #// + logE * (logNums[4][id]) ); #//System.out.println("LevelPops: id, logIonFracI, logIonFracII: " + id + " " + logE*logIonFracI + " " + logE*logIonFracII #// + "logNum, logNumI, logNums[0][id], logNums[1][id] " #// + logE*logNum + " " + logE*logNumI + " " + logE*logNums[0][id] + " " + logE*logNums[1][id]); #//System.out.println("LevelPops: id, logIonFracI: " + id + " " + logE*logIonFracI #// + "logNums[0][id], boltzFacL/temp[0][id], logNums[2][id]: " #// + logNums[0][id] + " " + boltzFacL/temp[0][id] + " " + logNums[2][id]); #//id loop #stop return logNums
def masterMetal(numDeps, numLams, temp, lambdaScale, stagePops): """/* Metal b-f opacity routines taken from Moog (moogjul2014/, MOOGJUL2014.tar) Chris Sneden (Universtiy of Texas at Austin) and collaborators http://www.as.utexas.edu/~chris/moog.html //From Moog source file Opacitymetals.f */""" #//System.out.println("masterMetal called..."); #//From Moog source file Opacitymetals.f #// From how values such as aC1[] are used in Moog file Opacit.f to compute the total opacity #// and then the optical depth scale, I infer that they are extinction coefficients #// in cm^-1 #// There does not seem to be any correction for stimulated emission logE = math.log10(math.e) masterBF = [[0.0 for i in range(numDeps)] for j in range(numLams)] logUC1 = [0.0 for i in range(5)] logUMg1 = [0.0 for i in range(5)] logUMg2 = [0.0 for i in range(5)] logUAl1 = [0.0 for i in range(5)] logUSi1 = [0.0 for i in range(5)] logUSi2 = [0.0 for i in range(5)] logUFe1 = [0.0 for i in range(5)] logStatWC1 = 0.0 logStatWMg1 = 0.0 logStatWMg2 = 0.0 logStatWAl1 = 0.0 logStatWSi1 = 0.0 logStatWSi2 = 0.0 logStatWFe1 = 0.0 theta = 1.0 species = "" logGroundPopsC1 = [0.0 for i in range(numDeps)] logGroundPopsMg1 = [0.0 for i in range(numDeps)] logGroundPopsMg2 = [0.0 for i in range(numDeps)] logGroundPopsAl1 = [0.0 for i in range(numDeps)] logGroundPopsSi1 = [0.0 for i in range(numDeps)] logGroundPopsSi2 = [0.0 for i in range(numDeps)] logGroundPopsFe1 = [0.0 for i in range(numDeps)] #// #// C I: Z=6 --> iZ=5: aC1 = [0.0 for i in range(numDeps)] #// Mg I: Z=12 --> iZ=11: aMg1 = [0.0 for i in range(numDeps)] #// Mg II: Z=12 --> iZ=11: aMg2 = [0.0 for i in range(numDeps)] #// Al I: Z=13 --> iZ=12: aAl1 = [0.0 for i in range(numDeps)] #// Si I: Z=14 --> iZ=13: aSi1 = [0.0 for i in range(numDeps)] #// Si II: Z=14 --> iZ =13: aSi2 = [0.0 for i in range(numDeps)] #// Fe I: Z=26 --> iZ=25 aFe1 = [0.0 for i in range(numDeps)] species = "CI" logUC1 = PartitionFn.getPartFn2(species) species = "MgI" logUMg1 = PartitionFn.getPartFn2(species) species = "MgII" logUMg2 = PartitionFn.getPartFn2(species) species = "AlI" logUAl1 = PartitionFn.getPartFn2(species) species = "SiI" logUSi1 = PartitionFn.getPartFn2(species) species = "SiII" logUSi2 = PartitionFn.getPartFn2(species) species = "FeI" logUFe1 = PartitionFn.getPartFn2(species) #//System.out.println("iD PpC1 PpMg1 PpMg2 PpAl1 PpSi1 PpSi2 PpFe1"); for iD in range(numDeps): #//neutral stage #//Assumes ground state stat weight, g_1, is 1.0 #theta = 5040.0 / temp[0][iD] #// U[0]: theta = 1.0, U[1]: theta = 0.5 """ if (theta <= 0.5): logStatWC1 = logUC1[1] logStatWMg1 = logUMg1[1] logStatWMg2 = logUMg2[1] logStatWAl1 = logUAl1[1] logStatWSi1 = logUSi1[1] logStatWSi2 = logUSi2[1] logStatWFe1 = logUFe1[1] elif ( (theta < 1.0) and (theta > 0.5) ): logStatWC1 = ( (theta-0.5) * logUC1[0] ) + ( (1.0-theta) * logUC1[1] ) #//divide by common factor of interpolation interval of 0.5 = (1.0 - 0.5): logStatWC1 = 2.0 * logStatWC1 logStatWMg1 = ( (theta-0.5) * logUMg1[0] ) + ( (1.0-theta) * logUMg1[1] ); logStatWMg1 = 2.0 * logStatWMg1; logStatWMg2 = ( (theta-0.5) * logUMg2[0] ) + ( (1.0-theta) * logUMg2[1] ); logStatWMg2 = 2.0 * logStatWMg2; logStatWAl1 = ( (theta-0.5) * logUAl1[0] ) + ( (1.0-theta) * logUAl1[1] ); logStatWAl1 = 2.0 * logStatWAl1; logStatWSi1 = ( (theta-0.5) * logUSi1[0] ) + ( (1.0-theta) * logUSi1[1] ); logStatWSi1 = 2.0 * logStatWSi1; logStatWSi2 = ( (theta-0.5) * logUSi2[0] ) + ( (1.0-theta) * logUSi2[1] ); logStatWSi2 = 2.0 * logStatWSi2; logStatWFe1 = ( (theta-0.5) * logUFe1[0] ) + ( (1.0-theta) * logUFe1[1] ); logStatWFe1 = 2.0 * logStatWFe1; else: logStatWC1 = logUC1[0] logStatWMg1 = logUMg1[0] logStatWMg2 = logUMg2[0] logStatWAl1 = logUAl1[0] logStatWSi1 = logUSi1[0] logStatWSi2 = logUSi2[0] logStatWFe1 = logUFe1[0] """ #// NEW Interpolation involving temperature for new partition function: lburns thisTemp = temp[0][iD] if (thisTemp <= 130): logStatWC1 = logUC1[0] logStatWMg1 = logUMg1[0] logStatWMg2 = logUMg2[0] logStatWAl1 = logUAl1[0] logStatWSi1 = logUSi1[0] logStatWSi2 = logUSi2[0] logStatWFe1 = logUFe1[0] elif (thisTemp > 130 and thisTemp <= 500): #// Add in interpolation here lburns logStatWC1 = logUC1[1] * (thisTemp - 130)/(500 - 130) \ + logUC1[0] * (500 - thisTemp)/(500 - 130) logStatWMg1 = logUMg1[1] * (thisTemp - 130)/(500 - 130) \ + logUMg1[0] * (500 - thisTemp)/(500 - 130) logStatWMg2 = logUMg2[1] * (thisTemp - 130)/(500 - 130) \ + logUMg2[0] * (500 - thisTemp)/(500 - 130) logStatWAl1 = logUAl1[1] * (thisTemp - 130)/(500 - 130) \ + logUAl1[0] * (500 - thisTemp)/(500 - 130) logStatWSi1 = logUSi1[1] * (thisTemp - 130)/(500 - 130) \ + logUSi1[0] * (500 - thisTemp)/(500 - 130) logStatWSi2 = logUSi2[1] * (thisTemp - 130)/(500 - 130) \ + logUSi2[0] * (500 - thisTemp)/(500 - 130) logStatWFe1 = logUFe1[1] * (thisTemp - 130)/(500 - 130) \ + logUFe1[0] * (500 - thisTemp)/(500 - 130) elif (thisTemp > 500 and thisTemp <= 3000): logStatWC1 = logUC1[2] * (thisTemp - 500)/(3000 - 500) \ + logUC1[1] * (3000 - thisTemp)/(3000 - 500) logStatWMg1 = logUMg1[2] * (thisTemp - 500)/(3000 - 500) \ + logUMg1[1] * (3000 - thisTemp)/(3000 - 500) logStatWMg2 = logUMg2[2] * (thisTemp - 500)/(3000 - 500) \ + logUMg2[1] * (3000 - thisTemp)/(3000 - 500) logStatWAl1 = logUAl1[2] * (thisTemp - 500)/(3000 - 500) \ + logUAl1[1] * (3000 - thisTemp)/(3000 - 500) logStatWSi1 = logUSi1[2] * (thisTemp - 500)/(3000 - 500) \ + logUSi1[1] * (3000 - thisTemp)/(3000 - 500) logStatWSi2 = logUSi2[2] * (thisTemp - 500)/(3000 - 500) \ + logUSi2[1] * (3000 - thisTemp)/(3000 - 500) logStatWFe1 = logUFe1[2] * (thisTemp - 500)/(3000 - 500) \ + logUFe1[1] * (3000 - thisTemp)/(3000 - 500) elif (thisTemp > 3000 and thisTemp <= 8000): logStatWC1 = logUC1[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUC1[2] * (8000 - thisTemp)/(8000 - 3000) logStatWMg1 = logUMg1[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUMg1[2] * (8000 - thisTemp)/(8000 - 3000) logStatWMg2 = logUMg2[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUMg2[2] * (8000 - thisTemp)/(8000 - 3000) logStatWAl1 = logUAl1[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUAl1[2] * (8000 - thisTemp)/(8000 - 3000) logStatWSi1 = logUSi1[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUSi1[2] * (8000 - thisTemp)/(8000 - 3000) logStatWSi2 = logUSi2[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUSi2[2] * (8000 - thisTemp)/(8000 - 3000) logStatWFe1 = logUFe1[3] * (thisTemp - 3000)/(8000 - 3000) \ + logUFe1[2] * (8000 - thisTemp)/(8000 - 3000) elif (thisTemp > 8000 and thisTemp < 10000): logStatWC1 = logUC1[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUC1[3] * (10000 - thisTemp)/(10000 - 8000) logStatWMg1 = logUMg1[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUMg1[3] * (10000 - thisTemp)/(10000 - 8000) logStatWMg2 = logUMg2[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUMg2[3] * (10000 - thisTemp)/(10000 - 8000) logStatWAl1 = logUAl1[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUAl1[3] * (10000 - thisTemp)/(10000 - 8000) logStatWSi1 = logUSi1[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUSi1[3] * (10000 - thisTemp)/(10000 - 8000) logStatWSi2 = logUSi2[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUSi2[3] * (10000 - thisTemp)/(10000 - 8000) logStatWFe1 = logUFe1[4] * (thisTemp - 8000)/(10000 - 8000) \ + logUFe1[3] * (10000 - thisTemp)/(10000 - 8000) else: #// for temperatures greater than or equal to 10000 logStatWC1 = logUC1[4] logStatWMg1 = logUMg1[4] logStatWMg2 = logUMg2[4] logStatWAl1 = logUAl1[4] logStatWSi1 = logUSi1[4] logStatWSi2 = logUSi2[4] logStatWFe1 = logUFe1[4] logGroundPopsC1[iD] = stagePops[5][0][iD] - logStatWC1 logGroundPopsMg1[iD] = stagePops[11][0][iD] - logStatWMg1 logGroundPopsMg2[iD] = stagePops[11][1][iD] - logStatWMg2 logGroundPopsAl1[iD] = stagePops[12][0][iD] - logStatWAl1 logGroundPopsSi1[iD] = stagePops[13][0][iD] - logStatWSi1 logGroundPopsSi2[iD] = stagePops[13][1][iD] - logStatWSi2 logGroundPopsFe1[iD] = stagePops[25][0][iD] - logStatWFe1 #// if (iD%5 == 1){ #// System.out.format("%03d, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f %n", #// iD, logE*(logGroundPopsC1[iD]+temp[1][iD]+Useful.logK()), #// logE*(logGroundPopsMg1[iD]+temp[1][iD]+Useful.logK()), #// logE*(logGroundPopsMg2[iD]+temp[1][iD]+Useful.logK()), #// logE*(logGroundPopsAl1[iD]+temp[1][iD]+Useful.logK()), #// logE*(logGroundPopsSi1[iD]+temp[1][iD]+Useful.logK()), #// logE*(logGroundPopsSi2[iD]+temp[1][iD]+Useful.logK()), #// logE*(logGroundPopsFe1[iD]+temp[1][iD]+Useful.logK())); #id loop// } #double waveno; //cm?? #double freq, logFreq, kapBF; #double stimEmExp, stimEmLogExp, stimEmLogExpHelp, stimEm; #//System.out.println("iD iL lambda stimEm aC1 aMg1 aMg2 aAl1 aSi1 aSi2 aFe1 "); for iL in range(numLams): #print("iL ", iL) #// #//initialization: for i in range(numDeps): aC1[i] = 0.0 aMg1[i] = 0.0 aMg2[i] = 0.0 aAl1[i] = 0.0 aSi1[i] = 0.0 aSi2[i] = 0.0 aFe1[i] = 0.0 waveno = 1.0 / lambdaScale[iL] #//cm^-1?? logFreq = Useful.logC() - math.log(lambdaScale[iL]) freq = math.exp(logFreq) #if (iL%20 == 1): # print("freq ", freq) stimEmLogExpHelp = Useful.logH() + logFreq - Useful.logK() #//System.out.println("Calling opacC1 from masterMetal..."); if (freq >= 2.0761e15): aC1 = opacC1(numDeps, temp, lambdaScale[iL], logGroundPopsC1) if (freq >= 2.997925e+14): #print("opacMg1 called") aMg1 = opacMg1(numDeps, temp, lambdaScale[iL], logGroundPopsMg1) if (freq >= 2.564306e15): aMg2 = opacMg2(numDeps, temp, lambdaScale[iL], logGroundPopsMg2) if (freq >= 1.443e15): aAl1 = opacAl1(numDeps, temp, lambdaScale[iL], logGroundPopsAl1) if (freq >= 2.997925e+14): #print("opacSi1 called") aSi1 = opacSi1(numDeps, temp, lambdaScale[iL], logGroundPopsSi1) if (freq >= 7.6869872e14): aSi2 = opacSi2(numDeps, temp, lambdaScale[iL], logGroundPopsSi2) if (waveno >= 21000.0): aFe1 = opacFe1(numDeps, temp, lambdaScale[iL], logGroundPopsFe1) for iD in range(numDeps): kapBF = 1.0e-99 #minimum safe value stimEmLogExp = stimEmLogExpHelp - temp[1][iD] stimEmExp = -1.0 * math.exp(stimEmLogExp) stimEm = (1.0 - math.exp(stimEmExp) ) #//LTE correction for stimulated emission kapBF = kapBF + aC1[iD] + aMg1[iD] + aMg2[iD] + aAl1[iD] + aSi1[ iD] + aSi2[iD] + aFe1[iD] #kapBF = aC1[iD] + aMg2[iD] + aAl1[iD] + aSi2[iD] + aFe1[iD] #if ( (iL%20 == 1) and (iD%10 == 1) ): #print("iL ", iL, " iD ", iD, " stimEm ", stimEm, " kapBF ", kapBF) # print("aMg1 ", aMg1[iD], " aSi1 ", aSi1[iD]) masterBF[iL][iD] = math.log(kapBF) + math.log(stimEm) #// if ( (iD%10 == 0) && (iL%10 == 0) ) { #// System.out.format("%03d, %03d, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f, %21.15f, %n", #// iD, iL, lambdaScale[iL], Math.log10(stimEm), Math.log10(aC1[iD]), Math.log10(aMg1[iD]), Math.log10(aMg2[iD]), Math.log10(aAl1[iD]), Math.log10(aSi1[iD]), Math.log10(aSi2[iD]), Math.log10(aFe1[iD])); #// } #} //iD #} //iL return masterBF
def jolaProfileQ(omega0, logf, vibConst, jolaPoints, alphQ, numDeps, temp): """//JOLA profile for Q (Delta J = 0) branch //Equation 24 from Zeidler & Koestler""" nm2cm = 1.0e-7 numPoints = len(jolaPoints) #// derivative of rotational-line oscillator strength with respect to frequency dfBydw = [[0.0 for i in range(numDeps)] for j in range(numPoints)] fvv = math.exp(logf) logHcBbyK = Useful.logH() + Useful.logC() + math.log(vibConst[0]) \ - Useful.logK() Bsum = vibConst[1] + vibConst[0] Bdiff = vibConst[1] - vibConst[0] #double mQ; #// related to J, for R & P branches, respectively #//value of m is closely related to rotational quantum number J, #//Near band origin, frequency, w, range should correspond to -1 <= m <= 1 - ???: #// double wMin = Useful.c / (1.0e-7*lambda[1]); //first frequency omega #// double wMax = Useful.c / (1.0e-7*lambda[0]); //last frequency omega #// double deltaW = 0.02; #double w, logW, mQFctr, mHelp; #double denom, mQTerm, wMinusw0OverBDiff; #double help1, logHcBbyKt, hcBbyKt; #//Outer loop over frequency omega #//for (int iW = -1; iW <= 1; iW++){ #for (int iW = numPoints-1; iW >= 0; iW--){ for iW in range(numPoints - 1, 0, -1): #//dW = (double) iW; #//w = wMin + (dW*deltaW); #//logW = Useful.logC() - Math.log(1.0e-7*jolaPoints[iW]); //if w is freq in Hz logW = 0.0 - math.log( nm2cm * jolaPoints[iW]) #//if w is waveno in cm^-1 w = math.exp(logW) #//I have no idea if this is right... wMinusw0OverBDiff = (w - omega0) / Bdiff mHelp = 0.25 + math.abs(wMinusw0OverBDiff) mHelp = math.sqrt(mHelp) #//Eq. 17 mQ = -0.5 + mHelp mQFctr = (mQ * mQ - mQ) denom = math.abs(Bdiff) for iD in range(numDeps): if (wMinusw0OverBDiff > 0): logHcBbyKt = logHcBbyK - temp[1][iD] hcBbyKt = math.exp(logHcBbyKt) help1 = -1.0 * hcBbyKt * mQFctr mQTerm = math.exp(help1) / denom #//Can this be used like a differential cross-section (once converted to sigma)? #//System.out.println("alphQ " + alphQ + " fvv " + " logHcBbyKt " + logHcBbyKt + " mQTerm " + mQTerm); dfBydw[iW][iD] = alphQ * fvv * hcBbyKt * mQTerm #// Eq. 24 else: dfBydw[iW][iD] = 0.0 #//if (iD%10 == 1){ #//System.out.println("Q iD " + iD + " iW " + iW + " dfBydw " + dfBydw[iW][iD]); #//} #//iD - depth loop #} //iW - frequency loop return dfBydw
def opacMg2(numDeps, temp, lambda2, logGroundPops): """//c****************************************************************************** //c This routine computes the bound-free absorption due to Mg II. //c******************************************************************************""" #//System.out.println("opacMg2 called..."); sigma = 0.0 aMg2 = [0.0 for i in range(numDeps)] #//cross-section is zero below threshold, so initialize: for i in range(numDeps): aMg2[i] = 0.0 freq = Useful.c / lambda2 #double logTkev; tkev = [0.0 for i in range(numDeps)] #// include 'Atmos.com' #// include 'Kappa.com' c1169 = [0.0 for i in range(100)] freq1 = 0.0 x824 = 0.0 x1169 = 0.0 #//data modcount/0/ #// initialize some quantities for each new model atmosphere #// if (modelnum .ne. modcount) then #// modcount = modelnum for i in range(numDeps): logTkev = temp[1][i] + Useful.logK() - Useful.logEv() tkev[i] = math.exp(logTkev) #//do i=1,ntau for i in range(numDeps): c1169[i] = 6.0 * math.exp(-4.43e+0 / tkev[i]) #// endif #//c initialize some quantities for each new model atmosphere or new frequency; #//c there are two edges, one at 824 A and the other at 1169 A #// if (modelnum.ne.modcount .or. freq.ne.freq1) then #freq1 = freq; if (freq >= 3.635492e15): x824 = seaton(3.635492e15, 1.40e-19, 4.0e0, 6.7e0, freq) else: x824 = 1.0e-99 if (freq >= 2.564306e15): x1169 = 5.11e-19 * math.pow((2.564306e15 / freq), 3.0) else: x1169 = 1.0e-99 #// endif for i in range(numDeps): if (x1169 >= 1.0e-90): sigma = (x824 * 2.0 + x1169 * c1169[i]) aMg2[i] = sigma * math.exp(logGroundPops[i]) #//System.out.println("i " + i + " sigma " + sigma + " aMg2 " + aMg2[i]); return aMg2
def jolaKap(jolaLogNums, dfBydw, jolaPoints, numDeps, temp, rho): log10E = math.log10(math.e) nm2cm = 1.0e-7 numPoints = len(jolaPoints) logKappaJola = [[0.0 for i in range(numDeps)] for j in range(numPoints)] #//Initialize this carefully: for iD in range(numDeps): for iW in range(numPoints): logKappaJola[iW][iD] = -999.0 #double stimEmExp, stimEmLogExp, stimEmLogExpHelp, stimEm; #double freq, lastFreq, w, lastW, deltaW, thisDeltaF; logSigma = -999.0 logFreq = Useful.logC() - math.log(nm2cm * jolaPoints[0]) logW = 0.0 - math.log(nm2cm * jolaPoints[0]) #//if w is waveno in cm^-1 #//lastFreq = Math.exp(logFreq) lastW = math.exp(logW) #//try accumulating oscillator strenth, f, across band - assumes f = 0 at first (largest) lambda- ?? thisF = 0.0 #//If f is cumulative in wavenumber, then we have to make the wavenumber loop the inner one even if it #//means re-calculating depth-independent quantities each time: for iD in range(numDeps): thisF = 0.0 #//re-set accumulator #//loop in order of *increasing* wavenumber #for (int iW = numPoints-1; iW >=1; iW--){ for iW in range(numPoints - 1, 1, -1): #//df/dv is a differential oscillator strength in *frequency* space: logFreq = Useful.logC() - math.log(nm2cm * jolaPoints[iW]) freq = math.exp(logFreq) logW = 0.0 - math.log( nm2cm * jolaPoints[iW]) #//if w is waveno in cm^-1 w = math.exp(logW) #//if w is waveno in cm^-1 #//System.out.println("w " + w); #//deltaW = Math.abs(freq - lastFreq); deltaW = abs(w - lastW) #//For LTE stimulated emission correction: stimEmLogExpHelp = Useful.logH() + logFreq - Useful.logK() #// for (int iD = 0; iD < numDeps; iD++){ thisDeltaF = deltaW * dfBydw[iW][iD] if (thisDeltaF > 0.0): #thisF += thisDeltaF #//cumulative version thisF = thisDeltaF #//non-cumulative version logSigma = math.log(thisF) + math.log( math.pi) + 2.0 * Useful.logEe() - Useful.logMe( ) - Useful.logC() else: logSigma = -999.0 #// LTE stimulated emission correction: stimEmLogExp = stimEmLogExpHelp - temp[1][iD] stimEmExp = -1.0 * math.exp(stimEmLogExp) stimEm = (1.0 - math.exp(stimEmExp)) #//extinction coefficient in cm^2 g^-1: logKappaJola[iW][iD] = logSigma + jolaLogNums[iD] - rho[1][ iD] + math.log(stimEm) #//logKappaJola[iW][iD] = -999.0; #//if (iD%10 == 1){ #//System.out.println("iD " + iD + " iW " + iW + " logFreq " + log10E*logFreq + " logW " + log10E*logW + " logStimEm " + log10E*Math.log(stimEm)); #//System.out.println("iD " + iD + " iW " + iW + " thisDeltaF " + thisDeltaF + " logSigma " + log10E*logSigma + " jolaLogNums " + log10E*jolaLogNums[iD] + " rho " + log10E*rho[1][iD] + " logKappaJola " + log10E*logKappaJola[iW][iD]); #//} #// } //iD loop - depths lastFreq = freq #} //iW loop - wavelength #} //iD loop - depths return logKappaJola
def jolaProfilePR(omega0, logf, vibConst, jolaPoints, alphP, alphR, numDeps, temp): """// //JOLA profile for P (Delta J = 1) and R (Delta J = -1) branches //Equation 19 from Zeidler & Koestler""" nm2cm = 1.0e-7 log10E = math.log10(math.e) numPoints = len(jolaPoints) #// derivative of rotational-line oscillator strength with respect to frequency dfBydw = [[0.0 for i in range(numDeps)] for j in range(numPoints)] fvv = math.exp(logf) logHcBbyK = Useful.logH() + Useful.logC() + math.log(vibConst[0]) \ - Useful.logK() #//System.out.println("omega0 " + omega0 + " logf " + log10E*logf + " vibConst " + vibConst[0] + " " + vibConst[1] + " alphP " + alphP + " alphR " + alphR); Bsum = vibConst[1] + vibConst[0] Bdiff = vibConst[1] - vibConst[0] #//value of J-related "m" at band-head: mH = -1.0 * Bsum / (2.0 * Bdiff) #//Eq. 14 #//Frequency (or wavenumber??) at band head: wH = (-1.0 * Bdiff * mH * mH) + omega0 #//Eq. 15 #//System.out.println("1.0/wH " + 1.0/wH + " 1.0/omega0 " + 1.0/omega0); mTheta1 = 1.0 #//R branch? mTheta2 = 1.0 #//P branch? #double m1, m2; // related to J, for R & P branches, respectively alpha1 = 1.0 alpha2 = 1.0 #//value of m is closely related to rotational quantum number J, #//Near band origin, frequency, w, range should correspond to -1 <= m <= 1 - ???: #//double wMin = Useful.c / (1.0e-7*jolaPoints[numPoints-1]); //first frequency omega #//double wMax = Useful.c / (1.0e-7*jolaPoints[0]); //last frequency omega #//double deltaW = 0.02; #double w, logW, m1Fctr, m2Fctr, mHelp, wMinuswHOverBDiff; #double denom1, denom2, m1Term, m2Term; #double help1, logHcBbyKt, hcBbyKt; #//Outer loop over frequency omega #// for (int iW = -1; iW <= 1; iW++){ #for (int iW = numPoints-1; iW >= 0; iW--){ for iW in range(numPoints - 1, 0, -1): #//dW = (double) iW; #//w = wMin + (dW*deltaW); #//logW = Useful.logC() - Math.log(1.0e-7*jolaPoints[iW]); //if w is freq in Hz logW = 0.0 - math.log( nm2cm * jolaPoints[iW]) #//if w is waveno in cm^-1 w = math.exp(logW) #//System.out.println("logW " + log10E*logW); #//I have no idea if this is right... wMinuswHOverBDiff = (w - wH) / Bdiff mHelp = math.sqrt(abs(wMinuswHOverBDiff)) #//Eq. 17 m1 = mH + mHelp m2 = mH - mHelp #//Eq. 18 #//System.out.println("mH " + mH + " m1 " + m1 + " m2 " + m2); m1Fctr = (m1 * m1 - m1) m2Fctr = (m2 * m2 - m2) #//The following association between the sign of m1 or m2 and whether #//it's the P or the R branch might be backwards: if (m1 < 0): alpha1 = alphP if (m1 >= 0): alpha1 = alphR if (m2 < 0): alpha2 = alphP if (m2 >= 0): alpha2 = alphR denom1 = abs(Bsum + 2.0 * m1 * Bdiff) denom2 = abs(Bsum + 2.0 * m2 * Bdiff) for iD in range(numDeps): if (wMinuswHOverBDiff > 0): logHcBbyKt = logHcBbyK - temp[1][iD] hcBbyKt = math.exp(logHcBbyKt) help1 = -1.0 * hcBbyKt * m1Fctr m1Term = alpha1 * mTheta1 * math.exp(help1) / denom1 help1 = -1.0 * hcBbyKt * m2Fctr m2Term = alpha2 * mTheta2 * math.exp(help1) / denom2 #//Can this be used like a differential cross-section (once converted to sigma)? #// System.out.println("fvv " + fvv + " hcBbyKt " + hcBbyKt + " m1Term " + m1Term + " m2Term " + m2Term); dfBydw[iW][iD] = fvv * hcBbyKt * (m1Term + m2Term) #// Eq. 19 else: dfBydw[iW][iD] = 0.0 #} #// if (iD%10 == 1){ #// System.out.println("PR iD " + iD + " iW " + iW + " dfBydw " + dfBydw[iW][iD]); #// } #} //iD - depth loop #} //iW - frequency loop return dfBydw
def gauss(linePoints, lam0In, numDeps, teff, tauRos, temp, tempSun): c = Useful.c() logC = Useful.logC() #//double k = Useful.k; logK = Useful.logK() #//double e = Useful.e; #//double mE = Useful.mE; lam0 = lam0In #// * 1.0E-7; //nm to cm logLam0 = math.log(lam0) ln10 = math.log(10.0) ln2 = math.log(2.0) ln4pi = math.log(4.0 * math.pi) lnSqRtPi = 0.5 * math.log(math.pi) sqPi = math.sqrt(math.pi) #//double ln100 = 2.0*Math.log(10.0); logE = math.log10(math.e) #// for debug output doppler = linePoints[0][1] / linePoints[1][1] logDopp = math.log(doppler) tiny = 1.0e-19 #//?? #//System.out.println("LineProf: doppler, logDopp: " + doppler + " " + logE*logDopp); #//Put input parameters into linear cgs units: #//System.out.println("LINEGRID: Tau1: " + tau1); #//logA = 2.0 * logLam0 + logGamma - ln4pi - logC - logDopp; #//a = Math.exp(logA); #//System.out.println("LINEGRID: logA: " + logE * logA); #//Set up a half-profile Delta_lambda grid in Doppler width units #//from line centre to wing numPoints = len(linePoints[0]) #//System.out.println("LineProf: numPoints: " + numPoints); #// Return a 2D numPoints X numDeps array of normalized line profile points (phi) lineProf = [ [ 0.0 for i in range(numDeps) ] for j in range(numPoints) ] #// Line profiel points in Doppler widths - needed for Voigt function, H(a,v): v = [ 0.0 for i in range(numPoints) ] #double logV, ii; #// lineProf[0][0] = 0.0; v[0] = 0.0; //Line centre - cannot do logaritmically! #double gauss, core, logGauss; gauss = tiny #//default initialization #//int il0 = 36; #//System.out.println("il0 " + il0 + " temp[il] " + temp[0][il0] + " press[il] " + logE*press[1][il0]); for id in range(numDeps): for il in range(numPoints): v[il] = linePoints[1][il] #//System.out.println("LineProf: il, v[il]: " + il + " " + v[il]); #//if (il <= numCore) { if (v[il] <= 3.5 and v[il] >= -3.5): #// - Gaussian ONLY - at line centre Lorentzian will diverge! core = math.exp(-1.0 * (v[il] * v[il])) gauss = core #//System.out.println("LINEGRID- CORE: core: " + core); #} #//System.out.println("LINEGRID: il, v[il]: " + il + " " + v[il] + " lineProf[0][il]: " + lineProf[0][il]); #//System.out.println("LINEGRID: il, Voigt, H(): " + il + " " + voigt); #//Convert from H(a,v) in dimensionless Voigt units to physical phi((Delta lambda) profile: logGauss = math.log(gauss) + 2.0 * logLam0 - lnSqRtPi - logDopp - logC lineProf[il][id] = math.exp(logGauss) #//if (id == 36) { #// System.out.println("il " + il + " linePoints " + 1.0e7 * linePoints[0][il] + " id " + id + " lineProf[il][id] " + lineProf[il][id]); #//} #//System.out.println("LineProf: il, id, lineProf[il][id]: " + il + " " + id + " " + lineProf[il][id]); #} // il lambda loop #// if (id == 20) { #// for (int il = 0; il < numPoints; il++) { #// System.out.format("Voigt: %20.16f %20.16f%n", linePoints[1][il], logE * Math.log(lineProf[il][id])); #// } #// } #} //id loop """ /* Debug // Check that line profile is area-normalized (it is NOT, area = 1.4845992503443734E-19!, but IS constant with depth - !?: double delta; for (int id = 0; id < numDeps; id++) { double sum = 0.0; for (int il = 1; il < numPoints2; il++) { delta = lineProf2[0][il][id] - lineProf2[0][il - 1][id]; sum = sum + (lineProf2[1][il][id] * delta); } System.out.println("LineGrid: id, Profile area = " + id + " " + sum ); } */ """ return lineProf