00001
00002
00003
00004 #include "cddefines.h"
00005 #include "physconst.h"
00006 #include "atmdat.h"
00007 #include "rfield.h"
00008 #include "hmi.h"
00009 #include "trace.h"
00010 #include "conv.h"
00011 #include "ionbal.h"
00012 #include "thermal.h"
00013 #include "phycon.h"
00014 #include "doppvel.h"
00015 #include "taulines.h"
00016 #include "mole.h"
00017 #include "heavy.h"
00018 #include "thirdparty.h"
00019 #include "dense.h"
00020 #include "ipoint.h"
00021 #include "elementnames.h"
00022 #include "grainvar.h"
00023 #include "grains.h"
00024
00025
00026
00027
00028
00029
00033 inline double ASINH(double x)
00034 {
00035 if( abs(x) <= 8.e-3 )
00036 return ((0.075*pow2(x) - 1./6.)*pow2(x) + 1.)*x;
00037 else if( abs(x) <= 1./sqrt(DBL_EPSILON) )
00038 {
00039 if( x < 0. )
00040 return -log(sqrt(1. + pow2(x)) - x);
00041 else
00042 return log(sqrt(1. + pow2(x)) + x);
00043 }
00044 else
00045 {
00046 if( x < 0. )
00047 return -(log(-x)+LN_TWO);
00048 else
00049 return log(x)+LN_TWO;
00050 }
00051 }
00052
00053
00054 #define FREE_CHECK(PTR) { ASSERT( PTR != NULL ); free( PTR ); PTR = NULL; }
00055 #define FREE_SAFE(PTR) { if( PTR != NULL ) free( PTR ); PTR = NULL; }
00056
00057 static const long MAGIC_AUGER_DATA = 20060126L;
00058
00059 static const bool INCL_TUNNEL = true;
00060 static const bool NO_TUNNEL = false;
00061
00062 static const bool ALL_STAGES = true;
00063
00064
00065
00066 static long int nCalledGrainDrive;
00067
00068 static bool lgGvInitialized = false;
00069
00070
00071
00072
00073
00074 static const long NTOP = NDEMS/5;
00075
00076
00077
00078 static const double TOLER = CONSERV_TOL/10.;
00079 static const long BRACKET_MAX = 50L;
00080
00081
00082
00083 static const int NCHU = NCHS/3;
00084
00085
00086
00087
00088 static const long CT_LOOP_MAX = 25L;
00089
00090
00091 static const long T_LOOP_MAX = 50L;
00092
00093
00094 static double HEAT_TOLER = DBL_MAX;
00095 static double HEAT_TOLER_BIN = DBL_MAX;
00096 static double CHRG_TOLER = DBL_MAX;
00097
00098
00099
00100
00101
00102
00103 static const double AC0 = 3.e-9;
00104 static const double AC1G = 4.e-8;
00105 static const double AC2G = 7.e-8;
00106
00107
00108 static const double ETILDE = 2.*SQRT2/EVRYD;
00109
00110
00111 static const double THERMCONST = PI4*ELECTRON_MASS*POW2(BOLTZMANN)/POW3(HPLANCK);
00112
00113
00114 static const double STICK_ELEC = 0.5;
00115 static const double STICK_ION = 1.0;
00116
00118 inline double one_elec(long nd)
00119 {
00120 return ELEM_CHARGE/EVRYD/gv.bin[nd]->Capacity;
00121 }
00122
00124 inline double pot2chrg(double x,
00125 long nd)
00126 {
00127 return x/one_elec(nd) - 1.;
00128 }
00129
00131 inline double chrg2pot(double x,
00132 long nd)
00133 {
00134 return (x+1.)*one_elec(nd);
00135 }
00136
00138 inline double elec_esc_length(double e,
00139 long nd)
00140 {
00141
00142 if( e <= gv.bin[nd]->le_thres )
00143 return 1.e-7;
00144 else
00145 return 3.e-6*gv.bin[nd]->eec*sqrt(pow3(e*EVRYD*1.e-3));
00146 }
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 STATIC void ReadAugerData();
00158
00159 STATIC void InitBinAugerData(long,long,long);
00160
00161 STATIC void GetNextLine(const char*, FILE*, char[]);
00162
00163 STATIC void InitEmissivities(void);
00164
00165 STATIC double PlanckIntegral(double,long,long);
00166
00167 STATIC void NewChargeData(long);
00168
00169 STATIC double GrnStdDpth(long);
00170
00171 STATIC void GrainChargeTemp(void);
00172
00173 STATIC void GrainCharge(long,double*);
00174
00175 STATIC double GrainElecRecomb1(long,long,double*,double*);
00176
00177 STATIC double GrainElecEmis1(long,long,double*,double*,double*,double*);
00178
00179
00180 STATIC void GrainScreen(long,long,long,double*,double*);
00181
00182 STATIC double ThetaNu(double);
00183
00184 STATIC void UpdatePot(long,long,long,double[],double[]);
00185
00186 STATIC void GetFracPop(long,long,double[],double[],long*);
00187
00188 STATIC void UpdatePot1(long,long,long,long);
00189
00190 STATIC void UpdatePot2(long,long);
00191
00192 inline void Yfunc(long,long,double,double,double,double,double,double*,double*,
00193 double*,double*);
00194
00195 STATIC double y0b(long,long,long);
00196
00197 STATIC double y0b01(long,long,long);
00198
00199 STATIC double y0psa(long,long,long,double);
00200
00201 STATIC double y1psa(long,long,double);
00202
00203 inline double y2pa(double,double,long,double*);
00204
00205 inline double y2s(double,double,long,double*);
00206
00207 STATIC long HighestIonStage(void);
00208
00209 STATIC void UpdateRecomZ0(long,long,bool);
00210
00211 STATIC void GetPotValues(long,long,double*,double*,double*,
00212 double*,double*,double*,bool);
00213
00214
00215
00216 STATIC void GrainIonColl(long,long,long,long,const double[],const double[],long*,
00217 realnum*,realnum*);
00218
00219 STATIC void GrainChrgTransferRates(long);
00220
00221 STATIC void GrainUpdateRadius1(void);
00222
00223 STATIC void GrainUpdateRadius2(bool);
00224
00225 STATIC void GrainTemperature(long,realnum*,double*,double*,
00226 double*);
00227
00228 STATIC void PE_init(long,long,long,double*,double*,double*,
00229 double*,double*,double*,double*);
00230
00231 STATIC void GrainCollHeating(long,realnum*,realnum*);
00232
00233 STATIC double GrnVryDpth(long);
00234
00235
00236
00237
00238
00239 void GrainZero(void)
00240 {
00241 long int nelem, ion, ion_to;
00242
00243 DEBUG_ENTRY( "GrainZero()" );
00244
00245 gv.lgAnyDustVary = false;
00246 gv.TotalEden = 0.;
00247 gv.dHeatdT = 0.;
00248 gv.lgQHeatAll = false;
00249
00250
00251 gv.lgGrainElectrons = true;
00252 gv.lgQHeatOn = true;
00253 gv.lgDHetOn = true;
00254 gv.lgDColOn = true;
00255 gv.GrainMetal = 1.;
00256 gv.dstAbundThresholdNear = 1.e-6f;
00257 gv.dstAbundThresholdFar = 1.e-3f;
00258 gv.lgBakes = false;
00259 gv.lgWD01 = false;
00260 gv.nChrgRequested = NCHRG_DEFAULT;
00261 gv.ReadPtr = 0L;
00262
00263 gv.lgReevaluate = true;
00264
00265 gv.lgNegGrnDrg = false;
00266
00267
00268 nCalledGrainDrive = 0;
00269
00270
00271
00272 gv.lgBakesPAH_heat = false;
00273
00274
00275
00276 gv.lgGrainPhysicsOn = true;
00277
00278
00279
00280 gv.GrainHeatScaleFactor = 1.f;
00281
00282
00283
00284
00285 gv.which_enth[MAT_CAR] = ENTH_CAR;
00286 gv.which_zmin[MAT_CAR] = ZMIN_CAR;
00287 gv.which_pot[MAT_CAR] = POT_CAR;
00288 gv.which_ial[MAT_CAR] = IAL_CAR;
00289 gv.which_pe[MAT_CAR] = PE_CAR;
00290 gv.which_strg[MAT_CAR] = STRG_CAR;
00291 gv.which_H2distr[MAT_CAR] = H2_CAR;
00292
00293 gv.which_enth[MAT_SIL] = ENTH_SIL;
00294 gv.which_zmin[MAT_SIL] = ZMIN_SIL;
00295 gv.which_pot[MAT_SIL] = POT_SIL;
00296 gv.which_ial[MAT_SIL] = IAL_SIL;
00297 gv.which_pe[MAT_SIL] = PE_SIL;
00298 gv.which_strg[MAT_SIL] = STRG_SIL;
00299 gv.which_H2distr[MAT_SIL] = H2_SIL;
00300
00301 gv.which_enth[MAT_PAH] = ENTH_PAH;
00302 gv.which_zmin[MAT_PAH] = ZMIN_CAR;
00303 gv.which_pot[MAT_PAH] = POT_CAR;
00304 gv.which_ial[MAT_PAH] = IAL_CAR;
00305 gv.which_pe[MAT_PAH] = PE_CAR;
00306 gv.which_strg[MAT_PAH] = STRG_CAR;
00307 gv.which_H2distr[MAT_PAH] = H2_CAR;
00308
00309 gv.which_enth[MAT_CAR2] = ENTH_CAR2;
00310 gv.which_zmin[MAT_CAR2] = ZMIN_CAR;
00311 gv.which_pot[MAT_CAR2] = POT_CAR;
00312 gv.which_ial[MAT_CAR2] = IAL_CAR;
00313 gv.which_pe[MAT_CAR2] = PE_CAR;
00314 gv.which_strg[MAT_CAR2] = STRG_CAR;
00315 gv.which_H2distr[MAT_CAR2] = H2_CAR;
00316
00317 gv.which_enth[MAT_SIL2] = ENTH_SIL2;
00318 gv.which_zmin[MAT_SIL2] = ZMIN_SIL;
00319 gv.which_pot[MAT_SIL2] = POT_SIL;
00320 gv.which_ial[MAT_SIL2] = IAL_SIL;
00321 gv.which_pe[MAT_SIL2] = PE_SIL;
00322 gv.which_strg[MAT_SIL2] = STRG_SIL;
00323 gv.which_H2distr[MAT_SIL2] = H2_SIL;
00324
00325 gv.which_enth[MAT_PAH2] = ENTH_PAH2;
00326 gv.which_zmin[MAT_PAH2] = ZMIN_CAR;
00327 gv.which_pot[MAT_PAH2] = POT_CAR;
00328 gv.which_ial[MAT_PAH2] = IAL_CAR;
00329 gv.which_pe[MAT_PAH2] = PE_CAR;
00330 gv.which_strg[MAT_PAH2] = STRG_CAR;
00331 gv.which_H2distr[MAT_PAH2] = H2_CAR;
00332
00333 for( nelem=0; nelem < LIMELM; nelem++ )
00334 {
00335 for( ion=0; ion <= nelem+1; ion++ )
00336 {
00337 for( ion_to=0; ion_to <= nelem+1; ion_to++ )
00338 {
00339 gv.GrainChTrRate[nelem][ion][ion_to] = 0.f;
00340 }
00341 }
00342 }
00343
00344
00345
00346
00347 strcpy( gv.chPAH_abundance_fcn , "H0" );
00348
00349
00350
00351 ReturnGrainBins();
00352 return;
00353 }
00354
00355
00356
00357
00358 void GrainStartIter(void)
00359 {
00360 long nd;
00361
00362 DEBUG_ENTRY( "GrainStartIter()" );
00363
00364 if( gv.lgDustOn && gv.lgGrainPhysicsOn )
00365 {
00366 for( nd=0; nd < gv.nBin; nd++ )
00367 {
00368
00369
00370 gv.bin[nd]->dstpotsav = gv.bin[nd]->dstpot;
00371 gv.bin[nd]->qtmin = ( gv.bin[nd]->qtmin_zone1 > 0. ) ?
00372 gv.bin[nd]->qtmin_zone1 : DBL_MAX;
00373 gv.bin[nd]->avdust = 0.;
00374 gv.bin[nd]->avdpot = 0.;
00375 gv.bin[nd]->avdft = 0.;
00376 gv.bin[nd]->avDGRatio = 0.;
00377 gv.bin[nd]->TeGrainMax = -1.f;
00378 gv.bin[nd]->lgEverQHeat = false;
00379 gv.bin[nd]->QHeatFailures = 0L;
00380 gv.bin[nd]->lgQHTooWide = false;
00381 gv.bin[nd]->lgPAHsInIonizedRegion = false;
00382 gv.bin[nd]->nChrgOrg = gv.bin[nd]->nChrg;
00383 }
00384 }
00385 return;
00386 }
00387
00388
00389
00390
00391 void GrainRestartIter(void)
00392 {
00393 long nd;
00394
00395 DEBUG_ENTRY( "GrainRestartIter()" );
00396
00397 if( gv.lgDustOn && gv.lgGrainPhysicsOn )
00398 {
00399 for( nd=0; nd < gv.nBin; nd++ )
00400 {
00401
00402
00403 gv.bin[nd]->dstpot = gv.bin[nd]->dstpotsav;
00404 gv.bin[nd]->nChrg = gv.bin[nd]->nChrgOrg;
00405 }
00406 }
00407 return;
00408 }
00409
00410
00411
00412 void SetNChrgStates(long nChrg)
00413 {
00414 DEBUG_ENTRY( "SetNChrgStates()" );
00415
00416 ASSERT( nChrg >= 2 && nChrg <= NCHU );
00417 gv.nChrgRequested = nChrg;
00418 return;
00419 }
00420
00421
00422 long NewGrainBin(void)
00423 {
00424 long nd, nz;
00425 unsigned long ns;
00426
00427 DEBUG_ENTRY( "NewGrainBin()" );
00428
00429 ASSERT( lgGvInitialized );
00430
00431 if( gv.nBin >= NDUST )
00432 {
00433 fprintf( ioQQQ, " The code has run out of grain bins; increase NDUST and recompile.\n" );
00434 cdEXIT(EXIT_FAILURE);
00435 }
00436 nd = gv.nBin;
00437
00438 ASSERT( gv.bin[nd] == NULL );
00439 gv.bin[nd] = (GrainBin *)MALLOC(sizeof(GrainBin));
00440
00441
00442 gv.bin[nd]->dstab1 = NULL;
00443 gv.bin[nd]->pure_sc1 = NULL;
00444 gv.bin[nd]->asym = NULL;
00445 for( ns=0; ns < NSHL; ns++ )
00446 gv.bin[nd]->sd[ns] = NULL;
00447 gv.bin[nd]->y0b06 = NULL;
00448 gv.bin[nd]->inv_att_len = NULL;
00449 for( nz=0; nz < NCHS; nz++ )
00450 gv.bin[nd]->chrg[nz] = NULL;
00451
00452 gv.lgDustOn = true;
00453 gv.bin[nd]->lgQHeat = false;
00454 gv.bin[nd]->qnflux = LONG_MAX;
00455 gv.bin[nd]->nfill = 0;
00456 gv.bin[nd]->lgDustFunc = false;
00457 gv.bin[nd]->DustDftVel = 1.e3f;
00458 gv.bin[nd]->TeGrainMax = FLT_MAX;
00459
00460 gv.bin[nd]->nChrg = gv.nChrgRequested;
00461
00462
00463
00464 gv.bin[nd]->tedust = 1.f;
00465 gv.bin[nd]->GrainHeat = DBL_MAX/10.;
00466 gv.bin[nd]->GrainGasCool = DBL_MAX/10.;
00467
00468
00469 gv.bin[nd]->EnergyCheck = 0.;
00470 gv.bin[nd]->le_thres = FLT_MAX;
00471 gv.bin[nd]->dstAbund = -FLT_MAX;
00472 gv.bin[nd]->dstfactor = 1.f;
00473 gv.bin[nd]->GrnVryDpth = 1.f;
00474 gv.bin[nd]->cnv_H_pGR = -DBL_MAX;
00475 gv.bin[nd]->cnv_H_pCM3 = -DBL_MAX;
00476 gv.bin[nd]->cnv_CM3_pGR = -DBL_MAX;
00477 gv.bin[nd]->cnv_CM3_pH = -DBL_MAX;
00478 gv.bin[nd]->cnv_GR_pH = -DBL_MAX;
00479 gv.bin[nd]->cnv_GR_pCM3 = -DBL_MAX;
00480
00481 gv.bin[nd]->rate_h2_form_grains_used = 0.;
00482
00483 gv.nBin++;
00484 return nd;
00485 }
00486
00487
00488 void ReturnGrainBins(void)
00489 {
00490 long nelem, nd, nz;
00491 unsigned long ns;
00492
00493 DEBUG_ENTRY( "ReturnGrainBins()" );
00494
00495 if( lgGvInitialized )
00496 {
00497
00498 for( nd=0; nd < gv.nBin; nd++ )
00499 {
00500 ASSERT( gv.bin[nd] != NULL );
00501
00502 FREE_SAFE( gv.bin[nd]->dstab1 );
00503 FREE_SAFE( gv.bin[nd]->pure_sc1 );
00504 FREE_SAFE( gv.bin[nd]->asym );
00505 FREE_SAFE( gv.bin[nd]->y0b06 );
00506 FREE_SAFE( gv.bin[nd]->inv_att_len );
00507
00508 for( nz=0; nz < NCHS; nz++ )
00509 {
00510 if( gv.bin[nd]->chrg[nz] != NULL )
00511 delete gv.bin[nd]->chrg[nz];
00512 }
00513
00514 for( ns=0; ns < NSHL; ns++ )
00515 {
00516 if( gv.bin[nd]->sd[ns] != NULL )
00517 {
00518 if( ns > 0 )
00519 {
00520 FREE_SAFE( gv.bin[nd]->sd[ns]->Ener );
00521 FREE_SAFE( gv.bin[nd]->sd[ns]->AvNr );
00522 }
00523 delete gv.bin[nd]->sd[ns];
00524 }
00525 }
00526
00527 FREE_CHECK( gv.bin[nd] );
00528 }
00529
00530 for( nelem=0; nelem < LIMELM; nelem++ )
00531 {
00532 if( gv.AugerData[nelem] != NULL )
00533 {
00534 for( ns=0; ns < gv.AugerData[nelem]->nSubShell; ns++ )
00535 {
00536 FREE_CHECK( gv.AugerData[nelem]->AvNumber[ns] );
00537 FREE_CHECK( gv.AugerData[nelem]->Energy[ns] );
00538 }
00539 FREE_CHECK( gv.AugerData[nelem]->AvNumber );
00540 FREE_CHECK( gv.AugerData[nelem]->Energy );
00541 FREE_CHECK( gv.AugerData[nelem]->IonThres );
00542 FREE_CHECK( gv.AugerData[nelem]->nData );
00543 FREE_CHECK( gv.AugerData[nelem] );
00544 }
00545 }
00546
00547 FREE_SAFE( gv.anumin );
00548 FREE_SAFE( gv.anumax );
00549 FREE_SAFE( gv.dstab );
00550 FREE_SAFE( gv.dstsc );
00551 FREE_SAFE( gv.GrainEmission );
00552 FREE_SAFE( gv.GraphiteEmission );
00553 FREE_SAFE( gv.SilicateEmission );
00554 }
00555 else
00556 {
00557
00558
00559 for( nd=0; nd < NDUST; nd++ )
00560 gv.bin[nd] = NULL;
00561
00562 for( nelem=0; nelem < LIMELM; nelem++ )
00563 gv.AugerData[nelem] = NULL;
00564
00565 gv.anumin = NULL;
00566 gv.anumax = NULL;
00567 gv.dstab = NULL;
00568 gv.dstsc = NULL;
00569 gv.GrainEmission = NULL;
00570 gv.GraphiteEmission = NULL;
00571 gv.SilicateEmission = NULL;
00572
00573 lgGvInitialized = true;
00574 }
00575
00576 gv.lgDustOn = false;
00577 gv.nBin = 0;
00578 return;
00579 }
00580
00581
00582
00583
00584
00585 void GrainsInit(void)
00586 {
00587 long int i,
00588 nelem,
00589 nd,
00590 nz;
00591 unsigned long int j,
00592 ns;
00593
00594 DEBUG_ENTRY( "GrainsInit()" );
00595
00596 if( trace.lgTrace && trace.lgDustBug )
00597 {
00598 fprintf( ioQQQ, " GrainsInit called.\n" );
00599 }
00600
00601
00602
00603 ASSERT( gv.anumin == NULL );
00604 gv.anumin = (realnum*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(realnum)));
00605 ASSERT( gv.anumax == NULL );
00606 gv.anumax = (realnum*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(realnum)));
00607 ASSERT( gv.dstab == NULL );
00608 gv.dstab = (double*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(double)));
00609 ASSERT( gv.dstsc == NULL );
00610 gv.dstsc = (double*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(double)));
00611 ASSERT( gv.GrainEmission == NULL );
00612 gv.GrainEmission = (realnum*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(realnum)));
00613 ASSERT( gv.GraphiteEmission == NULL );
00614 gv.GraphiteEmission = (realnum*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(realnum)));
00615 ASSERT( gv.SilicateEmission == NULL );
00616 gv.SilicateEmission = (realnum*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(realnum)));
00617
00618
00619 ASSERT( gv.nBin >= 0 && gv.nBin < NDUST );
00620 for( nd=gv.nBin; nd < NDUST; nd++ )
00621 {
00622 ASSERT( gv.bin[nd] == NULL );
00623 }
00624
00625
00626 for( nelem=0; nelem < LIMELM; nelem++ )
00627 {
00628 gv.elmSumAbund[nelem] = 0.f;
00629 }
00630
00631 for( i=0; i < rfield.nupper; i++ )
00632 {
00633 gv.dstab[i] = 0.;
00634 gv.dstsc[i] = 0.;
00635
00636 gv.GrainEmission[i] = 0.;
00637 gv.SilicateEmission[i] = 0.;
00638 gv.GraphiteEmission[i] = 0.;
00639 }
00640
00641 if( !gv.lgDustOn )
00642 {
00643
00644 gv.GrainHeatInc = 0.;
00645 gv.GrainHeatDif = 0.;
00646 gv.GrainHeatLya = 0.;
00647 gv.GrainHeatCollSum = 0.;
00648 gv.GrainHeatSum = 0.;
00649 gv.GasCoolColl = 0.;
00650 thermal.heating[0][13] = 0.;
00651 thermal.heating[0][14] = 0.;
00652 thermal.heating[0][25] = 0.;
00653
00654 if( trace.lgTrace && trace.lgDustBug )
00655 {
00656 fprintf( ioQQQ, " GrainsInit exits.\n" );
00657 }
00658 return;
00659 }
00660
00661 #ifdef WD_TEST2
00662 gv.lgWD01 = true;
00663 #endif
00664
00665 HEAT_TOLER = conv.HeatCoolRelErrorAllowed / 3.;
00666 HEAT_TOLER_BIN = HEAT_TOLER / sqrt((double)gv.nBin);
00667 CHRG_TOLER = conv.EdenErrorAllowed / 3.;
00668
00669
00670 gv.anumin[0] = 0.f;
00671 for( i=1; i < rfield.nupper; i++ )
00672 gv.anumax[i-1] = gv.anumin[i] = (realnum)sqrt(rfield.anu[i-1]*rfield.anu[i]);
00673 gv.anumax[rfield.nupper-1] = FLT_MAX;
00674
00675 ReadAugerData();
00676
00677 for( nd=0; nd < gv.nBin; nd++ )
00678 {
00679 double help,atoms,p_rad,ThresInf,ThresInfVal,Emin,d[5];
00680 long low1,low2,low3,lowm;
00681
00682
00683 ASSERT( gv.bin[nd] != NULL );
00684 ASSERT( gv.bin[nd]->nChrg >= 2 && gv.bin[nd]->nChrg <= NCHU );
00685
00686 if( gv.bin[nd]->DustWorkFcn < rfield.anu[0] || gv.bin[nd]->DustWorkFcn > rfield.anu[rfield.nupper] )
00687 {
00688 fprintf( ioQQQ, " Grain work function for %s has insane value: %.4e\n",
00689 gv.bin[nd]->chDstLab,gv.bin[nd]->DustWorkFcn );
00690 cdEXIT(EXIT_FAILURE);
00691 }
00692
00693
00694 if( gv.lgQHeatAll )
00695 {
00696 gv.bin[nd]->lgQHeat = true;
00697 }
00698
00699
00700 if( !gv.lgQHeatOn )
00701 {
00702 gv.bin[nd]->lgQHeat = false;
00703 }
00704
00705
00706 if( thermal.ConstGrainTemp > 0. )
00707 {
00708 gv.bin[nd]->lgQHeat = false;
00709 }
00710
00711 #ifndef IGNORE_QUANTUM_HEATING
00712 gv.bin[nd]->lgQHTooWide = false;
00713 gv.bin[nd]->qtmin = DBL_MAX;
00714 #endif
00715
00716 if( gv.bin[nd]->lgDustFunc || gv.bin[nd]->matType == MAT_PAH || gv.bin[nd]->matType == MAT_PAH2 )
00717 gv.lgAnyDustVary = true;
00718
00719
00720
00721 gv.bin[nd]->dstAbund = -FLT_MAX;
00722
00723 gv.bin[nd]->GrnVryDpth = 1.f;
00724
00725 gv.bin[nd]->qtmin_zone1 = -1.;
00726
00727
00728 gv.bin[nd]->le_thres = gv.lgWD01 ? FLT_MAX :
00729 (realnum)(pow(pow((double)gv.bin[nd]->dustp[0],0.85)/30.,2./3.)*1.e3/EVRYD);
00730
00731 for( nz=0; nz < NCHS; nz++ )
00732 {
00733 ASSERT( gv.bin[nd]->chrg[nz] == NULL );
00734 gv.bin[nd]->chrg[nz] = new ChargeBin;
00735 gv.bin[nd]->chrg[nz]->yhat.clear();
00736 gv.bin[nd]->chrg[nz]->yhat_primary.clear();
00737 gv.bin[nd]->chrg[nz]->ehat.clear();
00738 gv.bin[nd]->chrg[nz]->cs_pdt.clear();
00739 gv.bin[nd]->chrg[nz]->fac1.clear();
00740 gv.bin[nd]->chrg[nz]->fac2.clear();
00741 gv.bin[nd]->chrg[nz]->nfill = 0;
00742
00743 gv.bin[nd]->chrg[nz]->DustZ = LONG_MIN;
00744 gv.bin[nd]->chrg[nz]->FracPop = -DBL_MAX;
00745 gv.bin[nd]->chrg[nz]->tedust = 1.f;
00746 }
00747
00748
00749
00750 if( gv.lgBakes )
00751 {
00752
00753
00754 help = ceil(pot2chrg(gv.bin[nd]->BandGap-gv.bin[nd]->DustWorkFcn+one_elec(nd)/2.,nd));
00755 low1 = nint(help);
00756 }
00757 else
00758 {
00759 zmin_type zcase = gv.which_zmin[gv.bin[nd]->matType];
00760
00761 switch( zcase )
00762 {
00763 case ZMIN_CAR:
00764 help = gv.bin[nd]->AvRadius*1.e7;
00765 help = ceil(-(1.2*POW2(help)+3.9*help+0.2)/1.44);
00766 low1 = nint(help);
00767
00768
00769 break;
00770 case ZMIN_SIL:
00771 help = gv.bin[nd]->AvRadius*1.e7;
00772 help = ceil(-(0.7*POW2(help)+2.5*help+0.8)/1.44);
00773 low1 = nint(help);
00774
00775
00776 break;
00777 case ZMIN_BAKES:
00778
00779 help = ceil(pot2chrg(gv.bin[nd]->BandGap-gv.bin[nd]->DustWorkFcn+one_elec(nd)/2.,nd));
00780 low1 = nint(help);
00781 break;
00782 default:
00783 fprintf( ioQQQ, " GrainsInit detected unknown Zmin type: %d\n" , zcase );
00784 cdEXIT(EXIT_FAILURE);
00785 }
00786 }
00787
00788
00789 ASSERT( help > (double)(LONG_MIN+1) );
00790
00791
00792
00793
00794
00795
00796 low2 = low1;
00797 GetPotValues(nd,low2,&ThresInf,&d[0],&d[1],&d[2],&d[3],&d[4],INCL_TUNNEL);
00798 if( ThresInf < 0. )
00799 {
00800 low3 = 0;
00801
00802
00803 while( low3-low2 > 1 )
00804 {
00805 lowm = (low2+low3)/2;
00806 GetPotValues(nd,lowm,&ThresInf,&d[0],&d[1],&d[2],&d[3],&d[4],INCL_TUNNEL);
00807 if( ThresInf < 0. )
00808 low2 = lowm;
00809 else
00810 low3 = lowm;
00811 }
00812 low2 = low3;
00813 }
00814
00815
00816
00817
00818 gv.bin[nd]->LowestZg = MAX2(low1,low2);
00819 gv.bin[nd]->LowestPot = chrg2pot(gv.bin[nd]->LowestZg,nd);
00820
00821 ns = 0;
00822
00823 ASSERT( gv.bin[nd]->sd[ns] == NULL );
00824 gv.bin[nd]->sd[ns] = new ShellData;
00825
00826
00827 gv.bin[nd]->sd[ns]->nelem = -1;
00828 gv.bin[nd]->sd[ns]->ns = -1;
00829 gv.bin[nd]->sd[ns]->ionPot = gv.bin[nd]->DustWorkFcn;
00830 gv.bin[nd]->sd[ns]->p.clear();
00831 gv.bin[nd]->sd[ns]->y01.clear();
00832 gv.bin[nd]->sd[ns]->AvNr = NULL;
00833 gv.bin[nd]->sd[ns]->Ener = NULL;
00834
00835
00836 for( nelem=ipLITHIUM; nelem < LIMELM && !gv.lgWD01; nelem++ )
00837 {
00838 if( gv.bin[nd]->elmAbund[nelem] > 0. )
00839 {
00840 ASSERT( gv.AugerData[nelem] != NULL );
00841
00842 for( j=0; j < gv.AugerData[nelem]->nSubShell; j++ )
00843 {
00844 ++ns;
00845
00846 ASSERT( ns < NSHL && gv.bin[nd]->sd[ns] == NULL );
00847 gv.bin[nd]->sd[ns] = new ShellData;
00848
00849 gv.bin[nd]->sd[ns]->nelem = nelem;
00850 gv.bin[nd]->sd[ns]->ns = j;
00851 gv.bin[nd]->sd[ns]->ionPot = gv.AugerData[nelem]->IonThres[j];
00852 gv.bin[nd]->sd[ns]->p.clear();
00853 gv.bin[nd]->sd[ns]->y01.clear();
00854 gv.bin[nd]->sd[ns]->AvNr = NULL;
00855 gv.bin[nd]->sd[ns]->Ener = NULL;
00856 }
00857 }
00858 }
00859
00860 gv.bin[nd]->nShells = ++ns;
00861
00862 GetPotValues(nd,gv.bin[nd]->LowestZg,&d[0],&ThresInfVal,&d[1],&d[2],&d[3],&Emin,INCL_TUNNEL);
00863
00864 for( ns=0; ns < gv.bin[nd]->nShells; ns++ )
00865 {
00866 long ipLo;
00867 double Ethres = ( ns == 0 ) ? ThresInfVal : gv.bin[nd]->sd[ns]->ionPot;
00868 ShellData *sptr = gv.bin[nd]->sd[ns];
00869
00870 sptr->ipLo = hunt_bisect( rfield.anu, rfield.nupper, (realnum)Ethres ) + 1;
00871
00872 ipLo = sptr->ipLo;
00873
00874
00875 long len = rfield.nflux + 10 - ipLo;
00876
00877 sptr->p.reserve( len );
00878 sptr->p.alloc( ipLo, rfield.nflux );
00879
00880 sptr->y01.reserve( len );
00881 sptr->y01.alloc( ipLo, rfield.nflux );
00882
00883
00884 if( ns > 0 )
00885 {
00886 sptr->nData = gv.AugerData[sptr->nelem]->nData[sptr->ns];
00887 ASSERT( sptr->AvNr == NULL );
00888 sptr->AvNr = (double*)MALLOC((size_t)sptr->nData*sizeof(double));
00889 ASSERT( sptr->Ener == NULL );
00890 sptr->Ener = (double*)MALLOC((size_t)sptr->nData*sizeof(double));
00891 sptr->y01A.resize( sptr->nData );
00892
00893 for( long n=0; n < sptr->nData; n++ )
00894 {
00895 sptr->AvNr[n] = gv.AugerData[sptr->nelem]->AvNumber[sptr->ns][n];
00896 sptr->Ener[n] = gv.AugerData[sptr->nelem]->Energy[sptr->ns][n];
00897
00898 sptr->y01A[n].reserve( len );
00899 sptr->y01A[n].alloc( ipLo, rfield.nflux );
00900 }
00901 }
00902 }
00903
00904 ASSERT( gv.bin[nd]->y0b06 == NULL );
00905 gv.bin[nd]->y0b06 = (realnum*)MALLOC((size_t)rfield.nupper*sizeof(realnum));
00906
00907 InitBinAugerData( nd, 0, rfield.nflux );
00908
00909 gv.bin[nd]->nfill = rfield.nflux;
00910
00911
00912
00913
00914
00915
00917 gv.bin[nd]->StickElecPos = STICK_ELEC*(1. - exp(-gv.bin[nd]->AvRadius/elec_esc_length(0.,nd)));
00918 atoms = gv.bin[nd]->AvVol*gv.bin[nd]->dustp[0]/ATOMIC_MASS_UNIT/gv.bin[nd]->atomWeight;
00919 p_rad = 1./(1.+exp(20.-atoms));
00920 gv.bin[nd]->StickElecNeg = gv.bin[nd]->StickElecPos*p_rad;
00921
00922
00923
00924
00925 gv.bin[nd]->GrnVryDpth = (realnum)GrnVryDpth(nd);
00926 gv.bin[nd]->dstAbund = (realnum)(gv.bin[nd]->dstfactor*gv.GrainMetal*
00927 gv.bin[nd]->GrnVryDpth);
00928 ASSERT( gv.bin[nd]->dstAbund > 0.f );
00929
00930 gv.bin[nd]->cnv_H_pCM3 = dense.gas_phase[ipHYDROGEN]*gv.bin[nd]->dstAbund;
00931 gv.bin[nd]->cnv_CM3_pH = 1./gv.bin[nd]->cnv_H_pCM3;
00932
00933 gv.bin[nd]->cnv_CM3_pGR = gv.bin[nd]->cnv_H_pGR/gv.bin[nd]->cnv_H_pCM3;
00934 gv.bin[nd]->cnv_GR_pCM3 = 1./gv.bin[nd]->cnv_CM3_pGR;
00935 }
00936
00937
00938
00939
00940
00941
00942 for( nelem=0; nelem < LIMELM; nelem++ )
00943 {
00944 gv.elmSumAbund[nelem] = 0.f;
00945 }
00946
00947 for( nd=0; nd < gv.nBin; nd++ )
00948 {
00949 for( nelem=0; nelem < LIMELM; nelem++ )
00950 {
00951 gv.elmSumAbund[nelem] += gv.bin[nd]->elmAbund[nelem]*(realnum)gv.bin[nd]->cnv_H_pCM3;
00952 }
00953 }
00954
00955 gv.nfill = -1;
00956 gv.nzone = -1;
00957 gv.lgAnyNegCharge = false;
00958 gv.GrnRecomTe = -1.f;
00959
00960
00961
00962 for( i=0; i < rfield.nupper; i++ )
00963 {
00964
00965
00966 gv.dstab[i] = -DBL_MAX;
00967 gv.dstsc[i] = -DBL_MAX;
00968 }
00969
00970 InitEmissivities();
00971 InitEnthalpy();
00972
00973 if( trace.lgDustBug && trace.lgTrace )
00974 {
00975 fprintf( ioQQQ, " There are %ld grain types turned on.\n", gv.nBin );
00976
00977 fprintf( ioQQQ, " grain depletion factors, dstfactor*GrainMetal=" );
00978 for( nd=0; nd < gv.nBin; nd++ )
00979 {
00980 fprintf( ioQQQ, "%10.2e", gv.bin[nd]->dstfactor*gv.GrainMetal );
00981 }
00982 fprintf( ioQQQ, "\n" );
00983
00984 fprintf( ioQQQ, " nChrg =" );
00985 for( nd=0; nd < gv.nBin; nd++ )
00986 {
00987 fprintf( ioQQQ, " %ld", gv.bin[nd]->nChrg );
00988 }
00989 fprintf( ioQQQ, "\n" );
00990
00991 fprintf( ioQQQ, " lowest charge (e) =" );
00992 for( nd=0; nd < gv.nBin; nd++ )
00993 {
00994 fprintf( ioQQQ, "%10.2e", pot2chrg(gv.bin[nd]->LowestPot,nd) );
00995 }
00996 fprintf( ioQQQ, "\n" );
00997
00998 fprintf( ioQQQ, " lgDustFunc flag for user requested custom depth dependence:" );
00999 for( nd=0; nd < gv.nBin; nd++ )
01000 {
01001 fprintf( ioQQQ, "%2c", TorF(gv.bin[nd]->lgDustFunc) );
01002 }
01003 fprintf( ioQQQ, "\n" );
01004
01005 fprintf( ioQQQ, " Quantum heating flag:" );
01006 for( nd=0; nd < gv.nBin; nd++ )
01007 {
01008 fprintf( ioQQQ, "%2c", TorF(gv.bin[nd]->lgQHeat) );
01009 }
01010 fprintf( ioQQQ, "\n" );
01011
01012
01013 fprintf( ioQQQ, " NU(Ryd), Abs cross sec per proton\n" );
01014
01015 fprintf( ioQQQ, " Ryd " );
01016 for( nd=0; nd < gv.nBin; nd++ )
01017 {
01018 fprintf( ioQQQ, " %-12.12s", gv.bin[nd]->chDstLab );
01019 }
01020 fprintf( ioQQQ, "\n" );
01021
01022 for( i=0; i < rfield.nupper; i += 40 )
01023 {
01024 fprintf( ioQQQ, "%10.2e", rfield.anu[i] );
01025 for( nd=0; nd < gv.nBin; nd++ )
01026 {
01027 fprintf( ioQQQ, " %10.2e ", gv.bin[nd]->dstab1[i] );
01028 }
01029 fprintf( ioQQQ, "\n" );
01030 }
01031
01032 fprintf( ioQQQ, " NU(Ryd), Sct cross sec per proton\n" );
01033
01034 fprintf( ioQQQ, " Ryd " );
01035 for( nd=0; nd < gv.nBin; nd++ )
01036 {
01037 fprintf( ioQQQ, " %-12.12s", gv.bin[nd]->chDstLab );
01038 }
01039 fprintf( ioQQQ, "\n" );
01040
01041 for( i=0; i < rfield.nupper; i += 40 )
01042 {
01043 fprintf( ioQQQ, "%10.2e", rfield.anu[i] );
01044 for( nd=0; nd < gv.nBin; nd++ )
01045 {
01046 fprintf( ioQQQ, " %10.2e ", gv.bin[nd]->pure_sc1[i] );
01047 }
01048 fprintf( ioQQQ, "\n" );
01049 }
01050
01051 fprintf( ioQQQ, " NU(Ryd), Q abs\n" );
01052
01053 fprintf( ioQQQ, " Ryd " );
01054 for( nd=0; nd < gv.nBin; nd++ )
01055 {
01056 fprintf( ioQQQ, " %-12.12s", gv.bin[nd]->chDstLab );
01057 }
01058 fprintf( ioQQQ, "\n" );
01059
01060 for( i=0; i < rfield.nupper; i += 40 )
01061 {
01062 fprintf( ioQQQ, "%10.2e", rfield.anu[i] );
01063 for( nd=0; nd < gv.nBin; nd++ )
01064 {
01065 fprintf( ioQQQ, " %10.2e ", gv.bin[nd]->dstab1[i]*4./gv.bin[nd]->IntArea );
01066 }
01067 fprintf( ioQQQ, "\n" );
01068 }
01069
01070 fprintf( ioQQQ, " NU(Ryd), Q sct\n" );
01071
01072 fprintf( ioQQQ, " Ryd " );
01073 for( nd=0; nd < gv.nBin; nd++ )
01074 {
01075 fprintf( ioQQQ, " %-12.12s", gv.bin[nd]->chDstLab );
01076 }
01077 fprintf( ioQQQ, "\n" );
01078
01079 for( i=0; i < rfield.nupper; i += 40 )
01080 {
01081 fprintf( ioQQQ, "%10.2e", rfield.anu[i] );
01082 for( nd=0; nd < gv.nBin; nd++ )
01083 {
01084 fprintf( ioQQQ, " %10.2e ", gv.bin[nd]->pure_sc1[i]*4./gv.bin[nd]->IntArea );
01085 }
01086 fprintf( ioQQQ, "\n" );
01087 }
01088
01089 fprintf( ioQQQ, " NU(Ryd), asymmetry factor\n" );
01090
01091 fprintf( ioQQQ, " Ryd " );
01092 for( nd=0; nd < gv.nBin; nd++ )
01093 {
01094 fprintf( ioQQQ, " %-12.12s", gv.bin[nd]->chDstLab );
01095 }
01096 fprintf( ioQQQ, "\n" );
01097
01098 for( i=0; i < rfield.nupper; i += 40 )
01099 {
01100 fprintf( ioQQQ, "%10.2e", rfield.anu[i] );
01101 for( nd=0; nd < gv.nBin; nd++ )
01102 {
01103 fprintf( ioQQQ, " %10.2e ", gv.bin[nd]->asym[i] );
01104 }
01105 fprintf( ioQQQ, "\n" );
01106 }
01107
01108 fprintf( ioQQQ, " GrainsInit exits.\n" );
01109 }
01110 return;
01111 }
01112
01113
01114 STATIC void ReadAugerData()
01115 {
01116 char chString[FILENAME_PATH_LENGTH_2];
01117 long version;
01118 FILE *fdes;
01119
01120 DEBUG_ENTRY( "ReadAugerData()" );
01121
01122 static char chFile[] = "auger_spectrum.dat";
01123 fdes = open_data( chFile, "r" );
01124
01125 GetNextLine( chFile, fdes, chString );
01126 sscanf( chString, "%ld", &version );
01127 if( version != MAGIC_AUGER_DATA )
01128 {
01129 fprintf( ioQQQ, " File %s has wrong version number\n", chFile );
01130 fprintf( ioQQQ, " please update your installation...\n" );
01131 cdEXIT(EXIT_FAILURE);
01132 }
01133
01134 while( true )
01135 {
01136 int res;
01137 long nelem;
01138 unsigned long ns;
01139 AEInfo *ptr;
01140
01141 GetNextLine( chFile, fdes, chString );
01142 res = sscanf( chString, "%ld", &nelem );
01143 ASSERT( res == 1 );
01144
01145 if( nelem < 0 )
01146 break;
01147
01148 ASSERT( nelem < LIMELM );
01149
01150 ptr = (AEInfo*)MALLOC(sizeof(AEInfo));
01151
01152 GetNextLine( chFile, fdes, chString );
01153 res = sscanf( chString, "%lu", &ptr->nSubShell );
01154 ASSERT( res == 1 && ptr->nSubShell > 0 );
01155
01156 ptr->nData = (unsigned long*)MALLOC((size_t)(ptr->nSubShell*sizeof(unsigned long)));
01157 ptr->IonThres = (double*)MALLOC((size_t)(ptr->nSubShell*sizeof(double)));
01158 ptr->Energy = (double**)MALLOC((size_t)(ptr->nSubShell*sizeof(double*)));
01159 ptr->AvNumber = (double**)MALLOC((size_t)(ptr->nSubShell*sizeof(double*)));
01160
01161 for( ns=0; ns < ptr->nSubShell; ns++ )
01162 {
01163 unsigned long ss;
01164
01165 GetNextLine( chFile, fdes, chString );
01166 res = sscanf( chString, "%lu", &ss );
01167 ASSERT( res == 1 && ns == ss );
01168
01169 GetNextLine( chFile, fdes, chString );
01170 res = sscanf( chString, "%le", &ptr->IonThres[ns] );
01171 ASSERT( res == 1 );
01172 ptr->IonThres[ns] /= EVRYD;
01173
01174 GetNextLine( chFile, fdes, chString );
01175 res = sscanf( chString, "%lu", &ptr->nData[ns] );
01176 ASSERT( res == 1 && ptr->nData[ns] > 0 );
01177
01178 ptr->Energy[ns] = (double*)MALLOC((size_t)(ptr->nData[ns]*sizeof(double)));
01179 ptr->AvNumber[ns] = (double*)MALLOC((size_t)(ptr->nData[ns]*sizeof(double)));
01180
01181 for( unsigned long n=0; n < ptr->nData[ns]; n++ )
01182 {
01183 GetNextLine( chFile, fdes, chString );
01184 res = sscanf(chString,"%le %le",&ptr->Energy[ns][n],&ptr->AvNumber[ns][n]);
01185 ASSERT( res == 2 );
01186 ptr->Energy[ns][n] /= EVRYD;
01187 ASSERT( ptr->Energy[ns][n] < ptr->IonThres[ns] );
01188 }
01189 }
01190
01191 gv.AugerData[nelem] = ptr;
01192 }
01193
01194 fclose( fdes );
01195 }
01196
01198 STATIC void InitBinAugerData(long nd,
01199 long ipBegin,
01200 long ipEnd)
01201 {
01202 DEBUG_ENTRY( "InitBinAugerData()" );
01203
01204 long i, ipLo, nelem;
01205 unsigned long ns;
01206
01207 flex_arr<realnum> temp( ipBegin, ipEnd );
01208 temp.zero();
01209
01210
01211 double norm = gv.bin[nd]->cnv_H_pGR/gv.bin[nd]->AvVol;
01212
01213
01214 for( ns=0; ns < gv.bin[nd]->nShells; ns++ )
01215 {
01216 ipLo = max( gv.bin[nd]->sd[ns]->ipLo, ipBegin );
01217
01218 gv.bin[nd]->sd[ns]->p.realloc( ipEnd );
01219
01222 for( i=ipLo; i < ipEnd; i++ )
01223 {
01224 long nel,nsh;
01225 double phot_ev,cs;
01226
01227 phot_ev = rfield.anu[i]*EVRYD;
01228
01229 if( ns == 0 )
01230 {
01231
01232
01233 gv.bin[nd]->sd[ns]->p[i] = 0.;
01234
01235 for( nelem=ipHYDROGEN; nelem < LIMELM; nelem++ )
01236 {
01237 if( gv.bin[nd]->elmAbund[nelem] == 0. )
01238 continue;
01239
01240 long nshmax = Heavy.nsShells[nelem][0];
01241
01242 for( nsh = gv.AugerData[nelem]->nSubShell; nsh < nshmax; nsh++ )
01243 {
01244 nel = nelem+1;
01245 cs = t_ADfA::Inst().phfit(nelem+1,nel,nsh+1,phot_ev);
01246 gv.bin[nd]->sd[ns]->p[i] +=
01247 (realnum)(norm*gv.bin[nd]->elmAbund[nelem]*cs*1e-18);
01248 }
01249 }
01250
01251 temp[i] += gv.bin[nd]->sd[ns]->p[i];
01252 }
01253 else
01254 {
01255
01256 nelem = gv.bin[nd]->sd[ns]->nelem;
01257 nel = nelem+1;
01258 nsh = gv.bin[nd]->sd[ns]->ns;
01259 cs = t_ADfA::Inst().phfit(nelem+1,nel,nsh+1,phot_ev);
01260 gv.bin[nd]->sd[ns]->p[i] =
01261 (realnum)(norm*gv.bin[nd]->elmAbund[nelem]*cs*1e-18);
01262 temp[i] += gv.bin[nd]->sd[ns]->p[i];
01263 }
01264 }
01265 }
01266
01267 for( i=ipBegin; i < ipEnd && !gv.lgWD01; i++ )
01268 {
01269
01270 if( rfield.anu[i] > 20./EVRYD )
01271 gv.bin[nd]->inv_att_len[i] = temp[i];
01272 }
01273
01274 for( ns=0; ns < gv.bin[nd]->nShells; ns++ )
01275 {
01276 ipLo = max( gv.bin[nd]->sd[ns]->ipLo, ipBegin );
01277
01278 for( i=ipLo; i < ipEnd; i++ )
01279 {
01280 if( temp[i] > 0. )
01281 gv.bin[nd]->sd[ns]->p[i] /= temp[i];
01282 else
01283 gv.bin[nd]->sd[ns]->p[i] = ( ns == 0 ) ? 1.f : 0.f;
01284 }
01285 }
01286
01287 temp.clear();
01288
01289 for( ns=0; ns < gv.bin[nd]->nShells; ns++ )
01290 {
01291 long n;
01292 ShellData *sptr = gv.bin[nd]->sd[ns];
01293
01294 ipLo = max( sptr->ipLo, ipBegin );
01295
01296
01297 sptr->y01.realloc( ipEnd );
01298
01299 for( i=ipLo; i < ipEnd; i++ )
01300 {
01301 double elec_en,yzero,yone;
01302
01303 elec_en = MAX2(rfield.anu[i] - sptr->ionPot,0.);
01304 yzero = y0psa( nd, ns, i, elec_en );
01305
01306
01307
01308 yone = y1psa( nd, i, elec_en );
01309
01310 if( ns == 0 )
01311 {
01312 gv.bin[nd]->y0b06[i] = (realnum)yzero;
01313 sptr->y01[i] = (realnum)yone;
01314 }
01315 else
01316 {
01317 sptr->y01[i] = (realnum)(yzero*yone);
01318 }
01319 }
01320
01321
01322 if( ns > 0 )
01323 {
01324
01325 for( n=0; n < sptr->nData; n++ )
01326 {
01327 sptr->y01A[n].realloc( ipEnd );
01328
01329 for( i=ipLo; i < ipEnd; i++ )
01330 {
01331 double yzero = sptr->AvNr[n] * y0psa( nd, ns, i, sptr->Ener[n] );
01332
01333
01334
01335 double yone = y1psa( nd, i, sptr->Ener[n] );
01336
01337 sptr->y01A[n][i] = (realnum)(yzero*yone);
01338 }
01339 }
01340 }
01341 }
01342 }
01343
01344
01345 STATIC void GetNextLine(const char *chFile,
01346 FILE *io,
01347 char chLine[])
01348 {
01349 char *str;
01350
01351 DEBUG_ENTRY( "GetNextLine()" );
01352
01353 do
01354 {
01355 if( read_whole_line( chLine, FILENAME_PATH_LENGTH_2, io ) == NULL )
01356 {
01357 fprintf( ioQQQ, " Could not read from %s\n", chFile );
01358 if( feof(io) )
01359 fprintf( ioQQQ, " EOF reached\n");
01360 cdEXIT(EXIT_FAILURE);
01361 }
01362 }
01363 while( chLine[0] == '#' );
01364
01365
01366 str = strstr(chLine,"#");
01367 if( str != NULL )
01368 *str = '\0';
01369 return;
01370 }
01371
01372 STATIC void InitEmissivities(void)
01373 {
01374 double fac,
01375 fac2,
01376 mul,
01377 tdust;
01378 long int i,
01379 nd;
01380
01381 DEBUG_ENTRY( "InitEmissivities()" );
01382
01383 if( trace.lgTrace && trace.lgDustBug )
01384 {
01385 fprintf( ioQQQ, " InitEmissivities starts\n" );
01386 fprintf( ioQQQ, " ND Tdust Emis BB Check 4pi*a^2*<Q>\n" );
01387 }
01388
01389 ASSERT( NTOP >= 2 && NDEMS > 2*NTOP );
01390 fac = exp(log(GRAIN_TMID/GRAIN_TMIN)/(double)(NDEMS-NTOP));
01391 tdust = GRAIN_TMIN;
01392 for( i=0; i < NDEMS-NTOP; i++ )
01393 {
01394 gv.dsttmp[i] = log(tdust);
01395 for( nd=0; nd < gv.nBin; nd++ )
01396 {
01397 gv.bin[nd]->dstems[i] = log(PlanckIntegral(tdust,nd,i));
01398 }
01399 tdust *= fac;
01400 }
01401
01402
01403 fac2 = exp(log(GRAIN_TMAX/GRAIN_TMID/powi(fac,NTOP-1))/(double)((NTOP-1)*NTOP/2));
01404 for( i=NDEMS-NTOP; i < NDEMS; i++ )
01405 {
01406 gv.dsttmp[i] = log(tdust);
01407 for( nd=0; nd < gv.nBin; nd++ )
01408 {
01409 gv.bin[nd]->dstems[i] = log(PlanckIntegral(tdust,nd,i));
01410 }
01411 fac *= fac2;
01412 tdust *= fac;
01413 }
01414
01415
01416 mul = 1.;
01417 ASSERT( fabs(gv.dsttmp[0] - log(GRAIN_TMIN)) < 10.*mul*DBL_EPSILON );
01418 mul = sqrt((double)(NDEMS-NTOP));
01419 ASSERT( fabs(gv.dsttmp[NDEMS-NTOP] - log(GRAIN_TMID)) < 10.*mul*DBL_EPSILON );
01420 mul = (double)NTOP + sqrt((double)NDEMS);
01421 ASSERT( fabs(gv.dsttmp[NDEMS-1] - log(GRAIN_TMAX)) < 10.*mul*DBL_EPSILON );
01422
01423
01424 for( nd=0; nd < gv.nBin; nd++ )
01425 {
01426
01427 spline(gv.bin[nd]->dstems,gv.dsttmp,NDEMS,2e31,2e31,gv.bin[nd]->dstslp);
01428 spline(gv.dsttmp,gv.bin[nd]->dstems,NDEMS,2e31,2e31,gv.bin[nd]->dstslp2);
01429 }
01430
01431 # if 0
01432
01433 nd = nint(fudge(0));
01434 ASSERT( nd >= 0 && nd < gv.nBin );
01435 for( i=0; i < 2000; i++ )
01436 {
01437 double x,y,z;
01438 z = pow(10.,-40. + (double)i/50.);
01439 splint(gv.bin[nd]->dstems,gv.dsttmp,gv.bin[nd]->dstslp,NDEMS,log(z),&y);
01440 if( exp(y) > GRAIN_TMIN && exp(y) < GRAIN_TMAX )
01441 {
01442 x = PlanckIntegral(exp(y),nd,3);
01443 printf(" input %.6e temp %.6e output %.6e rel. diff. %.6e\n",z,exp(y),x,(x-z)/z);
01444 }
01445 }
01446 cdEXIT(EXIT_SUCCESS);
01447 # endif
01448 return;
01449 }
01450
01451
01452
01453 STATIC double PlanckIntegral(double tdust,
01454 long int nd,
01455 long int ip)
01456 {
01457 long int i;
01458
01459 double arg,
01460 ExpM1,
01461 integral1 = 0.,
01462 integral2 = 0.,
01463 Planck1,
01464 Planck2,
01465 TDustRyg,
01466 x;
01467
01468 DEBUG_ENTRY( "PlanckIntegral()" );
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479
01480 TDustRyg = TE1RYD/tdust;
01481
01482 x = 0.999*log(DBL_MAX);
01483
01484 for( i=0; i < rfield.nupper; i++ )
01485 {
01486
01487 arg = TDustRyg*rfield.anu[i];
01488
01489
01490 if( arg < 1.e-5 )
01491 {
01492
01493 ExpM1 = arg*(1. + arg/2.);
01494 }
01495 else
01496 {
01497
01498 ExpM1 = exp(MIN2(x,arg)) - 1.;
01499 }
01500
01501 Planck1 = PI4*2.*HPLANCK/POW2(SPEEDLIGHT)*POW2(FR1RYD)*POW2(FR1RYD)*
01502 rfield.anu3[i]/ExpM1*rfield.widflx[i];
01503 Planck2 = Planck1*gv.bin[nd]->dstab1[i];
01504
01505
01506 if( i == 0 )
01507 {
01508 integral1 = Planck1/rfield.widflx[0]*rfield.anu[0]/3.;
01509 integral2 = Planck2/rfield.widflx[0]*rfield.anu[0]/5.;
01510 }
01511
01512 if( Planck1/integral1 < DBL_EPSILON && Planck2/integral2 < DBL_EPSILON )
01513 break;
01514
01515 integral1 += Planck1;
01516 integral2 += Planck2;
01517 }
01518
01519
01520 if( trace.lgDustBug && trace.lgTrace && ip%10 == 0 )
01521 {
01522 fprintf( ioQQQ, " %4ld %11.4e %11.4e %11.4e %11.4e\n", nd, tdust,
01523 integral2, integral1/4./5.67051e-5/powi(tdust,4), integral2*4./integral1 );
01524 }
01525
01526 ASSERT( integral2 > 0. );
01527 return integral2;
01528 }
01529
01530
01531
01532 STATIC void NewChargeData(long nd)
01533 {
01534 long nz;
01535
01536 DEBUG_ENTRY( "NewChargeData()" );
01537
01538 for( nz=0; nz < NCHS; nz++ )
01539 {
01540 gv.bin[nd]->chrg[nz]->RSum1 = -DBL_MAX;
01541 gv.bin[nd]->chrg[nz]->RSum2 = -DBL_MAX;
01542 gv.bin[nd]->chrg[nz]->ESum1a = -DBL_MAX;
01543 gv.bin[nd]->chrg[nz]->ESum1b = -DBL_MAX;
01544 gv.bin[nd]->chrg[nz]->ESum2 = -DBL_MAX;
01545
01547 gv.bin[nd]->chrg[nz]->ThermRate = -DBL_MAX;
01548 gv.bin[nd]->chrg[nz]->HeatingRate2 = -DBL_MAX;
01549 gv.bin[nd]->chrg[nz]->GrainHeat = -DBL_MAX;
01550
01551 gv.bin[nd]->chrg[nz]->hots1 = -DBL_MAX;
01552 gv.bin[nd]->chrg[nz]->bolflux1 = -DBL_MAX;
01553 gv.bin[nd]->chrg[nz]->pe1 = -DBL_MAX;
01554 }
01555
01556 if( fabs(phycon.te-gv.GrnRecomTe) > 10.f*FLT_EPSILON*gv.GrnRecomTe )
01557 {
01558 for( nz=0; nz < NCHS; nz++ )
01559 {
01560 memset( gv.bin[nd]->chrg[nz]->eta, 0, (LIMELM+2)*sizeof(double) );
01561 memset( gv.bin[nd]->chrg[nz]->xi, 0, (LIMELM+2)*sizeof(double) );
01562 }
01563 }
01564
01565 if( nzone != gv.nzone )
01566 {
01567 for( nz=0; nz < NCHS; nz++ )
01568 {
01569 gv.bin[nd]->chrg[nz]->hcon1 = -DBL_MAX;
01570 }
01571 }
01572 return;
01573 }
01574
01575
01576
01577
01578 STATIC double GrnStdDpth(long int nd)
01579 {
01580 double GrnStdDpth_v;
01581
01582 DEBUG_ENTRY( "GrnStdDpth()" );
01583
01584
01585
01586
01587
01588
01589 if( gv.bin[nd]->matType == MAT_PAH || gv.bin[nd]->matType == MAT_PAH2 )
01590 {
01591
01592 if( strcmp( gv.chPAH_abundance_fcn , "H0" )==0 )
01593 {
01594
01595
01596
01597
01598 GrnStdDpth_v = max(1.e-10,dense.xIonDense[ipHYDROGEN][0]/dense.gas_phase[ipHYDROGEN]);
01599 }
01600 else if( strcmp( gv.chPAH_abundance_fcn , "CON" )==0 )
01601 {
01602
01603 GrnStdDpth_v = 1.;
01604 }
01605 else
01606 TotalInsanity();
01607 }
01608 else
01609 {
01610
01611 GrnStdDpth_v = 1.;
01612 }
01613
01614 ASSERT( GrnStdDpth_v > 0. && GrnStdDpth_v <= 1.000001 );
01615
01616 return GrnStdDpth_v;
01617 }
01618
01619
01620
01621 void GrainDrive(void)
01622 {
01623 DEBUG_ENTRY( "GrainDrive()" );
01624
01625
01626 if( gv.lgDustOn && gv.lgGrainPhysicsOn )
01627 {
01628 static double tesave=-1.f;
01629 static long int nzonesave=-1;
01630
01631
01632
01633
01634
01635
01636
01637 if( gv.lgReevaluate || conv.lgSearch || nzonesave != nzone ||
01638
01639 ! fp_equal( phycon.te, tesave ) ||
01640
01641
01642 fabs(gv.TotalEden)/dense.eden > conv.EdenErrorAllowed/5. ||
01643
01644
01645 (fabs( gv.GasCoolColl ) + fabs( thermal.heating[0][13] ))/SDIV(thermal.ctot)>0.1 )
01646
01647
01648 {
01649 nzonesave = nzone;
01650 tesave = phycon.te;
01651
01652 if( trace.nTrConvg >= 5 )
01653 {
01654 fprintf( ioQQQ, " grain5 calling GrainChargeTemp\n");
01655 }
01656
01657
01658 GrainChargeTemp();
01659
01660
01661 }
01662 }
01663 else if( gv.lgDustOn && !gv.lgGrainPhysicsOn )
01664 {
01665
01666
01667 if( nCalledGrainDrive == 0 )
01668 {
01669 long nelem, ion, ion_to, nd;
01670
01671
01672
01673 gv.GasHeatPhotoEl = 0.;
01674 for( nd=0; nd < gv.nBin; nd++ )
01675 {
01676 long nz;
01677
01678
01679 gv.bin[nd]->lgPAHsInIonizedRegion = false;
01680
01681 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
01682 {
01683 gv.bin[nd]->chrg[nz]->DustZ = nz;
01684 gv.bin[nd]->chrg[nz]->FracPop = ( nz == 0 ) ? 1. : 0.;
01685 gv.bin[nd]->chrg[nz]->nfill = 0;
01686 gv.bin[nd]->chrg[nz]->tedust = 100.f;
01687 }
01688
01689 gv.bin[nd]->AveDustZ = 0.;
01690 gv.bin[nd]->dstpot = chrg2pot(0.,nd);
01691
01692 gv.bin[nd]->tedust = 100.f;
01693 gv.bin[nd]->TeGrainMax = 100.;
01694
01695
01696 gv.bin[nd]->BolFlux = 0.;
01697 gv.bin[nd]->GrainCoolTherm = 0.;
01698 gv.bin[nd]->GasHeatPhotoEl = 0.;
01699 gv.bin[nd]->GrainHeat = 0.;
01700 gv.bin[nd]->GrainHeatColl = 0.;
01701 gv.bin[nd]->ChemEn = 0.;
01702 gv.bin[nd]->ChemEnH2 = 0.;
01703 gv.bin[nd]->thermionic = 0.;
01704
01705 gv.bin[nd]->lgUseQHeat = false;
01706 gv.bin[nd]->lgEverQHeat = false;
01707 gv.bin[nd]->QHeatFailures = 0;
01708
01709 gv.bin[nd]->DustDftVel = 0.;
01710
01711 gv.bin[nd]->avdust = gv.bin[nd]->tedust;
01712 gv.bin[nd]->avdft = 0.f;
01713 gv.bin[nd]->avdpot = (realnum)(gv.bin[nd]->dstpot*EVRYD);
01714 gv.bin[nd]->avDGRatio = -1.f;
01715
01716
01717
01718 if( 0 && gv.lgBakesPAH_heat )
01719 {
01720
01721
01722
01723
01724
01725 gv.bin[nd]->GasHeatPhotoEl = 1.e-24*hmi.UV_Cont_rel2_Habing_TH85_depth*
01726 dense.gas_phase[ipHYDROGEN]*(4.87e-2/(1.0+4e-3*pow((hmi.UV_Cont_rel2_Habing_TH85_depth*
01727 sqrt(phycon.te)/dense.eden),0.73)) + 3.65e-2*pow(phycon.te/1.e4,0.7)/
01728 (1.+2.e-4*(hmi.UV_Cont_rel2_Habing_TH85_depth*sqrt(phycon.te)/dense.eden)))/gv.nBin *
01729 gv.GrainHeatScaleFactor;
01730 gv.GasHeatPhotoEl += gv.bin[nd]->GasHeatPhotoEl;
01731 }
01732 }
01733
01734 gv.lgAnyNegCharge = false;
01735
01736 gv.TotalEden = 0.;
01737 gv.GrnElecDonateMax = 0.f;
01738 gv.GrnElecHoldMax = 0.f;
01739
01740 for( nelem=0; nelem < LIMELM; nelem++ )
01741 {
01742 for( ion=0; ion <= nelem+1; ion++ )
01743 {
01744 for( ion_to=0; ion_to <= nelem+1; ion_to++ )
01745 {
01746 gv.GrainChTrRate[nelem][ion][ion_to] = 0.f;
01747 }
01748 }
01749 }
01750
01751
01752 gv.GrainHeatInc = 0.;
01753 gv.GrainHeatDif = 0.;
01754 gv.GrainHeatLya = 0.;
01755 gv.GrainHeatCollSum = 0.;
01756 gv.GrainHeatSum = 0.;
01757 gv.GrainHeatChem = 0.;
01758 gv.GasCoolColl = 0.;
01759 gv.TotalDustHeat = 0.f;
01760 gv.dphmax = 0.f;
01761 gv.dclmax = 0.f;
01762
01763 thermal.heating[0][13] = 0.;
01764 thermal.heating[0][14] = 0.;
01765 thermal.heating[0][25] = 0.;
01766 }
01767
01768 if( nCalledGrainDrive == 0 || gv.lgAnyDustVary )
01769 {
01770 GrainUpdateRadius1();
01771 GrainUpdateRadius2(false);
01772 }
01773 }
01774
01775 ++nCalledGrainDrive;
01776 return;
01777 }
01778
01779
01780 STATIC void GrainChargeTemp(void)
01781 {
01782 bool lgAnyNegCharge = false;
01783 long int i,
01784 ion,
01785 ion_to,
01786 nelem,
01787 nd,
01788 nz;
01789 realnum dccool = FLT_MAX;
01790 double delta,
01791 GasHeatNet,
01792 hcon = DBL_MAX,
01793 hla = DBL_MAX,
01794 hots = DBL_MAX,
01795 oldtemp,
01796 oldTotalEden,
01797 ratio,
01798 ThermRatio;
01799
01800 static long int oldZone = -1;
01801 static double oldTe = -DBL_MAX,
01802 oldHeat = -DBL_MAX;
01803
01804 DEBUG_ENTRY( "GrainChargeTemp()" );
01805
01806 if( trace.lgTrace && trace.lgDustBug )
01807 {
01808 fprintf( ioQQQ, "\n GrainChargeTemp called lgSearch%2c\n\n", TorF(conv.lgSearch) );
01809 }
01810
01811 oldTotalEden = gv.TotalEden;
01812
01813
01814 gv.GrainHeatInc = 0.;
01815 gv.GrainHeatDif = 0.;
01816 gv.GrainHeatLya = 0.;
01817 gv.GrainHeatCollSum = 0.;
01818 gv.GrainHeatSum = 0.;
01819 gv.GrainHeatChem = 0.;
01820
01821 gv.GasCoolColl = 0.;
01822 gv.GasHeatPhotoEl = 0.;
01823 gv.GasHeatTherm = 0.;
01824
01825 gv.TotalEden = 0.;
01826
01827 for( nelem=0; nelem < LIMELM; nelem++ )
01828 {
01829 for( ion=0; ion <= nelem+1; ion++ )
01830 {
01831 for( ion_to=0; ion_to <= nelem+1; ion_to++ )
01832 {
01833 gv.GrainChTrRate[nelem][ion][ion_to] = 0.f;
01834 }
01835 }
01836 }
01837
01838 gv.HighestIon = HighestIonStage();
01839
01840
01841 GrainUpdateRadius1();
01842
01843 for( nd=0; nd < gv.nBin; nd++ )
01844 {
01845 double one;
01846 double ChTdBracketLo = 0., ChTdBracketHi = -DBL_MAX;
01847 long relax = ( conv.lgSearch ) ? 3 : 1;
01848
01849
01850 if( gv.bin[nd]->matType == MAT_PAH || gv.bin[nd]->matType == MAT_PAH2 )
01851 {
01852 if( dense.xIonDense[ipHYDROGEN][1]/dense.gas_phase[ipHYDROGEN] > 0.50 )
01853 {
01854 gv.bin[nd]->lgPAHsInIonizedRegion = true;
01855 }
01856 }
01857
01858
01859
01860
01861 NewChargeData(nd);
01862
01863 if( trace.lgTrace && trace.lgDustBug )
01864 {
01865 fprintf( ioQQQ, " >>GrainChargeTemp starting grain %s\n",
01866 gv.bin[nd]->chDstLab );
01867 }
01868
01869 delta = 2.*TOLER;
01870
01871 for( i=0; i < relax*CT_LOOP_MAX && delta > TOLER; ++i )
01872 {
01873 char which[20];
01874 long j;
01875 double TdBracketLo = 0., TdBracketHi = -DBL_MAX;
01876 double ThresEst = 0.;
01877 oldtemp = gv.bin[nd]->tedust;
01878
01879
01880
01881
01882
01883 GrainCharge(nd,&ThermRatio);
01884
01885 ASSERT( gv.bin[nd]->GrainHeat > 0. );
01886 ASSERT( gv.bin[nd]->tedust >= GRAIN_TMIN && gv.bin[nd]->tedust <= GRAIN_TMAX );
01887
01888
01889
01890
01891
01892 gv.bin[nd]->lgTdustConverged = false;
01893 for( j=0; j < relax*T_LOOP_MAX; ++j )
01894 {
01895 double oldTemp2 = gv.bin[nd]->tedust;
01896 double oldHeat2 = gv.bin[nd]->GrainHeat;
01897 double oldCool = gv.bin[nd]->GrainGasCool;
01898
01899
01900 GrainTemperature(nd,&dccool,&hcon,&hots,&hla);
01901
01902 gv.bin[nd]->GrainGasCool = dccool;
01903
01904 if( trace.lgTrace && trace.lgDustBug )
01905 {
01906 fprintf( ioQQQ, " >>loop %ld BracketLo %.6e BracketHi %.6e",
01907 j, TdBracketLo, TdBracketHi );
01908 }
01909
01910
01911
01912
01913
01914 if( fabs(gv.bin[nd]->GrainHeat-oldHeat2) < HEAT_TOLER*gv.bin[nd]->GrainHeat &&
01915 fabs(gv.bin[nd]->GrainGasCool-oldCool) < HEAT_TOLER_BIN*thermal.ctot )
01916 {
01917 gv.bin[nd]->lgTdustConverged = true;
01918 if( trace.lgTrace && trace.lgDustBug )
01919 fprintf( ioQQQ, " converged\n" );
01920 break;
01921 }
01922
01923
01924 if( gv.bin[nd]->tedust < oldTemp2 )
01925 TdBracketHi = oldTemp2;
01926 else
01927 TdBracketLo = oldTemp2;
01928
01929
01930
01931
01932
01933
01936
01937 if( TdBracketHi > TdBracketLo )
01938 {
01939
01940
01941 if( ( j >= 2 && TdBracketLo > 0. ) ||
01942 gv.bin[nd]->tedust <= TdBracketLo ||
01943 gv.bin[nd]->tedust >= TdBracketHi )
01944 {
01945 gv.bin[nd]->tedust = (realnum)(0.5*(TdBracketLo + TdBracketHi));
01946 if( trace.lgTrace && trace.lgDustBug )
01947 fprintf( ioQQQ, " bisection\n" );
01948 }
01949 else
01950 {
01951 if( trace.lgTrace && trace.lgDustBug )
01952 fprintf( ioQQQ, " iteration\n" );
01953 }
01954 }
01955 else
01956 {
01957 if( trace.lgTrace && trace.lgDustBug )
01958 fprintf( ioQQQ, " iteration\n" );
01959 }
01960
01961 ASSERT( gv.bin[nd]->tedust >= GRAIN_TMIN && gv.bin[nd]->tedust <= GRAIN_TMAX );
01962 }
01963
01964 if( gv.bin[nd]->lgTdustConverged )
01965 {
01966
01967 if( gv.bin[nd]->tedust < oldtemp )
01968 ChTdBracketHi = oldtemp;
01969 else
01970 ChTdBracketLo = oldtemp;
01971 }
01972 else
01973 {
01974 bool lgBoundErr;
01975 double y, x = log(gv.bin[nd]->tedust);
01976
01977 splint_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,x,&y,&lgBoundErr);
01978 gv.bin[nd]->GrainHeat = exp(y)*gv.bin[nd]->cnv_H_pCM3;
01979
01980 fprintf( ioQQQ," PROBLEM temperature of grain species %s (Tg=%.3eK) not converged\n",
01981 gv.bin[nd]->chDstLab , gv.bin[nd]->tedust );
01982 ConvFail("grai","");
01983 if( lgAbort )
01984 {
01985 return;
01986 }
01987 }
01988
01989 ASSERT( gv.bin[nd]->GrainHeat > 0. );
01990 ASSERT( gv.bin[nd]->tedust >= GRAIN_TMIN && gv.bin[nd]->tedust <= GRAIN_TMAX );
01991
01992
01993
01994
01995
01996
01998 ratio = gv.bin[nd]->tedust/oldtemp;
01999 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02000 {
02001 ThresEst += gv.bin[nd]->chrg[nz]->FracPop*gv.bin[nd]->chrg[nz]->ThresInf;
02002 }
02003 delta = ThresEst*TE1RYD/gv.bin[nd]->tedust*(ratio - 1.);
02005 delta = ( delta < 0.9*log(DBL_MAX) ) ?
02006 ThermRatio*fabs(POW2(ratio)*exp(delta)-1.) : DBL_MAX;
02007
02008
02009 if( delta > TOLER )
02010 {
02011 if( trace.lgTrace && trace.lgDustBug )
02012 strncpy( which, "iteration", sizeof(which) );
02013
02014
02015
02016
02017
02018
02021
02022 if( ChTdBracketHi > ChTdBracketLo )
02023 {
02024
02025
02026 if( ( i >= 2 && ChTdBracketLo > 0. ) ||
02027 gv.bin[nd]->tedust <= ChTdBracketLo ||
02028 gv.bin[nd]->tedust >= ChTdBracketHi )
02029 {
02030 gv.bin[nd]->tedust = (realnum)(0.5*(ChTdBracketLo + ChTdBracketHi));
02031 if( trace.lgTrace && trace.lgDustBug )
02032 strncpy( which, "bisection", sizeof(which) );
02033 }
02034 }
02035 }
02036
02037 if( trace.lgTrace && trace.lgDustBug )
02038 {
02039 fprintf( ioQQQ, " >>GrainChargeTemp finds delta=%.4e, ", delta );
02040 fprintf( ioQQQ, " old/new temp=%.5e %.5e, ", oldtemp, gv.bin[nd]->tedust );
02041 if( delta > TOLER )
02042 fprintf( ioQQQ, "doing another %s\n", which );
02043 else
02044 fprintf( ioQQQ, "converged\n" );
02045 }
02046 }
02047 if( delta > TOLER )
02048 {
02049 fprintf( ioQQQ, " PROBLEM charge/temperature not converged for %s zone %.2f\n",
02050 gv.bin[nd]->chDstLab , fnzone );
02051 ConvFail("grai","");
02052 }
02053
02054 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02055 {
02056 if( gv.bin[nd]->chrg[nz]->DustZ <= -1 )
02057 lgAnyNegCharge = true;
02058 }
02059
02060
02061
02062
02063 if( ionbal.lgGrainIonRecom )
02064 GrainChrgTransferRates(nd);
02065
02066
02067
02068
02069
02070
02071 gv.GrainHeatInc += hcon;
02072
02073 gv.GrainHeatDif += hots;
02074
02075
02076 gv.GrainHeatLya += hla;
02077
02078
02079 gv.GrainHeatCollSum += gv.bin[nd]->GrainHeatColl;
02080
02081
02082
02083
02084 gv.GrainHeatSum += gv.bin[nd]->GrainHeat;
02085
02086
02087 gv.GrainHeatChem += gv.bin[nd]->ChemEn + gv.bin[nd]->ChemEnH2;
02088
02089
02090
02091 gv.GasCoolColl += dccool;
02092 gv.GasHeatPhotoEl += gv.bin[nd]->GasHeatPhotoEl;
02093 gv.GasHeatTherm += gv.bin[nd]->thermionic;
02094
02095
02096
02097 one = 0.;
02098 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02099 {
02100 one += gv.bin[nd]->chrg[nz]->FracPop*(double)gv.bin[nd]->chrg[nz]->DustZ*
02101 gv.bin[nd]->cnv_GR_pCM3;
02102 }
02103
02104 gv.TotalEden += one;
02105 {
02106
02107 enum {DEBUG_LOC=false};
02108
02109 if( DEBUG_LOC )
02110 {
02111 fprintf(ioQQQ," DEBUG grn chr nz\t%.2f\teden\t%.3e\tnd\t%li",
02112 fnzone,
02113 dense.eden,
02114 nd);
02115 fprintf(ioQQQ,"\tne\t%.2e\tAveDustZ\t%.2e\t%.2e\t%.2e\t%.2e",
02116 one,
02117 gv.bin[nd]->AveDustZ,
02118 gv.bin[nd]->chrg[0]->FracPop,(double)gv.bin[nd]->chrg[0]->DustZ,
02119 gv.bin[nd]->cnv_GR_pCM3);
02120 fprintf(ioQQQ,"\n");
02121 }
02122 }
02123
02124 if( trace.lgTrace && trace.lgDustBug )
02125 {
02126 fprintf(ioQQQ," %s Pot %.5e Thermal %.5e GasCoolColl %.5e" ,
02127 gv.bin[nd]->chDstLab, gv.bin[nd]->dstpot, gv.bin[nd]->GrainHeat, dccool );
02128 fprintf(ioQQQ," GasPEHeat %.5e GasThermHeat %.5e ChemHeat %.5e\n\n" ,
02129 gv.bin[nd]->GasHeatPhotoEl, gv.bin[nd]->thermionic, gv.bin[nd]->ChemEn );
02130 }
02131 }
02132
02133
02134 GasHeatNet = gv.GasHeatPhotoEl + gv.GasHeatTherm - gv.GasCoolColl;
02135
02136 if( fabs(phycon.te-gv.GrnRecomTe) > 10.f*FLT_EPSILON*gv.GrnRecomTe )
02137 {
02138 oldZone = gv.nzone;
02139 oldTe = gv.GrnRecomTe;
02140 oldHeat = gv.GasHeatNet;
02141 }
02142
02143
02144 if( nzone == oldZone && fabs(phycon.te-oldTe) > 10.f*FLT_EPSILON*phycon.te )
02145 {
02146 gv.dHeatdT = (GasHeatNet-oldHeat)/(phycon.te-oldTe);
02147 }
02148
02149
02150 if( nzone != gv.nzone || fabs(phycon.te-gv.GrnRecomTe) > 10.f*FLT_EPSILON*gv.GrnRecomTe ||
02151 fabs(gv.GasHeatNet-GasHeatNet) > HEAT_TOLER*thermal.ctot ||
02152 fabs(gv.TotalEden-oldTotalEden) > CHRG_TOLER*dense.eden )
02153 {
02154
02155
02156
02157 conv.lgConvIoniz = false;
02158 if( fabs(gv.TotalEden-oldTotalEden) > CHRG_TOLER*dense.eden )
02159 {
02160 strcpy( conv.chConvIoniz, "grn eden chg" );
02161 conv.BadConvIoniz[0] = oldTotalEden;
02162 conv.BadConvIoniz[1] = gv.TotalEden;
02163 }
02164 else if( fabs(gv.GasHeatNet-GasHeatNet) > HEAT_TOLER*thermal.ctot )
02165 {
02166 strcpy( conv.chConvIoniz, "grn het chg" );
02167 conv.BadConvIoniz[0] = gv.GasHeatNet;
02168 conv.BadConvIoniz[1] = GasHeatNet;
02169 }
02170 else if( fabs(phycon.te-gv.GrnRecomTe) > 10.f*FLT_EPSILON*gv.GrnRecomTe )
02171 {
02172 strcpy( conv.chConvIoniz, "grn ter chg" );
02173 conv.BadConvIoniz[0] = gv.GrnRecomTe;
02174 conv.BadConvIoniz[1] = phycon.te;
02175 }
02176 else if( nzone != gv.nzone )
02177 {
02178 strcpy( conv.chConvIoniz, "grn zon chg" );
02179 conv.BadConvIoniz[0] = gv.nzone;
02180 conv.BadConvIoniz[1] = nzone;
02181 }
02182 else
02183 TotalInsanity();
02184 }
02185
02186
02187
02188
02189
02190 gv.nzone = nzone;
02191 gv.GrnRecomTe = (realnum)phycon.te;
02192 gv.GasHeatNet = GasHeatNet;
02193
02194
02195
02196
02197 GrainUpdateRadius2(lgAnyNegCharge);
02198
02199 #ifdef WD_TEST2
02200 printf("wd test: proton fraction %.5e Total DustZ %.6f heating/cooling rate %.5e %.5e\n",
02201 dense.xIonDense[ipHYDROGEN][1]/dense.gas_phase[ipHYDROGEN],
02202 gv.bin[0]->AveDustZ,gv.GasHeatPhotoEl/dense.gas_phase[ipHYDROGEN]/fudge(0),
02203 gv.GasCoolColl/dense.gas_phase[ipHYDROGEN]/fudge(0));
02204 #endif
02205
02206 if( trace.lgTrace )
02207 {
02208
02209 enum {DEBUG_LOC=false};
02210
02211 if( DEBUG_LOC )
02212 {
02213 fprintf( ioQQQ, " %.2f Grain surface charge transfer rates\n", fnzone );
02214
02215 for( nelem=0; nelem < LIMELM; nelem++ )
02216 {
02217 if( dense.lgElmtOn[nelem] )
02218 {
02219 printf( " %s:", elementnames.chElementSym[nelem] );
02220 for( ion=dense.IonLow[nelem]; ion <= dense.IonHigh[nelem]; ++ion )
02221 {
02222 for( ion_to=0; ion_to <= nelem+1; ion_to++ )
02223 {
02224 if( gv.GrainChTrRate[nelem][ion][ion_to] > 0.f )
02225 {
02226 printf( " %ld->%ld %.2e", ion, ion_to,
02227 gv.GrainChTrRate[nelem][ion][ion_to] );
02228 }
02229 }
02230 }
02231 printf( "\n" );
02232 }
02233 }
02234 }
02235
02236 fprintf( ioQQQ, " %.2f Grain contribution to electron density %.2e\n",
02237 fnzone , gv.TotalEden );
02238
02239 fprintf( ioQQQ, " Grain electons: " );
02240 for( nd=0; nd < gv.nBin; nd++ )
02241 {
02242 double sum = 0.;
02243 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02244 {
02245 sum += gv.bin[nd]->chrg[nz]->FracPop*(double)gv.bin[nd]->chrg[nz]->DustZ*
02246 gv.bin[nd]->cnv_GR_pCM3;
02247 }
02248 fprintf( ioQQQ, " %.2e", sum );
02249 }
02250 fprintf( ioQQQ, "\n" );
02251
02252 fprintf( ioQQQ, " Grain potentials:" );
02253 for( nd=0; nd < gv.nBin; nd++ )
02254 {
02255 fprintf( ioQQQ, " %.2e", gv.bin[nd]->dstpot );
02256 }
02257 fprintf( ioQQQ, "\n" );
02258
02259 fprintf( ioQQQ, " Grain temperatures:" );
02260 for( nd=0; nd < gv.nBin; nd++ )
02261 {
02262 fprintf( ioQQQ, " %.2e", gv.bin[nd]->tedust );
02263 }
02264 fprintf( ioQQQ, "\n" );
02265
02266 fprintf( ioQQQ, " GrainCollCool: %.6e\n", gv.GasCoolColl );
02267 }
02268
02269
02270
02271
02272
02273
02274
02275 return;
02276 }
02277
02278
02279 STATIC void GrainCharge(long int nd,
02280 double *ThermRatio)
02281 {
02282 bool lgBigError,
02283 lgInitial;
02284 long backup,
02285 i,
02286 loopMax,
02287 newZlo,
02288 nz,
02289 power,
02290 stride,
02291 stride0,
02292 Zlo;
02293 double crate,
02294 csum1,
02295 csum1a,
02296 csum1b,
02297 csum2,
02298 csum3,
02299 netloss0 = -DBL_MAX,
02300 netloss1 = -DBL_MAX,
02301 rate_up[NCHU],
02302 rate_dn[NCHU],
02303 step;
02304
02305 DEBUG_ENTRY( "GrainCharge()" );
02306
02307
02308 if( trace.lgTrace && trace.lgDustBug )
02309 {
02310 fprintf( ioQQQ, " Charge loop, search %c,", TorF(conv.lgSearch) );
02311 }
02312
02313 ASSERT( nd >= 0 && nd < gv.nBin );
02314
02315 for( nz=0; nz < NCHS; nz++ )
02316 {
02317 gv.bin[nd]->chrg[nz]->FracPop = -DBL_MAX;
02318 }
02319
02320
02321
02322
02323
02324
02325
02326
02327
02328
02329
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364
02365
02366
02367
02368 lgInitial = ( gv.bin[nd]->chrg[0]->DustZ == LONG_MIN );
02369
02370 backup = gv.bin[nd]->nChrg;
02371 gv.bin[nd]->nChrg = MAX2(gv.bin[nd]->nChrg,3);
02372
02373 stride0 = gv.bin[nd]->nChrg-1;
02374
02375
02376 if( lgInitial )
02377 {
02378 double xxx;
02379 step = MAX2((double)(-gv.bin[nd]->LowestZg),1.);
02380 power = (int)(log(step)/log((double)stride0));
02381 power = MAX2(power,0);
02382 xxx = powi((double)stride0,power);
02383 stride = nint(xxx);
02384 Zlo = gv.bin[nd]->LowestZg;
02385 }
02386 else
02387 {
02388
02389 stride = 1;
02390 Zlo = gv.bin[nd]->chrg[0]->DustZ;
02391 }
02392 UpdatePot( nd, Zlo, stride, rate_up, rate_dn );
02393
02394
02395 for( i=0; i < BRACKET_MAX; i++ )
02396 {
02397 netloss0 = rate_up[0] - rate_dn[0];
02398 netloss1 = rate_up[gv.bin[nd]->nChrg-1] - rate_dn[gv.bin[nd]->nChrg-1];
02399
02400 if( netloss0*netloss1 <= 0. )
02401 break;
02402
02403 if( netloss1 > 0. )
02404 Zlo += (gv.bin[nd]->nChrg-1)*stride;
02405
02406 if( i > 0 )
02407 stride *= stride0;
02408
02409 if( netloss1 < 0. )
02410 Zlo -= (gv.bin[nd]->nChrg-1)*stride;
02411
02412 Zlo = MAX2(Zlo,gv.bin[nd]->LowestZg);
02413 UpdatePot( nd, Zlo, stride, rate_up, rate_dn );
02414 }
02415
02416 if( netloss0*netloss1 > 0. ) {
02417 fprintf( ioQQQ, " insanity: could not bracket grain charge for %s\n", gv.bin[nd]->chDstLab );
02418 ShowMe();
02419 cdEXIT(EXIT_FAILURE);
02420 }
02421
02422
02423 while( stride > 1 )
02424 {
02425 stride /= stride0;
02426
02427 netloss0 = rate_up[0] - rate_dn[0];
02428 for( nz=0; nz < gv.bin[nd]->nChrg-1; nz++ )
02429 {
02430 netloss1 = rate_up[nz+1] - rate_dn[nz+1];
02431
02432 if( netloss0*netloss1 <= 0. )
02433 {
02434 Zlo = gv.bin[nd]->chrg[nz]->DustZ;
02435 break;
02436 }
02437
02438 netloss0 = netloss1;
02439 }
02440 UpdatePot( nd, Zlo, stride, rate_up, rate_dn );
02441 }
02442
02443 ASSERT( netloss0*netloss1 <= 0. );
02444
02445 gv.bin[nd]->nChrg = backup;
02446
02447
02448 loopMax = ( lgInitial ) ? 4*gv.bin[nd]->nChrg : 2*gv.bin[nd]->nChrg;
02449
02450 lgBigError = true;
02451 for( i=0; i < loopMax; i++ )
02452 {
02453 GetFracPop( nd, Zlo, rate_up, rate_dn, &newZlo );
02454
02455 if( newZlo == Zlo )
02456 {
02457 lgBigError = false;
02458 break;
02459 }
02460
02461 Zlo = newZlo;
02462 UpdatePot( nd, Zlo, 1, rate_up, rate_dn );
02463 }
02464
02465 if( lgBigError ) {
02466 fprintf( ioQQQ, " insanity: could not converge grain charge for %s\n", gv.bin[nd]->chDstLab );
02467 ShowMe();
02468 cdEXIT(EXIT_FAILURE);
02469 }
02470
02473 gv.bin[nd]->lgChrgConverged = true;
02474
02475 gv.bin[nd]->AveDustZ = 0.;
02476 crate = csum3 = 0.;
02477 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02478 {
02479 double d[4];
02480
02481 gv.bin[nd]->AveDustZ += gv.bin[nd]->chrg[nz]->FracPop*gv.bin[nd]->chrg[nz]->DustZ;
02482
02483 crate += gv.bin[nd]->chrg[nz]->FracPop*GrainElecEmis1(nd,nz,&d[0],&d[1],&d[2],&d[3]);
02484 csum3 += gv.bin[nd]->chrg[nz]->FracPop*d[3];
02485 }
02486 gv.bin[nd]->dstpot = chrg2pot(gv.bin[nd]->AveDustZ,nd);
02487 *ThermRatio = ( crate > 0. ) ? csum3/crate : 0.;
02488
02489 ASSERT( *ThermRatio >= 0. );
02490
02491 if( trace.lgTrace && trace.lgDustBug )
02492 {
02493 double d[4];
02494
02495 fprintf( ioQQQ, "\n" );
02496
02497 crate = csum1a = csum1b = csum2 = csum3 = 0.;
02498 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02499 {
02500 crate += gv.bin[nd]->chrg[nz]->FracPop*
02501 GrainElecEmis1(nd,nz,&d[0],&d[1],&d[2],&d[3]);
02502 csum1a += gv.bin[nd]->chrg[nz]->FracPop*d[0];
02503 csum1b += gv.bin[nd]->chrg[nz]->FracPop*d[1];
02504 csum2 += gv.bin[nd]->chrg[nz]->FracPop*d[2];
02505 csum3 += gv.bin[nd]->chrg[nz]->FracPop*d[3];
02506 }
02507
02508 fprintf( ioQQQ, " ElecEm rate1a=%.4e, rate1b=%.4e, ", csum1a, csum1b );
02509 fprintf( ioQQQ, "rate2=%.4e, rate3=%.4e, sum=%.4e\n", csum2, csum3, crate );
02510 if( crate > 0. )
02511 {
02512 fprintf( ioQQQ, " rate1a/sum=%.4e, rate1b/sum=%.4e, ", csum1a/crate, csum1b/crate );
02513 fprintf( ioQQQ, "rate2/sum=%.4e, rate3/sum=%.4e\n", csum2/crate, csum3/crate );
02514 }
02515
02516 crate = csum1 = csum2 = 0.;
02517 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02518 {
02519 crate += gv.bin[nd]->chrg[nz]->FracPop*GrainElecRecomb1(nd,nz,&d[0],&d[1]);
02520 csum1 += gv.bin[nd]->chrg[nz]->FracPop*d[0];
02521 csum2 += gv.bin[nd]->chrg[nz]->FracPop*d[1];
02522 }
02523
02524 fprintf( ioQQQ, " ElecRc rate1=%.4e, rate2=%.4e, sum=%.4e\n", csum1, csum2, crate );
02525 if( crate > 0. )
02526 {
02527 fprintf( ioQQQ, " rate1/sum=%.4e, rate2/sum=%.4e\n", csum1/crate, csum2/crate );
02528 }
02529
02530 fprintf( ioQQQ, " Charging rates:" );
02531 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02532 {
02533 fprintf( ioQQQ, " Zg %ld up %.4e dn %.4e",
02534 gv.bin[nd]->chrg[nz]->DustZ, rate_up[nz], rate_dn[nz] );
02535 }
02536 fprintf( ioQQQ, "\n" );
02537
02538 fprintf( ioQQQ, " FracPop: fnzone %.2f nd %ld AveZg %.5e", fnzone, nd, gv.bin[nd]->AveDustZ );
02539 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02540 {
02541 fprintf( ioQQQ, " Zg %ld %.5f", Zlo+nz, gv.bin[nd]->chrg[nz]->FracPop );
02542 }
02543 fprintf( ioQQQ, "\n" );
02544
02545 fprintf( ioQQQ, " >Grain potential:%12.12s %.6fV",
02546 gv.bin[nd]->chDstLab, gv.bin[nd]->dstpot*EVRYD );
02547 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02548 {
02549 fprintf( ioQQQ, " Thres[%ld]: %.4f V ThresVal[%ld]: %.4f V",
02550 gv.bin[nd]->chrg[nz]->DustZ, gv.bin[nd]->chrg[nz]->ThresInf*EVRYD,
02551 gv.bin[nd]->chrg[nz]->DustZ, gv.bin[nd]->chrg[nz]->ThresInfVal*EVRYD );
02552 }
02553 fprintf( ioQQQ, "\n" );
02554 }
02555 return;
02556 }
02557
02558
02559
02560 STATIC double GrainElecRecomb1(long nd,
02561 long nz,
02562 double *sum1,
02563 double *sum2)
02564 {
02565 long ion,
02566 nelem;
02567 double eta,
02568 rate,
02569 Stick,
02570 ve,
02571 xi;
02572
02573 DEBUG_ENTRY( "GrainElecRecomb1()" );
02574
02575 ASSERT( nd >= 0 && nd < gv.nBin );
02576 ASSERT( nz >= 0 && nz < gv.bin[nd]->nChrg );
02577
02578
02579
02580 if( gv.bin[nd]->chrg[nz]->RSum1 >= 0. )
02581 {
02582 *sum1 = gv.bin[nd]->chrg[nz]->RSum1;
02583 *sum2 = gv.bin[nd]->chrg[nz]->RSum2;
02584 rate = *sum1 + *sum2;
02585 return rate;
02586 }
02587
02588
02589 ion = -1;
02590
02591
02592 ve = sqrt(8.*BOLTZMANN/PI/ELECTRON_MASS*phycon.te);
02593
02594 Stick = ( gv.bin[nd]->chrg[nz]->DustZ <= -1 ) ? gv.bin[nd]->StickElecNeg : gv.bin[nd]->StickElecPos;
02595
02596
02597 GrainScreen(ion,nd,nz,&eta,&xi);
02598
02599 *sum1 = ( gv.bin[nd]->chrg[nz]->DustZ > gv.bin[nd]->LowestZg ) ? Stick*dense.eden*ve*eta : 0.;
02600
02601
02602 *sum2 = 0.;
02603
02604 #ifndef IGNORE_GRAIN_ION_COLLISIONS
02605 for( ion=0; ion <= LIMELM; ion++ )
02606 {
02607 double CollisionRateAll = 0.;
02608
02609 for( nelem=MAX2(ion-1,0); nelem < LIMELM; nelem++ )
02610 {
02611 if( dense.lgElmtOn[nelem] && dense.xIonDense[nelem][ion] > 0. &&
02612 gv.bin[nd]->chrg[nz]->RecomZ0[nelem][ion] > ion )
02613 {
02614
02615 CollisionRateAll += STICK_ION*dense.xIonDense[nelem][ion]*DoppVel.AveVel[nelem]*
02616 (double)(gv.bin[nd]->chrg[nz]->RecomZ0[nelem][ion]-ion);
02617 }
02618 }
02619
02620 if( CollisionRateAll > 0. )
02621 {
02622
02623
02624
02625 GrainScreen(ion,nd,nz,&eta,&xi);
02626 *sum2 += CollisionRateAll*eta;
02627 }
02628 }
02629 #endif
02630
02631 rate = *sum1 + *sum2;
02632
02633
02634 gv.bin[nd]->chrg[nz]->RSum1 = *sum1;
02635 gv.bin[nd]->chrg[nz]->RSum2 = *sum2;
02636
02637 ASSERT( *sum1 >= 0. && *sum2 >= 0. );
02638 return rate;
02639 }
02640
02641
02642
02643 STATIC double GrainElecEmis1(long nd,
02644 long nz,
02645 double *sum1a,
02646 double *sum1b,
02647 double *sum2,
02648 double *sum3)
02649 {
02650 long int i,
02651 ion,
02652 ipLo,
02653 nelem;
02654 double eta,
02655 rate,
02656 xi;
02657
02658 DEBUG_ENTRY( "GrainElecEmis1()" );
02659
02660 ASSERT( nd >= 0 && nd < gv.nBin );
02661 ASSERT( nz >= 0 && nz < gv.bin[nd]->nChrg );
02662
02663
02664
02665 if( gv.bin[nd]->chrg[nz]->ESum1a >= 0. )
02666 {
02667 *sum1a = gv.bin[nd]->chrg[nz]->ESum1a;
02668 *sum1b = gv.bin[nd]->chrg[nz]->ESum1b;
02669 *sum2 = gv.bin[nd]->chrg[nz]->ESum2;
02670
02671 *sum3 = 4.*gv.bin[nd]->chrg[nz]->ThermRate;
02672 rate = *sum1a + *sum1b + *sum2 + *sum3;
02673 return rate;
02674 }
02675
02676
02677
02678
02679
02680
02681
02682
02683
02684
02685
02686
02687 *sum1a = 0.;
02688 ipLo = gv.bin[nd]->chrg[nz]->ipThresInfVal;
02689 for( i=ipLo; i < rfield.nflux; i++ )
02690 {
02691 # ifdef WD_TEST2
02692 *sum1a += rfield.flux[i]*gv.bin[nd]->dstab1[i]*gv.bin[nd]->chrg[nz]->yhat[i];
02693 # else
02694 *sum1a += rfield.SummedCon[i]*gv.bin[nd]->dstab1[i]*gv.bin[nd]->chrg[nz]->yhat[i];
02695 # endif
02696 }
02697
02698 *sum1a /= gv.bin[nd]->IntArea/4.;
02699
02700 *sum1b = 0.;
02701 if( gv.bin[nd]->chrg[nz]->DustZ <= -1 )
02702 {
02703 ipLo = gv.bin[nd]->chrg[nz]->ipThresInf;
02704 for( i=ipLo; i < rfield.nflux; i++ )
02705 {
02706
02707 # ifdef WD_TEST2
02708 *sum1b += rfield.flux[i]*gv.bin[nd]->chrg[nz]->cs_pdt[i];
02709 # else
02710 *sum1b += rfield.SummedCon[i]*gv.bin[nd]->chrg[nz]->cs_pdt[i];
02711 # endif
02712 }
02713 *sum1b /= gv.bin[nd]->IntArea/4.;
02714 }
02715
02716
02717 *sum2 = 0.;
02718 # ifndef IGNORE_GRAIN_ION_COLLISIONS
02719 for( ion=0; ion <= LIMELM; ion++ )
02720 {
02721 double CollisionRateAll = 0.;
02722
02723 for( nelem=MAX2(ion-1,0); nelem < LIMELM; nelem++ )
02724 {
02725 if( dense.lgElmtOn[nelem] && dense.xIonDense[nelem][ion] > 0. &&
02726 ion > gv.bin[nd]->chrg[nz]->RecomZ0[nelem][ion] )
02727 {
02728
02729 CollisionRateAll += STICK_ION*dense.xIonDense[nelem][ion]*DoppVel.AveVel[nelem]*
02730 (double)(ion-gv.bin[nd]->chrg[nz]->RecomZ0[nelem][ion]);
02731 }
02732 }
02733
02734 if( CollisionRateAll > 0. )
02735 {
02736
02737
02738
02739 GrainScreen(ion,nd,nz,&eta,&xi);
02740 *sum2 += CollisionRateAll*eta;
02741 }
02742 }
02743 # endif
02744
02745
02746
02747
02748 *sum3 = 4.*gv.bin[nd]->chrg[nz]->ThermRate;
02749
02752 rate = *sum1a + *sum1b + *sum2 + *sum3;
02753
02754
02755 gv.bin[nd]->chrg[nz]->ESum1a = *sum1a;
02756 gv.bin[nd]->chrg[nz]->ESum1b = *sum1b;
02757 gv.bin[nd]->chrg[nz]->ESum2 = *sum2;
02758
02759 ASSERT( *sum1a >= 0. && *sum1b >= 0. && *sum2 >= 0. && *sum3 >= 0. );
02760 return rate;
02761 }
02762
02763
02764
02765
02766 STATIC void GrainScreen(long ion,
02767 long nd,
02768 long nz,
02769 double *eta,
02770 double *xi)
02771 {
02772
02773
02774
02775
02776 long ind = ion+1;
02777
02778 DEBUG_ENTRY( "GrainScreen()" );
02779
02780 ASSERT( ind >= 0 && ind < LIMELM+2 );
02781
02782 if( gv.bin[nd]->chrg[nz]->eta[ind] > 0. )
02783 {
02784 *eta = gv.bin[nd]->chrg[nz]->eta[ind];
02785 *xi = gv.bin[nd]->chrg[nz]->xi[ind];
02786 return;
02787 }
02788
02789
02790
02791 if( ion == 0 )
02792 {
02793 *eta = 1.;
02794 *xi = 1.;
02795 }
02796 else
02797 {
02798
02799
02800
02801
02802
02803
02804 double nu = (double)gv.bin[nd]->chrg[nz]->DustZ/(double)ion;
02805 double tau = gv.bin[nd]->Capacity*BOLTZMANN*phycon.te*1.e-7/POW2((double)ion*ELEM_CHARGE);
02806 if( nu < 0. )
02807 {
02808 *eta = (1. - nu/tau)*(1. + sqrt(2./(tau - 2.*nu)));
02809 *xi = (1. - nu/(2.*tau))*(1. + 1./sqrt(tau - nu));
02810 }
02811 else if( nu == 0. )
02812 {
02813 *eta = 1. + sqrt(PI/(2.*tau));
02814 *xi = 1. + 0.75*sqrt(PI/(2.*tau));
02815 }
02816 else
02817 {
02818 double theta_nu = ThetaNu(nu);
02819
02820 double xxx = 1. + 1./sqrt(4.*tau+3.*nu);
02821 *eta = POW2(xxx)*exp(-theta_nu/tau);
02822 # ifdef WD_TEST2
02823 *xi = (1. + nu/(2.*tau))*(1. + 1./sqrt(3./(2.*tau)+3.*nu))*exp(-theta_nu/tau);
02824 # else
02825
02826
02827
02828 xxx = 0.25*pow(nu/tau,0.75)/(pow(nu/tau,0.75) + pow((25.+3.*nu)/5.,0.75)) +
02829 (1. + 0.75*sqrt(PI/(2.*tau)))/(1. + sqrt(PI/(2.*tau)));
02830 *xi = (MIN2(xxx,1.) + theta_nu/(2.*tau))*(*eta);
02831 # endif
02832 }
02833
02834 ASSERT( *eta >= 0. && *xi >= 0. );
02835 }
02836
02837 gv.bin[nd]->chrg[nz]->eta[ind] = *eta;
02838 gv.bin[nd]->chrg[nz]->xi[ind] = *xi;
02839
02840 return;
02841 }
02842
02843
02844 STATIC double ThetaNu(double nu)
02845 {
02846 double theta_nu;
02847
02848 DEBUG_ENTRY( "ThetaNu()" );
02849
02850 if( nu > 0. )
02851 {
02852 double xxx;
02853 const double REL_TOLER = 10.*DBL_EPSILON;
02854
02855
02856 double xi_nu = 1. + 1./sqrt(3.*nu);
02857 double xi_nu2 = POW2(xi_nu);
02858 do
02859 {
02860 double old = xi_nu;
02861
02862
02863 double fnu = 2.*xi_nu2 - 1. - nu*xi_nu*POW2(xi_nu2 - 1.);
02864 double dfdxi = 4.*xi_nu - nu*((5.*xi_nu2 - 6.)*xi_nu2 + 1.);
02865 xi_nu -= fnu/dfdxi;
02866 xi_nu2 = POW2(xi_nu);
02867 xxx = fabs(old-xi_nu);
02868 } while( xxx > REL_TOLER*xi_nu );
02869
02870 theta_nu = nu/xi_nu - 1./(2.*xi_nu2*(xi_nu2-1.));
02871 }
02872 else
02873 {
02874 theta_nu = 0.;
02875 }
02876 return theta_nu;
02877 }
02878
02879
02880
02881 STATIC void UpdatePot(long nd,
02882 long Zlo,
02883 long stride,
02884 double rate_up[],
02885 double rate_dn[])
02886 {
02887 long nz,
02888 Zg;
02889 double BoltzFac,
02890 HighEnergy;
02891
02892 DEBUG_ENTRY( "UpdatePot()" );
02893
02894 ASSERT( nd >= 0 && nd < gv.nBin );
02895 ASSERT( Zlo >= gv.bin[nd]->LowestZg );
02896 ASSERT( stride >= 1 );
02897
02898
02899
02900
02901
02902
02903
02904
02905
02906
02907 if( trace.lgTrace && trace.lgDustBug )
02908 {
02909 fprintf( ioQQQ, " %ld/%ld", Zlo, stride );
02910 }
02911
02912 if( gv.bin[nd]->nfill < rfield.nflux )
02913 {
02914 InitBinAugerData( nd, gv.bin[nd]->nfill, rfield.nflux );
02915 gv.bin[nd]->nfill = rfield.nflux;
02916 }
02917
02918 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02919 {
02920 long ind, zz;
02921 double d[4];
02922 ChargeBin *ptr;
02923
02924 Zg = Zlo + nz*stride;
02925
02926
02927 ind = NCHS-1;
02928 for( zz=0; zz < NCHS-1; zz++ )
02929 {
02930 if( gv.bin[nd]->chrg[zz]->DustZ == Zg )
02931 {
02932 ind = zz;
02933 break;
02934 }
02935 }
02936
02937
02938
02939 ptr = gv.bin[nd]->chrg[ind];
02940
02941
02942 for( zz=ind-1; zz >= nz; zz-- )
02943 gv.bin[nd]->chrg[zz+1] = gv.bin[nd]->chrg[zz];
02944
02945 gv.bin[nd]->chrg[nz] = ptr;
02946
02947 if( gv.bin[nd]->chrg[nz]->DustZ != Zg )
02948 UpdatePot1(nd,nz,Zg,0);
02949 else if( gv.bin[nd]->chrg[nz]->nfill < rfield.nflux )
02950 UpdatePot1(nd,nz,Zg,gv.bin[nd]->chrg[nz]->nfill);
02951
02952 UpdatePot2(nd,nz);
02953
02954 rate_up[nz] = GrainElecEmis1(nd,nz,&d[0],&d[1],&d[2],&d[3]);
02955 rate_dn[nz] = GrainElecRecomb1(nd,nz,&d[0],&d[1]);
02956
02957
02958 ASSERT( gv.bin[nd]->chrg[nz]->DustZ == Zg );
02959 ASSERT( gv.bin[nd]->chrg[nz]->nfill >= rfield.nflux );
02960 ASSERT( rate_up[nz] >= 0. && rate_dn[nz] >= 0. );
02961 }
02962
02963
02964
02965
02966
02967
02968 BoltzFac = (-log(CONSERV_TOL) + 8.)*BOLTZMANN/EN1RYD;
02969 HighEnergy = 0.;
02970 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
02971 {
02972
02973 HighEnergy = MAX2(HighEnergy,
02974 MAX2(gv.bin[nd]->chrg[nz]->ThresInfInc,0.) + BoltzFac*MAX2(phycon.te,gv.bin[nd]->tedust));
02975 }
02976 HighEnergy = MIN2(HighEnergy,rfield.anu[rfield.nupper-1]);
02977 gv.bin[nd]->qnflux2 = ipoint(HighEnergy);
02978 gv.bin[nd]->qnflux = MAX2(rfield.nflux,gv.bin[nd]->qnflux2);
02979
02980 ASSERT( gv.bin[nd]->qnflux <= rfield.nupper-1 );
02981 return;
02982 }
02983
02984
02985
02986 STATIC void GetFracPop(long nd,
02987 long Zlo,
02988 double rate_up[],
02989 double rate_dn[],
02990 long *newZlo)
02991 {
02992 bool lgRedo;
02993 long i,
02994 nz;
02995 double netloss[2],
02996 pop[2][NCHU-1];
02997
02998
02999 DEBUG_ENTRY( "GetFracPop()" );
03000
03001 ASSERT( nd >= 0 && nd < gv.nBin );
03002 ASSERT( Zlo >= gv.bin[nd]->LowestZg );
03003
03004
03005
03006
03007
03008
03009
03010
03011 do
03012 {
03013 for( i=0; i < 2; i++ )
03014 {
03015 long j, k;
03016 double sum;
03017
03018 sum = pop[i][0] = 1.;
03019 for( j=1; j < gv.bin[nd]->nChrg-1; j++ )
03020 {
03021 nz = i + j;
03022 if( rate_dn[nz] > 10.*rate_up[nz-1]/sqrt(DBL_MAX) )
03023 {
03024 pop[i][j] = pop[i][j-1]*rate_up[nz-1]/rate_dn[nz];
03025 sum += pop[i][j];
03026 }
03027 else
03028 {
03029 for( k=0; k < j; k++ )
03030 {
03031 pop[i][k] = 0.;
03032 }
03033 pop[i][j] = 1.;
03034 sum = pop[i][j];
03035 }
03036
03037 if( pop[i][j] > sqrt(DBL_MAX) )
03038 {
03039 for( k=0; k <= j; k++ )
03040 {
03041 pop[i][k] /= DBL_MAX/10.;
03042 }
03043 sum /= DBL_MAX/10.;
03044 }
03045 }
03046 netloss[i] = 0.;
03047 for( j=0; j < gv.bin[nd]->nChrg-1; j++ )
03048 {
03049 nz = i + j;
03050 pop[i][j] /= sum;
03051 netloss[i] += pop[i][j]*(rate_up[nz] - rate_dn[nz]);
03052 }
03053 }
03054
03055
03056
03057 if( netloss[0]*netloss[1] > 0. )
03058 *newZlo = ( netloss[1] > 0. ) ? Zlo + 1 : Zlo - 1;
03059 else
03060 *newZlo = Zlo;
03061
03062
03063
03064
03065
03066
03067 if( gv.bin[nd]->nChrg > 2 &&
03068 ( *newZlo < gv.bin[nd]->LowestZg ||
03069 ( *newZlo == Zlo && pop[1][gv.bin[nd]->nChrg-2] < DBL_EPSILON ) ) )
03070 {
03071 gv.bin[nd]->nChrg--;
03072 lgRedo = true;
03073 }
03074 else
03075 {
03076 lgRedo = false;
03077 }
03078
03079 # if 0
03080 printf( " fnzone %.2f nd %ld Zlo %ld newZlo %ld netloss %.4e %.4e nChrg %ld lgRedo %d\n",
03081 fnzone, nd, Zlo, *newZlo, netloss[0], netloss[1], gv.bin[nd]->nChrg, lgRedo );
03082 # endif
03083 }
03084 while( lgRedo );
03085
03086 if( *newZlo < gv.bin[nd]->LowestZg )
03087 {
03088 fprintf( ioQQQ, " could not converge charge state populations for %s\n", gv.bin[nd]->chDstLab );
03089 ShowMe();
03090 cdEXIT(EXIT_FAILURE);
03091 }
03092
03093 if( *newZlo == Zlo )
03094 {
03095 double frac0, frac1;
03096 # ifndef NDEBUG
03097 double test1, test2, test3, x1, x2;
03098 # endif
03099
03100
03101 frac0 = netloss[1]/(netloss[1]-netloss[0]);
03102 frac1 = -netloss[0]/(netloss[1]-netloss[0]);
03103
03104 gv.bin[nd]->chrg[0]->FracPop = frac0*pop[0][0];
03105 gv.bin[nd]->chrg[gv.bin[nd]->nChrg-1]->FracPop = frac1*pop[1][gv.bin[nd]->nChrg-2];
03106 for( nz=1; nz < gv.bin[nd]->nChrg-1; nz++ )
03107 {
03108 gv.bin[nd]->chrg[nz]->FracPop = frac0*pop[0][nz] + frac1*pop[1][nz-1];
03109 }
03110
03111 # ifndef NDEBUG
03112 test1 = test2 = test3 = 0.;
03113 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
03114 {
03115 ASSERT( gv.bin[nd]->chrg[nz]->FracPop >= 0. );
03116 test1 += gv.bin[nd]->chrg[nz]->FracPop;
03117 test2 += gv.bin[nd]->chrg[nz]->FracPop*rate_up[nz];
03118 test3 += gv.bin[nd]->chrg[nz]->FracPop*(rate_up[nz]-rate_dn[nz]);
03119 }
03120 x1 = fabs(test1-1.);
03121 x2 = fabs(test3/test2);
03122 ASSERT( MAX2(x1,x2) < 10.*sqrt((double)gv.bin[nd]->nChrg)*DBL_EPSILON );
03123 # endif
03124 }
03125 return;
03126 }
03127
03128
03129
03130
03131
03132
03133
03134
03135
03136
03137
03138 STATIC void UpdatePot1(long nd,
03139 long nz,
03140 long Zg,
03141 long ipStart)
03142 {
03143 long i,
03144 ipLo,
03145 nfill;
03146 double c1,
03147 cnv_GR_pH,
03148 d[2],
03149 DustWorkFcn,
03150 Elo,
03151 Emin,
03152 ThresInf,
03153 ThresInfVal;
03154
03155 realnum *anu = rfield.anu;
03156
03157
03158
03159
03160 const double INV_DELTA_E = EVRYD/3.;
03161 const double CS_PDT = 1.2e-17;
03162
03163 DEBUG_ENTRY( "UpdatePot1()" );
03164
03165
03166
03167
03168
03169
03170
03171 if( ipStart == 0 )
03172 {
03173 gv.bin[nd]->chrg[nz]->DustZ = Zg;
03174
03175
03176 memset( gv.bin[nd]->chrg[nz]->eta, 0, (LIMELM+2)*sizeof(double) );
03177 memset( gv.bin[nd]->chrg[nz]->xi, 0, (LIMELM+2)*sizeof(double) );
03178
03179 GetPotValues(nd,Zg,&gv.bin[nd]->chrg[nz]->ThresInf,&gv.bin[nd]->chrg[nz]->ThresInfVal,
03180 &gv.bin[nd]->chrg[nz]->ThresSurf,&gv.bin[nd]->chrg[nz]->ThresSurfVal,
03181 &gv.bin[nd]->chrg[nz]->PotSurf,&gv.bin[nd]->chrg[nz]->Emin,INCL_TUNNEL);
03182
03183
03184
03185 GetPotValues(nd,Zg-1,&gv.bin[nd]->chrg[nz]->ThresInfInc,&d[0],&gv.bin[nd]->chrg[nz]->ThresSurfInc,
03186 &d[1],&gv.bin[nd]->chrg[nz]->PotSurfInc,&gv.bin[nd]->chrg[nz]->EminInc,NO_TUNNEL);
03187
03188 gv.bin[nd]->chrg[nz]->ipThresInfVal =
03189 hunt_bisect( rfield.anu, rfield.nupper, (realnum)gv.bin[nd]->chrg[nz]->ThresInfVal ) + 1;
03190 }
03191
03192 ipLo = gv.bin[nd]->chrg[nz]->ipThresInfVal;
03193
03194
03195 gv.bin[nd]->chrg[nz]->nfill = rfield.nflux;
03196 nfill = gv.bin[nd]->chrg[nz]->nfill;
03197
03198
03199 long len = nfill + 10 - ipLo;
03200 if( ipStart == 0 )
03201 {
03202 gv.bin[nd]->chrg[nz]->yhat.reserve( len );
03203 gv.bin[nd]->chrg[nz]->yhat_primary.reserve( len );
03204 gv.bin[nd]->chrg[nz]->ehat.reserve( len );
03205 gv.bin[nd]->chrg[nz]->yhat.alloc( ipLo, nfill );
03206 gv.bin[nd]->chrg[nz]->yhat_primary.alloc( ipLo, nfill );
03207 gv.bin[nd]->chrg[nz]->ehat.alloc( ipLo, nfill );
03208 }
03209 else
03210 {
03211 gv.bin[nd]->chrg[nz]->yhat.realloc( nfill );
03212 gv.bin[nd]->chrg[nz]->yhat_primary.realloc( nfill );
03213 gv.bin[nd]->chrg[nz]->ehat.realloc( nfill );
03214 }
03215
03216 double GrainPot = chrg2pot(Zg,nd);
03217
03218 if( nfill > ipLo )
03219 {
03220 DustWorkFcn = gv.bin[nd]->DustWorkFcn;
03221 Elo = -gv.bin[nd]->chrg[nz]->PotSurf;
03222 ThresInfVal = gv.bin[nd]->chrg[nz]->ThresInfVal;
03223 Emin = gv.bin[nd]->chrg[nz]->Emin;
03224
03228 ASSERT( gv.bin[nd]->sd[0]->ipLo <= ipLo );
03229
03230 for( i=max(ipLo,ipStart); i < nfill; i++ )
03231 {
03232 long n;
03233 unsigned long ns=0;
03234 double Yp1,Ys1,Ehp,Ehs,yp,ya,ys,eyp,eya,eys;
03235 double yzero,yone,Ehi,Ehi_band,Wcorr,Eel;
03236 ShellData *sptr = gv.bin[nd]->sd[ns];
03237
03238 yp = ya = ys = 0.;
03239 eyp = eya = eys = 0.;
03240
03241
03242 Ehi = Ehi_band = anu[i] - ThresInfVal - Emin;
03243 Wcorr = ThresInfVal + Emin - GrainPot;
03244 Eel = anu[i] - DustWorkFcn;
03245 yzero = y0b( nd, nz, i );
03246 yone = sptr->y01[i];
03247 Yfunc(nd,nz,yzero*yone,sptr->p[i],Elo,Ehi,Eel,&Yp1,&Ys1,&Ehp,&Ehs);
03248 yp += Yp1;
03249 ys += Ys1;
03250 eyp += Yp1*Ehp;
03251 eys += Ys1*Ehs;
03252
03253
03254 unsigned long nsmax = gv.bin[nd]->nShells;
03255 for( ns=1; ns < nsmax; ns++ )
03256 {
03257 sptr = gv.bin[nd]->sd[ns];
03258
03259 if( i < sptr->ipLo )
03260 continue;
03261
03262 Ehi = Ehi_band + Wcorr - sptr->ionPot;
03263 Eel = anu[i] - sptr->ionPot;
03264 Yfunc(nd,nz,sptr->y01[i],sptr->p[i],Elo,Ehi,Eel,&Yp1,&Ys1,&Ehp,&Ehs);
03265 yp += Yp1;
03266 ys += Ys1;
03267 eyp += Yp1*Ehp;
03268 eys += Ys1*Ehs;
03269
03270
03271 long nmax = sptr->nData;
03272 for( n=0; n < nmax; n++ )
03273 {
03274 double max = sptr->AvNr[n]*sptr->p[i];
03275 Ehi = sptr->Ener[n] - GrainPot;
03276 Eel = sptr->Ener[n];
03277 Yfunc(nd,nz,sptr->y01A[n][i],max,Elo,Ehi,Eel,&Yp1,&Ys1,&Ehp,&Ehs);
03278 ya += Yp1;
03279 ys += Ys1;
03280 eya += Yp1*Ehp;
03281 eys += Ys1*Ehs;
03282 }
03283 }
03284
03285 gv.bin[nd]->chrg[nz]->yhat[i] = (realnum)(yp + ya + ys);
03286 gv.bin[nd]->chrg[nz]->yhat_primary[i] = min((realnum)yp,1.f);
03287 gv.bin[nd]->chrg[nz]->ehat[i] = ( gv.bin[nd]->chrg[nz]->yhat[i] > 0. ) ?
03288 (realnum)((eyp + eya + eys)/gv.bin[nd]->chrg[nz]->yhat[i]) : 0.f;
03289
03290 ASSERT( yp <= 1.00001 );
03291 ASSERT( gv.bin[nd]->chrg[nz]->ehat[i] >= 0.f );
03292 }
03293 }
03294
03295 if( ipStart == 0 )
03296 {
03297
03298
03299
03300 gv.bin[nd]->chrg[nz]->ipThresInf =
03301 hunt_bisect( rfield.anu, rfield.nupper, (realnum)gv.bin[nd]->chrg[nz]->ThresInf ) + 1;
03302 }
03303
03304 ipLo = gv.bin[nd]->chrg[nz]->ipThresInf;
03305
03306 len = nfill + 10 - ipLo;
03307
03308 if( Zg <= -1 )
03309 {
03310
03311 if( ipStart == 0 )
03312 {
03313 gv.bin[nd]->chrg[nz]->cs_pdt.reserve( len );
03314 gv.bin[nd]->chrg[nz]->cs_pdt.alloc( ipLo, nfill );
03315 }
03316 else
03317 {
03318 gv.bin[nd]->chrg[nz]->cs_pdt.realloc( nfill );
03319 }
03320
03321 if( nfill > ipLo )
03322 {
03323 c1 = -CS_PDT*(double)Zg;
03324 ThresInf = gv.bin[nd]->chrg[nz]->ThresInf;
03325 cnv_GR_pH = gv.bin[nd]->cnv_GR_pH;
03326
03327 for( i=max(ipLo,ipStart); i < nfill; i++ )
03328 {
03329 double x = (anu[i] - ThresInf)*INV_DELTA_E;
03330 double cs = c1*x/POW2(1.+(1./3.)*POW2(x));
03331
03332
03333 gv.bin[nd]->chrg[nz]->cs_pdt[i] = MAX2(cs,0.)*cnv_GR_pH;
03334 }
03335 }
03336 }
03337
03338
03339 if( ipStart == 0 )
03340 {
03341 gv.bin[nd]->chrg[nz]->fac1.reserve( len );
03342 gv.bin[nd]->chrg[nz]->fac2.reserve( len );
03343 gv.bin[nd]->chrg[nz]->fac1.alloc( ipLo, nfill );
03344 gv.bin[nd]->chrg[nz]->fac2.alloc( ipLo, nfill );
03345 }
03346 else
03347 {
03348 gv.bin[nd]->chrg[nz]->fac1.realloc( nfill );
03349 gv.bin[nd]->chrg[nz]->fac2.realloc( nfill );
03350 }
03351
03352 if( nfill > ipLo )
03353 {
03354 for( i=max(ipLo,ipStart); i < nfill; i++ )
03355 {
03356 double cs1,cs2,cs_tot,cool1,cool2,ehat1,ehat2;
03357
03358
03359 PE_init(nd,nz,i,&cs1,&cs2,&cs_tot,&cool1,&cool2,&ehat1,&ehat2);
03360
03361 gv.bin[nd]->chrg[nz]->fac1[i] = cs_tot*anu[i]-cs1*cool1-cs2*cool2;
03362 gv.bin[nd]->chrg[nz]->fac2[i] = cs1*ehat1 + cs2*ehat2;
03363
03364 ASSERT( gv.bin[nd]->chrg[nz]->fac1[i] >= 0. && gv.bin[nd]->chrg[nz]->fac2[i] >= 0. );
03365 }
03366 }
03367
03368 if( ipStart == 0 )
03369 {
03370
03371
03372 UpdateRecomZ0(nd,nz,ALL_STAGES);
03373 }
03374
03375
03376 gv.bin[nd]->chrg[nz]->FracPop = -DBL_MAX;
03377
03378 gv.bin[nd]->chrg[nz]->RSum1 = -DBL_MAX;
03379 gv.bin[nd]->chrg[nz]->RSum2 = -DBL_MAX;
03380 gv.bin[nd]->chrg[nz]->ESum1a = -DBL_MAX;
03381 gv.bin[nd]->chrg[nz]->ESum1b = -DBL_MAX;
03382 gv.bin[nd]->chrg[nz]->ESum2 = -DBL_MAX;
03383
03384 gv.bin[nd]->chrg[nz]->tedust = 1.f;
03385
03386 gv.bin[nd]->chrg[nz]->hcon1 = -DBL_MAX;
03387 gv.bin[nd]->chrg[nz]->hots1 = -DBL_MAX;
03388 gv.bin[nd]->chrg[nz]->bolflux1 = -DBL_MAX;
03389 gv.bin[nd]->chrg[nz]->pe1 = -DBL_MAX;
03390
03391 gv.bin[nd]->chrg[nz]->BolFlux = -DBL_MAX;
03392 gv.bin[nd]->chrg[nz]->GrainHeat = -DBL_MAX;
03393 gv.bin[nd]->chrg[nz]->GrainHeatColl = -DBL_MAX;
03394 gv.bin[nd]->chrg[nz]->GasHeatPhotoEl = -DBL_MAX;
03395 gv.bin[nd]->chrg[nz]->GasHeatTherm = -DBL_MAX;
03396 gv.bin[nd]->chrg[nz]->GrainCoolTherm = -DBL_MAX;
03397 gv.bin[nd]->chrg[nz]->ChemEnIon = -DBL_MAX;
03398 gv.bin[nd]->chrg[nz]->ChemEnH2 = -DBL_MAX;
03399
03400 gv.bin[nd]->chrg[nz]->HeatingRate2 = -DBL_MAX;
03401
03402
03403 ASSERT( gv.bin[nd]->chrg[nz]->ipThresInf <= gv.bin[nd]->chrg[nz]->ipThresInfVal );
03404 return;
03405 }
03406
03407
03408
03409
03410
03411
03412
03413
03414
03415
03416
03417 STATIC void UpdatePot2(long nd,
03418 long nz)
03419 {
03420 double ThermExp;
03421
03422 DEBUG_ENTRY( "UpdatePot2()" );
03423
03424
03425 ThermExp = gv.bin[nd]->chrg[nz]->ThresInf*TE1RYD/gv.bin[nd]->tedust;
03426
03427 gv.bin[nd]->chrg[nz]->ThermRate = THERMCONST*gv.bin[nd]->ThermEff*POW2(gv.bin[nd]->tedust)*exp(-ThermExp);
03428 # if defined( WD_TEST2 ) || defined( IGNORE_THERMIONIC )
03429 gv.bin[nd]->chrg[nz]->ThermRate = 0.;
03430 # endif
03431 return;
03432 }
03433
03434
03435
03436 inline void Yfunc(long nd,
03437 long nz,
03438 double y01,
03439 double maxval,
03440 double Elo,
03441 double Ehi,
03442 double Eel,
03443 double *Yp,
03444 double *Ys,
03445 double *Ehp,
03446 double *Ehs)
03447 {
03448 DEBUG_ENTRY( "Yfunc()" );
03449
03450 long Zg = gv.bin[nd]->chrg[nz]->DustZ;
03451 double y2pr, y2sec;
03452
03453 ASSERT( Ehi >= Elo );
03454
03455 y2pr = y2pa( Elo, Ehi, Zg, Ehp );
03456
03457 if( y2pr > 0. )
03458 {
03459 pe_type pcase = gv.which_pe[gv.bin[nd]->matType];
03460 double eps, f3;
03461
03462 *Yp = y2pr*min(y01,maxval);
03463
03464 y2sec = y2s( Elo, Ehi, Zg, Ehs );
03465 if( pcase == PE_CAR )
03466 eps = 117./EVRYD;
03467 else if( pcase == PE_SIL )
03468 eps = 155./EVRYD;
03469 else
03470 {
03471 fprintf( ioQQQ, " Yfunc: unknown type for PE effect: %d\n" , pcase );
03472 cdEXIT(EXIT_FAILURE);
03473 }
03474
03475
03476 f3 = max(Eel,0.)/(eps*elec_esc_length(Eel,nd)*gv.bin[nd]->eyc);
03477 *Ys = y2sec*f3*min(y01,maxval);
03478 }
03479 else
03480 {
03481 *Yp = 0.;
03482 *Ys = 0.;
03483 *Ehp = 0.;
03484 *Ehs = 0.;
03485 }
03486 return;
03487 }
03488
03489
03490
03491 STATIC double y0b(long nd,
03492 long nz,
03493 long i)
03494 {
03495 double yzero;
03496
03497 DEBUG_ENTRY( "y0b()" );
03498
03499 if( gv.lgWD01 )
03500 yzero = y0b01( nd, nz, i );
03501 else
03502 {
03503 double Eph = rfield.anu[i];
03504
03505 if( Eph <= 20./EVRYD )
03506 yzero = y0b01( nd, nz, i );
03507 else if( Eph < 50./EVRYD )
03508 {
03509 double y0a = y0b01( nd, nz, i );
03510 double y0b = gv.bin[nd]->y0b06[i];
03511
03512 double frac = log(Eph*(EVRYD/20.))*1.0913566679372915;
03513
03514 yzero = y0a * exp(log(y0b/y0a)*frac);
03515 }
03516 else
03517 yzero = gv.bin[nd]->y0b06[i];
03518 }
03519
03520 ASSERT( yzero > 0. );
03521 return yzero;
03522 }
03523
03524
03525
03526 STATIC double y0b01(long nd,
03527 long nz,
03528 long i)
03529 {
03530 pe_type pcase = gv.which_pe[gv.bin[nd]->matType];
03531 double xv, yzero;
03532
03533 DEBUG_ENTRY( "y0b01()" );
03534
03535 xv = MAX2((rfield.anu[i] - gv.bin[nd]->chrg[nz]->ThresSurfVal)/gv.bin[nd]->DustWorkFcn,0.);
03536
03537 switch( pcase )
03538 {
03539 case PE_CAR:
03540
03541 xv = POW2(xv)*POW3(xv);
03542 yzero = xv/((1./9.e-3) + (3.7e-2/9.e-3)*xv);
03543 break;
03544 case PE_SIL:
03545
03546 yzero = xv/(2.+10.*xv);
03547 break;
03548 default:
03549 fprintf( ioQQQ, " y0b01: unknown type for PE effect: %d\n" , pcase );
03550 cdEXIT(EXIT_FAILURE);
03551 }
03552
03553 ASSERT( yzero > 0. );
03554 return yzero;
03555 }
03556
03557
03558
03559 STATIC double y0psa(long nd,
03560 long ns,
03561 long i,
03562 double Eel)
03563 {
03564 double yzero, leola;
03565
03566 DEBUG_ENTRY( "y0psa()" );
03567
03568 ASSERT( i >= gv.bin[nd]->sd[ns]->ipLo );
03569
03570
03571 leola = elec_esc_length(Eel,nd)*gv.bin[nd]->inv_att_len[i];
03572
03573 ASSERT( leola > 0. );
03574
03575
03576 if( leola < 1.e4 )
03577 yzero = gv.bin[nd]->sd[ns]->p[i]*leola*(1. - leola*log(1.+1./leola));
03578 else
03579 {
03580 double x = 1./leola;
03581 yzero = gv.bin[nd]->sd[ns]->p[i]*(((-1./5.*x+1./4.)*x-1./3.)*x+1./2.);
03582 }
03583
03584 ASSERT( yzero > 0. );
03585 return yzero;
03586 }
03587
03588
03589
03590 STATIC double y1psa(long nd,
03591 long i,
03592 double Eel)
03593 {
03594 double alpha, beta, af, bf, yone;
03595
03596 DEBUG_ENTRY( "y1psa()" );
03597
03598 beta = gv.bin[nd]->AvRadius*gv.bin[nd]->inv_att_len[i];
03599 if( beta > 1.e-4 )
03600 bf = pow2(beta) - 2.*beta + 2. - 2.*exp(-beta);
03601 else
03602 bf = ((1./60.*beta - 1./12.)*beta + 1./3.)*pow3(beta);
03603
03604 alpha = beta + gv.bin[nd]->AvRadius/elec_esc_length(Eel,nd);
03605 if( alpha > 1.e-4 )
03606 af = pow2(alpha) - 2.*alpha + 2. - 2.*exp(-alpha);
03607 else
03608 af = ((1./60.*alpha - 1./12.)*alpha + 1./3.)*pow3(alpha);
03609
03610 yone = pow2(beta/alpha)*af/bf;
03611
03612 ASSERT( yone > 0. );
03613 return yone;
03614 }
03615
03616
03617
03618 inline double y2pa(double Elo,
03619 double Ehi,
03620 long Zg,
03621 double *Ehp)
03622 {
03623 DEBUG_ENTRY( "y2pa()" );
03624
03625 double ytwo;
03626
03627 if( Zg > -1 )
03628 {
03629 if( Ehi > 0. )
03630 {
03631 double x = Elo/Ehi;
03632 *Ehp = 0.5*Ehi*(1.-2.*x)/(1.-3.*x);
03633
03634 ytwo = ( abs(x) > 1e-4 ) ? (1.-3.*x)/pow3(1.-x) : 1. - (3. + 8.*x)*x*x;
03635 ASSERT( *Ehp > 0. && *Ehp <= Ehi && ytwo > 0. && ytwo <= 1. );
03636 }
03637 else
03638 {
03639 *Ehp = 0.;
03640 ytwo = 0.;
03641 }
03642 }
03643 else
03644 {
03645 if( Ehi > Elo )
03646 {
03647 *Ehp = 0.5*(Elo+Ehi);
03648 ytwo = 1.;
03649 ASSERT( *Ehp >= Elo && *Ehp <= Ehi );
03650 }
03651 else
03652 {
03653 *Ehp = 0.;
03654 ytwo = 0.;
03655 }
03656 }
03657 return ytwo;
03658 }
03659
03660
03661
03662 inline double y2s(double Elo,
03663 double Ehi,
03664 long Zg,
03665 double *Ehs)
03666 {
03667 DEBUG_ENTRY( "y2s()" );
03668
03669 double ytwo;
03670
03671 if( Zg > -1 )
03672 {
03673 if( !gv.lgWD01 && Ehi > 0. )
03674 {
03675 double yl = Elo/ETILDE;
03676 double yh = Ehi/ETILDE;
03677 double x = yh - yl;
03678 double E0, N0;
03679 if( x < 0.01 )
03680 {
03681
03682 double x2 = x*x, x3 = x2*x, x4 = x3*x, x5 = x4*x;
03683 double yh2 = yh*yh, yh3 = yh2*yh, yh4 = yh3*yh, yh5 = yh4*yh;
03684 double help1 = 2.*x-yh;
03685 double help2 = (6.*x3-15.*yh*x2+12.*yh2*x-3.*yh3)/4.;
03686 double help3 = (22.*x5-95.*yh*x4+164.*yh2*x3-141.*yh3*x2+60.*yh4*x-10.*yh5)/16.;
03687 N0 = yh*(help1 - help2 + help3)/x2;
03688
03689 help1 = (3.*x-yh)/3.;
03690 help2 = (15.*x3-25.*yh*x2+15.*yh2*x-3.*yh3)/20.;
03691 help3 = (1155.*x5-3325.*yh*x4+4305.*yh2*x3-2961.*yh3*x2+1050.*yh4*x-150.*yh5)/1680.;
03692 E0 = ETILDE*yh2*(help1 - help2 + help3)/x2;
03693 }
03694 else
03695 {
03696 double sR0 = (1. + yl*yl);
03697 double sqR0 = sqrt(sR0);
03698 double sqRh = sqrt(1. + x*x);
03699 double alpha = sqRh/(sqRh - 1.);
03700 if( yh < 0.01 )
03701 {
03702
03703 double z = yh*(yh - 2.*yl)/sR0;
03704 N0 = ((((7./256.*z-5./128.)*z+1./16.)*z-1./8.)*z+1./2.)*z/(sqRh-1.);
03705
03706 double yl2 = yl*yl, yl3 = yl2*yl, yl4 = yl3*yl;
03707 double help1 = yl/2.;
03708 double help2 = (2.*yl2-1.)/3.;
03709 double help3 = (6.*yl3-9.*yl)/8.;
03710 double help4 = (8.*yl4-24.*yl2+3.)/10.;
03711 double h = yh/sR0;
03712 E0 = -alpha*Ehi*(((help4*h + help3)*h + help2)*h + help1)*h/sqR0;
03713 }
03714 else
03715 {
03716 N0 = alpha*(1./sqR0 - 1./sqRh);
03717 E0 = alpha*ETILDE*(ASINH(x*sqR0 + yl*sqRh) - yh/sqRh);
03718 }
03719 }
03720 ASSERT( N0 > 0. && N0 <= 1. );
03721
03722 *Ehs = E0/N0;
03723
03724 ASSERT( *Ehs > 0. && *Ehs <= Ehi );
03725
03726 ytwo = N0;
03727 }
03728 else
03729 {
03730 *Ehs = 0.;
03731 ytwo = 0.;
03732 }
03733 }
03734 else
03735 {
03736 if( !gv.lgWD01 && Ehi > Elo )
03737 {
03738 double yl = Elo/ETILDE;
03739 double yh = Ehi/ETILDE;
03740 double x = yh - yl;
03741 double x2 = x*x;
03742 if( x > 0.025 )
03743 {
03744 double sqRh = sqrt(1. + x2);
03745 double alpha = sqRh/(sqRh - 1.);
03746 *Ehs = alpha*ETILDE*(ASINH(x) - yh/sqRh + yl);
03747 }
03748 else
03749 {
03750
03751 *Ehs = Ehi - (Ehi-Elo)*((-37./840.*x2 + 1./10.)*x2 + 1./3.);
03752 }
03753
03754 ASSERT( *Ehs >= Elo && *Ehs <= Ehi );
03755
03756 ytwo = 1.;
03757 }
03758 else
03759 {
03760 *Ehs = 0.;
03761 ytwo = 0.;
03762 }
03763 }
03764 return ytwo;
03765 }
03766
03767
03768
03769 STATIC long HighestIonStage(void)
03770 {
03771 long high,
03772 ion,
03773 nelem;
03774
03775 DEBUG_ENTRY( "HighestIonStage()" );
03776
03777 high = 0;
03778 for( nelem=LIMELM-1; nelem >= 0; nelem-- )
03779 {
03780 if( dense.lgElmtOn[nelem] )
03781 {
03782 for( ion=nelem+1; ion >= 0; ion-- )
03783 {
03784 if( ion == high || dense.xIonDense[nelem][ion] > 0. )
03785 break;
03786 }
03787 high = MAX2(high,ion);
03788 }
03789 if( nelem <= high )
03790 break;
03791 }
03792 return high;
03793 }
03794
03795
03796 STATIC void UpdateRecomZ0(long nd,
03797 long nz,
03798 bool lgAllIonStages)
03799 {
03800 long hi_ion,
03801 i,
03802 ion,
03803 nelem,
03804 Zg;
03805 double d[5],
03806 phi_s_up[LIMELM+1],
03807 phi_s_dn[2];
03808
03809 DEBUG_ENTRY( "UpdateRecomZ0()" );
03810
03811 Zg = gv.bin[nd]->chrg[nz]->DustZ;
03812
03813 hi_ion = ( lgAllIonStages ) ? LIMELM : gv.HighestIon;
03814
03815 phi_s_up[0] = gv.bin[nd]->chrg[nz]->ThresSurf;
03816 for( i=1; i <= LIMELM; i++ )
03817 {
03818 if( i <= hi_ion )
03819 GetPotValues(nd,Zg+i,&d[0],&d[1],&phi_s_up[i],&d[2],&d[3],&d[4],INCL_TUNNEL);
03820 else
03821 phi_s_up[i] = -DBL_MAX;
03822 }
03823 phi_s_dn[0] = gv.bin[nd]->chrg[nz]->ThresSurfInc;
03824 GetPotValues(nd,Zg-2,&d[0],&d[1],&phi_s_dn[1],&d[2],&d[3],&d[4],NO_TUNNEL);
03825
03826
03827 for( nelem=0; nelem < LIMELM; nelem++ )
03828 {
03829 if( dense.lgElmtOn[nelem] )
03830 {
03831 for( ion=0; ion <= nelem+1; ion++ )
03832 {
03833 if( lgAllIonStages || dense.xIonDense[nelem][ion] > 0. )
03834 {
03835 GrainIonColl(nd,nz,nelem,ion,phi_s_up,phi_s_dn,
03836 &gv.bin[nd]->chrg[nz]->RecomZ0[nelem][ion],
03837 &gv.bin[nd]->chrg[nz]->RecomEn[nelem][ion],
03838 &gv.bin[nd]->chrg[nz]->ChemEn[nelem][ion]);
03839 }
03840 else
03841 {
03842 gv.bin[nd]->chrg[nz]->RecomZ0[nelem][ion] = ion;
03843 gv.bin[nd]->chrg[nz]->RecomEn[nelem][ion] = 0.f;
03844 gv.bin[nd]->chrg[nz]->ChemEn[nelem][ion] = 0.f;
03845 }
03846 }
03847 }
03848 }
03849 return;
03850 }
03851
03852 STATIC void GetPotValues(long nd,
03853 long Zg,
03854 double *ThresInf,
03855 double *ThresInfVal,
03856 double *ThresSurf,
03857 double *ThresSurfVal,
03858 double *PotSurf,
03859 double *Emin,
03860 bool lgUseTunnelCorr)
03861 {
03862 double dstpot,
03863 dZg = (double)Zg,
03864 IP_v;
03865
03866 DEBUG_ENTRY( "GetPotValues()" );
03867
03868
03869
03870
03871
03872
03873 dstpot = chrg2pot(dZg,nd);
03874
03875
03876
03877
03878 IP_v = gv.bin[nd]->DustWorkFcn + dstpot - 0.5*one_elec(nd) + (dZg+2.)*AC0/gv.bin[nd]->AvRadius*one_elec(nd);
03879
03880
03881
03882
03883
03884 if( Zg <= -1 )
03885 {
03886 pot_type pcase = gv.which_pot[gv.bin[nd]->matType];
03887 double IP;
03888
03889 IP = gv.bin[nd]->DustWorkFcn - gv.bin[nd]->BandGap + dstpot - 0.5*one_elec(nd);
03890 switch( pcase )
03891 {
03892 case POT_CAR:
03893 IP -= AC1G/(gv.bin[nd]->AvRadius+AC2G)*one_elec(nd);
03894 break;
03895 case POT_SIL:
03896
03897 break;
03898 default:
03899 fprintf( ioQQQ, " GetPotValues detected unknown type for ionization pot: %d\n" , pcase );
03900 cdEXIT(EXIT_FAILURE);
03901 }
03902
03903
03904
03905 IP_v = MAX2(IP,IP_v);
03906
03907 if( Zg < -1 )
03908 {
03909
03910 double help = fabs(dZg+1);
03911
03912 *Emin = -ThetaNu(help)*one_elec(nd);
03913 if( lgUseTunnelCorr )
03914 {
03915
03916 *Emin *= 1. - 2.124e-4/(pow(gv.bin[nd]->AvRadius,(realnum)0.45)*pow(help,0.26));
03917 }
03918 }
03919 else
03920 {
03921 *Emin = 0.;
03922 }
03923
03924 *ThresInf = IP - *Emin;
03925 *ThresInfVal = IP_v - *Emin;
03926 *ThresSurf = *ThresInf;
03927 *ThresSurfVal = *ThresInfVal;
03928 *PotSurf = *Emin;
03929 }
03930 else
03931 {
03932 *ThresInf = IP_v;
03933 *ThresInfVal = IP_v;
03934 *ThresSurf = *ThresInf - dstpot;
03935 *ThresSurfVal = *ThresInfVal - dstpot;
03936 *PotSurf = dstpot;
03937 *Emin = 0.;
03938 }
03939 return;
03940 }
03941
03942
03943
03944
03945
03946 STATIC void GrainIonColl(long int nd,
03947 long int nz,
03948 long int nelem,
03949 long int ion,
03950 const double phi_s_up[],
03951 const double phi_s_dn[],
03952 long *Z0,
03953 realnum *ChEn,
03954 realnum *ChemEn)
03955 {
03956 long Zg;
03957 double d[5];
03958 double phi_s;
03959
03960 long save = ion;
03961
03962 DEBUG_ENTRY( "GrainIonColl()" );
03963 if( ion > 0 && rfield.anu[Heavy.ipHeavy[nelem][ion-1]-1] > (realnum)phi_s_up[0] )
03964 {
03965
03966 *ChEn = 0.f;
03967 *ChemEn = 0.f;
03968 Zg = gv.bin[nd]->chrg[nz]->DustZ;
03969 phi_s = phi_s_up[0];
03970 do
03971 {
03972 *ChEn += rfield.anu[Heavy.ipHeavy[nelem][ion-1]-1] - (realnum)phi_s;
03973 *ChemEn += rfield.anu[Heavy.ipHeavy[nelem][ion-1]-1];
03974
03975
03976
03977 *ChemEn -= (realnum)(phi_s - phi_s_up[0]);
03978 --ion;
03979 ++Zg;
03980 phi_s = phi_s_up[save-ion];
03981 } while( ion > 0 && rfield.anu[Heavy.ipHeavy[nelem][ion-1]-1] > (realnum)phi_s );
03982
03983 *Z0 = ion;
03984 }
03985 else if( ion <= nelem && gv.bin[nd]->chrg[nz]->DustZ > gv.bin[nd]->LowestZg &&
03986 rfield.anu[Heavy.ipHeavy[nelem][ion]-1] < (realnum)phi_s_dn[0] )
03987 {
03988
03989 *ChEn = 0.f;
03990 *ChemEn = 0.f;
03991 Zg = gv.bin[nd]->chrg[nz]->DustZ;
03992 phi_s = phi_s_dn[0];
03993 do
03994 {
03995 *ChEn += (realnum)phi_s - rfield.anu[Heavy.ipHeavy[nelem][ion]-1];
03996 *ChemEn -= rfield.anu[Heavy.ipHeavy[nelem][ion]-1];
03997
03998
03999
04000 *ChemEn += (realnum)(phi_s - phi_s_dn[0]);
04001 ++ion;
04002 --Zg;
04003
04004 if( ion-save < 2 )
04005 phi_s = phi_s_dn[ion-save];
04006 else
04007 GetPotValues(nd,Zg-1,&d[0],&d[1],&phi_s,&d[2],&d[3],&d[4],NO_TUNNEL);
04008
04009 } while( ion <= nelem && Zg > gv.bin[nd]->LowestZg &&
04010 rfield.anu[Heavy.ipHeavy[nelem][ion]-1] < (realnum)phi_s );
04011 *Z0 = ion;
04012 }
04013 else
04014 {
04015
04016 *ChEn = 0.f;
04017 *ChemEn = 0.f;
04018 *Z0 = ion;
04019 }
04020
04021 return;
04022 }
04023
04024
04025
04026 STATIC void GrainChrgTransferRates(long nd)
04027 {
04028 long nz;
04029 double fac0 = STICK_ION*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3;
04030
04031 DEBUG_ENTRY( "GrainChrgTransferRates()" );
04032
04033 # ifndef IGNORE_GRAIN_ION_COLLISIONS
04034
04035 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
04036 {
04037 long ion;
04038 ChargeBin *gptr = gv.bin[nd]->chrg[nz];
04039 double fac1 = gptr->FracPop*fac0;
04040
04041 if( fac1 == 0. )
04042 continue;
04043
04044 for( ion=0; ion <= LIMELM; ion++ )
04045 {
04046 long nelem;
04047 double eta, fac2, xi;
04048
04049
04050
04051 GrainScreen(ion,nd,nz,&eta,&xi);
04052
04053 fac2 = eta*fac1;
04054
04055 if( fac2 == 0. )
04056 continue;
04057
04058 for( nelem=MAX2(0,ion-1); nelem < LIMELM; nelem++ )
04059 {
04060 if( dense.lgElmtOn[nelem] && ion != gptr->RecomZ0[nelem][ion] )
04061 {
04062 gv.GrainChTrRate[nelem][ion][gptr->RecomZ0[nelem][ion]] +=
04063 (realnum)(fac2*DoppVel.AveVel[nelem]);
04064 }
04065 }
04066 }
04067 }
04068 # endif
04069 return;
04070 }
04071
04072
04073
04074
04075 STATIC void GrainUpdateRadius1(void)
04076 {
04077 long nelem,
04078 nd;
04079
04080 DEBUG_ENTRY( "GrainUpdateRadius1()" );
04081
04082 for( nelem=0; nelem < LIMELM; nelem++ )
04083 {
04084 gv.elmSumAbund[nelem] = 0.f;
04085 }
04086
04087
04088 for( nd=0; nd < gv.nBin; nd++ )
04089 {
04090 gv.bin[nd]->GrnVryDpth = (realnum)GrnVryDpth(nd);
04091 gv.bin[nd]->dstAbund = (realnum)(gv.bin[nd]->dstfactor*gv.GrainMetal*
04092 gv.bin[nd]->GrnVryDpth);
04093 ASSERT( gv.bin[nd]->dstAbund > 0.f );
04094
04095
04096 gv.bin[nd]->cnv_H_pCM3 = dense.gas_phase[ipHYDROGEN]*gv.bin[nd]->dstAbund;
04097 gv.bin[nd]->cnv_CM3_pH = 1./gv.bin[nd]->cnv_H_pCM3;
04098
04099 gv.bin[nd]->cnv_CM3_pGR = gv.bin[nd]->cnv_H_pGR/gv.bin[nd]->cnv_H_pCM3;
04100 gv.bin[nd]->cnv_GR_pCM3 = 1./gv.bin[nd]->cnv_CM3_pGR;
04101
04102
04103
04104
04105 for( nelem=0; nelem < LIMELM; nelem++ )
04106 {
04107 gv.elmSumAbund[nelem] += gv.bin[nd]->elmAbund[nelem]*(realnum)gv.bin[nd]->cnv_H_pCM3;
04108 }
04109 }
04110 return;
04111 }
04112
04113
04114
04115
04116 STATIC void GrainUpdateRadius2(bool lgAnyNegCharge)
04117 {
04118 bool lgChangeCS_PDT;
04119 long i,
04120 nd,
04121 nz;
04122
04123 DEBUG_ENTRY( "GrainUpdateRadius2()" );
04124
04125
04126 lgChangeCS_PDT = gv.lgAnyNegCharge || lgAnyNegCharge;
04127
04128 if( rfield.nflux > gv.nfill || ( gv.lgAnyDustVary && nzone != gv.nzone ) || lgChangeCS_PDT )
04129 {
04130
04131 gv.nfill = rfield.nflux;
04132 gv.lgAnyNegCharge = lgAnyNegCharge;
04133
04134 for( i=0; i < rfield.nupper; i++ )
04135 {
04136 gv.dstab[i] = 0.;
04137 gv.dstsc[i] = 0.;
04138 }
04139
04140
04141 for( nd=0; nd < gv.nBin; nd++ )
04142 {
04143
04144 for( i=0; i < rfield.nflux; i++ )
04145 {
04146
04147
04148
04149
04150
04151
04152 gv.dstab[i] += gv.bin[nd]->dstab1[i]*gv.bin[nd]->dstAbund;
04153 gv.dstsc[i] += gv.bin[nd]->pure_sc1[i]*gv.bin[nd]->asym[i]*gv.bin[nd]->dstAbund;
04154
04155
04156
04157
04158
04159 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
04160 {
04161 ChargeBin *gptr = gv.bin[nd]->chrg[nz];
04162 long ipLo = gptr->ipThresInf;
04163
04164
04165 if( gptr->DustZ <= -1 && i >= ipLo )
04166 gv.dstab[i] +=
04167 gptr->FracPop*gptr->cs_pdt[i]*gv.bin[nd]->dstAbund;
04168 }
04169 }
04170 }
04171 for( i=0; i < rfield.nflux; i++ )
04172 {
04173
04174 ASSERT( gv.dstab[i] > 0. && gv.dstsc[i] > 0. );
04175 }
04176 }
04177 return;
04178 }
04179
04180
04181
04182 STATIC void GrainTemperature(long int nd,
04183 realnum *dccool,
04184 double *hcon,
04185 double *hots,
04186 double *hla)
04187 {
04188 long int i,
04189 ipLya,
04190 nz;
04191 double EhatThermionic,
04192 norm,
04193 rate,
04194 x,
04195 y;
04196 realnum dcheat;
04197
04198 DEBUG_ENTRY( "GrainTemperature()" );
04199
04200
04201 ASSERT( nd >= 0 && nd < gv.nBin );
04202
04203 if( trace.lgTrace && trace.lgDustBug )
04204 {
04205 fprintf( ioQQQ, " GrainTemperature starts for grain %s\n", gv.bin[nd]->chDstLab );
04206 }
04207
04208
04209
04210
04211
04212 *hcon = 0.;
04213
04214 *hots = 0.;
04215
04216 *hla = 0.;
04217
04218 ipLya = Transitions[ipH_LIKE][ipHYDROGEN][ipH2p][ipH1s].ipCont - 1;
04219
04220
04221
04222 gv.bin[nd]->GasHeatPhotoEl = 0.;
04223
04224 gv.bin[nd]->GrainCoolTherm = 0.;
04225 gv.bin[nd]->thermionic = 0.;
04226
04227 dcheat = 0.f;
04228 *dccool = 0.f;
04229
04230 gv.bin[nd]->BolFlux = 0.;
04231
04232
04233
04234 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
04235 {
04236 ChargeBin *gptr = gv.bin[nd]->chrg[nz];
04237
04238 double hcon1 = 0.;
04239 double hots1 = 0.;
04240 double hla1 = 0.;
04241 double bolflux1 = 0.;
04242 double pe1 = 0.;
04243
04244
04245 bool lgReEvaluate1 = gptr->hcon1 < 0.;
04246 bool lgReEvaluate2 = gptr->hots1 < 0.;
04247 bool lgReEvaluate = lgReEvaluate1 || lgReEvaluate2;
04248
04249
04250 if( lgReEvaluate )
04251 {
04252 long loopmax = MIN2(gptr->ipThresInf,rfield.nflux);
04253 for( i=0; i < loopmax; i++ )
04254 {
04255 double fac = gv.bin[nd]->dstab1[i]*rfield.anu[i];
04256
04257 if( lgReEvaluate1 )
04258 hcon1 += rfield.flux[i]*fac;
04259
04260 if( lgReEvaluate2 )
04261 {
04262 hots1 += rfield.SummedDif[i]*fac;
04263 # ifndef NDEBUG
04264 bolflux1 += rfield.SummedCon[i]*fac;
04265 # endif
04266 }
04267 }
04268 }
04269
04270
04271
04272
04273
04274
04275
04276 if( lgReEvaluate1 )
04277 {
04278 for( i=gptr->ipThresInf; i < rfield.nflux; i++ )
04279 {
04280 hcon1 += rfield.flux[i]*gptr->fac1[i];
04281 }
04282
04283 gptr->hcon1 = hcon1;
04284 }
04285 else
04286 {
04287 hcon1 = gptr->hcon1;
04288 }
04289
04290 if( lgReEvaluate2 )
04291 {
04292 for( i=gptr->ipThresInf; i < rfield.nflux; i++ )
04293 {
04294
04295
04296 hots1 += rfield.SummedDif[i]*gptr->fac1[i];
04297
04298 #ifdef WD_TEST2
04299 pe1 += rfield.flux[i]*gptr->fac2[i];
04300 #else
04301 pe1 += rfield.SummedCon[i]*gptr->fac2[i];
04302 #endif
04303 # ifndef NDEBUG
04304 bolflux1 += rfield.SummedCon[i]*gv.bin[nd]->dstab1[i]*rfield.anu[i];
04305 if( gptr->DustZ <= -1 )
04306 bolflux1 += rfield.SummedCon[i]*gptr->cs_pdt[i]*rfield.anu[i];
04307 # endif
04308 }
04309 gptr->hots1 = hots1;
04310 gptr->bolflux1 = bolflux1;
04311 gptr->pe1 = pe1;
04312 }
04313 else
04314 {
04315 hots1 = gptr->hots1;
04316 bolflux1 = gptr->bolflux1;
04317 pe1 = gptr->pe1;
04318 }
04319
04320
04321
04322
04323
04324 if( ipLya < MIN2(gptr->ipThresInf,rfield.nflux) )
04325 {
04326 hla1 = rfield.otslin[ipLya]*gv.bin[nd]->dstab1[ipLya]*0.75;
04327 }
04328 else if( ipLya < rfield.nflux )
04329 {
04330
04331 hla1 = rfield.otslin[ipLya]*gptr->fac1[ipLya];
04332 }
04333 else
04334 {
04335 hla1 = 0.;
04336 }
04337
04338 ASSERT( hcon1 >= 0. && hots1 >= 0. && hla1 >= 0. && bolflux1 >= 0. && pe1 >= 0. );
04339
04340 *hcon += gptr->FracPop*hcon1;
04341 *hots += gptr->FracPop*hots1;
04342 *hla += gptr->FracPop*hla1;
04343 gv.bin[nd]->BolFlux += gptr->FracPop*bolflux1;
04344 if( gv.lgDHetOn )
04345 gv.bin[nd]->GasHeatPhotoEl += gptr->FracPop*pe1;
04346
04347 # ifndef NDEBUG
04348 if( trace.lgTrace && trace.lgDustBug )
04349 {
04350 fprintf( ioQQQ, " Zg %ld bolflux: %.4e\n", gptr->DustZ,
04351 gptr->FracPop*bolflux1*EN1RYD*gv.bin[nd]->cnv_H_pCM3 );
04352 }
04353 # endif
04354
04355
04356
04357
04358
04359
04360
04361 rate = gptr->FracPop*gptr->ThermRate*gv.bin[nd]->IntArea*gv.bin[nd]->cnv_H_pCM3;
04362
04363 EhatThermionic = 2.*BOLTZMANN*gv.bin[nd]->tedust + MAX2(gptr->PotSurf*EN1RYD,0.);
04364 gv.bin[nd]->GrainCoolTherm += rate * (EhatThermionic + gptr->ThresSurf*EN1RYD);
04365 gv.bin[nd]->thermionic += rate * (EhatThermionic - gptr->PotSurf*EN1RYD);
04366 }
04367
04368
04369 norm = EN1RYD*gv.bin[nd]->cnv_H_pCM3;
04370
04371
04372 *hcon *= norm;
04373
04374
04375 *hots *= norm;
04376
04377
04378 *hla *= norm;
04379
04380 gv.bin[nd]->BolFlux *= norm;
04381
04382
04383
04384
04385
04386
04387
04388 GrainCollHeating(nd,&dcheat,dccool);
04389
04390
04391 gv.bin[nd]->GasHeatPhotoEl *= norm;
04392
04393 if( gv.lgBakesPAH_heat )
04394 {
04395
04396
04397
04398
04399
04400 gv.bin[nd]->GasHeatPhotoEl = 1.e-24*hmi.UV_Cont_rel2_Habing_TH85_depth*
04401 dense.gas_phase[ipHYDROGEN]*(4.87e-2/(1.0+4e-3*pow((hmi.UV_Cont_rel2_Habing_TH85_depth*
04402
04403 phycon.sqrte/dense.eden),0.73)) + 3.65e-2*pow(phycon.te/1.e4,0.7)/
04404 (1.+2.e-4*(hmi.UV_Cont_rel2_Habing_TH85_depth*phycon.sqrte/dense.eden)))/gv.nBin;
04405
04406 }
04407
04408
04409
04410 gv.bin[nd]->GasHeatPhotoEl *= gv.GrainHeatScaleFactor;
04411
04412
04413
04414
04415
04416
04417
04418
04419
04420
04421
04422
04423
04424
04425
04426
04427 gv.bin[nd]->GrainHeat = *hcon + *hots + dcheat - gv.bin[nd]->GrainCoolTherm;
04428
04429
04430 gv.bin[nd]->GrainHeatColl = dcheat;
04431
04432
04433
04434
04435
04436 if( gv.bin[nd]->GrainHeat > 0. )
04437 {
04438 bool lgOutOfBounds;
04439
04440
04441 x = log(MAX2(DBL_MIN,gv.bin[nd]->GrainHeat*gv.bin[nd]->cnv_CM3_pH));
04442
04443 splint_safe(gv.bin[nd]->dstems,gv.dsttmp,gv.bin[nd]->dstslp,NDEMS,x,&y,&lgOutOfBounds);
04444 gv.bin[nd]->tedust = (realnum)exp(y);
04445 }
04446 else
04447 {
04448 gv.bin[nd]->GrainHeat = -1.;
04449 gv.bin[nd]->tedust = -1.;
04450 }
04451
04452 if( thermal.ConstGrainTemp > 0. )
04453 {
04454 bool lgOutOfBounds;
04455
04456 gv.bin[nd]->tedust = thermal.ConstGrainTemp;
04457
04458 x = log(gv.bin[nd]->tedust);
04459 splint_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,x,&y,&lgOutOfBounds);
04460 gv.bin[nd]->GrainHeat = exp(y)*gv.bin[nd]->cnv_H_pCM3;
04461 }
04462
04463
04464 gv.bin[nd]->TeGrainMax = (realnum)MAX2(gv.bin[nd]->TeGrainMax,gv.bin[nd]->tedust);
04465
04466 if( trace.lgTrace && trace.lgDustBug )
04467 {
04468 fprintf( ioQQQ, " >GrainTemperature finds %s Tdst %.5e hcon %.4e ",
04469 gv.bin[nd]->chDstLab, gv.bin[nd]->tedust, *hcon);
04470 fprintf( ioQQQ, "hots %.4e dcheat %.4e GrainCoolTherm %.4e\n",
04471 *hots, dcheat, gv.bin[nd]->GrainCoolTherm );
04472 }
04473 return;
04474 }
04475
04476
04477
04478 STATIC void PE_init(long nd,
04479 long nz,
04480 long i,
04481 double *cs1,
04482 double *cs2,
04483 double *cs_tot,
04484 double *cool1,
04485 double *cool2,
04486 double *ehat1,
04487 double *ehat2)
04488 {
04489 ChargeBin *gptr = gv.bin[nd]->chrg[nz];
04490 long ipLo1 = gptr->ipThresInfVal;
04491 long ipLo2 = gptr->ipThresInf;
04492
04493 DEBUG_ENTRY( "PE_init()" );
04494
04495
04496 ASSERT( nd >= 0 && nd < gv.nBin );
04497 ASSERT( nz >= 0 && nz < gv.bin[nd]->nChrg );
04498 ASSERT( i >= 0 && i < rfield.nflux );
04499
04502
04503 if( i >= ipLo1 )
04504 {
04505
04506 *cs1 = gv.bin[nd]->dstab1[i]*gptr->yhat[i];
04507
04508
04509 *ehat1 = gptr->ehat[i];
04510
04511
04512
04513
04514
04515
04516
04517
04518
04519
04520
04521
04522
04523
04524
04525
04526
04527
04528
04529
04530 if( gptr->DustZ <= -1 )
04531 *cool1 = gptr->ThresSurf + gptr->PotSurf + *ehat1;
04532 else
04533 *cool1 = gptr->ThresSurfVal + gptr->PotSurf + *ehat1;
04534
04535 ASSERT( *ehat1 > 0. && *cool1 > 0. );
04536 }
04537 else
04538 {
04539 *cs1 = 0.;
04540 *ehat1 = 0.;
04541 *cool1 = 0.;
04542 }
04543
04544
04545 if( gptr->DustZ <= -1 && i >= ipLo2 )
04546 {
04547
04548 *cs2 = gptr->cs_pdt[i];
04549
04550 *ehat2 = rfield.anu[i] - gptr->ThresSurf - gptr->PotSurf;
04551
04552 *cool2 = rfield.anu[i];
04553
04554 ASSERT( *ehat2 > 0. && *cool2 > 0. );
04555 }
04556 else
04557 {
04558 *cs2 = 0.;
04559 *ehat2 = 0.;
04560 *cool2 = 0.;
04561 }
04562
04563 *cs_tot = gv.bin[nd]->dstab1[i] + *cs2;
04564 return;
04565 }
04566
04567
04568
04569 STATIC void GrainCollHeating(long int nd,
04570 realnum *dcheat,
04571 realnum *dccool)
04572 {
04573 long int ion,
04574 nelem,
04575 nz;
04576 H2_type ipH2;
04577 double Accommodation,
04578 CollisionRateElectr,
04579 CollisionRateMol,
04580 CollisionRateIon,
04581 CoolTot,
04582 CoolBounce,
04583 CoolEmitted,
04584 CoolElectrons,
04585 CoolMolecules,
04586 CoolPotential,
04587 CoolPotentialGas,
04588 eta,
04589 HeatTot,
04590 HeatBounce,
04591 HeatCollisions,
04592 HeatElectrons,
04593 HeatIons,
04594 HeatMolecules,
04595 HeatRecombination,
04596 HeatChem,
04597 HeatCor,
04598 Stick,
04599 ve,
04600 WeightMol,
04601 xi;
04602
04603
04604
04605 const double H2_FORMATION_GRAIN_HEATING[H2_TOP] = { 0.20, 0.4, 1.72 };
04606
04607 DEBUG_ENTRY( "GrainCollHeating()" );
04608
04609
04610
04611
04612
04613
04614
04615
04616
04617
04618
04619
04620 HeatTot = 0.;
04621 CoolTot = 0.;
04622
04623 HeatIons = 0.;
04624
04625 gv.bin[nd]->ChemEn = 0.;
04626
04627
04628 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
04629 {
04630 ChargeBin *gptr = gv.bin[nd]->chrg[nz];
04631
04632
04633
04634 double Heat1 = 0.;
04635 double Cool1 = 0.;
04636 double ChemEn1 = 0.;
04637
04638
04639
04640
04641
04642 for( ion=0; ion <= LIMELM; ion++ )
04643 {
04644
04645
04646
04647
04648
04649
04650
04651 CollisionRateIon = 0.;
04652 CoolPotential = 0.;
04653 CoolPotentialGas = 0.;
04654 HeatRecombination = 0.;
04655 HeatChem = 0.;
04656
04657
04658
04659 GrainScreen(ion,nd,nz,&eta,&xi);
04660
04661 for( nelem=MAX2(0,ion-1); nelem < LIMELM; nelem++ )
04662 {
04663 if( dense.lgElmtOn[nelem] && dense.xIonDense[nelem][ion] > 0. )
04664 {
04665 double CollisionRateOne;
04666
04667
04668
04669
04670 #if defined( IGNORE_GRAIN_ION_COLLISIONS )
04671 Stick = 0.;
04672 #elif defined( WD_TEST2 )
04673 Stick = ( ion == gptr->RecomZ0[nelem][ion] ) ?
04674 0. : STICK_ION;
04675 #else
04676 Stick = ( ion == gptr->RecomZ0[nelem][ion] ) ?
04677 gv.bin[nd]->AccomCoef[nelem] : STICK_ION;
04678 #endif
04679
04680
04681
04682 CollisionRateOne = Stick*dense.xIonDense[nelem][ion]*DoppVel.AveVel[nelem];
04683 CollisionRateIon += CollisionRateOne;
04684
04685
04686
04687
04688
04689
04690
04691
04692 if( ion >= gptr->RecomZ0[nelem][ion] )
04693 {
04694 CoolPotential += CollisionRateOne * (double)ion *
04695 gptr->PotSurf;
04696 CoolPotentialGas += CollisionRateOne *
04697 (double)gptr->RecomZ0[nelem][ion] *
04698 gptr->PotSurf;
04699 }
04700 else
04701 {
04702 CoolPotential += CollisionRateOne * (double)ion *
04703 gptr->PotSurfInc;
04704 CoolPotentialGas += CollisionRateOne *
04705 (double)gptr->RecomZ0[nelem][ion] *
04706 gptr->PotSurfInc;
04707 }
04708
04709
04710
04711
04712 HeatRecombination += CollisionRateOne *
04713 gptr->RecomEn[nelem][ion];
04714 HeatChem += CollisionRateOne * gptr->ChemEn[nelem][ion];
04715 }
04716 }
04717
04718
04719
04720
04721
04722
04723 HeatCollisions = CollisionRateIon * 2.*BOLTZMANN*phycon.te*xi;
04724
04725
04726
04727
04728 CoolPotential *= eta*EN1RYD;
04729 CoolPotentialGas *= eta*EN1RYD;
04730
04731 HeatRecombination *= eta*EN1RYD;
04732 HeatChem *= eta*EN1RYD;
04733
04734 CoolEmitted = CollisionRateIon * 2.*BOLTZMANN*gv.bin[nd]->tedust*eta;
04735
04736
04737 Heat1 += HeatCollisions - CoolPotential + HeatRecombination - CoolEmitted;
04738
04739
04740
04741
04742 Cool1 += HeatCollisions - CoolEmitted - CoolPotentialGas;
04743
04744 ChemEn1 += HeatChem;
04745 }
04746
04747
04748 HeatIons += gptr->FracPop*Heat1;
04749
04750 if( trace.lgTrace && trace.lgDustBug )
04751 {
04752 fprintf( ioQQQ, " Zg %ld ions heat/cool: %.4e %.4e\n", gptr->DustZ,
04753 gptr->FracPop*Heat1*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3,
04754 gptr->FracPop*Cool1*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3 );
04755 }
04756
04757
04758
04759
04760 ion = -1;
04761 Stick = ( gptr->DustZ <= -1 ) ? gv.bin[nd]->StickElecNeg : gv.bin[nd]->StickElecPos;
04762
04763
04764 ve = sqrt(8.*BOLTZMANN/PI/ELECTRON_MASS*phycon.te);
04765
04766
04767 CollisionRateElectr = Stick*dense.eden*ve;
04768
04769
04770
04771 GrainScreen(ion,nd,nz,&eta,&xi);
04772
04773 if( gptr->DustZ > gv.bin[nd]->LowestZg )
04774 {
04775 HeatCollisions = CollisionRateElectr*2.*BOLTZMANN*phycon.te*xi;
04776
04777
04778 CoolPotential = CollisionRateElectr * (double)ion*gptr->PotSurfInc*eta*EN1RYD;
04779
04780 HeatRecombination = CollisionRateElectr * gptr->ThresSurfInc*eta*EN1RYD;
04781 HeatBounce = 0.;
04782 CoolBounce = 0.;
04783 }
04784 else
04785 {
04786 HeatCollisions = 0.;
04787 CoolPotential = 0.;
04788 HeatRecombination = 0.;
04789
04790
04791
04792
04793 HeatBounce = CollisionRateElectr * 2.*BOLTZMANN*phycon.te*xi;
04794
04795
04796 CoolBounce = CollisionRateElectr *
04797 (-gptr->ThresSurfInc-gptr->PotSurfInc)*EN1RYD*eta;
04798 CoolBounce = MAX2(CoolBounce,0.);
04799 }
04800
04801
04802
04803 HeatElectrons = HeatCollisions-CoolPotential+HeatRecombination+HeatBounce-CoolBounce;
04804 Heat1 += HeatElectrons;
04805
04806 CoolElectrons = HeatCollisions+HeatBounce-CoolBounce;
04807 Cool1 += CoolElectrons;
04808
04809 if( trace.lgTrace && trace.lgDustBug )
04810 {
04811 fprintf( ioQQQ, " Zg %ld electrons heat/cool: %.4e %.4e\n", gptr->DustZ,
04812 gptr->FracPop*HeatElectrons*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3,
04813 gptr->FracPop*CoolElectrons*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3 );
04814 }
04815
04816
04817
04818
04819
04820
04821
04822
04823
04824
04825
04826
04827 gptr->HeatingRate2 = HeatElectrons*gv.bin[nd]->IntArea/4. -
04828 gv.bin[nd]->GrainCoolTherm*gv.bin[nd]->cnv_CM3_pH;
04829
04830
04831
04832
04833
04834 HeatTot += gptr->FracPop*Heat1;
04835
04836
04837 CoolTot += gptr->FracPop*Cool1;
04838
04839 gv.bin[nd]->ChemEn += gptr->FracPop*ChemEn1;
04840 }
04841
04842
04843
04844
04845
04846
04847
04848
04849 WeightMol = 2.*dense.AtomicWeight[ipHYDROGEN];
04850 Accommodation = 2.*gv.bin[nd]->atomWeight*WeightMol/POW2(gv.bin[nd]->atomWeight+WeightMol);
04851
04852 #ifndef IGNORE_GRAIN_ION_COLLISIONS
04853
04854 CollisionRateMol = Accommodation*hmi.H2_total*
04855 sqrt(8.*BOLTZMANN/PI/ATOMIC_MASS_UNIT/WeightMol*phycon.te);
04856
04857
04858 ipH2 = gv.which_H2distr[gv.bin[nd]->matType];
04859
04860
04861 gv.bin[nd]->ChemEnH2 = gv.bin[nd]->rate_h2_form_grains_used*dense.xIonDense[ipHYDROGEN][0]*
04862 H2_FORMATION_GRAIN_HEATING[ipH2]*EN1EV;
04863
04864 gv.bin[nd]->ChemEnH2 /= gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3;
04865 #else
04866 CollisionRateMol = 0.;
04867 gv.bin[nd]->ChemEnH2 = 0.;
04868 #endif
04869
04870
04871 WeightMol = dense.AtomicWeight[ipCARBON] + dense.AtomicWeight[ipOXYGEN];
04872 Accommodation = 2.*gv.bin[nd]->atomWeight*WeightMol/POW2(gv.bin[nd]->atomWeight+WeightMol);
04873 #ifndef IGNORE_GRAIN_ION_COLLISIONS
04874 CollisionRateMol += Accommodation*findspecies("CO")->hevmol*
04875 sqrt(8.*BOLTZMANN/PI/ATOMIC_MASS_UNIT/WeightMol*phycon.te);
04876 #else
04877 CollisionRateMol = 0.;
04878 #endif
04879
04880
04881 HeatCollisions = CollisionRateMol * 2.*BOLTZMANN*phycon.te;
04882 CoolEmitted = CollisionRateMol * 2.*BOLTZMANN*gv.bin[nd]->tedust;
04883
04884 HeatMolecules = HeatCollisions - CoolEmitted + gv.bin[nd]->ChemEnH2;
04885 HeatTot += HeatMolecules;
04886
04887
04888 CoolMolecules = HeatCollisions - CoolEmitted;
04889 CoolTot += CoolMolecules;
04890
04891 gv.bin[nd]->RateUp = 0.;
04892 gv.bin[nd]->RateDn = 0.;
04893 HeatCor = 0.;
04894 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
04895 {
04896 double d[4];
04897 double rate_dn = GrainElecRecomb1(nd,nz,&d[0],&d[1]);
04898 double rate_up = GrainElecEmis1(nd,nz,&d[0],&d[1],&d[2],&d[3]);
04899
04900 gv.bin[nd]->RateUp += gv.bin[nd]->chrg[nz]->FracPop*rate_up;
04901 gv.bin[nd]->RateDn += gv.bin[nd]->chrg[nz]->FracPop*rate_dn;
04902
04904 HeatCor += (gv.bin[nd]->chrg[nz]->FracPop*rate_up*gv.bin[nd]->chrg[nz]->ThresSurf -
04905 gv.bin[nd]->chrg[nz]->FracPop*rate_dn*gv.bin[nd]->chrg[nz]->ThresSurfInc +
04906 gv.bin[nd]->chrg[nz]->FracPop*rate_up*gv.bin[nd]->chrg[nz]->PotSurf -
04907 gv.bin[nd]->chrg[nz]->FracPop*rate_dn*gv.bin[nd]->chrg[nz]->PotSurfInc)*EN1RYD;
04908 }
04909
04910
04911 HeatTot += HeatCor;
04912
04913 if( trace.lgTrace && trace.lgDustBug )
04914 {
04915 fprintf( ioQQQ, " molecules heat/cool: %.4e %.4e heatcor: %.4e\n",
04916 HeatMolecules*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3,
04917 CoolMolecules*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3,
04918 HeatCor*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3 );
04919 }
04920
04921 *dcheat = (realnum)(HeatTot*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3);
04922 *dccool = ( gv.lgDColOn ) ? (realnum)(CoolTot*gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3) : 0.f;
04923
04924 gv.bin[nd]->ChemEn *= gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3;
04925 gv.bin[nd]->ChemEnH2 *= gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3;
04926
04927
04928
04929
04930
04931
04932
04933
04934
04935
04936
04937
04938
04939
04940
04941
04942 gv.bin[nd]->HeatingRate1 = (HeatMolecules+HeatIons+HeatCor)*gv.bin[nd]->IntArea/4.;
04943
04944
04945 return;
04946 }
04947
04948
04949
04950 void GrainDrift(void)
04951 {
04952 long int i,
04953 loop,
04954 nd;
04955 realnum *help;
04956 double alam,
04957 corr,
04958 dmomen,
04959 fac,
04960 fdrag,
04961 g0,
04962 g2,
04963 phi2lm,
04964 psi,
04965 rdust,
04966 si,
04967 vdold,
04968 volmom;
04969
04970 DEBUG_ENTRY( "GrainDrift()" );
04971
04972
04973 help = (realnum*)MALLOC((size_t)((unsigned)rfield.nflux*sizeof(realnum)));
04974 for( i=0; i < rfield.nflux; i++ )
04975 {
04976 help[i] = (rfield.flux[i]+rfield.ConInterOut[i]+rfield.outlin[i]+rfield.outlin_noplot[i])*
04977 rfield.anu[i];
04978 }
04979
04980 for( nd=0; nd < gv.nBin; nd++ )
04981 {
04982
04983 dmomen = 0.;
04984 for( i=0; i < rfield.nflux; i++ )
04985 {
04986
04987 dmomen += help[i]*(gv.bin[nd]->dstab1[i] + gv.bin[nd]->pure_sc1[i]*gv.bin[nd]->asym[i]);
04988 }
04989 ASSERT( dmomen >= 0. );
04990 dmomen *= EN1RYD*4./gv.bin[nd]->IntArea;
04991
04992
04993 fac = 2*BOLTZMANN*phycon.te;
04994
04995
04996
04997 psi = gv.bin[nd]->dstpot*TE1RYD/phycon.te;
04998 if( psi > 0. )
04999 {
05000 rdust = 1.e-6;
05001 alam = log(20.702/rdust/psi*phycon.sqrte/dense.SqrtEden);
05002 }
05003 else
05004 {
05005 alam = 0.;
05006 }
05007
05008 phi2lm = POW2(psi)*alam;
05009 corr = 2.;
05010
05011 for( loop = 0; loop < 50 && fabs(corr-1.) > 0.001; loop++ )
05012 {
05013 vdold = gv.bin[nd]->DustDftVel;
05014
05015
05016 si = gv.bin[nd]->DustDftVel/phycon.sqrte*7.755e-5;
05017 g0 = 1.5045*si*sqrt(1.+0.4418*si*si);
05018 g2 = si/(1.329 + POW3(si));
05019
05020
05021
05022 fdrag = fac*dense.xIonDense[ipHYDROGEN][1]*(g0 + phi2lm*g2);
05023
05024
05025 si = gv.bin[nd]->DustDftVel/phycon.sqrte*1.816e-6;
05026 g0 = 1.5045*si*sqrt(1.+0.4418*si*si);
05027 g2 = si/(1.329 + POW3(si));
05028 fdrag += fac*dense.eden*(g0 + phi2lm*g2);
05029
05030
05031 si = gv.bin[nd]->DustDftVel/phycon.sqrte*7.755e-5;
05032 g0 = 1.5045*si*sqrt(1.+0.4418*si*si);
05033 fdrag += fac*(dense.xIonDense[ipHYDROGEN][0] + 1.1*dense.xIonDense[ipHELIUM][0])*g0;
05034
05035
05036 si = gv.bin[nd]->DustDftVel/phycon.sqrte*1.551e-4;
05037 g0 = 1.5045*si*sqrt(1.+0.4418*si*si);
05038 g2 = si/(1.329 + POW3(si));
05039 fdrag += fac*dense.xIonDense[ipHELIUM][1]*(g0 + phi2lm*g2);
05040
05041
05042
05043
05044 volmom = dmomen/SPEEDLIGHT;
05045
05046 if( fdrag > 0. )
05047 {
05048 corr = sqrt(volmom/fdrag);
05049 gv.bin[nd]->DustDftVel = (realnum)(vdold*corr);
05050 }
05051 else
05052 {
05053 corr = 1.;
05054 gv.lgNegGrnDrg = true;
05055 gv.bin[nd]->DustDftVel = 0.;
05056 }
05057
05058 if( trace.lgTrace && trace.lgDustBug )
05059 {
05060 fprintf( ioQQQ, " %2ld new drift velocity:%10.2e momentum absorbed:%10.2e\n",
05061 loop, gv.bin[nd]->DustDftVel, volmom );
05062 }
05063 }
05064 }
05065
05066 free( help );
05067 return;
05068 }
05069
05070
05071 STATIC double GrnVryDpth(
05072
05073
05074
05075
05076
05077 long int nd)
05078 {
05079 double GrnVryDpth_v;
05080
05081 DEBUG_ENTRY( "GrnVryDpth()" );
05082
05083
05084
05085
05086
05087
05088 if( gv.bin[nd]->lgDustFunc )
05089 {
05090
05091
05092
05093
05094
05095
05096
05097 GrnVryDpth_v = dense.xIonDense[ipHYDROGEN][0]/dense.gas_phase[ipHYDROGEN];
05098 }
05099 else
05100 {
05101
05102
05103
05104 GrnVryDpth_v = GrnStdDpth(nd);
05105 }
05106
05107 ASSERT( GrnVryDpth_v > 0. && GrnVryDpth_v <= 1.000001 );
05108 return GrnVryDpth_v;
05109 }