00001
00002
00003
00004 #include "cddefines.h"
00005 #include "atmdat.h"
00006 #include "continuum.h"
00007 #include "conv.h"
00008 #include "dense.h"
00009 #include "dynamics.h"
00010 #include "elementnames.h"
00011 #include "grainvar.h"
00012 #include "he.h"
00013 #include "helike.h"
00014 #include "hydrogenic.h"
00015 #include "ionbal.h"
00016 #include "iso.h"
00017 #include "mole.h"
00018 #include "phycon.h"
00019 #include "physconst.h"
00020 #include "rfield.h"
00021 #include "secondaries.h"
00022 #include "taulines.h"
00023 #include "thirdparty.h"
00024 #include "trace.h"
00025
00026
00027 void iso_level( const long int ipISO, const long int nelem)
00028 {
00029 long int ipHi,
00030 ipLo,
00031 i,
00032 level,
00033 level_error;
00034
00035 const long int numlevels_local = iso.numLevels_local[ipISO][nelem];
00036
00037 double BigError;
00038
00039 int32 nerror;
00040 double HighestPColOut = 0.,
00041 collider,
00042 sum_popn_ov_ion,
00043 TooSmall;
00044 bool lgNegPop=false;
00045 valarray<int32> ipiv(numlevels_local);
00046
00047 valarray<double>
00048 creation(numlevels_local),
00049 error(numlevels_local),
00050 work(numlevels_local),
00051 Save_creation(numlevels_local);
00052 double source=0.,
00053 sink=0.;
00054 valarray<double> PopPerN(iso.n_HighestResolved_local[ipISO][nelem]+1);
00055
00056 multi_arr<double,2,C_TYPE> z, SaveZ;
00057
00058 DEBUG_ENTRY( "iso_level()" );
00059
00060
00061 ASSERT( nelem >= ipISO );
00062 ASSERT( nelem < LIMELM );
00063
00064
00065 z.alloc(numlevels_local,numlevels_local);
00066
00067
00068
00069
00070
00071
00072 if( ipISO == ipH_LIKE && nelem==ipHYDROGEN )
00073 {
00074
00075 collider = dense.EdenHontoHCorr;
00076 }
00077 else
00078 {
00079 collider = dense.EdenHCorr;
00080 }
00081
00082
00083 for( level=0; level < numlevels_local; level++ )
00084 {
00085
00086 creation[level] = iso.RateCont2Level[ipISO][nelem][level];
00087 }
00088
00089
00090
00091 ASSERT( ionbal.CollIonRate_Ground[nelem][nelem-ipISO][0]< SMALLFLOAT ||
00092 fabs( iso.ColIoniz[ipISO][nelem][0]* collider /
00093 SDIV(ionbal.CollIonRate_Ground[nelem][nelem-ipISO][0] ) - 1.) < 0.001 );
00094
00095
00096 if( ipISO == ipH_LIKE )
00097 {
00098 if( nelem==ipHYDROGEN )
00099 TooSmall = 1e-28;
00100 else if( nelem==ipHELIUM )
00101 TooSmall = 1e-18;
00102 else
00103 TooSmall = 2e-18;
00104 }
00105 else if( ipISO == ipHE_LIKE )
00106 {
00107
00108
00109 if( nelem==ipHELIUM )
00110 TooSmall = 1e-20;
00111 else
00112 TooSmall = 1e-10;
00113 }
00114 else
00115 TotalInsanity();
00116
00117
00118
00119 if( ipISO == ipH_LIKE &&
00120 ( iso.xIonSimple[ipISO][nelem] < 1e-35 ) )
00121 {
00122
00123 strcpy( iso.chTypeAtomUsed[ipISO][nelem], "zero " );
00124 if( trace.lgTrace && (nelem == trace.ipIsoTrace[ipISO]) )
00125 {
00126 fprintf( ioQQQ, " iso_level iso=%2ld nelem=%2ld simple II/I=%10.2e so not doing equilibrium, doing %s.\n",
00127 ipISO, nelem, iso.xIonSimple[ipISO][nelem], iso.chTypeAtomUsed[ipISO][nelem] );
00128 }
00129
00130
00131 for( i=0; i < iso.numLevels_max[ipISO][nelem]; i++ )
00132 {
00133 iso.DepartCoef[ipISO][nelem][i] = 1.;
00134 StatesElem[ipISO][nelem][i].Pop = 0.;
00135 }
00136
00137 ionbal.RateRecomTot[nelem][nelem-ipISO] = 0.;
00138 iso.xIonSimple[ipISO][nelem] = 0.;
00139 iso.pop_ion_ov_neut[ipISO][nelem] = 0.;
00140 iso.qTot2S[ipISO][nelem] = 0.;
00141 }
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 else if( ipISO == ipH_LIKE &&
00158 ((strcmp( iso.chTypeAtomSet[ipISO] , "LOWT" )==0) ||
00159 (iso.xIonSimple[ipISO][nelem] < TooSmall) ) )
00160 {
00161 strcpy( iso.chTypeAtomUsed[ipISO][nelem], "Low T" );
00162 if( trace.lgTrace && (nelem == trace.ipIsoTrace[ipISO]) )
00163 {
00164 fprintf( ioQQQ, " iso_level iso=%2ld nelem=%2ld simple II/I=%10.2e so not doing equilibrium, doing %s\n",
00165 ipISO, nelem, iso.xIonSimple[ipISO][nelem], iso.chTypeAtomUsed[ipISO][nelem] );
00166 }
00167
00168
00169 iso.pop_ion_ov_neut[ipISO][nelem] = iso.xIonSimple[ipISO][nelem];
00170
00171
00172
00173 HydroT2Low( ipISO, nelem );
00174 iso.qTot2S[ipISO][nelem] = 0.;
00175 }
00176
00177
00178 else if( ipISO == ipHE_LIKE &&
00179 ((strcmp( iso.chTypeAtomSet[ipISO] , "LOWT" )==0) ||
00180 (iso.xIonSimple[ipISO][nelem] < TooSmall) ) )
00181 {
00182 strcpy( iso.chTypeAtomUsed[ipISO][nelem], "Low T" );
00183
00184 ionbal.RateIonizTot[nelem][nelem-ipISO] = iso.RateLevel2Cont[ipISO][nelem][0];
00185
00186 lgNegPop = false;
00187
00188
00189
00190
00191 iso.pop_ion_ov_neut[ipISO][nelem] = iso.xIonSimple[ipISO][nelem];
00192 StatesElem[ipISO][nelem][0].Pop = 1./SDIV(iso.pop_ion_ov_neut[ipISO][nelem]);
00193 for( long n=1; n < numlevels_local; n++ )
00194 {
00195 StatesElem[ipISO][nelem][n].Pop = 0.;
00196 }
00197 iso.qTot2S[ipISO][nelem] = 0.;
00198 }
00199 else
00200 {
00201 ASSERT( NISO == 2 );
00202 long SpinUsed[NISO] = {2,1};
00203 long indexNmaxP =
00204 iso.QuantumNumbers2Index[ipISO][nelem][ iso.n_HighestResolved_local[ipISO][nelem] ][1][SpinUsed[ipISO]];
00205
00206 strcpy( iso.chTypeAtomUsed[ipISO][nelem], "Popul" );
00207 if( trace.lgTrace && (nelem == trace.ipIsoTrace[ipISO]) )
00208 {
00209 fprintf( ioQQQ, " iso_level iso=%2ld nelem=%2ld doing regular matrix inversion, %s\n",
00210 ipISO, nelem, iso.chTypeAtomUsed[ipISO][nelem] );
00211 }
00212
00213 multi_arr<quantumState,3>::const_iterator StElm = StatesElem.begin(ipISO,nelem);
00214
00215
00216 for( level=0; level < numlevels_local; level++ )
00217 {
00218
00219
00220 z[level][level] = iso.RateLevel2Cont[ipISO][nelem][level];
00221
00222 if( level == 1 )
00223
00224 iso.qTot2S[ipISO][nelem] = iso.ColIoniz[ipISO][nelem][level];
00225
00226 multi_arr<transition,4>::const_iterator Trans = Transitions.begin(ipISO,nelem,level);
00227 md4ci Boltz = iso.Boltzmann.begin(ipISO,nelem,level);
00228
00229
00230 for( ipLo=0; ipLo < level; ipLo++ )
00231 {
00232 double coll_down = Trans[ipLo].Coll.ColUL * collider;
00233
00234 double RadDecay = MAX2( iso.SmallA, Trans[ipLo].Emis->Aul*
00235 (Trans[ipLo].Emis->Pesc +
00236 Trans[ipLo].Emis->Pelec_esc +
00237 Trans[ipLo].Emis->Pdest)*
00238 KILL_BELOW_PLASMA(Trans[ipLo].EnergyWN*WAVNRYD) );
00239
00240 double pump = MAX2( iso.SmallA, Trans[ipLo].Emis->pump *
00241 KILL_BELOW_PLASMA(Trans[ipLo].EnergyWN*WAVNRYD) );
00242
00243 if( iso.lgRandErrGen[ipISO] )
00244 {
00245 coll_down *= iso.ErrorFactor[ipISO][nelem][level][ipLo][IPCOLLIS];
00246 RadDecay *= iso.ErrorFactor[ipISO][nelem][level][ipLo][IPRAD];
00247 pump *= iso.ErrorFactor[ipISO][nelem][level][ipLo][IPRAD];
00248 }
00249
00250 double coll_up = coll_down *
00251 (double)StElm[level].g/
00252 (double)StElm[ipLo].g*
00253 Boltz[ipLo];
00254
00255 z[ipLo][ipLo] += coll_up + pump ;
00256 z[ipLo][level] = - ( coll_up + pump );
00257
00258 double pump_down = pump *
00259 (double)StElm[ipLo].g/
00260 (double)StElm[level].g;
00261
00262 z[level][level] += RadDecay + pump_down + coll_down;
00263 z[level][ipLo] = - (RadDecay + pump_down + coll_down);
00264
00265 if( level == indexNmaxP )
00266 {
00267 HighestPColOut += coll_down;
00268 }
00269 if( ipLo == indexNmaxP )
00270 {
00271 HighestPColOut += coll_up;
00272 }
00273
00274
00275 if( (level == 1) && (ipLo==0) )
00276 {
00277 iso.qTot2S[ipISO][nelem] += coll_down/dense.EdenHCorr;
00278 }
00279 if( (ipLo == 1) && (ipISO==ipH_LIKE || StElm[level].S==1) )
00280 {
00281 iso.qTot2S[ipISO][nelem] += coll_up/dense.EdenHCorr;
00282 }
00283 }
00284 }
00285
00286 if( ipISO == nelem )
00287 {
00288
00289
00290
00291 if( HighestPColOut < 1./StatesElem[ipISO][nelem][indexNmaxP].lifetime )
00292 {
00293 iso.lgCritDensLMix[ipISO] = false;
00294 }
00295 }
00296
00298 ASSERT( ipISO <= ipHE_LIKE );
00299
00300
00301
00302
00303 z[1+ipISO][0] -= iso.TwoNu_induc_dn[ipISO][nelem]*iso.lgInd2nu_On;
00304 z[0][1+ipISO] -= iso.TwoNu_induc_up[ipISO][nelem]*iso.lgInd2nu_On;
00305
00306
00307 z[0][0] += iso.TwoNu_induc_up[ipISO][nelem]*iso.lgInd2nu_On;
00308 z[1+ipISO][1+ipISO] += iso.TwoNu_induc_dn[ipISO][nelem]*iso.lgInd2nu_On;
00309
00310
00311 for( long ion=0; ion<=nelem+1; ++ion )
00312 {
00313 if( ion!=nelem-ipISO )
00314 {
00315
00316 source += gv.GrainChTrRate[nelem][ion][nelem-ipISO] *
00317 dense.xIonDense[nelem][ion] ;
00318
00319 sink += gv.GrainChTrRate[nelem][nelem-ipISO][ion];
00320 }
00321 }
00322
00323
00324
00325 if( dynamics.lgAdvection && dynamics.lgISO[ipISO])
00326 {
00327
00328
00329 source += dynamics.Source[nelem][nelem-ipISO];
00330
00331
00332 sink += dynamics.Rate;
00333 }
00334
00335 #if 0
00336
00337 CodeReview();
00338 if( nelem == ipISO && conv.nTotalIoniz )
00339 {
00340
00341
00342 source += mole.source[nelem][nelem-ipISO];
00343 sink += mole.sink[nelem][nelem-ipISO];
00344 }
00345 #endif
00346
00347 creation[0] += source/SDIV(dense.xIonDense[nelem][nelem+1-ipISO]);
00348 for( level=0; level < numlevels_local; level++ )
00349 {
00350 z[level][level] += sink;
00351 }
00352
00353
00354 if( secondaries.Hx12[ipISO][nelem][iso.nLyaLevel[ipISO]] * iso.lgColl_excite[ipISO] > 0. )
00355 {
00356
00357 for( level=1; level < numlevels_local; level++ )
00358 {
00359 double RateUp , RateDown;
00360
00361 RateUp = secondaries.Hx12[ipISO][nelem][level];
00362 RateDown = RateUp * (double)StatesElem[ipISO][nelem][ipH1s].g /
00363 (double)StatesElem[ipISO][nelem][level].g;
00364
00365
00366 z[ipH1s][ipH1s] += RateUp;
00367
00368
00369 z[level][ipH1s] -= RateDown;
00370
00371
00372 z[ipH1s][level] -= RateUp;
00373
00374 z[level][level] += RateDown;
00375 }
00376 }
00377
00378
00379
00380
00381
00382
00383
00384
00385 SaveZ = z;
00386
00387 for( ipLo=0; ipLo < numlevels_local; ipLo++ )
00388 Save_creation[ipLo] = creation[ipLo];
00389
00390 if( trace.lgTrace && trace.lgIsoTraceFull[ipISO] && (nelem == trace.ipIsoTrace[ipISO]) )
00391 {
00392 fprintf( ioQQQ, " pop level others => (iso_level)\n" );
00393 for( long n=0; n < numlevels_local; n++ )
00394 {
00395 fprintf( ioQQQ, " %s %s %2ld", iso.chISO[ipISO], elementnames.chElementNameShort[nelem], n );
00396 for( long j=0; j < numlevels_local; j++ )
00397 {
00398 fprintf( ioQQQ,"\t%.9e", z[j][n] );
00399 }
00400 fprintf( ioQQQ, "\n" );
00401 }
00402 fprintf(ioQQQ," recomb ");
00403 for( long n=0; n < numlevels_local; n++ )
00404 {
00405 fprintf( ioQQQ,"\t%.9e", creation[n] );
00406 }
00407 fprintf( ioQQQ, "\n" );
00408 fprintf(ioQQQ," recomb ct %.2e co %.2e hectr %.2e hctr %.2e\n",
00409 atmdat.HeCharExcRecTotal,
00410 findspecies("CO")->hevmol ,
00411 atmdat.HeCharExcRecTo[nelem][nelem-ipISO]*dense.xIonDense[ipHELIUM][0] ,
00412 atmdat.HCharExcRecTo[nelem][nelem-ipISO]*dense.xIonDense[ipHYDROGEN][0] );
00413 }
00414
00415 nerror = 0;
00416
00417 getrf_wrapper(numlevels_local,numlevels_local,
00418 z.data(),numlevels_local,&ipiv[0],&nerror);
00419
00420 getrs_wrapper('N',numlevels_local,1,z.data(),numlevels_local,&ipiv[0],
00421 &creation[0],numlevels_local,&nerror);
00422
00423 if( nerror != 0 )
00424 {
00425 fprintf( ioQQQ, " iso_level: dgetrs finds singular or ill-conditioned matrix\n" );
00426 cdEXIT(EXIT_FAILURE);
00427 }
00428
00429
00430
00431 for( level=ipH1s; level < numlevels_local; level++ )
00432 {
00433 double BigSoln= 0.;
00434 error[level] = 0.;
00435 for( long n=ipH1s; n < numlevels_local; n++ )
00436 {
00437
00438 if( fabs(SaveZ[n][level] ) > BigSoln )
00439 BigSoln = fabs(SaveZ[n][level]);
00440
00441 error[level] += SaveZ[n][level]*creation[n];
00442 }
00443
00444 if( BigSoln > 0. )
00445 {
00446 error[level] = (error[level] - Save_creation[level])/ BigSoln;
00447 }
00448 else
00449 {
00450 error[level] = 0.;
00451 }
00452 }
00453
00454
00455 BigError = -1.;
00456 level_error = -1;
00457
00458 for( level=ipH1s; level < numlevels_local; level++ )
00459 {
00460 double abserror;
00461 abserror = fabs( error[level]);
00462
00463 if( abserror > BigError )
00464 {
00465 BigError = abserror;
00466 level_error = level;
00467 }
00468 }
00469
00470
00471
00472 if( BigError > FLT_EPSILON )
00473 {
00474 if( !conv.lgSearch )
00475 fprintf(ioQQQ," PROBLEM" );
00476
00477 fprintf(ioQQQ,
00478 " iso_level zone %.2f - largest residual in iso=%li %s nelem=%li matrix inversion is %g "
00479 "level was %li Search?%c \n",
00480 fnzone,
00481 ipISO,
00482 elementnames.chElementName[nelem],
00483 nelem ,
00484 BigError ,
00485 level_error,
00486 TorF(conv.lgSearch) );
00487 }
00488
00489
00490 for( level=0; level < numlevels_local; level++ )
00491 {
00492 StatesElem[ipISO][nelem][level].Pop = creation[level];
00493
00494
00495 if( StatesElem[ipISO][nelem][level].Pop < 0. )
00496 lgNegPop = true;
00497
00498 if( StatesElem[ipISO][nelem][level].Pop <= 0 )
00499 {
00500 fprintf(ioQQQ," non-positive level pop for iso = %li, nelem = %li = %s, level=%li val=%.3e\n",
00501 ipISO,
00502 nelem ,
00503 elementnames.chElementSym[nelem],
00504 level,
00505 StatesElem[ipISO][nelem][level].Pop );
00506 }
00507
00508 if( iso.PopLTE[ipISO][nelem][level] > 0. )
00509 {
00510
00511 iso.DepartCoef[ipISO][nelem][level] =
00512 StatesElem[ipISO][nelem][level].Pop/
00513 (iso.PopLTE[ipISO][nelem][level]* dense.eden);
00514 }
00515 else
00516 {
00517 iso.DepartCoef[ipISO][nelem][level] = 0.;
00518 }
00519 }
00520
00521
00522 for( level=numlevels_local; level < iso.numLevels_max[ipISO][nelem]; level++ )
00523 {
00524 StatesElem[ipISO][nelem][level].Pop = 0.;
00525
00526
00527 }
00528 }
00529
00530
00531
00532
00533 sum_popn_ov_ion = 0.;
00534
00535
00536 ionbal.RateIonizTot[nelem][nelem-ipISO] = 0.;
00537
00538 for( level=0; level < numlevels_local; level++ )
00539 {
00540
00541 ionbal.RateIonizTot[nelem][nelem-ipISO] +=
00542 StatesElem[ipISO][nelem][level].Pop * iso.RateLevel2Cont[ipISO][nelem][level];
00543
00544
00545 sum_popn_ov_ion += StatesElem[ipISO][nelem][level].Pop;
00546 }
00547
00548
00549 if( ( ionbal.RateIonizTot[nelem][nelem-ipISO]/MAX2(SMALLDOUBLE , sum_popn_ov_ion) ) > BIGDOUBLE )
00550 {
00551 fprintf( ioQQQ, "DISASTER RateIonizTot for Z=%li, ion %li is larger than BIGDOUBLE. This is a big problem.",
00552 nelem+1, nelem-ipISO);
00553 cdEXIT(EXIT_FAILURE);
00554 }
00555 else
00556 ionbal.RateIonizTot[nelem][nelem-ipISO] /= MAX2(SMALLDOUBLE , sum_popn_ov_ion);
00557
00558
00559
00560 if( sum_popn_ov_ion >= 1e-30 )
00561 {
00562 iso.pop_ion_ov_neut[ipISO][nelem] = 1./sum_popn_ov_ion;
00563 }
00564 else
00565 {
00566 iso.pop_ion_ov_neut[ipISO][nelem] = 0.;
00567
00568
00569 dense.xIonDense[nelem][nelem+1-ipISO] = 0.;
00570
00571
00572
00573 dense.IonHigh[nelem] = nelem;
00574 ionbal.RateIonizTot[nelem][nelem-ipISO] = 0.;
00575
00576
00577
00578 for( ipHi=0; ipHi < iso.numLevels_max[ipISO][nelem]; ipHi++ )
00579 {
00580
00581 StatesElem[ipISO][nelem][ipHi].Pop = 0.;
00582 }
00583 }
00584
00585 ASSERT( ionbal.RateIonizTot[nelem][nelem-ipISO] >= 0. );
00586
00587
00588 if( lgNegPop || iso.pop_ion_ov_neut[ipISO][nelem] < 0. )
00589 {
00590 fprintf( ioQQQ,
00591 " DISASTER iso_level finds negative ion fraction for iso=%2ld nelem=%2ld "\
00592 "%s using solver %s, IonFrac=%.3e simple=%.3e TE=%.3e ZONE=%4ld\n",
00593 ipISO,
00594 nelem,
00595 elementnames.chElementSym[nelem],
00596 iso.chTypeAtomUsed[ipISO][nelem],
00597 iso.pop_ion_ov_neut[ipISO][nelem],
00598 iso.xIonSimple[ipISO][nelem],
00599 phycon.te,
00600 nzone );
00601
00602 fprintf( ioQQQ, " level pop are:\n" );
00603 for( i=0; i < numlevels_local; i++ )
00604 {
00605 fprintf( ioQQQ,PrintEfmt("%8.1e", StatesElem[ipISO][nelem][i].Pop ));
00606 if( (i!=0) && !(i%10) ) fprintf( ioQQQ,"\n" );
00607 }
00608 fprintf( ioQQQ, "\n" );
00609 ContNegative();
00610 ShowMe();
00611 cdEXIT(EXIT_FAILURE);
00612 }
00613
00614 if( ipISO == ipHE_LIKE && nelem==ipHELIUM && nzone>0 )
00615 {
00616
00617 double ratio;
00618 double rateOutOf2TripS = StatesElem[ipISO][nelem][ipHe2s3S].Pop * iso.RateLevel2Cont[ipISO][nelem][ipHe2s3S];
00619 if( rateOutOf2TripS > SMALLFLOAT )
00620 {
00621 ratio = rateOutOf2TripS /
00622 ( rateOutOf2TripS + StatesElem[ipISO][nelem][ipHe1s1S].Pop*ionbal.RateIonizTot[nelem][nelem-ipISO] );
00623 }
00624 else
00625 {
00626 ratio = 0.;
00627 }
00628 if( ratio > he.frac_he0dest_23S )
00629 {
00630
00631 he.nzone = nzone;
00632 he.frac_he0dest_23S = ratio;
00633 rateOutOf2TripS = StatesElem[ipISO][nelem][ipHe2s3S].Pop *iso.gamnc[ipISO][nelem][ipHe2s3S];
00634 if( rateOutOf2TripS > SMALLFLOAT )
00635 {
00636 he.frac_he0dest_23S_photo = rateOutOf2TripS /
00637 ( rateOutOf2TripS + StatesElem[ipISO][nelem][ipHe1s1S].Pop*ionbal.RateIonizTot[nelem][nelem-ipISO] );
00638 }
00639 else
00640 {
00641 he.frac_he0dest_23S_photo = 0.;
00642 }
00643 }
00644 }
00645
00646 for( ipHi=1; ipHi<numlevels_local; ++ipHi )
00647 {
00648 for( ipLo=0; ipLo<ipHi; ++ipLo )
00649 {
00650 if( Transitions[ipISO][nelem][ipHi][ipLo].Emis->Aul <= iso.SmallA )
00651 continue;
00652
00653
00654 Transitions[ipISO][nelem][ipHi][ipLo].Emis->PopOpc =
00655 StatesElem[ipISO][nelem][ipLo].Pop - StatesElem[ipISO][nelem][ipHi].Pop*
00656 StatesElem[ipISO][nelem][ipLo].g/StatesElem[ipISO][nelem][ipHi].g;
00657
00658
00659 if( N_(ipHi) > iso.n_HighestResolved_local[ipISO][nelem] )
00660 Transitions[ipISO][nelem][ipHi][ipLo].Emis->PopOpc = StatesElem[ipISO][nelem][ipLo].Pop;
00661 }
00662 }
00663
00664 return;
00665 }