/home66/gary/public_html/cloudy/c08_branch/source/lines_service.cpp

Go to the documentation of this file.
00001 /* This file is part of Cloudy and is copyright (C)1978-2008 by Gary J. Ferland and
00002  * others.  For conditions of distribution and use see copyright notice in license.txt */
00003 /*PutLine enter local line intensity into the intensity stack for eventual printout */
00004 /*PutExtra enter and 'extra' intensity source for some line */
00005 /*linadd enter lines into the line storage array, called once per zone */
00006 /*DumpLine print various information about an emission line vector, 
00007  * used in debugging, print to std out, ioQQQ */
00008 /*TexcLine derive excitation temperature of line from contents of line array */
00009 /*GetGF convert Einstein A into oscillator strength */
00010 /*emit_frac returns fraction of populations the produce emission */
00011 /*abscf convert gf into absorption coefficient */
00012 /*chIonLbl use information in line array to generate a null terminated ion label in "Fe 2" */
00013 /*chLineLbl use information in line transfer arrays to generate a line label */
00014 /*RefIndex calculates the index of refraction of air using the line energy in wavenumbers,
00015  * used to convert vacuum wavelengths to air wavelengths. */
00016 /*eina convert a gf into an Einstein A */
00017 /*WavlenErrorGet - find difference between two wavelengths */
00018 /*PutCS enter a collision strength into an individual line vector */
00019 /*lindst add local line intensity to line luminosity stack */
00020 /*PntForLine generate pointer for forbidden line */
00021 /*TransitionZero zeros out TransitionZero */
00022 /*EmLineJunk set all elements of transition struc to dangerous values */
00023 /*EmLineZero set all elements of transition struc to zero */
00024 /*LineConvRate2CS convert down coll rate back into electron cs in case other parts of code need this for reference */
00025 /*lgTauGood returns true is we have not overrun optical depth scale */
00026 /*OccupationNumberLine - derive the photon occupation number at line center for any line */
00027 /*outline - adds line photons to reflin and outlin */
00028 /*MakeCS compute collision strength by g-bar approximations */
00029 /*gbar1 compute g-bar collision strength using Mewe approximations */
00030 /*gbar0 compute g-bar gaunt factor for neutrals */
00031 /*totlin sum total intensity of cooling, recombination, or intensity lines */
00032 /*FndLineHt search through line heat arrays to find the strongest heat source */
00033 /*ConvRate2CS convert down coll rate back into electron cs in case other parts of code need this for reference */
00034 #include "cddefines.h"
00035 #include "physconst.h"
00036 #include "phycon.h"
00037 #include "lines.h"
00038 #include "radius.h"
00039 #include "geometry.h"
00040 #include "elementnames.h"
00041 #include "rt.h"
00042 #include "dense.h"
00043 #include "rfield.h"
00044 #include "opacity.h"
00045 #include "ipoint.h"
00046 #include "iso.h"
00047 #include "taulines.h"
00048 #include "hydrogenic.h"
00049 #include "lines_service.h"
00050 
00051 /*outline - adds line photons to reflin and outlin */
00052 void outline( transition *t )
00053 {
00054         long int ip = t->ipCont-1;
00055         double xInWrd = t->Emis->phots*t->Emis->FracInwd;
00056 
00057         DEBUG_ENTRY( "outline()" );
00058 
00059         ASSERT( t->Emis->phots >= 0. );
00060         ASSERT( t->Emis->FracInwd >= 0. );
00061         ASSERT( radius.BeamInIn >= 0. );
00062         ASSERT( radius.BeamInOut >= 0. );
00063 
00064         /* the reflected part */
00065         rfield.reflin[ip] += (realnum)(xInWrd*radius.BeamInIn);
00066 
00067         /* inward beam that goes out since sphere set */
00068         rfield.outlin[ip] += (realnum)(xInWrd*radius.BeamInOut*opac.tmn[ip]*
00069                 t->Emis->ColOvTot);
00070 
00071         /* outward part */
00073         rfield.outlin[ip] += (realnum)(t->Emis->phots*
00074                 (1. - t->Emis->FracInwd)*radius.BeamOutOut* t->Emis->ColOvTot);
00075         return;
00076 }
00077 
00078 /*emit_frac returns fraction of populations the produce emission */
00079 double emit_frac( transition *t )
00080 {
00081         DEBUG_ENTRY( "emit_frac()" );
00082 
00083         ASSERT( t->ipCont > 0 );
00084 
00085         /* collisional deexcitation and destruction by background opacities
00086          * are loss of photons without net emission */
00087         double deexcit_loss = t->Coll.cs * dense.cdsqte + t->Emis->Aul*t->Emis->Pdest;
00088         /* this is what is observed */
00089         double rad_deexcit = t->Emis->Aul*(t->Emis->Pelec_esc + t->Emis->Pesc);
00090         return rad_deexcit/(deexcit_loss + rad_deexcit);
00091 }
00092 
00093 /*DumpLine print various information about an emission line vector, 
00094  * used in debugging, print to std out, ioQQQ */
00095 void DumpLine(transition * t)
00096 {
00097         char chLbl[11];
00098 
00099         DEBUG_ENTRY( "DumpLine()" );
00100 
00101         ASSERT( t->ipCont > 0 );
00102 
00103         /* routine to print contents of line arrays */
00104         strcpy( chLbl, chLineLbl(t) );
00105 
00106         fprintf( ioQQQ, 
00107                 "   %10.10s Te%.2e eden%.1e CS%.2e Aul%.1e Tex%.2e cool%.1e het%.1e conopc%.1e albdo%.2e\n", 
00108           chLbl, 
00109           phycon.te, 
00110           dense.eden, 
00111           t->Coll.cs, 
00112           t->Emis->Aul, 
00113           TexcLine(t), 
00114           t->Coll.cool, 
00115           t->Coll.heat ,
00116           opac.opacity_abs[t->ipCont-1],
00117           opac.albedo[t->ipCont-1]);
00118 
00119         fprintf( ioQQQ, 
00120                 "  Tin%.1e Tout%.1e Esc%.1e eEsc%.1e DesP%.1e Pump%.1e OTS%.1e PopL,U %.1e %.1e PopOpc%.1e\n", 
00121           t->Emis->TauIn, 
00122           t->Emis->TauTot, 
00123           t->Emis->Pesc, 
00124           t->Emis->Pelec_esc, 
00125           t->Emis->Pdest, 
00126           t->Emis->pump, 
00127           t->Emis->ots, 
00128           t->Lo->Pop, 
00129           t->Hi->Pop ,
00130           t->Emis->PopOpc );
00131         return;
00132 }
00133 
00134 
00135 /*OccupationNumberLine - derive the photon occupation number at line center for any line */
00136 double OccupationNumberLine( transition * t )
00137 {
00138         double OccupationNumberLine_v;
00139 
00140         DEBUG_ENTRY( "OccupationNumberLine()" );
00141 
00142         ASSERT( t->ipCont > 0 );
00143 
00144         /* routine to evaluate line photon occupation number */
00145         if( t->Lo->Pop > SMALLFLOAT )
00146         {
00147                 /* the lower population with correction for stimulated emission */
00148                 double PopLo_corr = t->Lo->Pop / t->Lo->g - t->Hi->Pop / t->Hi->g;
00149                 OccupationNumberLine_v = ( t->Hi->Pop / t->Hi->g )/SDIV(PopLo_corr ) *
00150                         (1. - t->Emis->Pesc);
00151         }
00152         else
00153         {
00154                 OccupationNumberLine_v = 0.;
00155         }
00156         return( OccupationNumberLine_v );
00157 }
00158 
00159 /*TexcLine derive excitation temperature of line from contents of line array */
00160 double TexcLine(transition * t)
00161 {
00162         double TexcLine_v;
00163 
00164         DEBUG_ENTRY( "TexcLine()" );
00165 
00166         /* routine to evaluate line excitation temp using contents of line array
00167          * */
00168         if( t->Hi->Pop * t->Lo->Pop > 0. )
00169         {
00170                 TexcLine_v = ( t->Hi->Pop / t->Hi->g )/( t->Lo->Pop / t->Lo->g );
00171                 TexcLine_v = log(TexcLine_v);
00172                 /* protect against infinite temp limit */
00173                 if( fabs(TexcLine_v) > SMALLFLOAT )
00174                 {
00175                         TexcLine_v = - t->EnergyK / TexcLine_v;
00176                 }
00177         }
00178         else
00179         {
00180                 TexcLine_v = 0.;
00181         }
00182         return( TexcLine_v );
00183 }
00184 
00185 /*eina convert a gf into an Einstein A */
00186 double eina(double gf,
00187           double enercm, 
00188           double gup)
00189 {
00190         double eina_v;
00191 
00192         DEBUG_ENTRY( "eina()" );
00193 
00194         /* derive the transition prob, given the following
00195          * call to function is gf, energy in cm^-1, g_up
00196          * gf is product of g and oscillator strength
00197          * eina = ( gf / 1.499e-8 ) / (wl/1e4)**2 / gup  */
00198         eina_v = (gf/gup)*TRANS_PROB_CONST*POW2(enercm);
00199         return( eina_v );
00200 }
00201 
00202 /*GetGF convert Einstein A into oscillator strength */
00203 double GetGF(double trans_prob, 
00204           double enercm, 
00205           double gup)
00206 {
00207         double GetGF_v;
00208 
00209         DEBUG_ENTRY( "GetGF()" );
00210 
00211         ASSERT( enercm > 0. );
00212         ASSERT( trans_prob > 0. );
00213         ASSERT( gup > 0.);
00214 
00215         /* derive the transition prob, given the following
00216          * call to function is gf, energy in cm^-1, g_up
00217          * gf is product of g and oscillator strength
00218          * trans_prob = ( GetGF/gup) / 1.499e-8 / ( 1e4/enercm )**2 */
00219         GetGF_v = trans_prob*gup/TRANS_PROB_CONST/POW2(enercm);
00220         return( GetGF_v );
00221 }
00222 
00223 /*abscf convert gf into absorption coefficient */
00224 double abscf(double gf, 
00225           double enercm, 
00226           double gl)
00227 {
00228         double abscf_v;
00229 
00230         DEBUG_ENTRY( "abscf()" );
00231 
00232         ASSERT(gl > 0. && enercm > 0. && gf > 0. );
00233 
00234         /* derive line absorption coefficient, given the following:
00235          * gf, enercm, g_low
00236          * gf is product of g and oscillator strength */
00237         abscf_v = 1.4974e-6*(gf/gl)*(1e4/enercm);
00238         return( abscf_v );
00239 }
00240 
00241 /*chIonLbl use information in line array to generate a null terminated ion label in "Fe 2" */
00242 void chIonLbl(char *chIonLbl_v , transition * t )
00243 {
00244         /*static char chIonLbl_v[5];*/
00245 
00246         DEBUG_ENTRY( "chIonLbl()" );
00247 
00248         /* function to use information within the line array
00249          * to generate an ion label, giving element and
00250          * ionization stage
00251          * */
00252         if( t->Hi->nelem < 0 )
00253         {
00254                 /* this line is to be ignored */
00255                 strcpy( chIonLbl_v, "Dumy" );
00256         }
00257         else if( t->Hi->nelem-1 >= LIMELM )
00258         {
00259                 /* this is one of the molecules, either 12CO or 13CO */
00260 
00261                 /* >>chng 02 may 15, from to chElementNameShort to go mole right */
00262                 strcpy( chIonLbl_v , elementnames.chElementNameShort[t->Hi->nelem-1] );
00263 
00264                 /* chIonStage is four char null terminated string, starting with "_1__" 
00265                 strcat( chIonLbl_v, "CO");*/
00266         }
00267 
00268         else
00269         {
00270                 ASSERT( t->Hi->nelem >= 1 );
00271                 /* ElmntSym.chElementSym is null terminated, 2 ch + null, string giving very
00272                  * short form of element name */
00273                 strcpy( chIonLbl_v , elementnames.chElementSym[t->Hi->nelem -1] );
00274 
00275                 /* chIonStage is four char null terminated string, starting with "_1__" */
00276                 strcat( chIonLbl_v, elementnames.chIonStage[t->Hi->IonStg-1]);
00277         }
00278         /* chIonLbl is four char null terminated string */
00279         return/*( chIonLbl_v )*/;
00280 }
00281 
00282 /*chLineLbl use information in line transfer arrays to generate a line label */
00283 /* this label is null terminated */
00284 /* ContCreatePointers has test this with full range of wavelengths */
00285 char* chLineLbl(transition * t )
00286 {
00287         static char chLineLbl_v[11];
00288 
00289         DEBUG_ENTRY( "chLineLbl()" );
00290 
00291 
00292         /* function to use information within the line array
00293          * to generate a line label, giving element and
00294          * ionization stage
00295          * rhs are set in large block data */
00296 
00297         /* NB this function is profoundly slow due to sprintf statement
00298          * also - it cannot be evaluated within a write statement itself*/
00299 
00300         if( t->WLAng > (realnum)INT_MAX )
00301         {
00302                 sprintf( chLineLbl_v, "%2.2s%2.2s%5i%c", 
00303                         elementnames.chElementSym[t->Hi->nelem -1], 
00304                         elementnames.chIonStage[t->Hi->IonStg-1], 
00305                    (int)(t->WLAng/1e8), 'c' );
00306         }
00307         else if( t->WLAng > 9999999. )
00308         {
00309                 /* wavelength is very large, convert to centimeters */
00310                 sprintf( chLineLbl_v, "%2.2s%2.2s%5.2f%c", 
00311                         elementnames.chElementSym[t->Hi->nelem -1], 
00312                         elementnames.chIonStage[t->Hi->IonStg-1], 
00313                         t->WLAng/1e8, 'c' );
00314         }
00315         else if( t->WLAng > 999999. )
00316         {
00317                 /* wavelength is very large, convert to microns */
00318                 sprintf( chLineLbl_v, "%2.2s%2.2s%5i%c", 
00319                         elementnames.chElementSym[t->Hi->nelem -1], 
00320                         elementnames.chIonStage[t->Hi->IonStg-1], 
00321                         (int)(t->WLAng/1e4), 'm' );
00322         }
00323         else if( t->WLAng > 99999. )
00324         {
00325                 /* wavelength is very large, convert to microns */
00326                 sprintf( chLineLbl_v, "%2.2s%2.2s%5.1f%c", 
00327                         elementnames.chElementSym[t->Hi->nelem -1], 
00328                         elementnames.chIonStage[t->Hi->IonStg-1], 
00329                         t->WLAng/1e4, 'm' );
00330         }
00331         else if( t->WLAng > 9999. )
00332         {
00333                 sprintf( chLineLbl_v, "%2.2s%2.2s%5.2f%c", 
00334                         elementnames.chElementSym[ t->Hi->nelem -1], 
00335                         elementnames.chIonStage[t->Hi->IonStg-1], 
00336                    t->WLAng/1e4, 'm' );
00337         }
00338         else if( t->WLAng >= 100. )
00339         {
00340                 sprintf( chLineLbl_v, "%2.2s%2.2s%5i%c", 
00341                         elementnames.chElementSym[ t->Hi->nelem -1], 
00342                         elementnames.chIonStage[t->Hi->IonStg-1], 
00343                    (int)t->WLAng, 'A' );
00344         }
00345         /* the following two formats should be changed for the
00346          * wavelength to get more precision */
00347         else if( t->WLAng >= 10. )
00348         {
00349                 sprintf( chLineLbl_v, "%2.2s%2.2s%5.1f%c", 
00350                         elementnames.chElementSym[ t->Hi->nelem -1], 
00351                         elementnames.chIonStage[t->Hi->IonStg-1], 
00352                    t->WLAng, 'A' );
00353         }
00354         else
00355         {
00356                 sprintf( chLineLbl_v, "%2.2s%2.2s%5.2f%c", 
00357                         elementnames.chElementSym[ t->Hi->nelem -1], 
00358                         elementnames.chIonStage[t->Hi->IonStg-1], 
00359                    t->WLAng, 'A' );
00360         }
00361         /* make sure that string ends with null character - this should
00362          * be redundant */
00363         ASSERT( chLineLbl_v[10]==0 );
00364         return( chLineLbl_v );
00365 }
00366 
00367 /*RefIndex calculates the index of refraction of air using the line energy in wavenumbers,
00368  * used to convert vacuum wavelengths to air wavelengths. */
00369 double RefIndex(double EnergyWN )
00370 {
00371         double RefIndex_v, 
00372           WaveMic, 
00373           xl, 
00374           xn;
00375 
00376         DEBUG_ENTRY( "RefIndex()" );
00377 
00378         ASSERT( EnergyWN > 0. );
00379 
00380         /* the wavelength in microns */
00381         WaveMic = 1.e4/EnergyWN;
00382 
00383         /* only do index of refraction if longward of 2000A */
00384         if( WaveMic > 0.2 )
00385         {
00386                 /* longward of 2000A
00387                  * xl is 1/WaveMic^2 */
00388                 xl = 1.0/WaveMic/WaveMic;
00389                 /* use a formula from 
00390                  *>>refer       air     index refraction        Allen, C.W. 1973, Astrophysical quantities, 
00391                  *>>refercon    3rd Edition (AQ), p.124 */
00392                 xn = 255.4/(41. - xl);
00393                 xn += 29498.1/(146. - xl);
00394                 xn += 64.328;
00395                 RefIndex_v = xn/1.e6 + 1.;
00396         }
00397         else
00398         {
00399                 RefIndex_v = 1.;
00400         }
00401         ASSERT( RefIndex_v > 0. );
00402         return( RefIndex_v );
00403 }
00404 
00405 /*PutCS enter a collision strength into an individual line vector */
00406 void PutCS(double cs, 
00407   transition * t)
00408 {
00409 
00410         DEBUG_ENTRY( "PutCS()" );
00411 
00412         /* collision strength must not be negative, had been test for being positive,
00413          * but called with zero - did not check why 98 jul 5 */
00414         ASSERT( cs >= 0. );
00415 
00416         t->Coll.cs = (realnum)cs;
00417         return;
00418 }
00419 
00420 /*WavlenErrorGet - given the real wavelength in A for a line
00421  * routine will find the error expected between the real 
00422  * wavelength and the wavelength printed in the output, with 4 sig figs,
00423  * function returns difference between exact and 4 sig fig wl, so 
00424  * we have found correct line is fabs(d wl) < return */
00425 realnum WavlenErrorGet( realnum wavelength )
00426 {
00427         double a;
00428         realnum errorwave;
00429 
00430         DEBUG_ENTRY( "WavlenErrorGet()" );
00431 
00432         ASSERT( LineSave.sig_figs <= 6 );
00433 
00434         if( wavelength > 0. )
00435         {
00436                 /* normal case, positive (non zero) wavelength */
00437                 a = log10( wavelength+FLT_EPSILON);
00438                 a = floor(a);
00439         }
00440         else
00441         {
00442                 /* might be called with wl of zero, this is that case */
00443                 /* errorwave = 1e-4f; */
00444                 a = 0.;
00445         }
00446 
00447         errorwave = 5.f * (realnum)pow( 10., a - (double)LineSave.sig_figs );
00448         return errorwave;
00449 }
00450 
00451 /*linadd enter lines into the line storage array, called once per zone for each line*/
00452 void linadd(
00453   double xInten,        /* xInten - local emissivity per unit vol, no fill fac */
00454   realnum wavelength,   /* realnum wavelength */
00455   const char *chLab,/* string label for ion */
00456   char chInfo,          /* character type of entry for line - given below */
00457                                         /* 'c' cooling, 'h' heating, 'i' info only, 'r' recom line */
00458   const char *chComment )
00459 {
00460         DEBUG_ENTRY( "linadd()" );
00461 
00462         /* main routine to actually enter lines into the line storage array
00463          * called at top level within routine lines
00464          * called series of times in routine PutLine for lines transferred
00465          */
00466 
00467         /* three values, -1 is just counting, 0 if init, 1 for calculation */
00468         if( LineSave.ipass > 0 )
00469         {
00470                 /* not first pass, sum lines only
00471                  * total emission from vol */
00472                 LineSv[LineSave.nsum].sumlin[0] += xInten*radius.dVeff;
00473                 /* local emissivity in line */
00474                 LineSv[LineSave.nsum].emslin[0] = xInten;
00475                 /* no need to increment or set [1] version since this is called with no continuum
00476                  * index, don't know what to do */
00477                 if( wavelength > 0 )
00478                 {
00479                         /* only put informational lines, like "Q(H) 4861", in this stack
00480                          * many continua have a wavelength of zero and are proper intensities,
00481                          * it would be wrong to predict their transferred intensity */
00482                         LineSv[LineSave.nsum].emslin[1] = LineSv[LineSave.nsum].emslin[0];
00483                         LineSv[LineSave.nsum].sumlin[1] = LineSv[LineSave.nsum].sumlin[0]; 
00484                 }
00485         }
00486 
00487         else if( LineSave.ipass == 0 )
00488         {
00489                 /* first call to stuff lines in array, confirm that label is one of
00490                  * the four correct ones */
00491                 /* >>chng 04 apr 24, last two had been != so this test never really happened; PvH */
00492                 ASSERT( (chInfo == 'c') || (chInfo == 'h') || (chInfo == 'i') || (chInfo == 'r' ) );
00493 
00494                 /* then save it into array */
00495                 LineSv[LineSave.nsum].chSumTyp = (char)chInfo;
00496 
00497                 /* number of lines ok, set parameters for first pass */
00498                 LineSv[LineSave.nsum].sumlin[0] = 0.;
00499                 LineSv[LineSave.nsum].emslin[0] = 0.;
00500                 LineSv[LineSave.nsum].wavelength = wavelength;
00501                 LineSv[LineSave.nsum].sumlin[1] = 0.;
00502                 LineSv[LineSave.nsum].emslin[1] = 0.;
00503                 LineSv[LineSave.nsum].chComment = chComment;
00504                 /* check that null is correct, string overruns have 
00505                  * been a problem in the past */
00506                 ASSERT( strlen( chLab )<5 );
00507                 strcpy( LineSv[LineSave.nsum].chALab, chLab );
00508         }
00509 
00510         /* increment the line counter */
00511         ++LineSave.nsum;
00512 
00513         /* routine can be called with negative LineSave.ipass, in this case
00514          * we are just counting number of lines for current setup */
00515         return;
00516 }
00517 
00518 
00519 /* this is energy in Ryd as set by call to PntForLine */
00520 static double EnergyRyd;
00521 
00522 /*emergent_line find absorption due to continuous opacity */
00523 double emergent_line( 
00524         /* emissivity [erg cm-3 s-1] in inward direction */
00525         double emissivity_in , 
00526         /* emissivity [erg cm-3 s-1] in outward direction */
00527         double emissivity_out , 
00528         /* array index for continuum frequency */
00529         long int ipCont )
00530 {
00531 
00532         double emergent_in , emergent_out;
00533         long int i = ipCont-1;
00534 
00535         DEBUG_ENTRY( "emergent_line()" );
00536 
00537         ASSERT( i >= 0 && i < rfield.nupper-1 );
00538 
00539         /* do nothing if first iteration since we do not know the outward-looking
00540          * optical depths.  In version C07.02.00 we assumed an infinite optical
00541          * depth in the outward direction, which would be appropriate for a 
00542          * HII region on the surface of a molecular cloud.  This converged onto
00543          * the correct solution in later iterations, but on the first iteration
00544          * this underestimated total emission if the infinite cloud were not
00545          * present.  With C07.02.01 we make no assuptions about what is in the
00546          * outward direction and simply use the local emission. 
00547          * Behavior is unchanged on later iterations */
00548         if( iteration == 1 )
00549         {
00550                 /* first iteration - do not know outer optical depths so assume very large 
00551                  * optical depths */
00552                 emergent_in = emissivity_in*opac.E2TauAbsFace[i];
00553                 emergent_out = emissivity_out;
00554         }
00555         else
00556         {
00557                 if( geometry.lgSphere )
00558                 {
00559                         /* second or later iteration in closed or spherical geometry */
00560                         /* inwardly directed emission must get to central hole then across entire
00561                          * far side of shell */
00562                         emergent_in = emissivity_in  * opac.E2TauAbsFace[i] *opac.E2TauAbsTotal[i];
00563 
00564                         /* E2 is outwardly directed emission to get to outer edge of cloud */
00565                         emergent_out = emissivity_out * opac.E2TauAbsOut[i];
00566                 }
00567                 else
00568                 {
00569                         /* open geometry in second or later iteration, outer optical depths are known 
00570                          * this is light emitted into the outer direction and backscattered
00571                          * into the inner */
00572                         double reflected = emissivity_out * opac.albedo[i] * (1.-opac.E2TauAbsOut[i]);
00573                         /* E2 is to get to central hole */
00574                         emergent_in = (emissivity_in + reflected) * opac.E2TauAbsFace[i];
00575                         /* E2 is to get to outer edge */
00576                         emergent_out = (emissivity_out - reflected) * opac.E2TauAbsOut[i];
00577                 }
00578         }
00579         /* return the net emissivity times a vol element */
00580         return( (emergent_in + emergent_out)  );
00581 }
00582 
00583 /*lindst add line with destruction and outward */
00584 void lindst(
00585   // xInten - local emissivity per unit vol
00586   double xInten, 
00587   // wavelength of line in Angstroms
00588   realnum wavelength, 
00589   // *chLab string label for ion
00590   const char *chLab, 
00591   // ipnt offset of line in continuum mesh
00592   long int ipnt, 
00593   // chInfo character type of entry for line - 'c' cooling, 'h' heating, 'i' info only, 'r' recom line
00594   char chInfo, 
00595   // lgOutToo should line be included in outward beam?
00596   bool lgOutToo,
00597   // *chComment string explaining line 
00598   const char *chComment )
00599 {
00600         double saveemis;
00601         DEBUG_ENTRY( "lindst()" );
00602 
00603         /* >>chng 06 feb 08, add test on xInten positive, no need to evaluate
00604          * for majority of zero */
00605         if( LineSave.ipass > 0 && xInten > 0. )
00606         {
00607                 /* LineSave.ipass > 0, integration across simulation, sum lines only 
00608                  * emissivity, emission per unit vol, for this zone */
00609                 LineSv[LineSave.nsum].emslin[0] = xInten;
00610                 /* integrated intensity or luminosity, the emissivity times the volume */
00611                 LineSv[LineSave.nsum].sumlin[0] += xInten*radius.dVeff;
00612 
00613                 /* add line to outward beam 
00614                  * there are lots of lines that are sums of other lines, or
00615                  * just for info of some sort.  These have flag lgOutToo false.
00616                  * Note that the EnergyRyd variable only has a rational
00617                  * value if PntForLine was called just before this routine - in all
00618                  * cases where this did not happen the flag is false. */
00619                 if( lgOutToo )
00620                 {
00621                         rfield.outlin[ipnt-1] += (realnum)(xInten/(rfield.anu[ipnt-1]*EN1RYD)*
00622                           radius.dVolOutwrd*opac.ExpZone[ipnt-1]);
00623                         rfield.reflin[ipnt-1] += (realnum)(xInten/(rfield.anu[ipnt-1]*EN1RYD)*
00624                           radius.dVolReflec);
00625                 }
00626                 /* main production pass, sum lines only */
00627                 if( ipnt <= rfield.nflux )
00628                 {
00629                         /* emergent_line accounts for destruction by absorption outside
00630                          * the line-forming region */
00631                         saveemis = emergent_line( 
00632                                 xInten*rt.fracin , xInten*(1.-rt.fracin) , ipnt );
00633                         LineSv[LineSave.nsum].emslin[1] = saveemis;
00634                         LineSv[LineSave.nsum].sumlin[1] += saveemis*radius.dVeff; 
00635                 }
00636         }
00637         else if( LineSave.ipass == 0 )
00638         {
00639                 ASSERT( ipnt > 0 );
00640 
00641                 /* first call to stuff lines in array, confirm that label is one of
00642                  * the four correct ones */
00643                 /* >>chng 04 apr 24, last two had been != so this test never really happened; PvH */
00644                 ASSERT( (chInfo == 'c') || (chInfo == 'h') || (chInfo == 'i') || (chInfo == 'r' ) );
00645                 LineSv[LineSave.nsum].chSumTyp = (char)chInfo;
00646                 /* number of lines ok, set parameters for first pass */
00647                 LineSv[LineSave.nsum].sumlin[0] = 0.;
00648                 LineSv[LineSave.nsum].emslin[0] = 0.;
00649                 LineSv[LineSave.nsum].wavelength = fabs(wavelength);
00650                 LineSv[LineSave.nsum].emslin[1] = 0.;
00651                 LineSv[LineSave.nsum].sumlin[1] = 0.;
00652                 LineSv[LineSave.nsum].chComment = chComment;
00653 
00654                 /* check that null is correct, string overruns have 
00655                 * been a problem in the past */
00656                 ASSERT( strlen( chLab )<5 );
00657                 strcpy( LineSv[LineSave.nsum].chALab, chLab );
00658 
00659                 // check that line wavelength and continuum index agree to some extent
00660                 // this check cannot be very precise because some lines have 
00661                 // "wavelengths" that are set by common usage rather than the correct
00662                 // wavelength derived from energy and index of refraction of air
00663                 double error = MAX2(0.1*rfield.AnuOrg[ipnt-1] , rfield.widflx[ipnt-1] );
00664                 ASSERT( wavelength<=0 ||
00665                         fabs( rfield.AnuOrg[ipnt-1] - RYDLAM / wavelength) < error );
00666         }
00667 
00668         /* increment the line counter */
00669         ++LineSave.nsum;
00670 
00671         EnergyRyd = 0.;
00672         return;
00673 }
00674 
00675 /*PntForLine generate pointer for forbidden line */
00676 void PntForLine(
00677   /* wavelength of transition in Angstroms */
00678   double wavelength, 
00679   /* label for this line */
00680   const char *chLabel,
00681   /* this is array index on the f, not c scale,
00682    * for the continuum cell holding the line */
00683   long int *ipnt)
00684 {
00685         /* 
00686          * maximum number of forbidden lines - this is a good bet since
00687          * new lines do not go into this group, and lines are slowly 
00688          * moving to level 1 
00689          */
00690 #       define  MAXFORLIN       1000
00691         static long int ipForLin[MAXFORLIN]={0};
00692 
00693         /* number of forbidden lines entered into continuum array */
00694         static long int nForLin;
00695 
00696         DEBUG_ENTRY( "PntForLine()" );
00697 
00698         /* must be 0 or greater */
00699         ASSERT( wavelength >= 0. );
00700 
00701         if( wavelength == 0. )
00702         {
00703                 /* zero is special flag to initialize */
00704                 nForLin = 0;
00705                 /* ipLineEnergy will only put in line label if nothing already there */
00706                 EnergyRyd = 0.;
00707         }
00708         else
00709         {
00710 
00711                 if( LineSave.ipass > 0 )
00712                 {
00713                         /* not first pass, sum lines only */
00714                         *ipnt = ipForLin[nForLin];
00715                 }
00716                 else if( LineSave.ipass == 0 )
00717                 {
00718                         /* check if number of lines in arrays exceeded */
00719                         if( nForLin >= MAXFORLIN )
00720                         {
00721                                 fprintf( ioQQQ, "PROBLEM %5ld lines is too many for PntForLine.\n", 
00722                                   nForLin );
00723                                 fprintf( ioQQQ, " Increase the value of maxForLine everywhere in the code.\n" );
00724                                 cdEXIT(EXIT_FAILURE);
00725                         }
00726 
00727                         /* ipLineEnergy will only put in line label if nothing already there */
00728                         EnergyRyd = RYDLAM/wavelength;
00729                         ipForLin[nForLin] = ipLineEnergy(EnergyRyd,chLabel , 0);
00730                         *ipnt = ipForLin[nForLin];
00731                 }
00732                 else
00733                 {
00734                         /* this is case where we are only counting lines */
00735                         *ipnt = 0;
00736                 }
00737                 ++nForLin;
00738         }
00739         return;
00740 }
00741 
00742 static realnum ExtraInten;
00743 
00744 /*PutLine enter local line intensity into the intensity stack for eventual printout */
00745 void PutLine(transition * t, const char *chComment, const char *chLabelTemp)
00746 {
00747         char chLabel[5];
00748         double xIntensity,
00749                 other,
00750                 xIntensity_in;
00751 
00752         DEBUG_ENTRY( "PutLine()" );
00753 
00754         /* routine to use line array data to generate input
00755          * for emission line array */
00756         ASSERT( t->ipCont > 0. );
00757 
00758         strcpy( chLabel, chLabelTemp );
00759         chLabel[4] = '\0';
00760 
00761         /* if ipass=0 then we must generate label info since first pass
00762          * gt.0 then only need line intensity data */
00763         if( LineSave.ipass == 0 )
00764         {
00765                 xIntensity = 0.;
00766         }
00767         else
00768         {
00769                 /* both the counting and integrating modes comes here */
00770                 /* not actually used so set to impossible values */
00771                 chLabel[0] = '\n';
00772                 /* total line intensity or luminosity 
00773                  * these may not be defined in initial calls so define here */
00774                 xIntensity = t->Emis->xIntensity + ExtraInten;
00775         }
00776         /* initial counting case, where ipass == -1, just ignored above, call linadd below */
00777 
00778         /* ExtraInten is option that allows extra intensity (i.e., recomb)
00779          * to be added to this line  with Call PutExtra( exta )
00780          * in main lines this extra
00781          * contribution must be identified explicitly */
00782         ExtraInten = 0.;
00783         /*linadd(xIntensity,wl,chLabel,'i');*/
00784         /*lindst add line with destruction and outward */
00785         rt.fracin = t->Emis->FracInwd;
00786         lindst(xIntensity, 
00787           t->WLAng, 
00788           chLabel, 
00789           t->ipCont, 
00790           /* this is information only - has been counted in cooling already */
00791           'i', 
00792           /* do not add to outward beam, also done separately */
00793           false,
00794           chComment);
00795         rt.fracin = 0.5;
00796 
00797         /* inward part of line - do not move this away from previous lines
00798          * since xIntensity is used here */
00799         xIntensity_in = xIntensity*t->Emis->FracInwd;
00800         linadd(xIntensity_in,t->WLAng,"Inwd",'i',chComment);
00801 
00802         /* cooling part of line */
00803         other = t->Coll.cool;
00804         linadd(other,t->WLAng,"Coll",'i',chComment);
00805 
00806         /* fluorescent excited part of line */
00807         other = t->Emis->PopOpc * t->Emis->pump * (1.-t->Emis->ColOvTot) * t->EnergyErg;
00808         linadd(other,t->WLAng,"Pump",'i',chComment);
00809 
00810         /* heating part of line */
00811         other = t->Coll.heat;
00812         linadd(other,t->WLAng,"Heat",'i',chComment);
00813         return;
00814 }
00815 
00816 /*PutLine enter local line intensity into the intensity stack for eventual printout */
00817 void PutLine(transition * t,
00818   const char *chComment)
00819 {
00820         char chLabel[5];
00821         double xIntensity,
00822                 other,
00823                 xIntensity_in;
00824 
00825         DEBUG_ENTRY( "PutLine()" );
00826 
00827         /* routine to use line array data to generate input
00828          * for emission line array */
00829         ASSERT( t->ipCont > 0. );
00830 
00831         /* if ipass=0 then we must generate label info since first pass
00832          * gt.0 then only need line intensity data */
00833         if( LineSave.ipass == 0 )
00834         {
00835                 /* these variables not used by linadd if ipass ne 0 */
00836                 /* chIonLbl is function that generates a null terminated 4 char string, of form "C  2" */
00837                 chIonLbl(chLabel, t);
00838 
00839                 xIntensity = 0.;
00840         }
00841         else
00842         {
00843                 /* both the counting and integrating modes comes here */
00844                 /* not actually used so set to impossible values */
00845                 /*strcpy( chLabel, "    " );*/
00846                 chLabel[0] = '\n';
00847                 /* total line intensity or luminosity 
00848                  * these may not be defined in initial calls so define here */
00849                 xIntensity = t->Emis->xIntensity + ExtraInten;
00850         }
00851         /* initial counting case, where ipass == -1, just ignored above, call linadd below */
00852 
00853         /* ExtraInten is option that allows extra intensity (i.e., recomb)
00854          * to be added to this line  with Call PutExtra( exta )
00855          * in main lines this extra
00856          * contribution must be identified explicitly */
00857         ExtraInten = 0.;
00858         /*lindst add line with destruction and outward */
00859         rt.fracin = t->Emis->FracInwd;
00860         lindst(xIntensity, 
00861           t->WLAng, 
00862           chLabel, 
00863           t->ipCont, 
00864           /* this is information only - has been counted in cooling already */
00865           'i', 
00866           /* do not add to outward beam, also done separately */
00867           false,
00868           chComment);
00869         rt.fracin = 0.5;
00870 
00871         /* inward part of line - do not move this away from previous lines
00872          * since xIntensity is used here */
00873         xIntensity_in = xIntensity*t->Emis->FracInwd;
00874         linadd(xIntensity_in,t->WLAng,"Inwd",'i',chComment);
00875 
00876         /* cooling part of line */
00877         other = t->Coll.cool;
00878         linadd(other,t->WLAng,"Coll",'i',chComment);
00879 
00880         /* fluorescent excited part of line */
00881         other = t->Emis->PopOpc * t->Emis->pump * (1.-t->Emis->ColOvTot) * t->EnergyErg;
00882         linadd(other,t->WLAng,"Pump",'i',chComment);
00883 
00884         /* heating part of line */
00885         other = t->Coll.heat;
00886         linadd(other,t->WLAng,"Heat",'i',chComment);
00887         return;
00888 }
00889 
00890 /* ==================================================================== */
00891 /*PutExtra enter and 'extra' intensity source for some line */
00892 void PutExtra(double Extra)
00893 {
00894 
00895         DEBUG_ENTRY( "PutExtra()" );
00896 
00897         ExtraInten = (realnum)Extra;
00898         return;
00899 }
00900 
00901 void TransitionJunk( transition *t )
00902 {
00903 
00904         DEBUG_ENTRY( "TransitionJunk()" );
00905 
00906                 /* wavelength, usually in A, used for printout */
00907         t->WLAng = -FLT_MAX;
00908 
00909                 /* transition energy in degrees kelvin*/
00910         t->EnergyK = -FLT_MAX;
00911                 /* transition energy in ergs */
00912         t->EnergyErg = -FLT_MAX;
00913                 /* transition energy in wavenumbers */
00914         t->EnergyWN = -FLT_MAX;
00915 
00916         /* array offset for radiative transition within continuum array 
00917          * is negative if transition is non-radiative. */
00918         t->ipCont = -10000;
00919 
00920         CollisionJunk( &t->Coll );
00921 
00922         /* set these equal to NULL first. That will cause the code to crash if
00923          * the variables are ever used before being deliberately set. */
00924         t->Emis = &DummyEmis;
00925         
00926         t->Lo = NULL;
00927         t->Hi = NULL;
00928         return;
00929 }
00930 
00931 
00932 /*EmLineJunk set all elements of transition struc to dangerous values */
00933 void EmLineJunk( emission *t )
00934 {
00935 
00936         DEBUG_ENTRY( "EmLineJunk()" );
00937 
00938         /* optical depth in continuum to ill face */
00939         t->TauCon = -FLT_MAX;
00940 
00941         /* inward and total line optical depths */
00942         t->TauIn = -FLT_MAX;
00943         t->TauTot = -FLT_MAX;
00944 
00945         /* type of redistribution function, */
00946         t->iRedisFun = INT_MIN;
00947 
00948         /* array offset for line within fine opacity */
00949         t->ipFine = -10000;
00950 
00951         /* inward fraction */
00952         t->FracInwd = -FLT_MAX;
00953 
00954         /* continuum pumping rate */
00955         t->pump = -FLT_MAX;
00956 
00957         /* line intensity */
00958         t->xIntensity = -FLT_MAX;
00959 
00960         /* number of photons emitted per sec in the line */
00961         t->phots = -FLT_MAX;
00962 
00963         /* gf value */
00964         t->gf = -FLT_MAX;
00965 
00966         /* escape and destruction probs */
00967         t->Pesc = -FLT_MAX;
00968         t->Pdest = -FLT_MAX;
00969         t->Pelec_esc = -FLT_MAX;
00970 
00971         /* damping constant, and number related to it */
00972         t->dampXvel = -FLT_MAX;
00973         t->damp = -FLT_MAX;
00974 
00975         /* ratio of collisional to radiative excitation*/
00976         t->ColOvTot = -FLT_MAX;
00977 
00978         /* line opacity */
00979         t->opacity = -FLT_MAX;
00980 
00981         t->PopOpc = -FLT_MAX;
00982 
00983         /* transition prob, Einstein A upper to lower */
00984         t->Aul = -FLT_MAX;
00985 
00986         /* ots rate */
00987         t->ots = -FLT_MAX;
00988         return;
00989 }
00990 
00991 /*CollisionJunk set all elements of transition struc to dangerous values */
00992 void CollisionJunk( collision * t )
00993 {
00994 
00995         DEBUG_ENTRY( "CollisionJunk()" );
00996 
00998         t->ColUL = -FLT_MAX;
00999 
01000         /* Coll->cooling and Coll->heating due to collisional excitation */
01001         t->cool = -FLT_MAX;
01002         t->heat = -FLT_MAX;
01003 
01004         /* collision strengths for transition */
01005         t->cs = -FLT_MAX;
01006         for( long i=0; i<ipNCOLLIDER; i++ )
01007                 t->csi[i] = -FLT_MAX;
01008         return;
01009 }
01010 
01011 /*StateJunk set all elements of transition struc to dangerous values */
01012 void StateJunk( quantumState * t )
01013 {
01014 
01015         DEBUG_ENTRY( "StateJunk()" );
01016 
01017         /* t->chLabel = ''; */
01018 
01020         t->g = -FLT_MAX;
01021 
01023         t->Pop = -FLT_MAX;
01024 
01026         t->IonStg = -10000;
01027 
01029         t->nelem = -10000;
01030         return;
01031 }
01032 
01033 /*TransitionZero zeros out TransitionZero */
01034 void TransitionZero( transition *t )
01035 {
01036 
01037         DEBUG_ENTRY( "TransitionZero()" );
01038 
01039         CollisionZero( &t->Coll );
01040 
01041         StateZero( t->Lo );
01042         StateZero( t->Hi );
01043         EmLineZero( t->Emis );
01044 
01045         return;
01046 }
01047 
01048 /*EmLineZero zeros out the emission line structure */
01049 void EmLineZero( emission * t )
01050 {
01051 
01052         DEBUG_ENTRY( "EmLineZero()" );
01053 
01054         /* total optical depth in all overlapping lines to illuminated face,
01055          * used for pumping */
01056         t->TauCon = opac.taumin;
01057 
01058         /* inward and total line optical depths */
01059         /* >>chng 03 feb 14, from 0 to opac.taumin */
01060         t->TauIn = opac.taumin;
01061 
01062         /* total optical depths */
01063         t->TauTot = 1e20f;
01064 
01065         /* inward fraction */
01066         /* >>chng 03 feb 14, from 0 to 1 */
01067         t->FracInwd = 1.;
01068 
01069         /* continuum pumping rate */
01070         t->pump = 0.;
01071 
01072         /* line intensity */
01073         t->xIntensity = 0.;
01074 
01075         /* number of photons emitted per sec in the line */
01076         t->phots = 0.;
01077 
01078         /* escape and destruction probs */
01079         /* >>chng 03 feb 14, change from 0 to 1 */
01080         t->Pesc = 1.;
01081         t->Pdest = 0.;
01082         t->Pelec_esc = 0.;
01083 
01084         /* ratio of collisional to radiative excitation*/
01085         t->ColOvTot = 0.;
01086 
01087         /* pop that enters net opacity */
01088         t->PopOpc = 0.;
01089 
01090         /* ots rate */
01091         t->ots = 0.;
01092         return;
01093 }
01094 
01095 /*CollisionZero zeros out the structure */
01096 void CollisionZero( collision * t )
01097 {
01098 
01099         DEBUG_ENTRY( "CollisionZero()" );
01100 
01101         /* Coll->cooling and Coll->heating due to collisional excitation */
01102         t->cool = 0.;
01103         t->heat = 0.;
01104         return;
01105 }
01106 
01107 /*StateZero zeros out the structure */
01108 void StateZero( quantumState * t )
01109 {
01110 
01111         DEBUG_ENTRY( "StateZero()" );
01112 
01114         t->Pop = 0.;
01115         return;
01116 }
01117 
01118 /*LineConvRate2CS convert down coll rate back into electron cs in case other parts of code need this for reference */
01119 void LineConvRate2CS( transition* t , realnum rate )
01120 {
01121 
01122         DEBUG_ENTRY( "LineConvRate2CS()" );
01123 
01124         /* return is collision strength, convert from collision rate from 
01125          * upper to lower, this assumes pure electron collisions, but that will
01126          * also be assumed by anything that uses cs, for self-consistency */
01127         t->Coll.cs = rate * t->Hi->g / (realnum)dense.cdsqte;
01128 
01129         /* change assert to non-negative - there can be cases (Iin H2) where cs has
01130          * underflowed to 0 on some platforms */
01131         ASSERT( t->Coll.cs >= 0. );
01132         return;
01133 }
01134 
01135 /*ConvRate2CS convert down coll rate back into electron cs in case other parts of code need this for reference */
01136 double ConvRate2CS( realnum gHi , realnum rate )
01137 {
01138 
01139         double cs;
01140 
01141         DEBUG_ENTRY( "ConvRate2CS()" );
01142 
01143         /* return is collision strength, convert from collision rate from 
01144          * upper to lower, this assumes pure electron collisions, but that will
01145          * also be assumed by anything that uses cs, for self-consistency */
01146         cs = rate * gHi / dense.cdsqte;
01147 
01148         /* change assert to non-negative - there can be cases (Iin H2) where cs has
01149          * underflowed to 0 on some platforms */
01150         ASSERT( cs >= 0. );
01151         return cs;
01152 }
01153 
01154 /* returns true is we have not overrun optical depth scale */
01155 bool lgTauGood( transition * t)
01156 {
01157         bool lgGoodTau;
01158 
01159         DEBUG_ENTRY( "lgTauGood()" );
01160 
01161         if( (t->Emis->TauTot*0.9 - t->Emis->TauIn < 0. && t->Emis->TauIn > 0.) && 
01162             ! fp_equal( t->Emis->TauTot, opac.taumin ) )
01163         {
01164                 /* do not do anything if we have overrun the scale, */
01165                 lgGoodTau = false;
01166         }
01167         else
01168         {
01169                 lgGoodTau = true;
01170         }
01171         return lgGoodTau;
01172 }
01173 
01174 /*gbar0 compute g-bar gaunt factor for neutrals */
01175 STATIC void gbar0(double ex, 
01176           realnum *g)
01177 {
01178         double a, 
01179           b, 
01180           c, 
01181           d, 
01182           y;
01183 
01184         DEBUG_ENTRY( "gbar0()" );
01185 
01186         /* written by Dima Verner
01187          *
01188          * Calculation of the effective Gaunt-factor by use of 
01189          * >>refer      gbar    cs      Van Regemorter, H., 1962, ApJ 136, 906
01190          * fits for neutrals
01191          *  Input parameters: 
01192          * ex - energy ryd - now K
01193          * t  - temperature in K
01194          *  Output parameter:
01195          * g  - effective Gaunt factor
01196          * */
01197 
01198         /* y = ex*157813.7/te */
01199         y = ex/phycon.te;
01200         if( y < 0.01 )
01201         {
01202                 *g = (realnum)(0.29*(log(1.0+1.0/y) - 0.4/POW2(y + 1.0))/exp(y));
01203         }
01204         else
01205         {
01206                 if( y > 10.0 )
01207                 {
01208                         *g = (realnum)(0.066/sqrt(y));
01209                 }
01210                 else
01211                 {
01212                         a = 1.5819068e-02;
01213                         b = 1.3018207e00;
01214                         c = 2.6896230e-03;
01215                         d = 2.5486007e00;
01216                         d = log(y/c)/d;
01217                         *g = (realnum)(a + b*exp(-0.5*POW2(d)));
01218                 }
01219         }
01220         return;
01221 }
01222 
01223 /*gbar1 compute g-bar collision strength using Mewe approximations */
01224 STATIC void gbar1(double ex, 
01225           realnum *g)
01226 {
01227         double y;
01228 
01229         DEBUG_ENTRY( "gbar1()" );
01230 
01231         /*      *written by Dima Verner
01232          *
01233          * Calculation of the effective Gaunt-factor by use of 
01234          * >>refer      gbar    cs      Mewe,R., 1972, A&A 20, 215
01235          * fits for permitted transitions in ions MgII, CaII, FeII (delta n = 0)
01236          * Input parameters: 
01237          * ex - excitation energy in Ryd - now K
01238          * te  - temperature in K
01239          * Output parameter:
01240          * g  - effective Gaunt factor
01241          */
01242 
01243         /* y = ex*157813.7/te */
01244         y = ex/phycon.te;
01245         *g = (realnum)(0.6 + 0.28*(log(1.0+1.0/y) - 0.4/POW2(y + 1.0)));
01246         return;
01247 }
01248 
01249 /*MakeCS compute collision strength by g-bar approximations */
01250 void MakeCS(transition * t)
01251 {
01252         long int ion;
01253         double Abun, 
01254           cs;
01255         realnum
01256           gbar;
01257 
01258         DEBUG_ENTRY( "MakeCS()" );
01259 
01260         /* routine to get cs from various approximations */
01261 
01262         /* check if abundance greater than 0 */
01263         ion = t->Hi->IonStg;
01264 
01265         Abun = dense.xIonDense[ t->Hi->nelem -1 ][ ion-1 ];
01266         if( Abun <= 0. )
01267         {
01268                 gbar = 1.;
01269         }
01270         else
01271         {
01272                 /* check if neutral or ion */
01273                 if( ion == 1 )
01274                 {
01275                         /* neutral - compute gbar for eventual cs */
01276                         gbar0(t->EnergyK,&gbar);
01277                 }
01278                 else
01279                 {
01280                         /* ion - compute gbar for eventual cs */
01281                         gbar1(t->EnergyK,&gbar);
01282                 }
01283         }
01284 
01285         /* above was g-bar, convert to cs */
01286         cs = gbar*(14.5104/WAVNRYD)*t->Emis->gf/t->EnergyWN;
01287 
01288         /* stuff the cs in place */
01289         t->Coll.cs = (realnum)cs;
01290         return;
01291 }
01292 
01293 /*totlin sum total intensity of cooling, recombination, or intensity lines */
01294 double totlin(
01295         /* chInfor is 1 char, 
01296         'i' information, 
01297         'r' recombination or 
01298         'c' collision */
01299         int chInfo)
01300 {
01301         long int i;
01302         double totlin_v;
01303 
01304         DEBUG_ENTRY( "totlin()" );
01305 
01306         /* routine goes through set of entered line
01307          * intensities and picks out those which have
01308          * types agreeing with chInfo.  Valid types are
01309          * 'c', 'r', and 'i'
01310          *begin sanity check */
01311         if( (chInfo != 'i' && chInfo != 'r') && chInfo != 'c' )
01312         {
01313                 fprintf( ioQQQ, " TOTLIN does not understand chInfo=%c\n", 
01314                   chInfo );
01315                 cdEXIT(EXIT_FAILURE);
01316         }
01317         /*end sanity check */
01318 
01319         /* now find sum of lines of type chInfo */
01320         totlin_v = 0.;
01321         for( i=0; i < LineSave.nsum; i++ )
01322         {
01323                 if( LineSv[i].chSumTyp == chInfo )
01324                 {
01325                         totlin_v += LineSv[i].sumlin[0];
01326                 }
01327         }
01328         return( totlin_v );
01329 }
01330 
01331 
01332 /*FndLineHt search through line heat arrays to find the strongest heat source */
01333 void FndLineHt(long int *level, 
01334   /* this is the index of the strongest line in the array on the c scale */
01335   long int *ipStrong, 
01336   double *Strong)
01337 {
01338         long int i; 
01339 
01340         DEBUG_ENTRY( "FndLineHt()" );
01341 
01342         *Strong = 0.;
01343         *level = 0;
01344 
01345         /* do the level 1 lines, 0 is dummy line, <=nLevel1 is correct for c scale */
01346         for( i=1; i <= nLevel1; i++ )
01347         {
01348                 /* check if a line was the major heat agent */
01349                 if( TauLines[i].Coll.heat > *Strong )
01350                 {
01351                         *ipStrong = i;
01352                         *level = 1;
01353                         *Strong = TauLines[i].Coll.heat;
01354                 }
01355         }
01356 
01357         /* now do the level 2 lines */
01358         for( i=0; i < nWindLine; i++ )
01359         {
01360                 if( TauLine2[i].Hi->IonStg < TauLine2[i].Hi->nelem+1-NISO )
01361                 {
01362                         /* check if a line was the major heat agent */
01363                         if( TauLine2[i].Coll.heat > *Strong )
01364                         {
01365                                 *ipStrong = i;
01366                                 *level = 2;
01367                                 *Strong = TauLine2[i].Coll.heat;
01368                         }
01369                 }
01370         }
01371 
01372         /* now do co carbon monoxide lines */
01373         for( i=0; i < nCORotate; i++ )
01374         {
01375                 /* check if a line was the major heat agent */
01376                 if( C12O16Rotate[i].Coll.heat > *Strong )
01377                 {
01378                         *ipStrong = i;
01379                         *level = 3;
01380                         *Strong = C12O16Rotate[i].Coll.heat;
01381                 }
01382         }
01383         for( i=0; i < nCORotate; i++ )
01384         {
01385                 /* check if a line was the major heat agent */
01386                 if( C13O16Rotate[i].Coll.heat > *Strong )
01387                 {
01388                         *ipStrong = i;
01389                         *level = 4;
01390                         *Strong = C13O16Rotate[i].Coll.heat;
01391                 }
01392         }
01393 
01394         /* now do the hyperfine structure lines */
01395         for( i=0; i < nHFLines; i++ )
01396         {
01397                 /* check if a line was the major heat agent */
01398                 if( HFLines[i].Coll.heat > *Strong )
01399                 {
01400                         *ipStrong = i;
01401                         *level = 5;
01402                         *Strong = HFLines[i].Coll.heat;
01403                 }
01404         }
01405 
01406         /*Chianti & Leiden Atoms & Molecules */
01407         for( i=0; i <linesAdded2; i++)
01408         {
01409                 /* check if a line was the major heat agent */
01410                 if( atmolEmis[i].tran->Coll.heat > *Strong )
01411                 {
01412                         *ipStrong = i;
01413                         *level = 6;
01414                         *Strong = atmolEmis[i].tran->Coll.heat;
01415                 }
01416         }
01417         return;
01418 }
01419 
01420 quantumState *AddState2Stack( void )
01421 {
01422         DEBUG_ENTRY( "AddState2Stack()" );
01423 
01424         ASSERT( !lgStatesAdded );
01425 
01426         currentState = new quantumState;
01427 
01428         StateJunk( currentState );
01429 
01430         if( statesAdded == 0 )
01431         {
01432                 GenericStates = currentState;
01433                 GenericStates->next = NULL;
01434                 lastState = GenericStates;
01435         }
01436         else
01437         {
01438                 StateZero( currentState );
01439                 lastState->next = currentState;
01440                 lastState = lastState->next;
01441         }
01442 
01443         statesAdded++;
01444 
01445         return currentState;
01446 }
01447 
01448 emission *AddLine2Stack( bool lgRadiativeTrans )
01449 {
01450         DEBUG_ENTRY( "AddLine2Stack()" );
01451 
01452         if( !lgRadiativeTrans )
01453         {
01454                 return &DummyEmis;
01455         }
01456         else
01457         {
01458                 ASSERT( lgLinesAdded == false );
01459 
01460                 currentLine = new emission;
01461 
01462                 EmLineJunk( currentLine );
01463 
01464                 if( linesAdded == 0 )
01465                 {
01466                         GenericLines = currentLine;
01467                         GenericLines->next = NULL;
01468                         lastLine = GenericLines;
01469                 }
01470                 else
01471                 {
01472                         /* 
01473                         \todo 2 Does doing EmLineZero here defeat the purpose of EmLineJunk? 
01474                         * maybe we should pass full set of Emis components, fill everything in 
01475                         * here, and THEN use EmLineZero?  */
01476                         EmLineZero( currentLine );
01477 
01478                         lastLine->next = currentLine;
01479                         lastLine = lastLine->next;
01480                 }
01481 
01482                 linesAdded++;
01483                 return currentLine;
01484         }
01485 }

Generated on Mon Feb 16 12:01:18 2009 for cloudy by  doxygen 1.4.7