00001 /* This file is part of Cloudy and is copyright (C)1978-2013 by Gary J. Ferland and 00002 * others. For conditions of distribution and use see copyright notice in license.txt */ 00003 /*atom_level2 do level population and cooling for two level atom, 00004 * side effects: 00005 * set elements of transition struc 00006 * cooling via CoolAdd( chLab, (long)t->WLAng , t->cool()); 00007 * cooling derivative */ 00008 #include "cddefines.h" 00009 #include "phycon.h" 00010 #include "transition.h" 00011 #include "dense.h" 00012 #include "rfield.h" 00013 #include "thermal.h" 00014 #include "cooling.h" 00015 #include "atoms.h" 00016 00017 void atom_level2(const TransitionProxy &t) 00018 { 00019 char chLab[5]; 00020 long int ion, 00021 ip, 00022 nelem; 00023 double AbunxIon, 00024 a21, 00025 boltz, 00026 col12, 00027 col21, 00028 coolng, 00029 g1, 00030 g2, 00031 omega, 00032 pfs1, 00033 pfs2, 00034 r, 00035 rate12, 00036 ri21; 00037 00038 DEBUG_ENTRY( "atom_level2()" ); 00039 00040 /* result is density (cm-3) of excited state times A21 00041 * result normalized to N1+N2=ABUND 00042 * routine increments dCooldT, call CoolAdd 00043 * CDSQTE is EDEN / SQRTE * 8.629E-6 00044 */ 00045 00046 ion = (*t.Hi()).IonStg(); 00047 nelem = (*t.Hi()).nelem(); 00048 00049 /* dense.xIonDense[nelem][i] is density of ith ionization stage (cm^-3) */ 00050 AbunxIon = dense.xIonDense[nelem-1][ion-1]; 00051 00052 /* continuum pointer */ 00053 ip = t.ipCont(); 00054 00055 /* approximate Boltzmann factor to see if results zero */ 00056 boltz = rfield.ContBoltz[ip-1]; 00057 00058 /* collision strength for this transition, omega is zero for hydrogenic 00059 * species which are done in special hydro routines */ 00060 omega = t.Coll().col_str(); 00061 00062 /* ROUGH check whether upper level has significant population,*/ 00063 r = (boltz*dense.cdsqte + t.Emis().pump())/(dense.cdsqte + t.Emis().Aul()); 00064 00065 /* following first test needed for 32 bit cpu on search phase 00066 * >>chng 96 jul 02, was 1e-30 crashed on dec, change to 1e-25 00067 * if( AbunxIon.lt.1e-25 .or. boltz.gt.30. ) then 00068 * >>chng 96 jul 11, to below, since can be strong pumping when 00069 * Boltzmann factor essentially zero */ 00070 /* omega in following is zero for hydrogenic species, since done 00071 * in hydro routines, so this should cause us to quit on this test */ 00072 /* >>chng 99 nov 29, from 1e-25 to 1e-30 to keep same result for 00073 * very low density models, where AbunxIon is very small but still significant*/ 00074 /*if( omega*AbunxIon < 1e-25 || r < 1e-25 )*/ 00075 if( omega*AbunxIon < 1e-30 || r < 1e-25 ) 00076 { 00077 /* put in pop since possible just too cool */ 00078 (*t.Lo()).Pop() = AbunxIon; 00079 t.Emis().PopOpc() = AbunxIon; 00080 (*t.Hi()).Pop() = 0.; 00081 t.Emis().xIntensity() = 0.; 00082 t.Coll().cool() = 0.; 00083 t.Emis().ots() = 0.; 00084 t.Emis().phots() = 0.; 00085 t.Emis().ColOvTot() = 0.; 00086 t.Coll().heat() = 0.; 00087 /* level populations */ 00088 atoms.PopLevels[0] = AbunxIon; 00089 atoms.PopLevels[1] = 0.; 00090 atoms.DepLTELevels[0] = 1.; 00091 atoms.DepLTELevels[1] = 0.; 00092 return; 00093 } 00094 00095 /* net rate down A21*(escape + destruction) */ 00096 a21 = t.Emis().Aul()*(t.Emis().Pesc()+ t.Emis().Pdest() + t.Emis().Pelec_esc()); 00097 00098 /* put null terminated line label into chLab using line array*/ 00099 chIonLbl(chLab,t); 00100 00101 /* statistical weights of upper and lower levels */ 00102 g1 = (*t.Lo()).g(); 00103 g2 = (*t.Hi()).g(); 00104 00105 /* now get real Boltzmann factor */ 00106 boltz = t.EnergyK()/phycon.te; 00107 00108 ASSERT( boltz > 0. ); 00109 boltz = sexp(boltz); 00110 00111 ASSERT( g1 > 0. && g2 > 0. ); 00112 00113 /* this lacks the upper statistical weight */ 00114 col21 = dense.cdsqte*omega; 00115 /* upward coll rate s-1 */ 00116 col12 = col21/g1*boltz; 00117 /* downward coll rate s-1 */ 00118 col21 /= g2; 00119 00120 /* rate 1 to 2 is both collisions and pumping */ 00121 /* the total excitation rate from lower to upper, collisional and pumping */ 00122 rate12 = col12 + t.Emis().pump(); 00123 00124 /* induced emissions down */ 00125 ri21 = t.Emis().pump()*g1/g2; 00126 00127 /* this is the ratio of lower to upper level populations */ 00128 r = (a21 + col21 + ri21)/rate12; 00129 00130 /* upper level pop */ 00131 pfs2 = AbunxIon/(r + 1.); 00132 atoms.PopLevels[1] = pfs2; 00133 (*t.Hi()).Pop() = pfs2; 00134 00135 /* pop of ground */ 00136 pfs1 = pfs2*r; 00137 atoms.PopLevels[0] = pfs1; 00138 00139 /* compute ratio Aul/(Aul+Cul) */ 00140 /* level population with correction for stimulated emission */ 00141 (*t.Lo()).Pop() = atoms.PopLevels[0]; 00142 00143 t.Emis().PopOpc() = (atoms.PopLevels[0] - atoms.PopLevels[1]*g1/g2 ); 00144 00145 /* departure coef of excited state rel to ground */ 00146 atoms.DepLTELevels[0] = 1.; 00147 if( boltz > 1e-20 && atoms.PopLevels[1] > 1e-20 ) 00148 { 00149 /* this line could have zero boltz factor but radiatively excited 00150 * dec alpha does not obey () in fast mode?? */ 00151 atoms.DepLTELevels[1] = (atoms.PopLevels[1]/atoms.PopLevels[0])/ 00152 (boltz*g2/g1); 00153 } 00154 else 00155 { 00156 atoms.DepLTELevels[1] = 0.; 00157 } 00158 00159 /* number of escaping line photons, used elsewhere for outward beam */ 00160 t.Emis().phots() = t.Emis().Aul()*(t.Emis().Pesc() + t.Emis().Pelec_esc())*pfs2; 00161 00162 /* intensity of line */ 00163 t.Emis().xIntensity() = t.Emis().phots()*t.EnergyErg(); 00164 //double plower = AbunxIon - pfs2; 00165 00166 /* ratio of collisional to total (collisional + pumped) excitation */ 00167 t.Emis().ColOvTot() = col12/rate12; 00168 00169 /* two cases - collisionally excited (usual case) or 00170 * radiatively excited - in which case line can be a heat source 00171 * following are correct heat exchange, they will mix to get correct deriv 00172 * the sum of heat-cool will add up to EnrUL - EnrLU - this is a trick to 00173 * keep stable solution by effectively dividing up heating and cooling, 00174 * so that negative cooling does not occur */ 00175 00176 //double Enr12 = plower*col12*t.EnergyErg; 00177 //double Enr21 = pfs2*col21*t.EnergyErg; 00178 00179 /* energy exchange due to this level 00180 * net cooling due to excit minus part of de-excit - 00181 * note that ColOvTot cancels out in the sum heat - cool */ 00182 //coolng = Enr12 - Enr21*t.Emis().ColOvTot(); 00183 00184 /* this form of coolng is guaranteed to be non-negative */ 00185 coolng = t.EnergyErg()*AbunxIon*col12*(a21 + ri21)/(a21 + col21 + ri21 + rate12); 00186 ASSERT( coolng >= 0. ); 00187 00188 t.Coll().cool() = coolng; 00189 00190 /* net heating is remainder */ 00191 t.Coll().heat() = t.EnergyErg()*AbunxIon*col21*(t.Emis().pump())/(a21 + col21 + ri21 + rate12); 00192 00193 /* expression pre jul 3 95, changed for case where line heating dominates 00194 * coolng = (plower*col12 - pfs2*col21)*t.t(ipLnEnrErg) 00195 * t.t(ipLnCool) = cooling */ 00196 00197 /* add to cooling - heating part was taken out above, 00198 * and is not added in here - it will be added to thermal.heating[0][22] 00199 * in CoolSum */ 00200 CoolAdd( chLab, t.WLAng() , t.Coll().cool()); 00201 00202 /* derivative of cooling function */ 00203 thermal.dCooldT += coolng * (t.EnergyK() * thermal.tsq1 - thermal.halfte ); 00204 return; 00205 }