00001
00002
00003
00004 #include "cddefines.h"
00005 #include "yield.h"
00006 #include "prt.h"
00007 #include "continuum.h"
00008 #include "iso.h"
00009 #include "dynamics.h"
00010 #include "grainvar.h"
00011 #include "hmi.h"
00012 #include "mole.h"
00013 #include "thermal.h"
00014 #include "thirdparty.h"
00015 #include "conv.h"
00016 #include "secondaries.h"
00017 #include "phycon.h"
00018 #include "atmdat.h"
00019 #include "heavy.h"
00020 #include "elementnames.h"
00021 #include "dense.h"
00022 #include "radius.h"
00023 #include "ionbal.h"
00024
00025 void solveions(double *ion, double *rec, double *snk, double *src,
00026 long int nlev, long int nmax);
00027
00028 void ion_solver(
00029
00030 long int nelem,
00031
00032 bool lgPrintIt)
00033 {
00034
00035 bool lgTriDiag=false;
00036 long int ion,
00037 limit,
00038 IonProduced,
00039 nej,
00040 ns,
00041 jmax=-1;
00042 double totsrc;
00043 double dennew, rateone;
00044 bool lgNegPop;
00045 static bool lgMustMalloc=true;
00046 static double *sink, *source , *sourcesave;
00047 int32 nerror;
00048 static int32 *ipiv;
00049 long ion_low, ion_range, i, j, ion_to , ion_from;
00050 static double sum_dense;
00051
00052 static double *auger;
00053
00054 double abund_total, renormnew;
00055 bool lgHomogeneous = true;
00056 static double *xmat , *xmatsave;
00057
00058 DEBUG_ENTRY( "ion_solver()" );
00059
00060
00061 ASSERT( nelem >= 0);
00062 ASSERT( dense.IonLow[nelem] >= 0 );
00063 ASSERT( dense.IonHigh[nelem] >= 0 );
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 if( nelem == ipHYDROGEN )
00076 {
00077
00078
00079
00080
00081 abund_total = dense.xIonDense[nelem][0] + dense.xIonDense[nelem][1];
00082 }
00083 else
00084 {
00085 abund_total = SDIV( dense.gas_phase[nelem] - dense.xMolecules[nelem] );
00086 }
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100 if( nelem>ipHYDROGEN && dense.xMolecules[nelem]/SDIV(dense.gas_phase[nelem]) < 1e-10 )
00101 {
00102 for( ion=dense.IonLow[nelem]; ion<=dense.IonHigh[nelem]; ++ion )
00103 {
00104 mole.source[nelem][ion] = 0.;
00105 mole.sink[nelem][ion] = 0.;
00106 }
00107 }
00108
00109
00110
00111
00112
00113
00114
00115 if( fabs( dense.gas_phase[nelem] - dense.xMolecules[nelem])/SDIV(dense.gas_phase[nelem] ) <
00116 10.*FLT_EPSILON )
00117 {
00118 double renorm;
00119
00120
00121
00122
00123
00124
00125
00126 realnum sum = 0.;
00127 for( ion=dense.IonLow[nelem]; ion<=dense.IonHigh[nelem]; ++ion )
00128 sum += dense.xIonDense[nelem][ion];
00129
00130
00131 renorm = dense.gas_phase[nelem] / SDIV(sum + dense.xMolecules[nelem] );
00132
00133 abund_total = renorm * sum;
00134 }
00135
00136
00137 if( abund_total < 0. )
00138 {
00139
00140
00141
00142
00143
00144
00145
00146
00147 if(!conv.lgSearch )
00148 fprintf(ioQQQ,
00149 "PROBLEM ion_solver - neg net atomic abundance zero for nelem= %li, rel val= %.2e conv.nTotalIoniz=%li, fixed\n",
00150 nelem,
00151 fabs(abund_total) / SDIV(dense.xMolecules[nelem]),
00152 conv.nTotalIoniz );
00153
00154
00155 abund_total = -abund_total/2.;
00156
00157
00158
00159
00160 conv.lgConvIoniz = false;
00161 strcpy( conv.chConvIoniz, "neg ion" );
00162 }
00163
00164
00165 if( dense.IonHigh[nelem] == 0 )
00166 {
00167
00168 dense.xIonDense[nelem][0] = (realnum)abund_total;
00169 return;
00170 }
00171
00172
00173
00174 if( dense.lgSetIoniz[nelem] )
00175 {
00176 for( ion=0; ion<nelem+2; ++ion )
00177 {
00178 dense.xIonDense[nelem][ion] = dense.SetIoniz[nelem][ion]*
00179 (realnum)abund_total;
00180 }
00181 return;
00182 }
00183
00184
00185
00186
00187 ASSERT( (dense.IonHigh[nelem] < nelem + 1) ||
00188 iso.pop_ion_ov_neut[ipH_LIKE][nelem] > 0. );
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 limit = MIN2(nelem-NISO,dense.IonHigh[nelem]-1);
00200
00201
00202 ion_range = dense.IonHigh[nelem]-dense.IonLow[nelem]+1;
00203 ASSERT( ion_range <= nelem+2 );
00204
00205 ion_low = dense.IonLow[nelem];
00206
00207
00208
00209
00210 if( PARALLEL_MODE || lgMustMalloc )
00211 {
00212 long int ncell=LIMELM+1;
00213 lgMustMalloc = false;
00214 if( PARALLEL_MODE )
00215 ncell = ion_range;
00216
00217
00218 xmat=(double*)MALLOC( (sizeof(double)*(unsigned)(ncell*ncell) ));
00219 xmatsave=(double*)MALLOC( (sizeof(double)*(unsigned)(ncell*ncell) ));
00220 sink=(double*)MALLOC( (sizeof(double)*(unsigned)ncell ));
00221 source=(double*)MALLOC( (sizeof(double)*(unsigned)ncell ));
00222 sourcesave=(double*)MALLOC( (sizeof(double)*(unsigned)ncell ));
00223 ipiv=(int32*)MALLOC( (sizeof(int32)*(unsigned)ncell ));
00224
00225
00226
00227 auger=(double*)MALLOC( (sizeof(double)*(unsigned)(LIMELM+1) ));
00228 }
00229
00230 for( i=0; i<ion_range;i++ )
00231 {
00232 source[i] = 0.;
00233 }
00234
00235
00236 # undef MAT
00237 # define MAT(M_,I_,J_) (*((M_)+(I_)*(ion_range)+(J_)))
00238
00239
00240
00241
00242
00243 for( ion=0; ion <= limit; ion++ )
00244 {
00245 ionbal.RateIonizTot[nelem][ion] = 0.;
00246 }
00247
00248
00249
00250
00251 for( i=0; i< LIMELM+1; ++i )
00252 {
00253 auger[i] = 0.;
00254 }
00255
00256
00257 for( i=0; i< ion_range; ++i )
00258 {
00259 for( j=0; j< ion_range; ++j )
00260 {
00261 MAT( xmat, i, j ) = 0.;
00262 }
00263 }
00264
00265 {
00266
00267
00268 enum {DEBUG_LOC=false};
00269 if( DEBUG_LOC && nelem==ipCARBON )
00270 {
00271 broken();
00272 dense.IonLow[nelem] = 0;
00273 dense.IonHigh[nelem] = 3;
00274 abund_total = 1.;
00275 ion_range = dense.IonHigh[nelem]-dense.IonLow[nelem]+1;
00276
00277 for( ion=dense.IonLow[nelem]; ion <= limit; ion++ )
00278 {
00279 double fac=1;
00280 if(ion)
00281 fac = 1e-10;
00282 ionbal.RateRecomTot[nelem][ion] = 100.;
00283 for( ns=0; ns < Heavy.nsShells[nelem][ion]; ns++ )
00284 {
00285
00286 ionbal.PhotoRate_Shell[nelem][ion][ns][0] = fac;
00287 }
00288 }
00289 }
00290 }
00291
00292
00293
00294
00295
00296 for( ion_to=dense.IonLow[nelem]; ion_to <= limit; ion_to++ )
00297 {
00298 for( ion_from=dense.IonLow[nelem]; ion_from <= dense.IonHigh[nelem]; ++ion_from )
00299 {
00300
00301 if( ion_to != ion_from )
00302 {
00303
00304
00305 rateone = mole.xMoleChTrRate[nelem][ion_from][ion_to];
00306 ASSERT( rateone >= 0. );
00307 MAT( xmat, ion_from-ion_low, ion_from-ion_low ) -= rateone;
00308 MAT( xmat, ion_from-ion_low, ion_to-ion_low ) += rateone;
00309 }
00310 }
00311 }
00312
00313
00314
00315
00316
00317
00318 if( gv.lgDustOn && ionbal.lgGrainIonRecom && gv.lgGrainPhysicsOn )
00319 {
00320 long int low;
00321
00322
00323
00324 if( mole.lgElem_in_chemistry[nelem] )
00325
00326
00327 {
00328 low = MAX2(1, dense.IonLow[nelem] );
00329 }
00330 else
00331 low = dense.IonLow[nelem];
00332
00333 for( ion_to=low; ion_to <= dense.IonHigh[nelem]; ion_to++ )
00334 {
00335 for( ion_from=dense.IonLow[nelem]; ion_from <= dense.IonHigh[nelem]; ++ion_from )
00336 {
00337
00338 if( ion_to != ion_from )
00339 {
00340
00341
00342 rateone = gv.GrainChTrRate[nelem][ion_from][ion_to];
00343 MAT( xmat, ion_from-ion_low, ion_from-ion_low ) -= rateone;
00344 MAT( xmat, ion_from-ion_low, ion_to-ion_low ) += rateone;
00345 }
00346 }
00347 }
00348 }
00349
00350 for( ion=dense.IonLow[nelem]; ion <= limit; ion++ )
00351 {
00352
00353 rateone = ionbal.CollIonRate_Ground[nelem][ion][0] +
00354 secondaries.csupra[nelem][ion] +
00355
00356 ionbal.UTA_ionize_rate[nelem][ion];
00357 ionbal.RateIonizTot[nelem][ion] += rateone;
00358
00359
00360 if( ion+1-ion_low < ion_range )
00361 {
00362
00363 MAT( xmat, ion-ion_low, ion-ion_low ) -= rateone;
00364 MAT( xmat, ion-ion_low, ion+1-ion_low ) += rateone;
00365 }
00366
00367
00368 if( ion-1-ion_low >= 0 )
00369 {
00370
00371 MAT( xmat,ion-ion_low, ion-ion_low ) -= ionbal.RateRecomTot[nelem][ion-1];
00372 MAT( xmat,ion-ion_low, ion-1-ion_low ) += ionbal.RateRecomTot[nelem][ion-1];
00373 }
00374
00375
00376 for( ns=0; ns < Heavy.nsShells[nelem][ion]; ns++ )
00377 {
00378
00379 ionbal.RateIonizTot[nelem][ion] += ionbal.PhotoRate_Shell[nelem][ion][ns][0];
00380
00381
00382
00383
00384
00385
00386
00387
00388 if( ion+1-ion_low < ion_range )
00389 {
00390
00391 MAT( xmat, ion-ion_low, ion-ion_low ) -= ionbal.PhotoRate_Shell[nelem][ion][ns][0];
00392
00393
00394
00395
00396
00397
00398 for( nej=1; nej <= t_yield::Inst().nelec_eject(nelem,ion,ns); nej++ )
00399 {
00400
00401
00402
00403 IonProduced = MIN2(ion+nej,dense.IonHigh[nelem]);
00404 rateone = ionbal.PhotoRate_Shell[nelem][ion][ns][0]*
00405 t_yield::Inst().elec_eject_frac(nelem,ion,ns,nej-1);
00406
00407
00408
00409
00410
00411
00412
00413 MAT( xmat, ion-ion_low, IonProduced-ion_low ) += rateone;
00414
00415
00416
00417 if( nej>1 )
00418 auger[IonProduced-1] += rateone;
00419 }
00420 }
00421 }
00422
00423
00424 rateone =
00425 atmdat.HeCharExcIonOf[nelem][ion]*dense.xIonDense[ipHELIUM][1]+
00426 atmdat.HCharExcIonOf[nelem][ion]*dense.xIonDense[ipHYDROGEN][1];
00427 ionbal.RateIonizTot[nelem][ion] += rateone;
00428 if( ion+1-ion_low < ion_range )
00429 {
00430 MAT( xmat, ion-ion_low, ion-ion_low ) -= rateone;
00431 MAT( xmat, ion-ion_low, ion+1-ion_low ) += rateone;
00432 }
00433 }
00434
00435
00436
00437
00438 j = MAX2(0,limit+1);
00439
00440 j = MAX2( ion_low , j );
00441 for( ion=j; ion<=dense.IonHigh[nelem]; ion++ )
00442 {
00443 ASSERT( ion>=0 && ion<nelem+2 );
00444
00445 if( ion+1-ion_low < ion_range )
00446 {
00447
00448 MAT( xmat, ion-ion_low, ion-ion_low ) -= ionbal.RateIonizTot[nelem][ion];
00449 MAT( xmat, ion-ion_low, ion+1-ion_low ) += ionbal.RateIonizTot[nelem][ion];
00450 }
00451
00452 if( ion-1-ion_low >= 0 )
00453 {
00454
00455 MAT( xmat,ion-ion_low, ion-ion_low ) -= ionbal.RateRecomTot[nelem][ion-1];
00456 MAT( xmat,ion-ion_low, ion-1-ion_low ) += ionbal.RateRecomTot[nelem][ion-1];
00457 }
00458 }
00459
00460
00461
00462 totsrc = 0.;
00463
00464
00465
00466 if( conv.nTotalIoniz > 1 || iteration > 1 )
00467 {
00468
00469 for( i=0; i<ion_range;i++ )
00470 {
00471 ion = i+ion_low;
00472
00473
00474
00475
00476 source[i] -= mole.source[nelem][ion];
00477
00478 totsrc += mole.source[nelem][ion];
00479
00480
00481 MAT( xmat, i, i ) -= mole.sink[nelem][ion];
00482 }
00483 }
00484
00485
00486 if( totsrc != 0. )
00487 lgHomogeneous = false;
00488
00489
00490
00491 if( iteration > dynamics.n_initial_relax+1 && dynamics.lgAdvection
00492 )
00493 {
00494 for( i=0; i<ion_range;i++ )
00495 {
00496 ion = i+ion_low;
00497
00498 MAT( xmat, i, i ) -= dynamics.Rate;
00499
00500 source[i] -= dynamics.Source[nelem][ion];
00501
00502
00503 }
00504 lgHomogeneous = false;
00505 }
00506
00507 for( i=0; i< ion_range; ++i )
00508 {
00509 for( j=0; j< ion_range; ++j )
00510 {
00511 MAT( xmatsave, i, j ) = MAT( xmat, i, j );
00512 }
00513 sourcesave[i] = source[i];
00514 }
00515
00516
00517
00518
00519
00520
00521
00522
00523 if( !lgHomogeneous && ion_range==2 )
00524 {
00525
00526 double a = MAT( xmatsave, 0, 0 ),
00527 b = MAT( xmatsave, 1, 0 ) ,
00528 c = MAT( xmatsave, 0, 1 ) ,
00529 d = MAT( xmatsave, 1, 1 );
00530 b = SDIV(b);
00531 d = SDIV(d);
00532 double ratio1 = a/b , ratio2 = c/d , fratio1=fabs(a/b),fratio2=fabs(c/d);
00533 if( fabs(ratio1-ratio2)/MAX2(fratio1,fratio2) <DBL_EPSILON*1e4 )
00534 {
00535 abund_total = SDIV( dense.gas_phase[nelem] - dense.xMolecules[nelem] );
00536 lgHomogeneous = true;
00537 }
00538 }
00539 # if 0
00540 if( nelem==ipHYDROGEN &&
00541 fabs(dense.xMolecules[nelem]) / SDIV(dense.gas_phase[ipHYDROGEN]) <DBL_EPSILON*100. )
00542 {
00543 abund_total = SDIV( dense.gas_phase[nelem] - dense.xMolecules[nelem] );
00544 lgHomogeneous = true;
00545 }
00546 # endif
00547
00548
00549
00550
00551 if( lgHomogeneous )
00552 {
00553 double rate_ioniz=1., rate_recomb ;
00554
00555 jmax = 0;
00556 for( i=0; i<ion_range-1;i++)
00557 {
00558 ion = i+ion_low;
00559 rate_ioniz *= ionbal.RateIonizTot[nelem][ion];
00560 rate_recomb = ionbal.RateRecomTot[nelem][ion];
00561
00562 if( rate_ioniz == 0)
00563 break;
00564
00565 if( rate_recomb <= 0.)
00566 break;
00567
00568 rate_ioniz /= rate_recomb;
00569 if( rate_ioniz > 1.)
00570 {
00571
00572 jmax = i;
00573 rate_ioniz = 1.;
00574 }
00575 }
00576
00577
00578 for( i=0; i<ion_range;i++ )
00579 {
00580 MAT(xmat,i,jmax) = 1.;
00581 }
00582 source[jmax] = abund_total;
00583 }
00584
00585 for( i=0; i< ion_range; ++i )
00586 {
00587 for( j=0; j< ion_range; ++j )
00588 {
00589 MAT( xmatsave, i, j ) = MAT( xmat, i, j );
00590 }
00591 sourcesave[i] = source[i];
00592 }
00593
00594 if( false && nelem == ipHYDROGEN && dynamics.lgAdvection&& iteration>1 )
00595 {
00596 fprintf(ioQQQ,"DEBUGG Rate %.2f %.3e \n",fnzone,dynamics.Rate);
00597 fprintf(ioQQQ," %.3e %.3e\n", ionbal.RateIonizTot[nelem][0], ionbal.RateIonizTot[nelem][1]);
00598 fprintf(ioQQQ," %.3e %.3e\n", ionbal.RateRecomTot[nelem][0], ionbal.RateRecomTot[nelem][1]);
00599 fprintf(ioQQQ," %.3e %.3e %.3e\n\n", dynamics.Source[nelem][0], dynamics.Source[nelem][1], dynamics.Source[nelem][2]);
00600 }
00601
00602 {
00603
00604
00605 enum {DEBUG_LOC=false};
00606
00607 if( DEBUG_LOC && nzone==1 && lgPrintIt )
00608 {
00609 fprintf( ioQQQ,
00610 " DEBUG ion_solver: nelem=%li ion_range=%li, limit=%li, nConv %li xmat follows\n",
00611 nelem , ion_range,limit , conv.nTotalIoniz );
00612 if( lgHomogeneous )
00613 fprintf(ioQQQ , "Homogeneous \n");
00614 for( i=0; i<ion_range; ++i )
00615 {
00616 for( j=0;j<ion_range;j++ )
00617 {
00618 fprintf(ioQQQ,"%e\t",MAT(xmatsave,i,j));
00619 }
00620 fprintf(ioQQQ,"\n");
00621 }
00622 fprintf(ioQQQ,"source follows\n");
00623 for( i=0; i<ion_range;i++ )
00624 {
00625 fprintf(ioQQQ,"%e\t",source[i]);
00626 }
00627 fprintf(ioQQQ,"\n");
00628 }
00629 }
00630
00631
00632 if( lgTriDiag )
00633 {
00634 bool lgLapack=false , lgTriOptimized=true;
00635
00636 if(lgLapack)
00637 {
00638
00639
00640
00641
00642 bool lgBad = false;
00643
00644 double *dl, *d, *du;
00645
00646 d = (double *) MALLOC((unsigned)ion_range*sizeof(double));
00647 du = (double *) MALLOC((unsigned)(ion_range-1)*sizeof(double));
00648 dl = (double *) MALLOC((unsigned)(ion_range-1)*sizeof(double));
00649
00650 for(i=0;i<ion_range-1;i++)
00651 {
00652 du[i] = MAT(xmat,i+1,i);
00653 d[i] = MAT(xmat,i,i);
00654 dl[i] = MAT(xmat,i,i+1);
00655 }
00656 d[i] = MAT(xmat,i,i);
00657
00658 # if 0
00659 int i1, i2;
00660 i1 = ion_range;
00661 i2 = 1;
00662 lgBad = dgtsv_wrapper(&i1, &i2, dl, d, du, source, &i2);
00663 # endif
00664 if( lgBad )
00665 fprintf(ioQQQ," dgtsz error.\n");
00666
00667 free(dl);free(du);free(d);
00668 }
00669 else if(lgTriOptimized)
00670 {
00671
00672
00673
00674
00675
00676 for(i=0;i<ion_range;i++)
00677 {
00678 source[i] = sink[i] = 0.;
00679 }
00680 if(nelem == ipHYDROGEN && !hmi.lgNoH2Mole)
00681 {
00682 for(i=0;i<ion_range;i++)
00683 {
00684 ion = i+ion_low;
00685 source[i] += mole.source[nelem][ion];
00686 sink[i] += mole.sink[nelem][ion];
00687 }
00688 }
00689 if( iteration > dynamics.n_initial_relax+1 && dynamics.lgAdvection && radius.depth < dynamics.oldFullDepth )
00690 {
00691 for(i=0;i<ion_range;i++)
00692 {
00693 ion = i+ion_low;
00694 source[i] += dynamics.Source[nelem][ion];
00695 sink[i] += dynamics.Rate;
00696 }
00697 }
00698
00699 solveions(ionbal.RateIonizTot[nelem]+ion_low,ionbal.RateRecomTot[nelem]+ion_low,
00700 sink,source,ion_range,jmax);
00701 }
00702 }
00703 else
00704 {
00705
00706 nerror = 0;
00707
00708 getrf_wrapper(ion_range, ion_range, xmat, ion_range, ipiv, &nerror);
00709 if( nerror != 0 )
00710 {
00711 fprintf( ioQQQ,
00712 " DISASTER ion_solver: dgetrf finds singular or ill-conditioned matrix nelem=%li %s ion_range=%li, limit=%li, nConv %li xmat follows\n",
00713 nelem ,
00714 elementnames.chElementSym[nelem],
00715 ion_range,
00716 limit ,
00717 conv.nTotalIoniz );
00718 for( i=0; i<ion_range; ++i )
00719 {
00720 for( j=0;j<ion_range;j++ )
00721 {
00722 fprintf(ioQQQ,"%e\t",MAT(xmatsave,j,i));
00723 }
00724 fprintf(ioQQQ,"\n");
00725 }
00726 fprintf(ioQQQ,"source follows\n");
00727 for( i=0; i<ion_range;i++ )
00728 {
00729 fprintf(ioQQQ,"%e\t",source[i]);
00730 }
00731 fprintf(ioQQQ,"\n");
00732 cdEXIT(EXIT_FAILURE);
00733 }
00734 getrs_wrapper('N', ion_range, 1, xmat, ion_range, ipiv, source, ion_range, &nerror);
00735 if( nerror != 0 )
00736 {
00737 fprintf( ioQQQ, " DISASTER ion_solver: dgetrs finds singular or ill-conditioned matrix nelem=%li ionrange=%li\n",
00738 nelem , ion_range );
00739 cdEXIT(EXIT_FAILURE);
00740 }
00741 }
00742
00743 {
00744
00745
00746 enum {DEBUG_LOC=false};
00747
00748 if( DEBUG_LOC && nelem == ipHYDROGEN )
00749 {
00750 fprintf(ioQQQ,"debuggg ion_solver1 \t%.2f\t%.4e\t%.4e\tIon\t%.3e\tRec\t%.3e\n",
00751 fnzone,
00752 phycon.te,
00753 dense.eden,
00754 ionbal.RateIonizTot[nelem][0] ,
00755 ionbal.RateRecomTot[nelem][0]);
00756 fprintf(ioQQQ," Msrc %.3e %.3e\n", mole.source[ipHYDROGEN][0], mole.source[ipHYDROGEN][1]);
00757 fprintf(ioQQQ," Msnk %.3e %.3e\n", mole.sink[ipHYDROGEN][0], mole.sink[ipHYDROGEN][1]);
00758 fprintf(ioQQQ," Poprat %.3e nomol %.3e\n",source[1]/source[0],
00759 ionbal.RateIonizTot[nelem][0]/ionbal.RateRecomTot[nelem][0]);
00760 }
00761 }
00762
00763 for( i=0; i<ion_range; i++ )
00764 {
00765
00766 ASSERT( !isnan( source[i] ) );
00767 }
00768
00769 # define RJRW 0
00770 if( RJRW && 0 )
00771 {
00772
00773 double test;
00774 for(i=0; i<ion_range; i++) {
00775 test = 0.;
00776 for(j=0; j<ion_range; j++) {
00777 test = test+source[j]*MAT(xmatsave,j,i);
00778 }
00779 fprintf(ioQQQ,"%e\t",test);
00780 }
00781 fprintf(ioQQQ,"\n");
00782
00783 test = 0.;
00784 fprintf(ioQQQ," ion %li abundance %.3e\n",nelem,abund_total);
00785 for( ion=dense.IonLow[nelem]; ion < dense.IonHigh[nelem]; ion++ )
00786 {
00787 if( ionbal.RateRecomTot[nelem][ion] != 0 && source[ion-ion_low] != 0 )
00788 fprintf(ioQQQ," %li %.3e %.3e : %.3e\n",ion,source[ion-ion_low],
00789 source[ion-ion_low+1]/source[ion-ion_low],
00790 ionbal.RateIonizTot[nelem][ion]/ionbal.RateRecomTot[nelem][ion]);
00791 else
00792 fprintf(ioQQQ," %li %.3e [One ratio infinity]\n",ion,source[ion-ion_low]);
00793 test += source[ion-ion_low];
00794 }
00795 test += source[ion-ion_low];
00796 }
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818 if( lgHomogeneous )
00819 {
00820 dense.xIonDense[nelem][ion_low] = (realnum)abund_total;
00821 for( i=1;i < ion_range; i++ )
00822 {
00823 dense.xIonDense[nelem][i+ion_low] = 0.;
00824 }
00825 }
00826
00827 renormnew = 1.;
00828
00829 if(iteration > dynamics.n_initial_relax+1 && dynamics.lgAdvection &&
00830 nelem == ipHYDROGEN && hmi.lgNoH2Mole)
00831 {
00832
00833
00834
00835
00836 renormnew = 1.;
00837 }
00838 else
00839 {
00840 dennew = 0.;
00841 sum_dense = 0.;
00842
00843
00844 for( i=0;i < ion_range; i++ )
00845 {
00846 ion = i+ion_low;
00847 sum_dense += dense.xIonDense[nelem][ion];
00848 dennew += source[i];
00849 }
00850
00851 if( dennew > 0.)
00852 {
00853 renormnew = sum_dense / dennew;
00858 }
00859 else
00860 {
00861 renormnew = 1.;
00862 }
00863 }
00864
00865
00866 if( renormnew < 0)
00867 {
00868 fprintf(ioQQQ,"PROBLEM impossible value of renorm \n");
00869 }
00870 ASSERT( renormnew>=0 );
00871
00872
00873
00874
00875 lgNegPop = false;
00876 for( i=0; i < ion_range; i++ )
00877 {
00878 ion = i+ion_low;
00879
00880
00881
00882 if( source[i] < 0. )
00883 {
00884
00885
00886
00887
00888 if( source[i]<-2e-9 )
00889 fprintf(ioQQQ,
00890 " PROBLEM negative ion population in ion_solver, nelem=%li, %s ion=%li val=%.3e Search?%c zone=%li iteration=%li\n",
00891 nelem ,
00892 elementnames.chElementSym[nelem],
00893 ion ,
00894 source[i] ,
00895 TorF(conv.lgSearch) ,
00896 nzone ,
00897 iteration );
00898 source[i] = 0.;
00899
00900 if( ion == nelem+1-NISO )
00901 {
00902 long int ipISO = nelem - ion;
00903 ASSERT( ipISO>=0 && ipISO<NISO );
00904 StatesElem[ipISO][nelem][0].Pop = 0.;
00905 }
00906 }
00907
00908
00909 dense.xIonDense[nelem][ion] = (realnum)(source[i]*renormnew);
00910 }
00911
00912
00913 while( dense.IonHigh[nelem] > dense.IonLow[nelem] &&
00914 dense.xIonDense[nelem][dense.IonHigh[nelem]] < 1e-25*abund_total )
00915 {
00916 ASSERT( dense.xIonDense[nelem][dense.IonHigh[nelem]] >= 0. );
00917
00918 dense.xIonDense[nelem][dense.IonHigh[nelem]] = 0.;
00919 thermal.heating[nelem][dense.IonHigh[nelem]-1] = 0.;
00920
00921 --dense.IonHigh[nelem];
00922 }
00923
00924
00925
00926 ASSERT( (dense.IonLow[nelem] < dense.IonHigh[nelem]) ||
00927 (dense.IonLow[nelem]==0 && dense.IonHigh[nelem]==0 ) );
00928
00929
00930 if( lgNegPop )
00931 {
00932 fprintf( ioQQQ, " PROBLEM Negative population found for abundance of ionization stage of element %4.4s, ZONE=%4ld\n",
00933 elementnames.chElementNameShort[nelem], nzone );
00934
00935 fprintf( ioQQQ, " Populations were" );
00936 for( ion=1; ion <= dense.IonHigh[nelem]+1; ion++ )
00937 {
00938 fprintf( ioQQQ, "%9.1e", dense.xIonDense[nelem][ion-1] );
00939 }
00940 fprintf( ioQQQ, "\n" );
00941
00942 fprintf( ioQQQ, " destroy vector =" );
00943 for( ion=1; ion <= dense.IonHigh[nelem]; ion++ )
00944 {
00945 fprintf( ioQQQ, "%9.1e", ionbal.RateIonizTot[nelem][ion-1] );
00946 }
00947 fprintf( ioQQQ, "\n" );
00948
00949
00950 if( lgNegPop )
00951 {
00952 fprintf( ioQQQ, " CTHeavy vector =" );
00953 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
00954 {
00955 fprintf( ioQQQ, "%9.1e", atmdat.HeCharExcIonOf[nelem][ion] );
00956 }
00957 fprintf( ioQQQ, "\n" );
00958
00959 fprintf( ioQQQ, " HCharExcIonOf vtr=" );
00960 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
00961 {
00962 fprintf( ioQQQ, "%9.1e", atmdat.HCharExcIonOf[nelem][ion] );
00963 }
00964 fprintf( ioQQQ, "\n" );
00965
00966 fprintf( ioQQQ, " CollidRate vtr=" );
00967 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
00968 {
00969 fprintf( ioQQQ, "%9.1e", ionbal.CollIonRate_Ground[nelem][ion][0] );
00970 }
00971 fprintf( ioQQQ, "\n" );
00972
00973
00974 fprintf( ioQQQ, " photo rates per subshell, ion\n" );
00975 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
00976 {
00977 fprintf( ioQQQ, "%3ld", ion );
00978 for( ns=0; ns < Heavy.nsShells[nelem][ion]; ns++ )
00979 {
00980 fprintf( ioQQQ, "%9.1e", ionbal.PhotoRate_Shell[nelem][ion][ns][0] );
00981 }
00982 fprintf( ioQQQ, "\n" );
00983 }
00984 }
00985
00986
00987 fprintf( ioQQQ, " create vector =" );
00988 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
00989 {
00990 fprintf( ioQQQ, "%9.1e", ionbal.RateRecomTot[nelem][ion] );
00991 }
00992 fprintf( ioQQQ, "\n" );
00993
00994 ContNegative();
00995 ShowMe();
00996 cdEXIT(EXIT_FAILURE);
00997 }
00998
00999
01000
01001 if( prt.lgPrtArry[nelem] || lgPrintIt )
01002 {
01003
01004 fprintf( ioQQQ,
01005 "\n %s ion_solver DEBUG ion/rec rt [s-1] %s nz%.2f Te%.4e ne%.4e Tot abun:%.3e ion abun%.2e mole%.2e\n",
01006 elementnames.chElementSym[nelem],
01007 elementnames.chElementName[nelem],
01008 fnzone,
01009 phycon.te ,
01010 dense.eden,
01011 dense.gas_phase[nelem],
01012 abund_total ,
01013 dense.xMolecules[nelem] );
01014
01015 fprintf( ioQQQ, " %s Ioniz total " ,elementnames.chElementSym[nelem]);
01016 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01017 {
01018 fprintf( ioQQQ, " %9.2e", ionbal.RateIonizTot[nelem][ion] );
01019 }
01020 fprintf( ioQQQ, "\n" );
01021
01022
01023 fprintf(ioQQQ," %s mole sink ",
01024 elementnames.chElementSym[nelem]);
01025 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01026 {
01027 fprintf(ioQQQ," %9.2e", mole.sink[nelem][ion] );
01028 }
01029 fprintf( ioQQQ, "\n" );
01030
01031 if( dynamics.lgAdvection )
01032 {
01033
01034 fprintf(ioQQQ," %s dynm sink ",
01035 elementnames.chElementSym[nelem]);
01036 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01037 {
01038 fprintf(ioQQQ," %9.2e", dynamics.Rate );
01039 }
01040 fprintf( ioQQQ, "\n" );
01041 }
01042
01043
01044 fprintf( ioQQQ, " %s Recom total " ,elementnames.chElementSym[nelem]);
01045 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01046 {
01047 fprintf( ioQQQ, " %9.2e", ionbal.RateRecomTot[nelem][ion] );
01048 }
01049 fprintf( ioQQQ, "\n" );
01050
01051
01052 fprintf(ioQQQ," %s mole source ",
01053 elementnames.chElementSym[nelem]);
01054 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01055 {
01056 fprintf(ioQQQ," %9.2e", mole.source[nelem][ion] );
01057 }
01058 fprintf( ioQQQ, "\n" );
01059
01060 if( dynamics.lgAdvection )
01061 {
01062
01063 fprintf(ioQQQ," %s dynm sour ",
01064 elementnames.chElementSym[nelem]);
01065 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01066 {
01067 fprintf(ioQQQ," %9.2e", dynamics.Source[nelem][ion] );
01068 }
01069 fprintf( ioQQQ, "\n" );
01070 }
01071
01072
01073 fprintf( ioQQQ, " %s Coll ioniz " ,elementnames.chElementSym[nelem] );
01074 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01075 {
01076 fprintf( ioQQQ, " %9.2e", ionbal.CollIonRate_Ground[nelem][ion][0] );
01077 }
01078 fprintf( ioQQQ, "\n" );
01079
01080
01081 fprintf( ioQQQ, " %s UTA ioniz " ,elementnames.chElementSym[nelem] );
01082 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01083 {
01084 fprintf( ioQQQ, " %9.2e", ionbal.UTA_ionize_rate[nelem][ion] );
01085 }
01086 fprintf( ioQQQ, "\n" );
01087
01088
01089 fprintf( ioQQQ, " %s Phot ioniz " ,elementnames.chElementSym[nelem]);
01090 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01091 {
01092 fprintf( ioQQQ, " %9.2e",
01093 ionbal.PhotoRate_Shell[nelem][ion][Heavy.nsShells[nelem][ion]-1][0] );
01094 }
01095 fprintf( ioQQQ, "\n" );
01096
01097
01098 fprintf( ioQQQ, " %s Auger ioniz " ,elementnames.chElementSym[nelem]);
01099 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01100 {
01101 fprintf( ioQQQ, " %9.2e",
01102 auger[ion] );
01103 }
01104 fprintf( ioQQQ, "\n" );
01105
01106
01107 fprintf( ioQQQ, " %s Secon ioniz " ,elementnames.chElementSym[nelem]);
01108 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01109 {
01110 fprintf( ioQQQ, " %9.2e",
01111 secondaries.csupra[nelem][ion] );
01112 }
01113 fprintf( ioQQQ, "\n" );
01114
01115
01116 fprintf( ioQQQ, " %s ion on grn " ,elementnames.chElementSym[nelem]);
01117 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01118 {
01119 fprintf( ioQQQ, " %9.2e", gv.GrainChTrRate[nelem][ion][ion+1] );
01120 }
01121 fprintf( ioQQQ, "\n" );
01122
01123
01124
01125 fprintf( ioQQQ, " %s ion in chem " ,elementnames.chElementSym[nelem]);
01126 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01127 {
01128 fprintf( ioQQQ, " %9.2e", mole.xMoleChTrRate[nelem][ion][ion+1] );
01129 }
01130 fprintf( ioQQQ, "\n" );
01131
01132
01133 fprintf( ioQQQ, " %s chr trn ion " ,elementnames.chElementSym[nelem] );
01134 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01135 {
01136
01137 double sum = atmdat.HeCharExcIonOf[nelem][ion]*dense.xIonDense[ipHELIUM][1]+
01138 atmdat.HCharExcIonOf[nelem][ion]*dense.xIonDense[ipHYDROGEN][1];
01139
01140 if( nelem==ipHELIUM && ion==0 )
01141 {
01142 sum += atmdat.HeCharExcIonTotal;
01143 }
01144 else if( nelem==ipHYDROGEN && ion==0 )
01145 {
01146 sum += atmdat.HCharExcIonTotal;
01147 }
01148 fprintf( ioQQQ, " %9.2e", sum );
01149 }
01150 fprintf( ioQQQ, "\n" );
01151
01152
01153 fprintf( ioQQQ, " %s radiati rec " ,elementnames.chElementSym[nelem]);
01154 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01155 {
01156 fprintf( ioQQQ, " %9.2e", dense.eden*ionbal.RR_rate_coef_used[nelem][ion] );
01157 }
01158 fprintf( ioQQQ, "\n" );
01159
01160
01161 fprintf( ioQQQ, " %s dr old rec " ,elementnames.chElementSym[nelem]);
01162 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01163 {
01164 fprintf( ioQQQ, " %9.2e", dense.eden*ionbal.DR_old_rate_coef[nelem][ion] );
01165 }
01166 fprintf( ioQQQ, "\n" );
01167
01168
01169 fprintf( ioQQQ, " %s drBadnel rec" ,elementnames.chElementSym[nelem]);
01170 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01171 {
01172 fprintf( ioQQQ, " %9.2e", dense.eden*ionbal.DR_Badnell_rate_coef[nelem][ion] );
01173 }
01174 fprintf( ioQQQ, "\n" );
01175
01176
01177
01178 fprintf( ioQQQ, " %s rec on grn " ,elementnames.chElementSym[nelem]);
01179 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01180 {
01181 fprintf( ioQQQ, " %9.2e", gv.GrainChTrRate[nelem][ion+1][ion] );
01182 }
01183 fprintf( ioQQQ, "\n" );
01184
01185
01186
01187 fprintf( ioQQQ, " %s rec in chem " ,elementnames.chElementSym[nelem]);
01188 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01189 {
01190 fprintf( ioQQQ, " %9.2e", mole.xMoleChTrRate[nelem][ion+1][ion] );
01191 }
01192 fprintf( ioQQQ, "\n" );
01193
01194
01195 fprintf( ioQQQ, " %s chr trn rec " ,elementnames.chElementSym[nelem]);
01196 for( ion=0; ion < dense.IonHigh[nelem]; ion++ )
01197 {
01198 double sum =
01199 atmdat.HCharExcRecTo[nelem][ion]*
01200 StatesElem[ipH_LIKE][ipHYDROGEN][ipH1s].Pop*
01201 dense.xIonDense[ipHYDROGEN][1] +
01202
01203 atmdat.HeCharExcRecTo[nelem][ion]*
01204 StatesElem[ipHE_LIKE][ipHELIUM][ipHe1s1S].Pop*
01205 dense.xIonDense[ipHELIUM][1];
01206
01207 if( nelem==ipHELIUM && ion==0 )
01208 {
01209 sum += atmdat.HeCharExcRecTotal;
01210 }
01211 else if( nelem==ipHYDROGEN && ion==0 )
01212 {
01213 sum += atmdat.HCharExcRecTotal;
01214 }
01215 fprintf( ioQQQ, " %9.2e", sum );
01216 }
01217 fprintf( ioQQQ, "\n" );
01218
01219
01220 fprintf( ioQQQ, " %s Abun [cm-3] " ,elementnames.chElementSym[nelem] );
01221 for( ion=0; ion <= dense.IonHigh[nelem]; ion++ )
01222 {
01223 fprintf( ioQQQ, " %9.2e", dense.xIonDense[nelem][ion] );
01224 }
01225 fprintf( ioQQQ, "\n" );
01226 }
01227
01228 if( PARALLEL_MODE )
01229 {
01230 free(ipiv);
01231 free(source);
01232 free(sink);
01233 free(xmat);
01234 free(xmatsave);
01235 free(auger);
01236 }
01237 return;
01238 }
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267 void solveions(double *ion, double *rec, double *snk, double *src,
01268 long int nlev, long int nmax)
01269 {
01270 double kap, bet;
01271 long int i;
01272
01273 DEBUG_ENTRY( "solveions()" );
01274
01275 if(nmax != -1)
01276 {
01277
01278 src[nmax] = 1.;
01279 for(i=nmax;i<nlev-1;i++)
01280 src[i+1] = src[i]*ion[i]/rec[i];
01281 for(i=nmax-1;i>=0;i--)
01282 src[i] = src[i+1]*rec[i]/ion[i];
01283 }
01284 else
01285 {
01286 i = 0;
01287 kap = snk[0];
01288 for(i=0;i<nlev-1;i++)
01289 {
01290 bet = ion[i]+kap;
01291 if(bet == 0.)
01292 {
01293 fprintf(ioQQQ,"Ionization solver error\n");
01294 cdEXIT(EXIT_FAILURE);
01295 }
01296 bet = 1./bet;
01297 src[i] *= bet;
01298 src[i+1] += ion[i]*src[i];
01299 snk[i] = bet*rec[i];
01300 kap = kap*snk[i]+snk[i+1];
01301 }
01302 bet = kap;
01303 if(bet == 0.)
01304 {
01305 fprintf(ioQQQ,"Ionization solver error\n");
01306 cdEXIT(EXIT_FAILURE);
01307 }
01308 src[i] /= bet;
01309
01310 for(i=nlev-2;i>=0;i--)
01311 {
01312 src[i] += snk[i]*src[i+1];
01313 }
01314 }
01315 }