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

Go to the documentation of this file.
00001 /* This file is part of Cloudy and is copyright (C)1978-2008 by Gary J. Ferland and
00002  * others.  For conditions of distribution and use see copyright notice in license.txt */
00003 /*ZoneEnd last routine called after all zone calculations, before iter_end_check,
00004  * upon exit radiation field is for outer edge of current zone */
00005 /*ZoneStart set variables that change with each zone, like radius, depth,
00006  * upon exit flux will be flux at center of zone about to be computed */
00007 #include "cddefines.h"
00008 #include "phycon.h"
00009 #include "opacity.h"
00010 #include "rfield.h"
00011 #include "struc.h"
00012 #include "thermal.h"
00013 #include "dense.h"
00014 #include "h2.h"
00015 #include "geometry.h"
00016 #include "conv.h"
00017 #include "dynamics.h"
00018 #include "radius.h"
00019 #include "zones.h"
00020 #include "doppvel.h"
00021 /* this is number of zones to include in guess for next temperatures */
00022 #define IOFF    3
00023 
00024 void ZoneStart(const char *chMode)
00025 {
00026         bool lgNeOscil,
00027                 lgTeOscil;
00028         long int i;
00029 
00030         double dt1 , de1, 
00031           /* this is correction factor for dilution of radiation
00032            * across current zone, set in ZoneStart to correct
00033                 * flux for center of current zone, and undone in ZoneDone */
00034           DilutionCorrec ,
00035                 drFac ,
00036                 dTauThisZone,
00037           outwrd, 
00038           ratio, 
00039           rm, 
00040           rad_middle_zone,
00041           vin, 
00042           vout;
00043 
00044         static double DTaver , DEaver, 
00045           dt2 , de2;
00046 
00047         DEBUG_ENTRY( "ZoneStart()" );
00048 
00050         /* this is a turbulent velocity power law.  */
00051         if( DoppVel.lgTurbLawOn )
00052         {
00053                 DoppVel.TurbVel = DoppVel.TurbVelZero * 
00054                         pow( (realnum)(radius.Radius/radius.rinner), DoppVel.TurbVelLaw );
00055         }
00056 
00057         /* this sub can be called in two ways, 'incr' means increment
00058          * radius variables, while 'init' means only initialize rest */
00059         /* called at start of a zone to update all variables
00060          * having to do with starting this zone */
00061 
00062         /* first establish current filling factor (normally 1) since used within
00063          * following branches */
00064         geometry.FillFac = (realnum)(geometry.fiscal*
00065                 pow( radius.Radius/radius.rinner ,(double)geometry.filpow));
00066         geometry.FillFac = (realnum)MIN2(1.,geometry.FillFac);
00067 
00068         if( strcmp(chMode,"init") == 0 )
00069         {
00070                 /* initialize the variables at start of calculation, after this exits
00071                  * radius is equal to the initial radius, not outer edge of zone */
00072 
00073                 /* depth to illuminated face */
00074                 radius.depth = 1e-30;
00075 
00076                 /* integral of depth times filling factor */
00077                 radius.depth_x_fillfac = radius.depth*geometry.FillFac;
00078                 /* effective radius */
00079                 radius.drad_x_fillfac = radius.drad*geometry.FillFac;
00080 
00081                 /* reset radius to inner radius, at this point equal to illuminated face radius */
00082                 radius.Radius = radius.rinner;
00083                 radius.Radius_mid_zone = radius.rinner + radius.drad/2.;
00084 
00085                 /* thickness of next zone */
00086                 radius.drNext = radius.drad;
00087 
00088                 /* depth to illuminated face */
00089                 radius.depth_mid_zone = radius.drad/2.;
00090 
00091                 /* depth variable for globule */
00092                 radius.glbdst = radius.glbrad;
00093 
00094                 /* this is case where outer radius is set */
00095                 if( radius.router[iteration-1] < 5e29 )
00096                 {
00097                         radius.Depth2Go = radius.router[iteration-1];
00098                 }
00099                 else if( iteration > 1 )
00100                 {
00101                         /* this case second or later iteration, use previous thickness */
00102                         radius.Depth2Go = radius.router[iteration-2];
00103                 }
00104                 else
00105                 {
00106                         /* first iteration and depth not set, so we cannot estimate it */
00107                         radius.Depth2Go = 0.;
00108                 }
00109         }
00110         else if( strcmp(chMode,"incr") == 0 )
00111         {
00112                 /* update radius variables - called by cloudy at start of this zone's calcs */
00113                 radius.drad = radius.drNext;
00114                 /* >>chng 06 mar 21, remember largest and smallest dr over this iteration
00115                  * for use in recombination time dep logic */
00116                 radius.dr_min_last_iter = MIN2( radius.dr_min_last_iter , radius.drNext );
00117                 radius.dr_max_last_iter = MAX2( radius.dr_max_last_iter , radius.drNext );
00118 
00119                 ASSERT( radius.drad > 0. );
00120                 radius.depth += radius.drad;
00121                 radius.depth_mid_zone = radius.depth - radius.drad/2.;
00122 
00123                 /* effective radius */
00124                 radius.drad_x_fillfac = radius.drad*geometry.FillFac;
00125 
00126                 /* integral of depth times filling factor */
00127                 radius.depth_x_fillfac += radius.drad_x_fillfac;
00128 
00129                 /* decrement Depth2Go but do not let become negative */
00130                 radius.Depth2Go = MAX2( 0.,radius.Depth2Go - radius.drad );
00131 
00132                 /* increment the radius, during the calculation Radius is the
00133                  * outer radius of the current zone*/
00134                 radius.Radius += radius.drad*radius.dRadSign;
00135 
00136                 /* Radius is now outer edge of this zone since incremented above, 
00137                  * so need to add drad to it */
00138                 radius.Radius_mid_zone = radius.Radius - radius.drad/2.;
00139 
00140                 /***********************************************************************
00141                  *
00142                  * attenuate rfield to center of this zone
00143                  *
00144                  ***********************************************************************/
00145 
00146                  /* radius was just incremented above, so this resets continuum to
00147                   * flux at center of zone we are about to compute.  radius will be
00148                   * incremented immediately following this.  this correction is undone
00149                   * when ZoneDone called */
00150 
00151                 /* this will be the optical thickness of the next zone
00152                  * AngleIllumRadian is 1/COS(theta), is usually 1, reset with illuminate command,
00153                  * option for illumination of slab at an angle */
00154 
00155                 /* radius.dRNeff is next dr with filling factor, this will only be
00156                  * used to get approximate correction for attenuation
00157                  * of radiation in this zone,
00158                  * equations derived in hazy, Netzer&Ferland 83, for factor accounting
00159                  * any changes here should be checked with both sphonly.in and pphonly*/
00160                 /* honlyotspp seems most sensitive to the 1.35 */
00161                 drFac = radius.drad*geometry.FillFac*geometry.DirectionalCosin*1.35;
00162 
00163                 /* dilute radiation so that rfield is in center of zone, this
00164                  * goes into tmn at start of zone here, but is later divided out
00165                  * when ZoneEnd called */
00166                 DilutionCorrec = 1./POW2(
00167                         (radius.Radius-radius.drad/2.)/(radius.Radius-radius.drad) );
00168 
00169                 /* note that this for loop is through <= nflux, to include the [nflux]
00170                  * unit integration verification token.  The token was set in
00171                  * in MadeDiffuse and propagated out in metdif.  the opacity at that energy is
00172                  * zero, so only the geometrical part of tmn will affect things.  The final
00173                  * sum is verified in PrtComment */
00174                 for( i=0; i <= rfield.nflux; i++ )
00175                 {
00176                         dTauThisZone = opac.opacity_abs[i]*drFac;
00177                         /* TMN is array of scale factors which account for attenuation
00178                          * of continuum across the zone (necessary to conserve energy
00179                          * at the 1% - 5% level.) sphere effects in
00180                          * drNext was set by NEXTDR and will be next dr */
00181 
00182                         if( dTauThisZone < 1e-4 )
00183                         {
00184                                 /* this small tau limit is the most likely branch, about 60% in parispn.in */
00185                                 opac.tmn[i] = 1.f;
00186                         }
00187                         else if( dTauThisZone < 5. )
00188                         {
00189                                 /* this middle tau limit happens in the remaining 40% */
00190                                 opac.tmn[i] = (realnum)((1. - exp(-dTauThisZone))/(dTauThisZone));
00191                         }
00192                         else
00193                         {
00194                                 /* this happens almost not at all */
00195                                 opac.tmn[i] = (realnum)(1./(dTauThisZone));
00196                         }
00197 
00198                         /* now add on correction for dilution across this zone */
00199                         opac.tmn[i] *= (realnum)DilutionCorrec;
00200 
00201                         rfield.flux_beam_const[i] *= opac.tmn[i];
00202                         rfield.flux_beam_time[i] *= opac.tmn[i];
00203                         rfield.flux_isotropic[i] *= opac.tmn[i];
00204                         rfield.flux[i] = rfield.flux_beam_const[i] + rfield.flux_beam_time[i] +
00205                                 rfield.flux_isotropic[i];
00206 
00207                         /* actually do the corrections
00208                         rfield.flux[i] *= opac.tmn[i]; */
00209 
00210                         /* >>chng 03 nov 08, update SummedCon here since flux changed */
00211                         rfield.SummedCon[i] = rfield.flux[i] + rfield.SummedDif[i];
00212                 }
00213 
00214                 /* following is distance to globule, usually does not matter */
00215                 radius.glbdst -= (realnum)radius.drad;
00216 
00217                 /* if gt 5th zone, and not constant pressure, and not oscillating te
00218                  * then guess next temperature : option to predict next temperature
00219                  * NZLIM is dim of structure variables saving temp, do data if nzone>NZLIM
00220                  * IOFF is number of zones to look over, set set to 3 in the define above */
00221                 /* >>chng 01 mar 12, add option to not do this, set with no tepred command */
00222                 if( (strcmp(dense.chDenseLaw,"CPRE") != 0) && 
00223                         thermal.lgPredNextTe && 
00224                         (nzone > IOFF + 1)  )
00225                 {
00226                         phycon.TeInit = phycon.te;
00227                         phycon.EdenInit = dense.eden;
00228                         lgTeOscil = false;
00229                         lgNeOscil = false;
00230                         dt1 = 0.;
00231                         dt2 = 0.;
00232                         de1 = 0.;
00233                         de2 = 0.;
00234                         DTaver = 0.;
00235                         DEaver = 0.;
00236                         for( i=nzone - IOFF-1; i < (nzone - 1); i++ )
00237                         {
00238                                 dt1 = dt2;
00239                                 de1 = de2;
00240                                 /* this will get the average change in temperature for the 
00241                                  * past IOFF zones */
00242                                 dt2 = struc.testr[i-1] - struc.testr[i];
00243                                 de2 = struc.ednstr[i-1] - struc.ednstr[i];
00244                                 DTaver += dt2;
00245                                 DEaver += de2;
00246                                 if( dt1*dt2 < 0. )
00247                                 {
00248                                         lgTeOscil = true;
00249                                 }
00250                                 if( de1*de2 < 0. )
00251                                 {
00252                                         lgNeOscil = true;
00253                                 }
00254                         }
00255 
00256                         /* option to guess next electron temperature if no oscillations */
00257                         if( !lgTeOscil )
00258                         {
00259                                 DTaver /= (double)(IOFF);
00260                                 /* don't want to over correct, use smaller of two */
00261                                 dt2 = fabs(dt2);
00262                                 rm = fabs(DTaver);
00263                                 DTaver = sign(MIN2(rm,dt2),DTaver);
00264                                 /* do not let it change by more than 5% of current temp */
00265                                 /* >>chng 03 mar 18, from 5% to 1% - convergence is much better
00266                                  * now, and throwing the next Te off by 5% could easily disturb
00267                                  * the solvers - primal.in was a good case */
00268                                 DTaver = sign(MIN2(rm,0.01*phycon.te),DTaver);
00269                                 /* this actually changes the temperature */
00270                                 TempChange(phycon.te - DTaver , true);
00271                         }
00272                         else
00273                         {
00274                                 /* temp was oscillating - too dangerous to do anything */
00275                                 DTaver = 0.;
00276                         }
00277                         /* this is used to remember the proposed temperature, for output
00278                          * in the punch predictor command */
00279                         phycon.TeProp = phycon.te;
00280 
00281                         /* option to guess next electron density if no oscillations */
00282                         if( !lgNeOscil )
00283                         {
00284                                 DEaver /= IOFF;
00285                                 de2 = fabs(de2);
00286                                 rm = fabs(DEaver);
00287                                 /* don't want to over correct, use smaller of two */
00288                                 DEaver = sign(MIN2(rm,de2),DEaver);
00289                                 /* do not let it change by more than 5% of current temp */
00290                                 DEaver = sign(MIN2(rm,0.05*dense.eden),DEaver);
00291                                 /* this actually changes the temperature */
00292                                 dense.eden -= DEaver;
00293                         }
00294                         else
00295                         {
00296                                 /* temp was oscillating - too dangerous to do anything */
00297                                 DEaver = 0.;
00298                         }
00299                         /* this is used to remember the proposed temperature, for output
00300                          * in the punch predictor command */
00301                         phycon.EdenProp = dense.eden;
00302                         /* must call TempChange since ionization has changed, there are some
00303                         * terms that affect collision rates (H0 term in electron collision) */
00304                         TempChange(phycon.te , false);
00305                 }
00306         }
00307 
00308         else
00309         {
00310                 fprintf( ioQQQ, " PROBLEM ZoneStart called with insane argument, %4.4s\n", 
00311                   chMode );
00312                 /* TotalInsanity exits after announcing a problem */
00313                 TotalInsanity();
00314         }
00315 
00316         /* find the mean dr for the past few zones - this var is used
00317          * in setting the optical scale in induced processes, and large
00318          * changes in it will drive oscillations in the H+ fraction */
00319         radius.drad_x_fillfac_mean = radius.drad_x_fillfac;
00320         i = 1;
00321         while( i < 5 && nzone-i-1>=0 )
00322         {
00323                 radius.drad_x_fillfac_mean += struc.drad_x_fillfac[nzone-i-1];
00324                 ++i;
00325         }
00326         radius.drad_x_fillfac_mean /= i;
00327 
00328         /* do advection if enabled */
00329         if( dynamics.lgAdvection )
00330                 DynaStartZone();
00331 
00332         /* clear flag indicating the ionization convergence failures 
00333          * have ever occurred in current zone
00334         conv.lgConvIonizThisZone = false; */
00335 
00336         /* this will say how many times the large H2 molecule has been called in this zone -
00337          * if not called (due to low H2 abundance) then not need to update its line arrays */
00338         h2.nCallH2_this_zone = 0;
00339 
00340         /* check whether zone thickness is too small relative to radius */
00341         if( strcmp(dense.chDenseLaw,"GLOB") == 0 )
00342         {
00343                 ratio = radius.drad/(radius.glbdst + radius.drad);
00344                 /* this only matters for globule command */
00345                 if( ratio < 1e-14 )
00346                 {
00347                         radius.lgdR2Small = true;
00348                 }
00349                 else
00350                 {
00351                         radius.lgdR2Small = false;
00352                 }
00353         }
00354 
00355         /* area factor, ratio of radius to out edge of this zone 
00356          * relative to radius of illuminated face of cloud */
00357         /*radius.r1r0sq = (realnum)POW2(
00358                 (radius.Radius - radius.drad*radius.dRadSign/2.)/radius.rinner);*/
00359         /*>>>chng 99 jan 25, definition of r1r0sq is now outer edge of current zone
00360          * relative to inner edge of cloud */
00361         radius.r1r0sq = POW2( radius.Radius/radius.rinner );
00362 
00363         /* following only approximate, used for center of next zone */
00364         radius.dRNeff = radius.drNext*geometry.FillFac;
00365 
00366         /* this is the middle of the zone */
00367         rad_middle_zone = radius.Radius - radius.drad/2.;
00368 
00369         /* Radius is outer radius of this zone, so radius - drad is inner radius
00370          * rinner is inner radius of nebula; dVeff is the effective vol element
00371          * dVeff is vol rel to inner radius, so has units cm
00372          * for plane parallel dVeff is dReff */
00373         if( radius.drad/radius.Radius > 0.01 )
00374         {
00375                 vin = ((radius.Radius - radius.drad)/radius.rinner)*
00376                         (MIN2((radius.Radius-radius.drad),radius.CylindHigh)/radius.rinner)*
00377                         (radius.Radius - radius.drad);
00378 
00379                 vout = (radius.Radius/radius.rinner)*
00380                   (MIN2(radius.Radius,radius.CylindHigh)/radius.rinner)*
00381                   radius.Radius;
00382 
00383                 /* default of iEmissPower is 2, observing the full sphere */
00384                 /* this is the usual case the full volume, just the difference in the two vols */
00385                 /* this will become the true vol when multiplied by the square of the inner radius */
00386                 radius.dVeff = (vout - vin)/3.*geometry.FillFac;
00387         }
00388         else
00389         {
00390                 /* thin cell limit */
00391                 /* rad_middle_zone is the middle of the zone;
00392                  * radius is always the OUTER edge of the current zone */
00393                 radius.dVeff = (rad_middle_zone/radius.rinner)*radius.drad*geometry.FillFac*
00394                         (MIN2(rad_middle_zone,radius.CylindHigh)/radius.rinner);
00395         }
00396 
00397         /* this is possible correction for slit projected onto resolved object -
00398          * this only happens when aperture command is entered.
00399          * NB not clear what happens when slit and cylinder are both used -
00400          * this would be very orientation specific */
00401 
00402         /* default of iEmissPower is 2, set to 0 is just aperture beam, 
00403          * and to 1 if aperture slit set */
00404         if( geometry.iEmissPower == 1 )
00405         {
00406                 /* this is long slit option, remove one power of radius */
00407                 radius.dVeff /= (rad_middle_zone/radius.rinner);
00408         }
00409         else if( geometry.iEmissPower == 0 )
00410         {
00411                 /* this is short slit option, remove two powers of radius */
00412                 radius.dVeff /= POW2(rad_middle_zone/radius.rinner);
00413         }
00414 
00415         /* covering factor, default is unity */
00416         radius.dVeff *= geometry.covgeo;
00417 
00418         /* these are needed for line intensities in outward beam
00419          * lgSphere set, geometry.covrt usually 1, 0 when not lgSphere
00420          * so outward is 1 when lgSphere set 1/2 when not set, */
00421         outwrd = (1. + geometry.covrt)/2.;
00422 
00423         /*>>>chng 99 apr 23, from above to below */
00424         /*radius.dVolOutwrd = outwrd*POW2( (radius.Radius-radius.drad_x_fillfac/2.)/radius.Radius) * 
00425           radius.drad;*/
00426         /* this includes covering fact, the effective vol,, and 1/r^2 dilution,
00427          * dReff includes filling factor.  the covering factor part is 1 for sphere,
00428          * 1/2 for open */
00429         /*radius.dVolOutwrd = outwrd*radius.Radius*radius.drad_x_fillfac/(radius.Radius + 
00430           2.*radius.drad);*/
00431         /* dReff from above, includes filling factor */
00432         radius.dVolOutwrd = outwrd*radius.drad_x_fillfac;
00433         ASSERT( radius.dVolOutwrd > 0. );
00434 
00435         /* following will be used to "uncorrect" for this in lines when predicting continua
00436         radius.GeoDil = radius.Radius/(radius.Radius + 2.*radius.drad);*/
00437 
00438         /* this should multiply the line to become the net inward.  geo part is 1/2 for
00439          * open geometry, 0 for closed.  for case of isotropic emitter only this and dVolOutwrd
00440          * above are used */
00441         radius.dVolReflec = (1. - outwrd)*radius.drad_x_fillfac*radius.r1r0sq;
00442 
00443         if( geometry.lgSphere )
00444         {
00445                 /* inward beams do not go in when lgSphere set since geometry symmetric */
00446                 radius.BeamInIn = 0.;
00447                 radius.BeamInOut = radius.Radius*radius.drad_x_fillfac/(radius.Radius + 
00448                   2.*radius.drad);
00449         }
00450         else
00451         {
00452                 radius.BeamInIn = radius.drad_x_fillfac*radius.r1r0sq;
00453 
00454                 /* inward beams do not go out */
00455                 radius.BeamInOut = 0.;
00456         }
00457         /* factor for outwardly directed part of line */
00458         radius.BeamOutOut = radius.Radius*radius.drad_x_fillfac/(radius.Radius + 
00459           2.*radius.drad);
00460         return;
00461 }
00462 
00463 void ZoneEnd(void)
00464 {
00465         long i;
00466 
00467         DEBUG_ENTRY( "ZoneEnd()" );
00468 
00469         /***********************************************************************
00470          *
00471          *  correct rfield for attenuation from center of zone to inner edge
00472          *
00473          ***********************************************************************/
00474 
00475          /* radius is outer radius of this zone, this resets continuum to
00476           * flux at illuminated face of zone we have already computed. */
00477 
00478         /* opac.tmn defined in ZoneStart */
00479         /* undilute radiation so that rfield is at face of zone */
00480         /* NB, upper limit of sum includes [nflux] to test unit verification cell */
00481         for( i=0; i <= rfield.nflux; i++ )
00482         {
00483                 rfield.flux_beam_const[i] /= opac.tmn[i];
00484                 rfield.flux_beam_time[i] /= opac.tmn[i];
00485                 rfield.flux_isotropic[i] /= opac.tmn[i];
00486                 rfield.flux[i] = rfield.flux_beam_const[i] + rfield.flux_beam_time[i] +
00487                         rfield.flux_isotropic[i];
00488                 /*rfield.flux[i] /= opac.tmn[i];*/
00489                 /* >>chng 03 nov 08, update SummedCon here since flux changed */
00490                 rfield.SummedCon[i] = rfield.flux[i] + rfield.SummedDif[i];
00491         }
00492 
00493         /* do advection if enabled */
00494         if( dynamics.lgAdvection )
00495                 DynaEndZone();
00496         return;
00497 }

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