00001
00002
00003
00004 #include "cddefines.h"
00005 #include "physconst.h"
00006 #include "rfield.h"
00007 #include "phycon.h"
00008 #include "dense.h"
00009 #include "hmi.h"
00010 #include "thermal.h"
00011 #include "trace.h"
00012 #include "thirdparty.h"
00013 #include "iterations.h"
00014 #include "grainvar.h"
00015 #include "grains.h"
00016
00017 #define NO_ATOMS(ND) (gv.bin[ND]->AvVol*gv.bin[ND]->dustp[0]/ATOMIC_MASS_UNIT/gv.bin[ND]->atomWeight)
00018
00019
00020
00021
00022
00023
00024 typedef enum {
00025
00026
00027 QH_OK, QH_ANALYTIC, QH_ANALYTIC_RELAX, QH_DELTA,
00028
00029
00030 QH_NEGRATE_FAIL, QH_LOOP_FAIL, QH_ARRAY_FAIL, QH_THIGH_FAIL,
00031
00032
00033 QH_RETRY, QH_CONV_FAIL, QH_BOUND_FAIL, QH_DELTA_FAIL,
00034
00035
00036
00037 QH_NO_REBIN, QH_LOW_FAIL, QH_HIGH_FAIL, QH_STEP_FAIL,
00038
00039
00040 QH_FATAL, QH_WIDE_FAIL, QH_NBIN_FAIL, QH_REBIN_FAIL
00041 } QH_Code;
00042
00043
00044
00045
00046
00047 static const long NQMIN = 10L;
00048
00049
00050 static const double PROB_CUTOFF_LO = 1.e-15;
00051 static const double PROB_CUTOFF_HI = 1.e-20;
00052 static const double SAFETY = 1.e+8;
00053
00054
00055 static const long NSTARTUP = 5L;
00056
00057
00058
00059 static const double MAX_EVENTS = 150.;
00060
00061
00062
00063 static const long LOOP_MAX = 20L;
00064
00065
00066 static const double DEF_FAC = 3.;
00067
00068
00069 static const double PROB_TOL = 0.02;
00070
00071
00072
00073 static const long NQTEST = 500L;
00074
00075
00076
00077 static const double FWHM_RATIO = 0.1;
00078
00079
00080 static const double FWHM_RATIO2 = 0.007;
00081
00082
00083 static const long MAX_LOOP = 2*NQGRID;
00084
00085
00086 static const double QHEAT_TOL = 5.e-3;
00087
00088
00089 static const long WIDE_FAIL_MAX = 3;
00090
00091
00092 static const double STRICT = 1.;
00093 static const double RELAX = 15.;
00094
00095
00096
00097
00098
00099
00100 static const double QT_RATIO = 1.03;
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115 static const double DEN_SIL = 3.30;
00116
00117
00118 static const double MW_SIL = 24.6051;
00119
00120
00121 static const double tlim[5]={0.,50.,150.,500.,DBL_MAX};
00122 static const double ppower[4]={2.00,1.30,0.68,0.00};
00123 static const double cval[4]={
00124 1.40e3/DEN_SIL*ATOMIC_MASS_UNIT*MW_SIL/EN1RYD,
00125 2.20e4/DEN_SIL*ATOMIC_MASS_UNIT*MW_SIL/EN1RYD,
00126 4.80e5/DEN_SIL*ATOMIC_MASS_UNIT*MW_SIL/EN1RYD,
00127 3.41e7/DEN_SIL*ATOMIC_MASS_UNIT*MW_SIL/EN1RYD};
00128
00129
00130
00131 STATIC void qheat_init(long,double[],double*);
00132
00133 STATIC void GetProbDistr_LowLimit(long,double,double,double,double[],double[],
00134 double[],double[],double[],
00135 long*,double*,long*,QH_Code*);
00136
00137
00138 STATIC double TryDoubleStep(double[],double[],double[],double[],double[],double[],
00139 double[],double,double*,long,long,bool*);
00140
00141 STATIC double log_integral(double,double,double,double);
00142
00143 STATIC void ScanProbDistr(double[],double[],long,double,long,long*,long*,
00144 long*,long*,QH_Code*);
00145
00146 STATIC long RebinQHeatResults(long,long,long,double[],double[],double[],double[],double[],
00147 double[],double[],QH_Code*);
00148
00149 STATIC void GetProbDistr_HighLimit(long,double,double,double,double[],double[],
00150 double[],double*,long*,
00151 double*,QH_Code*);
00152
00153 STATIC double uderiv(double,long);
00154
00155 STATIC double ufunct(double,long,bool*);
00156
00157 STATIC double inv_ufunct(double,long,bool*);
00158
00159 STATIC double DebyeDeriv(double,long);
00160
00161
00162
00163
00164
00165 void GrainMakeDiffuse(void)
00166 {
00167 long i,
00168 j,
00169 nd;
00170 double bfac,
00171 f,
00172 factor,
00173 flux,
00174 x;
00175
00176
00177
00178 double *qtemp, *qprob;
00179
00180 # ifndef NDEBUG
00181 bool lgNoTdustFailures;
00182 double BolFlux,
00183 Comparison1,
00184 Comparison2;
00185 # endif
00186
00187
00188 const double LIM1 = pow(2.e-6,1./2.);
00189 const double LIM2 = pow(6.e-6,1./3.);
00190
00191 DEBUG_ENTRY( "GrainMakeDiffuse()" );
00192
00193 factor = 2.*PI4*POW2(FR1RYD/SPEEDLIGHT)*FR1RYD;
00194
00195
00196 x = log(0.999*DBL_MAX);
00197
00198
00199 for( i=0; i < rfield.nflux; i++ )
00200 {
00201
00202 gv.GrainEmission[i] = 0.;
00203 gv.SilicateEmission[i] = 0.;
00204 gv.GraphiteEmission[i] = 0.;
00205 }
00206
00207 qtemp = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
00208 qprob = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
00209
00210 for( nd=0; nd < gv.nBin; nd++ )
00211 {
00212 strg_type scase;
00213 bool lgLocalQHeat;
00214 long qnbin=-200;
00215 realnum *grn, threshold;
00216 double xx;
00217
00218
00219 scase = gv.which_strg[gv.bin[nd]->matType];
00220 switch( scase )
00221 {
00222 case STRG_SIL:
00223 grn = gv.SilicateEmission;
00224 break;
00225 case STRG_CAR:
00226 grn = gv.GraphiteEmission;
00227 break;
00228 default:
00229 fprintf( ioQQQ, " GrainMakeDiffuse called with unknown storage class: %d\n", scase );
00230 cdEXIT(EXIT_FAILURE);
00231 }
00232
00233
00234 lgLocalQHeat = gv.bin[nd]->lgQHeat;
00235
00236
00237
00238 threshold = ( dense.xIonDense[ipHYDROGEN][0]+dense.xIonDense[ipHYDROGEN][1] > hmi.H2_total ) ?
00239 gv.dstAbundThresholdNear : gv.dstAbundThresholdFar;
00240
00241 if( lgLocalQHeat && gv.bin[nd]->dstAbund >= threshold )
00242 {
00243 qheat(qtemp,qprob,&qnbin,nd);
00244
00245 if( gv.bin[nd]->lgUseQHeat )
00246 {
00247 ASSERT( qnbin > 0 );
00248 }
00249 }
00250 else
00251 {
00252
00253 gv.bin[nd]->lgUseQHeat = false;
00254 }
00255
00256 flux = 1.;
00257
00258
00259 for( i=0; i < rfield.nflux && (realnum)flux > 0.f; i++ )
00260 {
00261 flux = 0.;
00262 if( lgLocalQHeat && gv.bin[nd]->lgUseQHeat )
00263 {
00264 xx = 1.;
00265
00266
00267 for( j=qnbin-1; j >= 0 && xx > flux*DBL_EPSILON; j-- )
00268 {
00269 f = TE1RYD/qtemp[j]*rfield.anu[i];
00270 if( f < x )
00271 {
00272
00273
00274 if( f > LIM2 )
00275 bfac = exp(f) - 1.;
00276 else if( f > LIM1 )
00277 bfac = (1. + 0.5*f)*f;
00278 else
00279 bfac = f;
00280 xx = qprob[j]*gv.bin[nd]->dstab1[i]*rfield.anu2[i]*
00281 rfield.widflx[i]/bfac;
00282 flux += xx;
00283 }
00284 else
00285 {
00286 xx = 0.;
00287 }
00288 }
00289 }
00290 else
00291 {
00292 f = TE1RYD/gv.bin[nd]->tedust*rfield.anu[i];
00293 if( f < x )
00294 {
00295
00296 if( f > LIM2 )
00297 bfac = exp(f) - 1.;
00298 else if( f > LIM1 )
00299 bfac = (1. + 0.5*f)*f;
00300 else
00301 bfac = f;
00302 flux = gv.bin[nd]->dstab1[i]*rfield.anu2[i]*rfield.widflx[i]/bfac;
00303 }
00304 }
00305
00306
00307 flux *= factor*gv.bin[nd]->cnv_H_pCM3;
00308
00309
00310
00311 gv.GrainEmission[i] += (realnum)flux;
00312
00313 grn[i] += (realnum)flux;
00314 }
00315 }
00316
00317 free( qprob );
00318 free( qtemp );
00319
00320 # ifndef NDEBUG
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375 lgNoTdustFailures = true;
00376 for( nd=0; nd < gv.nBin; nd++ )
00377 {
00378 if( !gv.bin[nd]->lgTdustConverged )
00379 {
00380 lgNoTdustFailures = false;
00381 break;
00382 }
00383 }
00384
00385
00386 BolFlux = 0.;
00387 for( i=0; i < rfield.nflux; i++ )
00388 {
00389 BolFlux += gv.GrainEmission[i]*rfield.anu[i]*EN1RYD;
00390 }
00391 Comparison1 = 0.;
00392 for( nd=0; nd < gv.nBin; nd++ )
00393 {
00394 if( gv.bin[nd]->tedust < gv.bin[nd]->Tsublimat )
00395 Comparison1 += CONSERV_TOL*gv.bin[nd]->GrainHeat;
00396 else
00397
00398
00399 Comparison1 += 10.*CONSERV_TOL*gv.bin[nd]->GrainHeat;
00400 }
00401
00402
00403
00404 ASSERT( fabs(BolFlux-gv.GrainHeatSum) < Comparison1 );
00405
00406
00407 for( nd=0; nd < gv.nBin; nd++ )
00408 {
00409 if( gv.bin[nd]->lgChrgConverged )
00410 {
00411 double ave = 0.5*(gv.bin[nd]->RateDn+gv.bin[nd]->RateUp);
00412
00413
00414 ASSERT( lgAbort || fabs(gv.bin[nd]->RateDn-gv.bin[nd]->RateUp) < CONSERV_TOL*ave );
00415 }
00416 }
00417
00418 if( lgNoTdustFailures && gv.lgDHetOn && gv.lgDColOn && thermal.ConstGrainTemp == 0. )
00419 {
00420
00421
00422 Comparison1 = 0.;
00423 for( nd=0; nd < gv.nBin; nd++ )
00424 {
00425 Comparison1 += gv.bin[nd]->BolFlux;
00426 }
00427
00428 Comparison1 += MAX2(gv.GasCoolColl,0.);
00429
00430 Comparison1 += gv.GrainHeatChem;
00431
00432
00433 Comparison2 = gv.GrainHeatSum+thermal.heating[0][13]+thermal.heating[0][14]+thermal.heating[0][25];
00434
00435
00436
00437 ASSERT( lgAbort || gv.GrainHeatScaleFactor != 1.f || gv.lgBakesPAH_heat ||
00438 fabs(Comparison1-Comparison2)/Comparison2 < CONSERV_TOL );
00439 }
00440 # endif
00441 return;
00442 }
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459 void qheat( double qtemp[],
00460 double qprob[],
00461 long int *qnbin,
00462 long int nd)
00463 {
00464 bool lgBoundErr,
00465 lgDelta,
00466 lgNegRate,
00467 lgOK,
00468 lgTried;
00469 long int i,
00470 nWideFail;
00471 QH_Code ErrorCode,
00472 ErrorCode2,
00473 ErrorStart;
00474 double c0,
00475 c1,
00476 c2,
00477 check,
00478 DefFac,
00479 deriv,
00480 *dPdlnT,
00481 fwhm,
00482 FwhmRatio,
00483 integral,
00484 minBracket,
00485 maxBracket,
00486 new_tmin,
00487 NumEvents,
00488 oldy,
00489 *Phi,
00490 *PhiDrv,
00491 *phiTilde,
00492 rel_tol,
00493 Tmax,
00494 tol,
00495 Umax,
00496 xx,
00497 y;
00498
00499
00500 DEBUG_ENTRY( "qheat()" );
00501
00502
00503 ASSERT( gv.bin[nd]->lgQHeat );
00504 ASSERT( nd >= 0 && nd < gv.nBin );
00505
00506 if( trace.lgTrace && trace.lgDustBug )
00507 {
00508 fprintf( ioQQQ, "\n >>>>qheat called for grain %s\n", gv.bin[nd]->chDstLab );
00509 }
00510
00511
00512
00513 phiTilde = (double*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(double)) );
00514 Phi = (double*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(double)) );
00515 PhiDrv = (double*)MALLOC((size_t)((unsigned)rfield.nupper*sizeof(double)) );
00516 dPdlnT = (double*)MALLOC((size_t)(NQGRID*sizeof(double)) );
00517
00518 qheat_init( nd, phiTilde, &check );
00519
00520 check += gv.bin[nd]->GrainHeatColl-gv.bin[nd]->GrainCoolTherm;
00521
00522 xx = integral = 0.;
00523 c0 = c1 = c2 = 0.;
00524 lgNegRate = false;
00525 oldy = 0.;
00526 tol = 1.;
00527
00528
00529
00530 for( i=gv.bin[nd]->qnflux-1; i >= 0; i-- )
00531 {
00532
00533
00534
00535
00536
00537 double fac = ( i < gv.bin[nd]->qnflux-1 ) ? 1./rfield.widflx[i] : 0.;
00538
00539 y = phiTilde[i]*gv.bin[nd]->cnv_H_pGR*fac;
00540
00541 PhiDrv[i] = -0.5*(y + oldy);
00542
00543 xx -= PhiDrv[i]*(rfield.anu[i+1]-rfield.anu[i]);
00544
00545 Phi[i] = xx;
00546
00547 # ifndef NDEBUG
00548
00549 integral += phiTilde[i]*gv.bin[nd]->cnv_H_pCM3*rfield.anu[i]*EN1RYD;
00550 # endif
00551
00552
00553 c0 += Phi[i]*rfield.widflx[i];
00554 c1 += Phi[i]*rfield.anu[i]*rfield.widflx[i];
00555 c2 += Phi[i]*POW2(rfield.anu[i])*rfield.widflx[i];
00556
00557 lgNegRate = lgNegRate || ( phiTilde[i] < 0. );
00558
00559 oldy = y;
00560 }
00561
00562
00563 ASSERT( fabs(check-integral)/check <= CONSERV_TOL );
00564
00565 # if 0
00566 {
00567 char fnam[50];
00568 FILE *file;
00569
00570 sprintf(fnam,"Lambda_%2.2ld.asc",nd);
00571 file = fopen(fnam,"w");
00572 for( i=0; i < NDEMS; ++i )
00573 fprintf(file,"%e %e %e\n",
00574 exp(gv.dsttmp[i]),
00575 ufunct(exp(gv.dsttmp[i]),nd,&lgBoundErr),
00576 exp(gv.bin[nd]->dstems[i])*gv.bin[nd]->cnv_H_pGR/EN1RYD);
00577 fclose(file);
00578
00579 sprintf(fnam,"Phi_%2.2ld.asc",nd);
00580 file = fopen(fnam,"w");
00581 for( i=0; i < gv.bin[nd]->qnflux; ++i )
00582 fprintf(file,"%e %e\n", rfield.anu[i],Phi[i]);
00583 fclose(file);
00584 }
00585 # endif
00586
00587
00588 Tmax = gv.bin[nd]->tedust;
00589
00590 Umax = ufunct(Tmax,nd,&lgBoundErr);
00591 ASSERT( !lgBoundErr );
00592
00593 spldrv_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,log(Tmax),&y,&lgBoundErr);
00594 ASSERT( !lgBoundErr );
00595
00596 deriv = y*c0/(uderiv(Tmax,nd)*Tmax);
00597
00598 fwhm = sqrt(8.*LN_TWO*c1/deriv);
00599
00600 NumEvents = POW2(fwhm)*c0/(4.*LN_TWO*c2);
00601 FwhmRatio = fwhm/Umax;
00602
00603
00604 lgDelta = ( FwhmRatio < FWHM_RATIO2 );
00605
00606 lgOK = lgDelta;
00607
00608 ErrorStart = QH_OK;
00609
00610 if( lgDelta )
00611 {
00612
00613 lgNegRate = false;
00614 ErrorStart = MAX2(ErrorStart,QH_DELTA);
00615 }
00616
00617 if( lgNegRate )
00618 ErrorStart = MAX2(ErrorStart,QH_NEGRATE_FAIL);
00619
00620 ErrorCode = ErrorStart;
00621
00622 if( trace.lgTrace && trace.lgDustBug )
00623 {
00624 double Rate2 = 0.;
00625 for( int nz=0; nz < gv.bin[nd]->nChrg; nz++ )
00626 Rate2 += gv.bin[nd]->chrg[nz]->FracPop*gv.bin[nd]->chrg[nz]->HeatingRate2;
00627
00628 fprintf( ioQQQ, " grain heating: %.4e, integral %.4e, total rate %.4e lgNegRate %c\n",
00629 gv.bin[nd]->GrainHeat,integral,Phi[0],TorF(lgNegRate));
00630 fprintf( ioQQQ, " av grain temp %.4e av grain enthalpy (Ryd) %.4e\n",
00631 gv.bin[nd]->tedust,Umax);
00632 fprintf( ioQQQ, " fwhm^2/(4ln2*c2/c0): %.4e fwhm (Ryd) %.4e fwhm/Umax %.4e\n",
00633 NumEvents,fwhm,FwhmRatio );
00634 fprintf( ioQQQ, " HeatingRate1 %.4e HeatingRate2 %.4e lgQHTooWide %c\n",
00635 gv.bin[nd]->HeatingRate1*gv.bin[nd]->cnv_H_pCM3, Rate2*gv.bin[nd]->cnv_H_pCM3,
00636 TorF(gv.bin[nd]->lgQHTooWide) );
00637 }
00638
00639
00640 minBracket = GRAIN_TMIN;
00641 maxBracket = gv.bin[nd]->tedust;
00642
00643
00644 lgTried = false;
00645
00646 nWideFail = 0;
00647
00648 DefFac = DEF_FAC;
00649
00650 rel_tol = 1.;
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660 for( i=0; i < LOOP_MAX && !lgOK && !gv.bin[nd]->lgQHTooWide; i++ )
00661 {
00662 if( gv.bin[nd]->qtmin >= gv.bin[nd]->tedust )
00663 {
00664
00665
00666 double Ulo = Umax*exp(-sqrt(-log(PROB_CUTOFF_LO)/(4.*LN_TWO))*fwhm/Umax);
00667 double MinEnth = exp(gv.bin[nd]->DustEnth[0]);
00668 Ulo = MAX2(Ulo,MinEnth);
00669 gv.bin[nd]->qtmin = inv_ufunct(Ulo,nd,&lgBoundErr);
00670 ASSERT( !lgBoundErr );
00671
00672 if( gv.bin[nd]->qtmin <= minBracket || gv.bin[nd]->qtmin >= maxBracket )
00673 gv.bin[nd]->qtmin = sqrt(minBracket*maxBracket);
00674 }
00675 gv.bin[nd]->qtmin = MAX2(gv.bin[nd]->qtmin,GRAIN_TMIN);
00676
00677 ASSERT( minBracket <= gv.bin[nd]->qtmin && gv.bin[nd]->qtmin <= maxBracket );
00678
00679 ErrorCode = ErrorStart;
00680
00681
00682 if( FwhmRatio >= FWHM_RATIO && NumEvents <= MAX_EVENTS )
00683 {
00684 GetProbDistr_LowLimit(nd,rel_tol,Umax,fwhm,Phi,PhiDrv,qtemp,qprob,dPdlnT,qnbin,
00685 &new_tmin,&nWideFail,&ErrorCode);
00686
00687
00688 if( ErrorCode == QH_DELTA_FAIL && fwhm < Umax && !lgTried )
00689 {
00690 double dummy;
00691
00692
00693
00694
00695
00696
00697
00698 ErrorCode = MAX2(ErrorStart,QH_ANALYTIC);
00699
00700
00701 GetProbDistr_HighLimit(nd,STRICT,Umax,fwhm,qtemp,qprob,dPdlnT,&tol,qnbin,&dummy,
00702 &ErrorCode);
00703
00704 if( ErrorCode >= QH_RETRY )
00705 {
00706 ErrorCode = QH_DELTA_FAIL;
00707 lgTried = true;
00708 }
00709 }
00710
00711
00712 if( ErrorCode < QH_NO_REBIN )
00713 {
00714 if( new_tmin < minBracket || new_tmin > maxBracket )
00715 ++nWideFail;
00716
00717 if( nWideFail < WIDE_FAIL_MAX )
00718 {
00719 if( new_tmin <= minBracket )
00720 new_tmin = sqrt(gv.bin[nd]->qtmin*minBracket);
00721 if( new_tmin >= maxBracket )
00722 new_tmin = sqrt(gv.bin[nd]->qtmin*maxBracket);
00723 }
00724 else
00725 {
00726 ErrorCode = MAX2(ErrorCode,QH_WIDE_FAIL);
00727 }
00728
00729 if( ErrorCode == QH_CONV_FAIL )
00730 {
00731 rel_tol *= 0.9;
00732 }
00733 }
00734 else if( ErrorCode == QH_LOW_FAIL )
00735 {
00736 double help1 = gv.bin[nd]->qtmin*sqrt(DefFac);
00737 double help2 = sqrt(gv.bin[nd]->qtmin*maxBracket);
00738 minBracket = gv.bin[nd]->qtmin;
00739 new_tmin = MIN2(help1,help2);
00740
00741 DefFac += 1.5;
00742 }
00743 else if( ErrorCode == QH_HIGH_FAIL )
00744 {
00745 double help = sqrt(gv.bin[nd]->qtmin*minBracket);
00746 maxBracket = gv.bin[nd]->qtmin;
00747 new_tmin = MAX2(gv.bin[nd]->qtmin/DEF_FAC,help);
00748 }
00749 else
00750 {
00751 new_tmin = sqrt(minBracket*maxBracket);
00752 }
00753 }
00754 else
00755 {
00756 GetProbDistr_HighLimit(nd,STRICT,Umax,fwhm,qtemp,qprob,dPdlnT,&tol,qnbin,&new_tmin,
00757 &ErrorCode);
00758 }
00759
00760 gv.bin[nd]->qtmin = new_tmin;
00761
00762 lgOK = ( ErrorCode < QH_RETRY );
00763
00764 if( ErrorCode >= QH_FATAL )
00765 break;
00766
00767 if( ErrorCode != QH_LOW_FAIL )
00768 DefFac = DEF_FAC;
00769
00770 if( trace.lgTrace && trace.lgDustBug )
00771 {
00772 fprintf( ioQQQ, " GetProbDistr returns code %d\n", ErrorCode );
00773 if( !lgOK )
00774 {
00775 fprintf( ioQQQ, " >>>>qheat trying another iteration, qtmin bracket %.4e %.4e",
00776 minBracket,maxBracket );
00777 fprintf( ioQQQ, " nWideFail %ld\n", nWideFail );
00778 }
00779 }
00780 }
00781
00782 if( ErrorCode == QH_WIDE_FAIL )
00783 gv.bin[nd]->lgQHTooWide = true;
00784
00785
00786
00787 if( gv.bin[nd]->lgQHTooWide && !lgDelta )
00788 ErrorCode = MAX2(ErrorCode,QH_WIDE_FAIL);
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805 if( !lgOK && !lgDelta )
00806 {
00807 double Umax2 = Umax*sqrt(tol);
00808 double fwhm2 = fwhm*sqrt(tol);
00809
00810 for( i=0; i < LOOP_MAX; ++i )
00811 {
00812 double dummy;
00813
00814 ErrorCode2 = MAX2(ErrorStart,QH_ANALYTIC);
00815 GetProbDistr_HighLimit(nd,RELAX,Umax2,fwhm2,qtemp,qprob,dPdlnT,&tol,qnbin,&dummy,
00816 &ErrorCode2);
00817
00818 lgOK = ( ErrorCode2 < QH_RETRY );
00819 if( lgOK )
00820 {
00821 gv.bin[nd]->qtmin = dummy;
00822 ErrorCode = ErrorCode2;
00823 break;
00824 }
00825 else
00826 {
00827 Umax2 *= sqrt(tol);
00828 fwhm2 *= sqrt(tol);
00829 }
00830 }
00831 }
00832
00833 if( nzone == 1 )
00834 gv.bin[nd]->qtmin_zone1 = gv.bin[nd]->qtmin;
00835
00836 gv.bin[nd]->lgUseQHeat = ( lgOK && !lgDelta );
00837 gv.bin[nd]->lgEverQHeat = ( gv.bin[nd]->lgEverQHeat || gv.bin[nd]->lgUseQHeat );
00838
00839 if( lgOK )
00840 {
00841 if( trace.lgTrace && trace.lgDustBug )
00842 fprintf( ioQQQ, " >>>>qheat converged with code: %d\n", ErrorCode );
00843 }
00844 else
00845 {
00846 *qnbin = 0;
00847 ++gv.bin[nd]->QHeatFailures;
00848 fprintf( ioQQQ, " PROBLEM qheat did not converge grain %s in zone %ld, error code = %d\n",
00849 gv.bin[nd]->chDstLab,nzone,ErrorCode );
00850 }
00851
00852 if( gv.QHPunchFile != NULL && ( iterations.lgLastIt || !gv.lgQHPunLast ) )
00853 {
00854 fprintf( gv.QHPunchFile, "\nDust Temperature Distribution: grain %s zone %ld\n",
00855 gv.bin[nd]->chDstLab,nzone );
00856
00857 fprintf( gv.QHPunchFile, "Equilibrium temperature: %.2f\n", gv.bin[nd]->tedust );
00858
00859 if( gv.bin[nd]->lgUseQHeat )
00860 {
00861
00862 fprintf( gv.QHPunchFile, "Number of bins: %ld\n", *qnbin );
00863 fprintf( gv.QHPunchFile, " Tgrain dP/dlnT\n" );
00864 for( i=0; i < *qnbin; i++ )
00865 {
00866 fprintf( gv.QHPunchFile, "%.4e %.4e\n", qtemp[i],dPdlnT[i] );
00867 }
00868 }
00869 else
00870 {
00871 fprintf( gv.QHPunchFile, "**** quantum heating was not used\n" );
00872 }
00873 }
00874
00875 free ( phiTilde );
00876 free ( Phi );
00877 free ( PhiDrv );
00878 free ( dPdlnT );
00879 return;
00880 }
00881
00882
00883
00884 STATIC void qheat_init(long nd,
00885 double phiTilde[],
00886 double *check)
00887 {
00888 long i,
00889 nz;
00890 double sum = 0.;
00891
00892
00893 enum {DEBUG_LOC=false};
00894
00895
00896 DEBUG_ENTRY( "qheat_init()" );
00897
00898
00899 ASSERT( gv.bin[nd]->lgQHeat );
00900 ASSERT( nd >= 0 && nd < gv.nBin );
00901
00902 *check = 0.;
00903
00904
00905
00906 for( i=0; i < gv.bin[nd]->qnflux; i++ )
00907 {
00908 phiTilde[i] = 0.;
00909 }
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920 double NegHeatingRate = 0.;
00921
00922 for( nz=0; nz < gv.bin[nd]->nChrg; nz++ )
00923 {
00924 double check1 = 0.;
00925 ChargeBin *gptr = gv.bin[nd]->chrg[nz];
00926
00927
00928 for( i=0; i < MIN2(gptr->ipThresInf,rfield.nflux); i++ )
00929 {
00930 check1 += rfield.SummedCon[i]*gv.bin[nd]->dstab1[i]*rfield.anu[i];
00931 phiTilde[i] += gptr->FracPop*rfield.SummedCon[i]*gv.bin[nd]->dstab1[i];
00932 }
00933
00934
00935
00936 for( i=gptr->ipThresInf; i < rfield.nflux; i++ )
00937 {
00938 long ipLo2 = gptr->ipThresInfVal;
00939 double cs1 = ( i >= ipLo2 ) ? gv.bin[nd]->dstab1[i]*gptr->yhat_primary[i] : 0.;
00940
00941 check1 += rfield.SummedCon[i]*gptr->fac1[i];
00942
00943 phiTilde[i] += gptr->FracPop*rfield.SummedCon[i]*MAX2(gv.bin[nd]->dstab1[i]-cs1,0.);
00944
00945
00946
00947
00948 if( cs1 > 0. )
00949 {
00950
00951
00952
00953
00954 double ratio = ( gv.lgWD01 ) ? 1. : gptr->yhat[i]/gptr->yhat_primary[i];
00955
00956 double ehat = gptr->ehat[i];
00957 double cool1, sign = 1.;
00958 realnum xx;
00959
00960 if( gptr->DustZ <= -1 )
00961 cool1 = gptr->ThresSurf + gptr->PotSurf + ehat;
00962 else
00963 cool1 = gptr->ThresSurfVal + gptr->PotSurf + ehat;
00964
00965
00966
00967
00968
00969
00970
00971 xx = rfield.anu[i] - (realnum)(ratio*cool1);
00972 if( xx < 0.f )
00973 {
00974 xx = -xx;
00975 sign = -1.;
00976 }
00977 long ipLo = hunt_bisect( gv.anumin, i+1, xx );
00978 phiTilde[ipLo] += sign*gptr->FracPop*rfield.SummedCon[i]*cs1;
00979 }
00980
00981
00982
00983 }
00984
00985 *check += gptr->FracPop*check1*EN1RYD*gv.bin[nd]->cnv_H_pCM3;
00986
00987 sum += gptr->FracPop*check1*EN1RYD*gv.bin[nd]->cnv_H_pCM3;
00988
00989 if( DEBUG_LOC )
00990 {
00991 double integral = 0.;
00992 for( i=0; i < gv.bin[nd]->qnflux; i++ )
00993 {
00994 integral += phiTilde[i]*gv.bin[nd]->cnv_H_pCM3*rfield.anu[i]*EN1RYD;
00995 }
00996 dprintf( ioQQQ, " integral test 1: integral %.6e %.6e\n", integral, sum );
00997 }
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012 if( gptr->HeatingRate2*gv.bin[nd]->cnv_H_pCM3 > 0.05*CONSERV_TOL*gv.bin[nd]->GrainHeat )
01013 {
01014 double *RateArr,Sum,ESum,DSum,Delta,E_av2,Corr;
01015 double fac = BOLTZMANN/EN1RYD*phycon.te;
01016
01017
01018
01019 double E0 = -(MIN2(gptr->PotSurfInc,0.) + MIN2(gptr->ThresInfInc,0.));
01020
01021
01022
01023 double Einf = gptr->ThresInfInc + MIN2(gptr->PotSurfInc,0.);
01024
01025
01026
01027
01028
01029 double E_av = MAX2(gptr->ThresInfInc,0.)*EN1RYD + 2.*BOLTZMANN*phycon.te;
01030
01031 double rate = gptr->HeatingRate2/E_av;
01032
01033 double ylo = -exp(-E0/fac);
01034
01035
01036
01037 double Ehi = gv.anumax[gv.bin[nd]->qnflux-1]-Einf;
01038 double yhi = ((E0-Ehi)/fac-1.)*exp(-Ehi/fac);
01039
01040 rate /= yhi-ylo;
01041
01042
01043 rate *= gptr->FracPop;
01044
01045
01046 RateArr=(double*)MALLOC((size_t)((unsigned)gv.bin[nd]->qnflux*sizeof(double)));
01047 Sum = ESum = DSum = 0.;
01048
01049
01050 for( i=0; i < gv.bin[nd]->qnflux2; i++ )
01051 {
01052 Ehi = gv.anumax[i] - Einf;
01053 if( Ehi >= E0 )
01054 {
01055
01056 yhi = ((E0-Ehi)/fac-1.)*exp(-Ehi/fac);
01057
01058 RateArr[i] = rate*MAX2(yhi-ylo,0.);
01059 Sum += RateArr[i];
01060 ESum += rfield.anu[i]*RateArr[i];
01061 # ifndef NDEBUG
01062 DSum += rfield.widflx[i]*RateArr[i];
01063 # endif
01064 ylo = yhi;
01065 }
01066 else
01067 {
01068 RateArr[i] = 0.;
01069 }
01070 }
01071 E_av2 = ESum/Sum*EN1RYD;
01072 Delta = DSum/Sum*EN1RYD;
01073 ASSERT( fabs(E_av-E_av2) <= Delta );
01074 Corr = E_av/E_av2;
01075
01076 for( i=0; i < gv.bin[nd]->qnflux2; i++ )
01077 {
01078 phiTilde[i] += RateArr[i]*Corr;
01079 }
01080
01081 sum += gptr->FracPop*gptr->HeatingRate2*gv.bin[nd]->cnv_H_pCM3;
01082
01083 if( DEBUG_LOC )
01084 {
01085 double integral = 0.;
01086 for( i=0; i < gv.bin[nd]->qnflux; i++ )
01087 {
01088 integral += phiTilde[i]*gv.bin[nd]->cnv_H_pCM3*rfield.anu[i]*EN1RYD;
01089 }
01090 dprintf( ioQQQ, " integral test 2: integral %.6e %.6e\n", integral, sum );
01091 }
01092
01093 free( RateArr );
01094 }
01095 else
01096 {
01097 NegHeatingRate += gptr->FracPop*gptr->HeatingRate2*gv.bin[nd]->cnv_H_pCM3;
01098 }
01099 }
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119 if( gv.bin[nd]->HeatingRate1*gv.bin[nd]->cnv_H_pCM3 > 0.05*CONSERV_TOL*gv.bin[nd]->GrainHeat )
01120 {
01121
01122
01123 const double LIM2 = pow(3.e-6,1./3.);
01124 const double LIM3 = pow(8.e-6,1./4.);
01125
01126
01127
01128
01129 double fac = BOLTZMANN/EN1RYD*MAX2(phycon.te,gv.bin[nd]->tedust);
01130
01131 double E_av = 2.*BOLTZMANN*MAX2(phycon.te,gv.bin[nd]->tedust);
01132
01133 double rate = gv.bin[nd]->HeatingRate1/E_av;
01134
01135 double ylo = -1.;
01136
01137
01138
01139 double Ehi = gv.anumax[gv.bin[nd]->qnflux-1];
01140 double yhi = -(Ehi/fac+1.)*exp(-Ehi/fac);
01141
01142 rate /= yhi-ylo;
01143
01144 for( i=0; i < gv.bin[nd]->qnflux2; i++ )
01145 {
01146
01147
01148
01149 double x = gv.anumax[i]/fac;
01150
01151
01152 if( x > LIM3 )
01153 yhi = -(x+1.)*exp(-x);
01154 else if( x > LIM2 )
01155 yhi = -(((1./3.)*x - 0.5)*x*x + 1.);
01156 else
01157 yhi = -(1. - 0.5*x*x);
01158
01159
01160 phiTilde[i] += rate*MAX2(yhi-ylo,0.);
01161 ylo = yhi;
01162 }
01163
01164 sum += gv.bin[nd]->HeatingRate1*gv.bin[nd]->cnv_H_pCM3;
01165
01166 if( DEBUG_LOC )
01167 {
01168 double integral = 0.;
01169 for( i=0; i < gv.bin[nd]->qnflux; i++ )
01170 {
01171 integral += phiTilde[i]*gv.bin[nd]->cnv_H_pCM3*rfield.anu[i]*EN1RYD;
01172 }
01173 dprintf( ioQQQ, " integral test 3: integral %.6e %.6e\n", integral, sum );
01174 }
01175 }
01176 else
01177 {
01178 NegHeatingRate += gv.bin[nd]->HeatingRate1*gv.bin[nd]->cnv_H_pCM3;
01179 }
01180
01181
01182
01183
01184 if( NegHeatingRate < 0. )
01185 {
01186 double scale_fac = (sum+NegHeatingRate)/sum;
01187 for( i=0; i < gv.bin[nd]->qnflux; i++ )
01188 phiTilde[i] *= scale_fac;
01189
01190 sum += NegHeatingRate;
01191
01192 if( DEBUG_LOC )
01193 {
01194 double integral = 0.;
01195 for( i=0; i < gv.bin[nd]->qnflux; i++ )
01196 {
01197 integral += phiTilde[i]*gv.bin[nd]->cnv_H_pCM3*rfield.anu[i]*EN1RYD;
01198 }
01199 dprintf( ioQQQ, " integral test 4: integral %.6e %.6e\n", integral, sum );
01200 }
01201 }
01202
01203 return;
01204 }
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220 STATIC void GetProbDistr_LowLimit(long int nd,
01221 double rel_tol,
01222 double Umax,
01223 double fwhm,
01224 double Phi[],
01225 double PhiDrv[],
01226 double qtemp[],
01227 double qprob[],
01228 double dPdlnT[],
01229 long int *qnbin,
01230 double *new_tmin,
01231 long *nWideFail,
01232 QH_Code *ErrorCode)
01233 {
01234 bool lgAllNegSlope,
01235 lgBoundErr;
01236 long int j,
01237 k,
01238 l,
01239 nbad,
01240 nbin,
01241 nend=0,
01242 nmax,
01243 nok,
01244 nstart=0,
01245 nstart2=0;
01246 double dCool,
01247 delu[NQGRID],
01248 dlnLdlnT,
01249 dlnpdlnU,
01250 fac = 0.,
01251 Lambda[NQGRID],
01252 maxVal,
01253 NextStep,
01254 p[NQGRID],
01255 qtmin1,
01256 RadCooling,
01257 sum,
01258 u1[NQGRID],
01259 y;
01260
01261
01262 DEBUG_ENTRY( "GetProbDistr_LowLimit()" );
01263
01264
01265 ASSERT( nd >= 0 && nd < gv.nBin );
01266
01267 if( trace.lgTrace && trace.lgDustBug )
01268 {
01269 fprintf( ioQQQ, " GetProbDistr_LowLimit called for grain %s\n", gv.bin[nd]->chDstLab );
01270 fprintf( ioQQQ, " got qtmin1 %.4e\n", gv.bin[nd]->qtmin);
01271 }
01272
01273 qtmin1 = gv.bin[nd]->qtmin;
01274 qtemp[0] = qtmin1;
01275
01276 u1[0] = ufunct(qtemp[0],nd,&lgBoundErr);
01277 ASSERT( !lgBoundErr );
01278
01279
01280 splint_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,log(qtemp[0]),&y,&lgBoundErr);
01281 ASSERT( !lgBoundErr );
01282
01283 Lambda[0] = exp(y)*gv.bin[nd]->cnv_H_pGR/EN1RYD;
01284
01285
01286
01287 delu[0] = 2.*Lambda[0]/Phi[0];
01288 p[0] = PROB_CUTOFF_LO;
01289 dPdlnT[0] = p[0]*qtemp[0]*uderiv(qtemp[0],nd);
01290 RadCooling = 0.5*p[0]*Lambda[0]*delu[0];
01291 NextStep = 0.01*Lambda[0]/Phi[0];
01292
01293 if( NextStep < 0.05*DBL_EPSILON*u1[0] )
01294 {
01295 *ErrorCode = MAX2(*ErrorCode,QH_STEP_FAIL);
01296 return;
01297 }
01298 else if( NextStep < 5.*DBL_EPSILON*u1[0] )
01299 {
01300
01301 NextStep *= 100.;
01302 }
01303
01304 nbad = 0;
01305 k = 0;
01306
01307 *qnbin = 0;
01308 *new_tmin = qtmin1;
01309 lgAllNegSlope = true;
01310 maxVal = dPdlnT[0];
01311 nmax = 0;
01312
01313
01314
01315
01316 spldrv_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,log(qtemp[0]),&dlnLdlnT,&lgBoundErr);
01317 ASSERT( !lgBoundErr );
01318 dlnpdlnU = u1[0]*Phi[0]/Lambda[0] - dlnLdlnT*u1[0]/(qtemp[0]*uderiv(qtemp[0],nd));
01319 if( dlnpdlnU < 0. )
01320 {
01321
01322 (void)TryDoubleStep(u1,delu,p,qtemp,Lambda,Phi,PhiDrv,NextStep,&dCool,k,nd,&lgBoundErr);
01323 dPdlnT[2] = p[2]*qtemp[2]*uderiv(qtemp[2],nd);
01324
01325 if( dPdlnT[2] < dPdlnT[0] ) {
01326
01327
01328 *ErrorCode = MAX2(*ErrorCode,QH_HIGH_FAIL);
01329 return;
01330 }
01331 }
01332
01333
01334 for( l=0; l < MAX_LOOP; ++l )
01335 {
01336 double RelError = TryDoubleStep(u1,delu,p,qtemp,Lambda,Phi,PhiDrv,NextStep,&dCool,k,nd,&lgBoundErr);
01337
01338
01339
01340 if( lgBoundErr )
01341 {
01342 nbad += 2;
01343 *ErrorCode = MAX2(*ErrorCode,QH_THIGH_FAIL);
01344 break;
01345 }
01346
01347
01348 if( RelError > rel_tol*QHEAT_TOL )
01349 {
01350 nbad += 2;
01351
01352
01353 NextStep *= sqrt(0.9*rel_tol*QHEAT_TOL/RelError);
01354
01355
01356 if( NextStep < 5.*DBL_EPSILON*u1[k] )
01357 {
01358 *ErrorCode = MAX2(*ErrorCode,QH_STEP_FAIL);
01359 break;
01360 }
01361
01362 continue;
01363 }
01364 else
01365 {
01366
01367 k += 2;
01368
01369
01370 NextStep *= MIN2(pow(0.9*rel_tol*QHEAT_TOL/MAX2(RelError,1.e-50),1./3.),4.);
01371 NextStep = MIN2(NextStep,Lambda[k]/Phi[0]);
01372 }
01373
01374 dPdlnT[k-1] = p[k-1]*qtemp[k-1]*uderiv(qtemp[k-1],nd);
01375 dPdlnT[k] = p[k]*qtemp[k]*uderiv(qtemp[k],nd);
01376
01377 lgAllNegSlope = lgAllNegSlope && ( dPdlnT[k] < dPdlnT[k-2] );
01378
01379 if( dPdlnT[k-1] > maxVal )
01380 {
01381 maxVal = dPdlnT[k-1];
01382 nmax = k-1;
01383 }
01384 if( dPdlnT[k] > maxVal )
01385 {
01386 maxVal = dPdlnT[k];
01387 nmax = k;
01388 }
01389
01390 RadCooling += dCool;
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400 if( p[k] > sqrt(DBL_MAX/100.) )
01401 {
01402 *ErrorCode = MAX2(*ErrorCode,QH_LOW_FAIL);
01403 break;
01404
01405 #if 0
01406
01407
01408 for( j=0; j <= k; j++ )
01409 {
01410 p[j] /= DBL_MAX/100.;
01411 dPdlnT[j] /= DBL_MAX/100.;
01412 }
01413 RadCooling /= DBL_MAX/100.;
01414 #endif
01415 }
01416
01417
01418
01419 ASSERT( p[k] > 0. && dPdlnT[k] > 0. && RadCooling > 0. );
01420
01421
01422
01423
01424 if( k > 0 && k%NQTEST == 0 )
01425 {
01426 double wid, avStep, factor;
01427
01428
01429 if( lgAllNegSlope )
01430 {
01431 *ErrorCode = MAX2(*ErrorCode,QH_HIGH_FAIL);
01432 break;
01433 }
01434
01435
01436
01437 wid = (sqrt(-log(PROB_CUTOFF_LO)/(4.*LN_TWO)) +
01438 sqrt(-log(PROB_CUTOFF_HI)/(4.*LN_TWO)))*fwhm/Umax;
01439 avStep = log(u1[k]/u1[0])/(double)k;
01440
01441
01442
01443 factor = 1.1 + 3.9*(1.0 - sqrt((double)k/(double)NQGRID));
01444 if( wid/avStep > factor*(double)NQGRID )
01445 {
01446 *ErrorCode = MAX2(*ErrorCode,QH_ARRAY_FAIL);
01447 break;
01448 }
01449 }
01450
01451
01452
01453 if( k >= NQGRID-2 )
01454 {
01455 *ErrorCode = MAX2(*ErrorCode,QH_ARRAY_FAIL);
01456 break;
01457 }
01458
01459
01460 fac = RadCooling*gv.bin[nd]->cnv_GR_pCM3*EN1RYD/gv.bin[nd]->GrainHeat;
01461
01462
01463 if( dPdlnT[k] < dPdlnT[k-1] && dPdlnT[k]/fac < PROB_CUTOFF_HI )
01464 {
01465 break;
01466 }
01467 }
01468
01469 if( l == MAX_LOOP )
01470 *ErrorCode = MAX2(*ErrorCode,QH_LOOP_FAIL);
01471
01472 nok = k;
01473 nbin = k+1;
01474
01475
01476 if( *ErrorCode < QH_NO_REBIN && nbin < NQMIN )
01477 *ErrorCode = MAX2(*ErrorCode,QH_NBIN_FAIL);
01478
01479
01480 if( *ErrorCode < QH_NO_REBIN )
01481 ScanProbDistr(u1,dPdlnT,nbin,maxVal,nmax,&nstart,&nstart2,&nend,nWideFail,ErrorCode);
01482
01483 if( *ErrorCode >= QH_NO_REBIN )
01484 {
01485 return;
01486 }
01487
01488 for( j=0; j < nbin; j++ )
01489 {
01490 p[j] /= fac;
01491 dPdlnT[j] /= fac;
01492 }
01493 RadCooling /= fac;
01494
01495
01496 *new_tmin = 0.;
01497 for( j=nstart; j < nbin; j++ )
01498 {
01499 if( dPdlnT[j] < PROB_CUTOFF_LO )
01500 {
01501 *new_tmin = qtemp[j];
01502 }
01503 else
01504 {
01505 if( j == nstart )
01506 {
01507 if( dPdlnT[j] > SAFETY*PROB_CUTOFF_LO )
01508 *ErrorCode = MAX2(*ErrorCode,QH_BOUND_FAIL);
01509
01510
01511 if( dPdlnT[nstart2] < 0.999*dPdlnT[nstart2+NSTARTUP] )
01512 {
01513
01514
01515
01516 double T1 = qtemp[nstart2];
01517 double T2 = qtemp[nstart2+NSTARTUP];
01518 double delta_y = log(dPdlnT[nstart2+NSTARTUP]/dPdlnT[nstart2]);
01519 double c2 = delta_y/(1./POW3(T1)-1./POW3(T2));
01520 double help = c2/POW3(T1) + log(dPdlnT[nstart2]/PROB_CUTOFF_LO);
01521 *new_tmin = pow(c2/help,1./3.);
01522 }
01523
01524
01525 if( dPdlnT[j] > SAFETY*PROB_CUTOFF_LO && *new_tmin >= qtmin1 )
01526 {
01527 double delta_x = log(qtemp[nstart2+NSTARTUP]/qtemp[nstart2]);
01528 double delta_y = log(dPdlnT[nstart2+NSTARTUP]/dPdlnT[nstart2]);
01529 delta_x *= log(PROB_CUTOFF_LO/dPdlnT[nstart2])/delta_y;
01530 *new_tmin = qtemp[nstart2]*exp(delta_x);
01531 if( *new_tmin < qtmin1 )
01532
01533 *new_tmin = sqrt( *new_tmin*qtmin1 );
01534 else
01535
01536 *new_tmin = qtmin1/DEF_FAC;
01537 }
01538 }
01539 break;
01540 }
01541 }
01542 *new_tmin = MAX3(*new_tmin,qtmin1/DEF_FAC,GRAIN_TMIN);
01543
01544 ASSERT( *new_tmin < gv.bin[nd]->tedust );
01545
01546
01547 if( dPdlnT[nbin-1] > SAFETY*PROB_CUTOFF_HI )
01548 {
01549 if( *ErrorCode == QH_ARRAY_FAIL || *ErrorCode == QH_LOOP_FAIL )
01550 {
01551 ++(*nWideFail);
01552
01553 if( *nWideFail < WIDE_FAIL_MAX )
01554 {
01555
01556
01557 *ErrorCode = MAX2(*ErrorCode,QH_DELTA_FAIL);
01558 }
01559 else
01560 {
01561 *ErrorCode = MAX2(*ErrorCode,QH_WIDE_FAIL);
01562 }
01563 }
01564 else
01565 {
01566 *ErrorCode = MAX2(*ErrorCode,QH_BOUND_FAIL);
01567 }
01568 }
01569
01570
01571
01572
01573
01574
01575
01576
01577
01578
01579
01580 nbin = RebinQHeatResults(nd,nstart,nend,p,qtemp,qprob,dPdlnT,u1,delu,Lambda,ErrorCode);
01581
01582
01583 if( nbin == 0 )
01584 {
01585 return;
01586 }
01587
01588 *qnbin = nbin;
01589
01590 sum = 0.;
01591 for( j=0; j < nbin; j++ )
01592 {
01593 sum += qprob[j];
01594 }
01595
01596
01597
01598 if( fabs(sum-1.) > PROB_TOL )
01599 *ErrorCode = MAX2(*ErrorCode,QH_CONV_FAIL);
01600
01601 if( trace.lgTrace && trace.lgDustBug )
01602 {
01603 fprintf( ioQQQ,
01604 " zone %ld %s nbin %ld nok %ld nbad %ld total prob %.4e rel_tol %.3e new_tmin %.4e\n",
01605 nzone,gv.bin[nd]->chDstLab,nbin,nok,nbad,sum,rel_tol,*new_tmin );
01606 }
01607 return;
01608 }
01609
01610
01611
01612
01613
01614
01615 STATIC double TryDoubleStep(double u1[],
01616 double delu[],
01617 double p[],
01618 double qtemp[],
01619 double Lambda[],
01620 double Phi[],
01621 double PhiDrv[],
01622 double step,
01623 double *cooling,
01624 long index,
01625 long nd,
01626 bool *lgBoundFail)
01627 {
01628 long i,
01629 j,
01630 jlo,
01631 k=-1000;
01632 double bval_jk,
01633 cooling2,
01634 p2k,
01635 p_max,
01636 RelErrCool,
01637 RelErrPk,
01638 sum,
01639 sum2 = -DBL_MAX,
01640 trap1,
01641 trap12 = -DBL_MAX,
01642 trap2,
01643 uhi,
01644 ulo,
01645 umin,
01646 y;
01647
01648 DEBUG_ENTRY( "TryDoubleStep()" );
01649
01650
01651 ASSERT( index >= 0 && index < NQGRID-2 && nd >= 0 && nd < gv.nBin && step > 0. );
01652
01653 ulo = rfield.anu[0];
01654
01655
01656 uhi = rfield.anu[gv.bin[nd]->qnflux-1];
01657
01658
01659 p_max = 0.;
01660 for( i=0; i <= index; i++ )
01661 p_max = MAX2(p_max,p[i]);
01662 jlo = 0;
01663 while( p[jlo] < PROB_CUTOFF_LO*p_max )
01664 jlo++;
01665
01666 for( i=1; i <= 2; i++ )
01667 {
01668 bool lgErr;
01669 long ipLo = 0;
01670 long ipHi = gv.bin[nd]->qnflux-1;
01671 k = index + i;
01672 delu[k] = step/2.;
01673 u1[k] = u1[k-1] + delu[k];
01674 qtemp[k] = inv_ufunct(u1[k],nd,lgBoundFail);
01675 splint_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,log(qtemp[k]),&y,&lgErr);
01676 *lgBoundFail = *lgBoundFail || lgErr;
01677 Lambda[k] = exp(y)*gv.bin[nd]->cnv_H_pGR/EN1RYD;
01678
01679 sum = sum2 = 0.;
01680 trap1 = trap2 = trap12 = 0.;
01681
01682
01683 for( j=jlo; j < k; j++ )
01684 {
01685 umin = u1[k] - u1[j];
01686
01687 if( umin >= uhi )
01688 {
01689
01690
01691
01692
01693 continue;
01694 }
01695 else if( umin > ulo )
01696 {
01697
01698
01699
01700
01701
01702
01703 long ipStep = 1;
01704
01705 while( rfield.anu[ipLo] > (realnum)umin )
01706 {
01707 ipHi = ipLo;
01708 ipLo -= ipStep;
01709 if( ipLo <= 0 )
01710 {
01711 ipLo = 0;
01712 break;
01713 }
01714 ipStep *= 2;
01715 }
01716
01717 while( ipHi-ipLo > 1 )
01718 {
01719 long ipMd = (ipLo+ipHi)/2;
01720 if( rfield.anu[ipMd] > (realnum)umin )
01721 ipHi = ipMd;
01722 else
01723 ipLo = ipMd;
01724 }
01725 bval_jk = Phi[ipLo] + (umin - rfield.anu[ipLo])*PhiDrv[ipLo];
01726 }
01727 else
01728 {
01729 bval_jk = Phi[0];
01730 }
01731
01732
01733 trap12 = trap1;
01734 sum2 = sum;
01735
01736
01737
01738
01739
01740
01741
01742
01743 trap2 = p[j]*bval_jk;
01744
01745 sum += (trap1 + trap2)*delu[j];
01746 trap1 = trap2;
01747 }
01748
01749
01750
01751
01752
01753
01754 p[k] = (sum + trap1*delu[k])/(2.*Lambda[k] - Phi[0]*delu[k]);
01755
01756
01757 if( p[k] <= 0. )
01758 return 3.*QHEAT_TOL;
01759 }
01760
01761
01762 p2k = (sum2 + trap12*step)/(2.*Lambda[k] - Phi[0]*step);
01763
01764
01765 if( p2k <= 0. )
01766 return 3.*QHEAT_TOL;
01767
01768 RelErrPk = fabs(p2k-p[k])/p[k];
01769
01770
01771
01772 *cooling = log_integral(u1[k-2],p[k-2]*Lambda[k-2],u1[k-1],p[k-1]*Lambda[k-1]);
01773 *cooling += log_integral(u1[k-1],p[k-1]*Lambda[k-1],u1[k],p[k]*Lambda[k]);
01774
01775
01776 cooling2 = log_integral(u1[k-2],p[k-2]*Lambda[k-2],u1[k],p2k*Lambda[k]);
01777
01778
01779 RelErrCool = ( index > 0 ) ? fabs(cooling2-(*cooling))/(*cooling) : 0.;
01780
01781
01782
01783 return MAX2(RelErrPk,RelErrCool)/3.;
01784 }
01785
01786
01787
01788 STATIC double log_integral(double xx1,
01789 double yy1,
01790 double xx2,
01791 double yy2)
01792 {
01793 double eps,
01794 result,
01795 xx;
01796
01797 DEBUG_ENTRY( "log_integral()" );
01798
01799
01800 ASSERT( xx1 > 0. && yy1 > 0. && xx2 > 0. && yy2 > 0. );
01801
01802 xx = log(xx2/xx1);
01803 eps = log((xx2/xx1)*(yy2/yy1));
01804 if( fabs(eps) < 1.e-4 )
01805 {
01806 result = xx1*yy1*xx*((eps/6. + 0.5)*eps + 1.);
01807 }
01808 else
01809 {
01810 result = (xx2*yy2 - xx1*yy1)*xx/eps;
01811 }
01812 return result;
01813 }
01814
01815
01816
01817 STATIC void ScanProbDistr(double u1[],
01818 double dPdlnT[],
01819 long nbin,
01820 double maxVal,
01821 long nmax,
01822 long *nstart,
01823 long *nstart2,
01824 long *nend,
01825 long *nWideFail,
01826 QH_Code *ErrorCode)
01827 {
01828 bool lgSetLo,
01829 lgSetHi;
01830 long i;
01831 double deriv_max,
01832 minVal;
01833 const double MIN_FAC_LO = 1.e4;
01834 const double MIN_FAC_HI = 1.e4;
01835
01836 DEBUG_ENTRY( "ScanProbDistr()" );
01837
01838
01839 ASSERT( nbin > 0 && nmax >= 0 && nmax < nbin && maxVal > 0. );
01840
01841
01842
01843
01844
01845
01846
01847 minVal = maxVal;
01848 *nstart = nmax;
01849 for( i=nmax; i >= 0; --i )
01850 {
01851 if( dPdlnT[i] < minVal )
01852 {
01853 *nstart = i;
01854 minVal = dPdlnT[i];
01855 }
01856 }
01857 deriv_max = 0.;
01858 *nstart2 = nmax;
01859 for( i=nmax; i > *nstart; --i )
01860 {
01861 double deriv = log(dPdlnT[i]/dPdlnT[i-1])/log(u1[i]/u1[i-1]);
01862 if( deriv > deriv_max )
01863 {
01864 *nstart2 = i-1;
01865 deriv_max = deriv;
01866 }
01867 }
01868 *nend = nbin-1;
01869
01870
01871 lgSetLo = ( nmax >= *nend || maxVal/dPdlnT[*nend] < MIN_FAC_HI );
01872
01873
01874
01875 if( lgSetLo )
01876
01877 lgSetHi = ( nmax <= *nstart || maxVal/dPdlnT[*nstart] < MIN_FAC_LO );
01878 else
01879 lgSetHi = ( nmax <= *nstart2 || maxVal/dPdlnT[*nstart2] < MIN_FAC_LO );
01880
01881 if( lgSetLo && lgSetHi )
01882 {
01883 ++(*nWideFail);
01884
01885 if( *nWideFail >= WIDE_FAIL_MAX )
01886 *ErrorCode = MAX2(*ErrorCode,QH_WIDE_FAIL);
01887 }
01888
01889 if( lgSetLo )
01890 *ErrorCode = MAX2(*ErrorCode,QH_LOW_FAIL);
01891
01892 if( lgSetHi )
01893 *ErrorCode = MAX2(*ErrorCode,QH_HIGH_FAIL);
01894
01895
01896 if( *ErrorCode < QH_NO_REBIN && (*nend - *nstart) < NQMIN )
01897 *ErrorCode = MAX2(*ErrorCode,QH_NBIN_FAIL);
01898
01899 if( trace.lgTrace && trace.lgDustBug )
01900 {
01901 fprintf( ioQQQ, " ScanProbDistr nstart %ld nstart2 %ld nend %ld nmax %ld maxVal %.3e",
01902 *nstart,*nstart2,*nend,nmax,maxVal );
01903 fprintf( ioQQQ, " dPdlnT[nstart] %.3e dPdlnT[nstart2] %.3e dPdlnT[nend] %.3e code %d\n",
01904 dPdlnT[*nstart],dPdlnT[*nstart2],dPdlnT[*nend],*ErrorCode );
01905 }
01906
01907 if( *ErrorCode >= QH_NO_REBIN )
01908 {
01909 *nstart = -1;
01910 *nstart2 = -1;
01911 *nend = -2;
01912 }
01913 return;
01914 }
01915
01916
01917
01918 STATIC long RebinQHeatResults(long nd,
01919 long nstart,
01920 long nend,
01921 double p[],
01922 double qtemp[],
01923 double qprob[],
01924 double dPdlnT[],
01925 double u1[],
01926 double delu[],
01927 double Lambda[],
01928 QH_Code *ErrorCode)
01929 {
01930 long i,
01931 newnbin;
01932 double fac,
01933 help,
01934 mul_fac,
01935 *new_delu,
01936 *new_dPdlnT,
01937 *new_Lambda,
01938 *new_p,
01939 *new_qprob,
01940 *new_qtemp,
01941 *new_u1,
01942 PP1,
01943 PP2,
01944 RadCooling,
01945 T1,
01946 T2,
01947 Ucheck,
01948 uu1,
01949 uu2;
01950
01951 DEBUG_ENTRY( "RebinQHeatResults()" );
01952
01953
01954 ASSERT( nd >= 0 && nd < gv.nBin );
01955
01956 ASSERT( nstart >= 0 && nstart < nend && nend < NQGRID );
01957
01958
01959 for( i=nstart; i <= nend && dPdlnT[i] < PROB_CUTOFF_LO; i++ ) {}
01960
01961
01962 if( i >= NQGRID )
01963 {
01964 *ErrorCode = MAX2(*ErrorCode,QH_REBIN_FAIL);
01965 return 0;
01966 }
01967
01968
01969 new_delu = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
01970 new_dPdlnT = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
01971 new_Lambda = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
01972 new_p = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
01973 new_qprob = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
01974 new_qtemp = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
01975 new_u1 = (double*)MALLOC((size_t)(NQGRID*sizeof(double)));
01976
01977 newnbin = 0;
01978
01979 T1 = qtemp[i];
01980 PP1 = p[i];
01981 uu1 = u1[i];
01982
01983
01984 help = pow(qtemp[nend]/qtemp[i],1./(1.5*NQMIN));
01985 mul_fac = MIN2(QT_RATIO,help);
01986
01987 Ucheck = u1[i];
01988 RadCooling = 0.;
01989
01990 while( i < nend )
01991 {
01992 bool lgBoundErr;
01993 bool lgDone= false;
01994 double s0 = 0.;
01995 double s1 = 0.;
01996 double wid = 0.;
01997 double xx,y;
01998
01999 T2 = T1*mul_fac;
02000
02001 do
02002 {
02003 double p1,p2,L1,L2,frac,slope;
02004 if( qtemp[i] <= T1 && T1 <= qtemp[i+1] )
02005 {
02006
02007
02008 frac = log(T1/qtemp[i]);
02009 slope = log(p[i+1]/p[i])/log(qtemp[i+1]/qtemp[i]);
02010 p1 = p[i]*exp(frac*slope);
02011 slope = log(Lambda[i+1]/Lambda[i])/log(qtemp[i+1]/qtemp[i]);
02012 L1 = Lambda[i]*exp(frac*slope);
02013 }
02014 else
02015 {
02016
02017
02018 p1 = p[i];
02019 L1 = Lambda[i];
02020 }
02021 if( qtemp[i] <= T2 && T2 <= qtemp[i+1] )
02022 {
02023
02024 help = ufunct(T2,nd,&lgBoundErr);
02025 uu2 = MIN2(help,u1[i+1]);
02026 ASSERT( !lgBoundErr );
02027 frac = log(T2/qtemp[i]);
02028 slope = log(p[i+1]/p[i])/log(qtemp[i+1]/qtemp[i]);
02029 p2 = p[i]*exp(frac*slope);
02030 slope = log(Lambda[i+1]/Lambda[i])/log(qtemp[i+1]/qtemp[i]);
02031 L2 = Lambda[i]*exp(frac*slope);
02032 lgDone = true;
02033 }
02034 else
02035 {
02036 uu2 = u1[i+1];
02037 p2 = p[i+1];
02038 L2 = Lambda[i+1];
02039
02040
02041 if( MAX2(p2,PP1)/MIN2(p2,PP1) > sqrt(SAFETY) )
02042 {
02043 lgDone = true;
02044 T2 = qtemp[i+1];
02045 }
02046 ++i;
02047 }
02048 PP2 = p2;
02049 wid += uu2 - uu1;
02050
02051 ASSERT( wid >= 0. );
02052 s0 += log_integral(uu1,p1,uu2,p2);
02053 s1 += log_integral(uu1,p1*L1,uu2,p2*L2);
02054 uu1 = uu2;
02055
02056 } while( i < nend && ! lgDone );
02057
02058
02059
02060
02061 if( s0 <= 0. )
02062 {
02063 ASSERT( wid == 0. );
02064 break;
02065 }
02066
02067 new_qprob[newnbin] = s0;
02068 new_Lambda[newnbin] = s1/s0;
02069 xx = log(new_Lambda[newnbin]*EN1RYD*gv.bin[nd]->cnv_GR_pH);
02070 splint_safe(gv.bin[nd]->dstems,gv.dsttmp,gv.bin[nd]->dstslp,NDEMS,xx,&y,&lgBoundErr);
02071 ASSERT( !lgBoundErr );
02072 new_qtemp[newnbin] = exp(y);
02073 new_u1[newnbin] = ufunct(new_qtemp[newnbin],nd,&lgBoundErr);
02074 ASSERT( !lgBoundErr );
02075 new_delu[newnbin] = wid;
02076 new_p[newnbin] = new_qprob[newnbin]/new_delu[newnbin];
02077 new_dPdlnT[newnbin] = new_p[newnbin]*new_qtemp[newnbin]*uderiv(new_qtemp[newnbin],nd);
02078
02079 Ucheck += wid;
02080 RadCooling += new_qprob[newnbin]*new_Lambda[newnbin];
02081
02082 T1 = T2;
02083 PP1 = PP2;
02084 ++newnbin;
02085 }
02086
02087
02088 if( newnbin < NQMIN )
02089 {
02090 *ErrorCode = MAX2(*ErrorCode,QH_REBIN_FAIL);
02091
02092 free(new_delu);
02093 free(new_dPdlnT);
02094 free(new_Lambda);
02095 free(new_p);
02096 free(new_qprob);
02097 free(new_qtemp);
02098 free(new_u1);
02099 return 0;
02100 }
02101
02102 fac = RadCooling*EN1RYD*gv.bin[nd]->cnv_GR_pCM3/gv.bin[nd]->GrainHeat;
02103
02104 if( trace.lgTrace && trace.lgDustBug )
02105 {
02106 fprintf( ioQQQ, " RebinQHeatResults found tol1 %.4e tol2 %.4e\n",
02107 fabs(u1[nend]/Ucheck-1.), fabs(fac-1.) );
02108 }
02109
02110
02111
02112 ASSERT( fabs(u1[nend]/Ucheck-1.) < 10.*sqrt((double)(nend-nstart+newnbin))*DBL_EPSILON );
02113
02114 if( fabs(fac-1.) > CONSERV_TOL )
02115 *ErrorCode = MAX2(*ErrorCode,QH_CONV_FAIL);
02116
02117 for( i=0; i < newnbin; i++ )
02118 {
02119
02120 p[i] = new_p[i]/fac;
02121 qtemp[i] = new_qtemp[i];
02122 qprob[i] = new_qprob[i]/fac;
02123 dPdlnT[i] = new_dPdlnT[i]/fac;
02124 u1[i] = new_u1[i];
02125 delu[i] = new_delu[i];
02126 Lambda[i] = new_Lambda[i];
02127
02128
02129 ASSERT( qtemp[i] > 0. && qprob[i] > 0. );
02130
02131
02132 }
02133
02134 free(new_delu);
02135 free(new_dPdlnT);
02136 free(new_Lambda);
02137 free(new_p);
02138 free(new_qprob);
02139 free(new_qtemp);
02140 free(new_u1);
02141 return newnbin;
02142 }
02143
02144
02145
02146 STATIC void GetProbDistr_HighLimit(long nd,
02147 double TolFac,
02148 double Umax,
02149 double fwhm,
02150 double qtemp[],
02151 double qprob[],
02152 double dPdlnT[],
02153 double *tol,
02154 long *qnbin,
02155 double *new_tmin,
02156 QH_Code *ErrorCode)
02157 {
02158 bool lgBoundErr,
02159 lgErr;
02160 long i,
02161 nbin;
02162 double c1,
02163 c2,
02164 delu[NQGRID],
02165 fac,
02166 fac1,
02167 fac2,
02168 help1,
02169 help2,
02170 L1,
02171 L2,
02172 Lambda[NQGRID],
02173 mul_fac,
02174 p[NQGRID],
02175 p1,
02176 p2,
02177 RadCooling,
02178 sum,
02179 T1,
02180 T2,
02181 Tlo,
02182 Thi,
02183 Ulo,
02184 Uhi,
02185 uu1,
02186 uu2,
02187 xx,
02188 y;
02189
02190 DEBUG_ENTRY( "GetProbDistr_HighLimit()" );
02191
02192 if( trace.lgTrace && trace.lgDustBug )
02193 {
02194 fprintf( ioQQQ, " GetProbDistr_HighLimit called for grain %s\n", gv.bin[nd]->chDstLab );
02195 }
02196
02197 c1 = sqrt(4.*LN_TWO/PI)/fwhm*exp(-POW2(fwhm/Umax)/(16.*LN_TWO));
02198 c2 = 4.*LN_TWO*POW2(Umax/fwhm);
02199
02200 fac1 = fwhm/Umax*sqrt(-log(PROB_CUTOFF_LO)/(4.*LN_TWO));
02201
02202 help1 = Umax*exp(-fac1);
02203 help2 = exp(gv.bin[nd]->DustEnth[0]);
02204 Ulo = MAX2(help1,help2);
02205
02206 Tlo = inv_ufunct(Ulo,nd,&lgBoundErr);
02207
02208 fac2 = fwhm/Umax*sqrt(-log(PROB_CUTOFF_HI)/(4.*LN_TWO));
02209
02210 if( fac2 > log(DBL_MAX/10.) )
02211 {
02212 *ErrorCode = MAX2(*ErrorCode,QH_WIDE_FAIL);
02213 return;
02214 }
02215 Uhi = Umax*exp(fac2);
02216 Thi = inv_ufunct(Uhi,nd,&lgBoundErr);
02217
02218 nbin = 0;
02219
02220 T1 = Tlo;
02221 uu1 = ufunct(T1,nd,&lgErr);
02222 lgBoundErr = lgBoundErr || lgErr;
02223 help1 = log(uu1/Umax);
02224 p1 = c1*exp(-c2*POW2(help1));
02225 splint_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,log(T1),&y,&lgErr);
02226 lgBoundErr = lgBoundErr || lgErr;
02227 L1 = exp(y)*gv.bin[nd]->cnv_H_pGR/EN1RYD;
02228
02229
02230 if( uu1*p1*L1 < 1.e5*DBL_MIN )
02231 {
02232 *ErrorCode = MAX2(*ErrorCode,QH_WIDE_FAIL);
02233 return;
02234 }
02235
02236
02237 help1 = pow(Thi/Tlo,1./(1.2*NQMIN));
02238 mul_fac = MIN2(QT_RATIO,help1);
02239
02240 sum = 0.;
02241 RadCooling = 0.;
02242
02243 do
02244 {
02245 double s0,s1,wid;
02246
02247 T2 = T1*mul_fac;
02248 uu2 = ufunct(T2,nd,&lgErr);
02249 lgBoundErr = lgBoundErr || lgErr;
02250 help1 = log(uu2/Umax);
02251 p2 = c1*exp(-c2*POW2(help1));
02252 splint_safe(gv.dsttmp,gv.bin[nd]->dstems,gv.bin[nd]->dstslp2,NDEMS,log(T2),&y,&lgErr);
02253 lgBoundErr = lgBoundErr || lgErr;
02254 L2 = exp(y)*gv.bin[nd]->cnv_H_pGR/EN1RYD;
02255
02256 wid = uu2 - uu1;
02257 s0 = log_integral(uu1,p1,uu2,p2);
02258 s1 = log_integral(uu1,p1*L1,uu2,p2*L2);
02259
02260 qprob[nbin] = s0;
02261 Lambda[nbin] = s1/s0;
02262 xx = log(Lambda[nbin]*EN1RYD*gv.bin[nd]->cnv_GR_pH);
02263 splint_safe(gv.bin[nd]->dstems,gv.dsttmp,gv.bin[nd]->dstslp,NDEMS,xx,&y,&lgErr);
02264 lgBoundErr = lgBoundErr || lgErr;
02265 qtemp[nbin] = exp(y);
02266 delu[nbin] = wid;
02267 p[nbin] = qprob[nbin]/delu[nbin];
02268 dPdlnT[nbin] = p[nbin]*qtemp[nbin]*uderiv(qtemp[nbin],nd);
02269
02270 sum += qprob[nbin];
02271 RadCooling += qprob[nbin]*Lambda[nbin];
02272
02273 T1 = T2;
02274 uu1 = uu2;
02275 p1 = p2;
02276 L1 = L2;
02277
02278 ++nbin;
02279
02280 } while( T2 < Thi && nbin < NQGRID );
02281
02282 fac = RadCooling*EN1RYD*gv.bin[nd]->cnv_GR_pCM3/gv.bin[nd]->GrainHeat;
02283
02284 for( i=0; i < nbin; ++i )
02285 {
02286 qprob[i] /= fac;
02287 dPdlnT[i] /= fac;
02288 }
02289
02290 *tol = sum/fac;
02291 *qnbin = nbin;
02292 *new_tmin = qtemp[0];
02293 *ErrorCode = MAX2(*ErrorCode,QH_ANALYTIC);
02294
02295
02296 if( TolFac > STRICT )
02297 *ErrorCode = MAX2(*ErrorCode,QH_ANALYTIC_RELAX);
02298
02299 if( lgBoundErr )
02300 *ErrorCode = MAX2(*ErrorCode,QH_THIGH_FAIL);
02301
02302 if( fabs(sum/fac-1.) > PROB_TOL )
02303 *ErrorCode = MAX2(*ErrorCode,QH_CONV_FAIL);
02304
02305 if( dPdlnT[0] > SAFETY*PROB_CUTOFF_LO || dPdlnT[nbin-1] > SAFETY*PROB_CUTOFF_HI )
02306 *ErrorCode = MAX2(*ErrorCode,QH_BOUND_FAIL);
02307
02308 if( trace.lgTrace && trace.lgDustBug )
02309 {
02310 fprintf( ioQQQ, " GetProbDistr_HighLimit found tol1 %.4e tol2 %.4e\n",
02311 fabs(sum-1.), fabs(sum/fac-1.) );
02312 fprintf( ioQQQ, " zone %ld %s nbin %ld total prob %.4e new_tmin %.4e\n",
02313 nzone,gv.bin[nd]->chDstLab,nbin,sum/fac,*new_tmin );
02314 }
02315 return;
02316 }
02317
02318
02319
02320 STATIC double uderiv(double temp,
02321 long int nd)
02322 {
02323 enth_type ecase;
02324 long int i,
02325 j;
02326 double N_C,
02327 N_H;
02328 double deriv = 0.,
02329 hok[3] = {1275., 1670., 4359.},
02330 numer,
02331 dnumer,
02332 denom,
02333 ddenom,
02334 x;
02335
02336
02337 DEBUG_ENTRY( "uderiv()" );
02338
02339 if( temp <= 0. )
02340 {
02341 fprintf( ioQQQ, " uderiv called with non-positive temperature: %.6e\n" , temp );
02342 cdEXIT(EXIT_FAILURE);
02343 }
02344 ASSERT( nd >= 0 && nd < gv.nBin );
02345
02346 ecase = gv.which_enth[gv.bin[nd]->matType];
02347 switch( ecase )
02348 {
02349 case ENTH_CAR:
02350 numer = (4.15e-22/EN1RYD)*pow(temp,3.3);
02351 dnumer = (3.3*4.15e-22/EN1RYD)*pow(temp,2.3);
02352 denom = 1. + 6.51e-03*temp + 1.5e-06*temp*temp + 8.3e-07*pow(temp,2.3);
02353 ddenom = 6.51e-03 + 2.*1.5e-06*temp + 2.3*8.3e-07*pow(temp,1.3);
02354
02355
02356 deriv = (dnumer*denom - numer*ddenom)/POW2(denom);
02357 break;
02358 case ENTH_CAR2:
02359
02360
02361 deriv = (DebyeDeriv(temp/863.,2) + 2.*DebyeDeriv(temp/2504.,2))*BOLTZMANN/EN1RYD;
02362 break;
02363 case ENTH_SIL:
02364
02365
02366
02367 for( j = 0; j < 4; j++ )
02368 {
02369 if( temp > tlim[j] && temp <= tlim[j+1] )
02370 {
02371 deriv = cval[j]*pow(temp,ppower[j]);
02372 break;
02373 }
02374 }
02375 break;
02376 case ENTH_SIL2:
02377
02378
02379 deriv = (2.*DebyeDeriv(temp/500.,2) + DebyeDeriv(temp/1500.,3))*BOLTZMANN/EN1RYD;
02380 break;
02381 case ENTH_PAH:
02382
02383
02384
02385 x = log10(MIN2(temp,2000.));
02386 deriv = pow(10.,-21.26+3.1688*x-0.401894*POW2(x))/EN1RYD;
02387 break;
02388 case ENTH_PAH2:
02389
02390
02391
02392
02393 N_C = NO_ATOMS(nd);
02394 if( N_C <= 25. )
02395 {
02396 N_H = 0.5*N_C;
02397 }
02398 else if( N_C <= 100. )
02399 {
02400 N_H = 2.5*sqrt(N_C);
02401 }
02402 else
02403 {
02404 N_H = 0.25*N_C;
02405 }
02406 deriv = 0.;
02407 for( i=0; i < 3; i++ )
02408 {
02409 double help1 = hok[i]/temp;
02410 if( help1 < 300. )
02411 {
02412 double help2 = exp(help1);
02413 double help3 = ( help1 < 1.e-7 ) ? help1*(1.+0.5*help1) : help2-1.;
02414 deriv += N_H/(N_C-2.)*POW2(help1)*help2/POW2(help3)*BOLTZMANN/EN1RYD;
02415 }
02416 }
02417 deriv += (DebyeDeriv(temp/863.,2) + 2.*DebyeDeriv(temp/2504.,2))*BOLTZMANN/EN1RYD;
02418 break;
02419 default:
02420 fprintf( ioQQQ, " uderiv called with unknown type for enthalpy function: %d\n", ecase );
02421 cdEXIT(EXIT_FAILURE);
02422 }
02423
02424
02425
02426 deriv *= MAX2(NO_ATOMS(nd)-2.,1.);
02427
02428 if( deriv <= 0. )
02429 {
02430 fprintf( ioQQQ, " uderiv finds non-positive derivative: %.6e, what's up?\n" , deriv );
02431 cdEXIT(EXIT_FAILURE);
02432 }
02433 return( deriv );
02434 }
02435
02436
02437
02438 STATIC double ufunct(double temp,
02439 long int nd,
02440 bool *lgBoundErr)
02441 {
02442 double enthalpy,
02443 y;
02444
02445 DEBUG_ENTRY( "ufunct()" );
02446
02447 if( temp <= 0. )
02448 {
02449 fprintf( ioQQQ, " ufunct called with non-positive temperature: %.6e\n" , temp );
02450 cdEXIT(EXIT_FAILURE);
02451 }
02452 ASSERT( nd >= 0 && nd < gv.nBin );
02453
02454
02455 splint_safe(gv.dsttmp,gv.bin[nd]->DustEnth,gv.bin[nd]->EnthSlp,NDEMS,log(temp),&y,lgBoundErr);
02456 enthalpy = exp(y);
02457
02458 ASSERT( enthalpy > 0. );
02459 return( enthalpy );
02460 }
02461
02462
02463
02464 STATIC double inv_ufunct(double enthalpy,
02465 long int nd,
02466 bool *lgBoundErr)
02467 {
02468 double temp,
02469 y;
02470
02471 DEBUG_ENTRY( "inv_ufunct()" );
02472
02473 if( enthalpy <= 0. )
02474 {
02475 fprintf( ioQQQ, " inv_ufunct called with non-positive enthalpy: %.6e\n" , enthalpy );
02476 cdEXIT(EXIT_FAILURE);
02477 }
02478 ASSERT( nd >= 0 && nd < gv.nBin );
02479
02480
02481 splint_safe(gv.bin[nd]->DustEnth,gv.dsttmp,gv.bin[nd]->EnthSlp2,NDEMS,log(enthalpy),&y,lgBoundErr);
02482 temp = exp(y);
02483
02484 ASSERT( temp > 0. );
02485 return( temp );
02486 }
02487
02488
02489
02490 void InitEnthalpy(void)
02491 {
02492 double C_V1,
02493 C_V2,
02494 tdust1,
02495 tdust2;
02496 long int i,
02497 j,
02498 nd;
02499
02500 DEBUG_ENTRY( "InitEnthalpy()" );
02501
02502 for( nd=0; nd < gv.nBin; nd++ )
02503 {
02504 tdust2 = GRAIN_TMIN;
02505 C_V2 = uderiv(tdust2,nd);
02506
02507 gv.bin[nd]->DustEnth[0] = C_V2*tdust2/4.;
02508 tdust1 = tdust2;
02509 C_V1 = C_V2;
02510
02511 for( i=1; i < NDEMS; i++ )
02512 {
02513 double tmid,Cmid;
02514 tdust2 = exp(gv.dsttmp[i]);
02515 C_V2 = uderiv(tdust2,nd);
02516 tmid = sqrt(tdust1*tdust2);
02517
02518 for( j=1; j < 4; j++ )
02519 {
02520 if( tdust1 < tlim[j] && tlim[j] < tdust2 )
02521 {
02522 tmid = tlim[j];
02523 break;
02524 }
02525 }
02526 Cmid = uderiv(tmid,nd);
02527 gv.bin[nd]->DustEnth[i] = gv.bin[nd]->DustEnth[i-1] +
02528 log_integral(tdust1,C_V1,tmid,Cmid) +
02529 log_integral(tmid,Cmid,tdust2,C_V2);
02530 tdust1 = tdust2;
02531 C_V1 = C_V2;
02532 }
02533 }
02534
02535
02536 for( nd=0; nd < gv.nBin; nd++ )
02537 {
02538 for( i=0; i < NDEMS; i++ )
02539 {
02540 gv.bin[nd]->DustEnth[i] = log(gv.bin[nd]->DustEnth[i]);
02541 }
02542 }
02543
02544
02545 for( nd=0; nd < gv.nBin; nd++ )
02546 {
02547
02548 spline(gv.dsttmp,gv.bin[nd]->DustEnth,NDEMS,2e31,2e31,gv.bin[nd]->EnthSlp);
02549 spline(gv.bin[nd]->DustEnth,gv.dsttmp,NDEMS,2e31,2e31,gv.bin[nd]->EnthSlp2);
02550 }
02551 return;
02552 }
02553
02554
02555
02556 STATIC double DebyeDeriv(double x,
02557 long n)
02558 {
02559 long i,
02560 nn;
02561 double res,
02562 *xx,
02563 *rr,
02564 *aa,
02565 *ww;
02566
02567 DEBUG_ENTRY( "DebyeDeriv()" );
02568
02569 ASSERT( x > 0. );
02570 ASSERT( n == 2 || n == 3 );
02571
02572 if( x < 0.001 )
02573 {
02574
02575 if( n == 2 )
02576 {
02577 res = 6.*1.202056903159594*POW2(x);
02578 }
02579 else if( n == 3 )
02580 {
02581 res = 24.*1.082323233711138*POW3(x);
02582 }
02583 else
02584
02585
02586 TotalInsanity();
02587 }
02588 else
02589 {
02590 nn = 4*MAX2(4,2*(long)(0.05/x));
02591 xx = (double *)MALLOC(sizeof(double)*(unsigned)nn);
02592 rr = (double *)MALLOC(sizeof(double)*(unsigned)nn);
02593 aa = (double *)MALLOC(sizeof(double)*(unsigned)nn);
02594 ww = (double *)MALLOC(sizeof(double)*(unsigned)nn);
02595 gauss_legendre(nn,xx,aa);
02596 gauss_init(nn,0.,1.,xx,aa,rr,ww);
02597
02598 res = 0.;
02599 for( i=0; i < nn; i++ )
02600 {
02601 double help1 = rr[i]/x;
02602 if( help1 < 300. )
02603 {
02604 double help2 = exp(help1);
02605 double help3 = ( help1 < 1.e-7 ) ? help1*(1.+0.5*help1) : help2-1.;
02606 res += ww[i]*powi(rr[i],n+1)*help2/POW2(help3);
02607 }
02608 }
02609 res /= POW2(x);
02610
02611 free(ww);
02612 free(aa);
02613 free(rr);
02614 free(xx);
02615 }
02616 return (double)n*res;
02617 }