cloudy trunk
Loading...
Searching...
No Matches
zone_startend.cpp
Go to the documentation of this file.
1/* This file is part of Cloudy and is copyright (C)1978-2013 by Gary J. Ferland and
2 * others. For conditions of distribution and use see copyright notice in license.txt */
3/*ZoneEnd last routine called after all zone calculations, before iter_end_check,
4 * upon exit radiation field is for outer edge of current zone */
5/*ZoneStart set variables that change with each zone, like radius, depth,
6 * upon exit flux will be flux at center of zone about to be computed */
7#include "cddefines.h"
8#include "phycon.h"
9#include "opacity.h"
10#include "rfield.h"
11#include "struc.h"
12#include "thermal.h"
13#include "dense.h"
14#include "h2.h"
15#include "geometry.h"
16#include "conv.h"
17#include "dynamics.h"
18#include "radius.h"
19#include "zones.h"
20#include "doppvel.h"
21#include "mole.h"
22/* this is number of zones to include in guess for next temperatures */
23#define IOFF 3
24
25void ZoneStart(const char *chMode)
26{
27 bool lgNeOscil,
28 lgTeOscil;
29 long int i;
30
31 double dt1 , de1,
32 /* this is correction factor for dilution of radiation
33 * across current zone, set in ZoneStart to correct
34 * flux for center of current zone, and undone in ZoneDone */
35 DilutionCorrec ,
36 drFac ,
37 dTauThisZone,
38 outwrd,
39 ratio,
40 rm,
41 rad_middle_zone,
42 vin,
43 vout;
44
45 static double DTaver , DEaver,
46 dt2 , de2;
47
48 DEBUG_ENTRY( "ZoneStart()" );
49
51 /* this is a turbulent velocity power law. */
53 {
56 }
57
58 /* this sub can be called in two ways, 'incr' means increment
59 * radius variables, while 'init' means only initialize rest */
60 /* called at start of a zone to update all variables
61 * having to do with starting this zone */
62
63 /* first establish current filling factor (normally 1) since used within
64 * following branches */
66 pow( radius.Radius/radius.rinner ,(double)geometry.filpow));
68
69 if( strcmp(chMode,"init") == 0 )
70 {
71 /* initialize the variables at start of calculation, after this exits
72 * radius is equal to the initial radius, not outer edge of zone */
73
74 /* depth to illuminated face */
75 radius.depth = 1e-30;
76
77 /* integral of depth times filling factor */
79 /* effective radius */
81
82 /* reset radius to inner radius, at this point equal to illuminated face radius */
85
86 /* thickness of next zone */
88
89 /* depth to illuminated face */
91
92 /* depth variable for globule */
94
95 /* this is case where outer radius is set */
96 if( radius.StopThickness[iteration-1] < 5e29 )
97 {
99 }
100 else if( iteration > 1 )
101 {
102 /* this case second or later iteration, use previous thickness */
104 }
105 else
106 {
107 /* first iteration and depth not set, so we cannot estimate it */
108 radius.Depth2Go = 0.;
109 }
110 }
111 else if( strcmp(chMode,"incr") == 0 )
112 {
113 /* update radius variables - called by cloudy at start of this zone's calcs */
116 /* >>chng 06 mar 21, remember largest and smallest dr over this iteration
117 * for use in recombination time dep logic */
120
121 ASSERT( radius.drad > 0. );
124
125 /* effective radius */
127
128 /* integral of depth times filling factor */
130
131 /* decrement Depth2Go but do not let become negative */
133
134 /* increment the radius, during the calculation Radius is the
135 * outer radius of the current zone*/
137
138 /* Radius is now outer edge of this zone since incremented above,
139 * so need to add drad to it */
141
142 /***********************************************************************
143 *
144 * attenuate rfield to center of this zone
145 *
146 ***********************************************************************/
147
148 /* radius was just incremented above, so this resets continuum to
149 * flux at center of zone we are about to compute. radius will be
150 * incremented immediately following this. this correction is undone
151 * when ZoneDone called */
152
153 /* this will be the optical thickness of the next zone
154 * AngleIllumRadian is 1/COS(theta), is usually 1, reset with illuminate command,
155 * option for illumination of slab at an angle */
156
157 /* radius.dRNeff is next dr with filling factor, this will only be
158 * used to get approximate correction for attenuation
159 * of radiation in this zone,
160 * equations derived in hazy, Netzer&Ferland 83, for factor accounting
161 * any changes here should be checked with both sphonly.in and pphonly*/
162 /* honlyotspp seems most sensitive to the 1.35 */
164
165 /* dilute radiation so that rfield is in center of zone, this
166 * goes into tmn at start of zone here, but is later divided out
167 * when ZoneEnd called */
168 DilutionCorrec = 1./POW2(
170
171 /* note that this for loop is through <= nflux, to include the [nflux]
172 * unit integration verification token. The token was set in
173 * in MadeDiffuse and propagated out in metdif. the opacity at that energy is
174 * zero, so only the geometrical part of tmn will affect things. The final
175 * sum is verified in PrtComment */
176 for( i=0; i <= rfield.nflux; i++ )
177 {
178 dTauThisZone = opac.opacity_abs[i]*drFac;
179 /* TMN is array of scale factors which account for attenuation
180 * of continuum across the zone (necessary to conserve energy
181 * at the 1% - 5% level.) sphere effects in
182 * drNext was set by NEXTDR and will be next dr */
183
184 if( dTauThisZone < 1e-4 )
185 {
186 /* this small tau limit is the most likely branch, about 60% in parispn.in */
187 opac.tmn[i] = 1.f;
188 }
189 else if( dTauThisZone < 5. )
190 {
191 /* this middle tau limit happens in the remaining 40% */
192 opac.tmn[i] = (realnum)((1. - exp(-dTauThisZone))/(dTauThisZone));
193 }
194 else
195 {
196 /* this happens almost not at all */
197 opac.tmn[i] = (realnum)(1./(dTauThisZone));
198 }
199
200 /* now add on correction for dilution across this zone */
201 opac.tmn[i] *= (realnum)DilutionCorrec;
202
204 rfield.flux_beam_time[i] *= opac.tmn[i];
205 rfield.flux_isotropic[i] *= opac.tmn[i];
208
209 /* >>chng 03 nov 08, update SummedCon here since flux changed */
210 rfield.SummedCon[i] = rfield.flux[0][i] + rfield.SummedDif[i];
211 }
212
213 /* following is distance to globule, usually does not matter */
215
216 /* if gt 5th zone, and not constant pressure, and not oscillating te
217 * then guess next temperature : option to predict next temperature
218 * NZLIM is dim of structure variables saving temp, do data if nzone>NZLIM
219 * IOFF is number of zones to look over, set set to 3 in the define above */
220 /* >>chng 01 mar 12, add option to not do this, set with no tepred command */
221 if( (strcmp(dense.chDenseLaw,"CPRE") != 0) &&
223 (nzone > IOFF + 1) )
224 {
227 lgTeOscil = false;
228 lgNeOscil = false;
229 dt1 = 0.;
230 dt2 = 0.;
231 de1 = 0.;
232 de2 = 0.;
233 DTaver = 0.;
234 DEaver = 0.;
235 for( i=nzone - IOFF-1; i < (nzone - 1); i++ )
236 {
237 dt1 = dt2;
238 de1 = de2;
239 /* this will get the average change in temperature for the
240 * past IOFF zones */
241 dt2 = struc.testr[i-1] - struc.testr[i];
242 de2 = struc.ednstr[i-1] - struc.ednstr[i];
243 DTaver += dt2;
244 DEaver += de2;
245 if( dt1*dt2 < 0. )
246 {
247 lgTeOscil = true;
248 }
249 if( de1*de2 < 0. )
250 {
251 lgNeOscil = true;
252 }
253 }
254
255 /* option to guess next electron temperature if no oscillations */
256 if( !lgTeOscil )
257 {
258 DTaver /= (double)(IOFF);
259 /* don't want to over correct, use smaller of two */
260 dt2 = fabs(dt2);
261 rm = fabs(DTaver);
262 DTaver = sign(MIN2(rm,dt2),DTaver);
263 /* do not let it change by more than 5% of current temp */
264 /* >>chng 03 mar 18, from 5% to 1% - convergence is much better
265 * now, and throwing the next Te off by 5% could easily disturb
266 * the solvers - primal.in was a good case */
267 DTaver = sign(MIN2(rm,0.01*phycon.te),DTaver);
268 /* this actually changes the temperature */
269 TempChange(phycon.te - DTaver , true);
270 }
271 else
272 {
273 /* temp was oscillating - too dangerous to do anything */
274 DTaver = 0.;
275 }
276 /* this is used to remember the proposed temperature, for output
277 * in the save predictor command */
279
280 /* option to guess next electron density if no oscillations */
281 if( !lgNeOscil )
282 {
283 DEaver /= IOFF;
284 de2 = fabs(de2);
285 rm = fabs(DEaver);
286 /* don't want to over correct, use smaller of two */
287 DEaver = sign(MIN2(rm,de2),DEaver);
288 /* do not let it change by more than 5% of current temp */
289 DEaver = sign(MIN2(rm,0.05*dense.eden),DEaver);
290 /* this actually changes the temperature */
291 EdenChange( dense.eden - DEaver );
292 }
293 else
294 {
295 /* temp was oscillating - too dangerous to do anything */
296 DEaver = 0.;
297 }
298 /* this is used to remember the proposed temperature, for output
299 * in the save predictor command */
301 /* must call TempChange since ionization has changed, there are some
302 * terms that affect collision rates (H0 term in electron collision) */
303 TempChange(phycon.te , false);
304 }
305 }
306
307 else
308 {
309 fprintf( ioQQQ, " PROBLEM ZoneStart called with insane argument, %4.4s\n",
310 chMode );
311 /* TotalInsanity exits after announcing a problem */
313 }
314
315 /* do advection if enabled */
318
319 /* clear flag indicating the ionization convergence failures
320 * have ever occurred in current zone
321 conv.lgConvIonizThisZone = false; */
323
324 /* this will say how many times the large H2 molecule has been called in this zone -
325 * if not called (due to low H2 abundance) then not need to update its line arrays */
326 for( diatom_iter diatom = diatoms.begin(); diatom != diatoms.end(); ++diatom)
327 (*diatom)->nCall_this_zone = 0;
328
329 /* check whether zone thickness is too small relative to radius */
330 if( strcmp(dense.chDenseLaw,"GLOB") == 0 )
331 {
332 ratio = radius.drad/(radius.glbdst + radius.drad);
333 /* this only matters for globule command */
334 if( ratio < 1e-14 )
335 {
336 radius.lgdR2Small = true;
337 }
338 else
339 {
340 radius.lgdR2Small = false;
341 }
342 }
343
344 /* area factor, ratio of radius to out edge of this zone
345 * relative to radius of illuminated face of cloud */
346 /*radius.r1r0sq = (realnum)POW2(
347 (radius.Radius - radius.drad*radius.dRadSign/2.)/radius.rinner);*/
348 /*>>>chng 99 jan 25, definition of r1r0sq is now outer edge of current zone
349 * relative to inner edge of cloud */
351
352 /* following only approximate, used for center of next zone */
354
355 /* this is the middle of the zone */
356 rad_middle_zone = radius.Radius - radius.dRadSign*radius.drad/2.;
357
358 /* this is used for long slit integrals */
359 if( radius.drad/radius.Radius > 0.01 )
360 {
361 double Ropp = radius.Radius - radius.dRadSign*radius.drad;
363 }
364 else
366
367 /* Radius is outer radius of this zone, so radius - drad is inner radius
368 * rinner is inner radius of nebula; dVeffVol is the effective vol element
369 * dVeffVol is vol rel to inner radius, so has units cm
370 * for plane parallel dVeffVol is dReff */
371 if( radius.drad/radius.Radius > 0.01 )
372 {
373 double r1 = radius.Radius - radius.dRadSign*radius.drad;
374 double rin_zone = min(r1,radius.Radius);
375 double rout_zone = max(r1,radius.Radius);
376 vin = pow2(rin_zone/radius.rinner)*rin_zone/3.;
377 if( rin_zone > radius.CylindHigh )
378 {
379 // the volume of the cap of a sphere is given here:
380 // http://en.wikipedia.org/wiki/Spherical_cap
381 // we need two of these...
382 double h = rin_zone-radius.CylindHigh;
383 double v2cap = pow2(h/radius.rinner)*(rin_zone - h/3.)/2.;
384 vin -= v2cap;
385 }
386 vout = pow2(rout_zone/radius.rinner)*rout_zone/3.;
387 if( rout_zone > radius.CylindHigh )
388 {
389 double h = rout_zone-radius.CylindHigh;
390 double v2cap = pow2(h/radius.rinner)*(rout_zone - h/3.)/2.;
391 vout -= v2cap;
392 }
393 /* this is the usual case the full volume, just the difference in the two vols */
394 /* this will become the true vol when multiplied by 4pi*rinner^2 */
395 radius.dVeffVol = (vout - vin)*geometry.FillFac;
396 /* this is correction for slit projected onto resolved object -
397 * this only happens when aperture command is entered.
398 * when slit and cylinder are both used it is assumed that the slit
399 * is oriented along the rotational axis of the cylinder
400 * the case where the slit is perpendicular to this axis is identical
401 * to the normal spherical case, so can be done by removing the cylinder command
402 * slits oriented at any other angle can be done by dividing the cylinder height
403 * by the cosine of the angle */
404 /* default of iEmissPower is 2, set to 0 is just aperture beam,
405 * and to 1 if aperture long slit set */
406 if( geometry.iEmissPower == 2 )
407 {
409 }
410 else if( geometry.iEmissPower == 1 )
411 {
412 double ain = (rin_zone/radius.rinner)*rin_zone/2.;
413 if( rin_zone > radius.CylindHigh )
414 {
415 // the area of a circular segment is given here:
416 // http://en.wikipedia.org/wiki/Circular_segment
417 // we need two of those...
418 double Theta = 2.*acos(min(radius.CylindHigh/rin_zone,1.));
419 ain *= 1. - max(Theta - sin(Theta),0.)/PI;
420 }
421 double aout = (rout_zone/radius.rinner)*rout_zone/2.;
422 if( rout_zone > radius.CylindHigh )
423 {
424 double Theta = 2.*acos(min(radius.CylindHigh/rout_zone,1.));
425 aout *= 1. - max(Theta - sin(Theta),0.)/PI;
426 }
427 radius.dVeffAper = (aout - ain)*geometry.FillFac;
428 }
429 else if( geometry.iEmissPower == 0 )
430 {
432 }
433 }
434 else
435 {
436 /* thin cell limit */
437 /* rad_middle_zone is the middle of the zone */
439 (min(rad_middle_zone,radius.CylindHigh)/radius.rinner);
440 if( geometry.iEmissPower == 2 )
441 {
443 }
444 else if( geometry.iEmissPower == 1 )
445 {
447 if( rad_middle_zone > radius.CylindHigh )
448 {
449 double Theta = 2.*acos(min(radius.CylindHigh/rad_middle_zone,1.));
450 double q = sqrt(max(1.-pow2(radius.CylindHigh/rad_middle_zone),0.))*rad_middle_zone;
451 radius.dVeffAper *= 1. - max(Theta - sin(Theta),0.)/PI - max(1. - cos(Theta),0.)*radius.CylindHigh/(PI*q);
452 }
453 }
454 else if( geometry.iEmissPower == 0 )
455 {
457 }
458 }
459
460 /* covering factor, default is unity */
463
464 /* these are needed for line intensities in outward beam
465 * lgSphere set, geometry.covrt usually 1, 0 when not lgSphere
466 * so outward is 1 when lgSphere set 1/2 when not set, */
467 outwrd = (1. + geometry.covrt)/2.;
468
469 /*>>>chng 99 apr 23, from above to below */
470 /*radius.dVolOutwrd = outwrd*POW2( (radius.Radius-radius.drad_x_fillfac/2.)/radius.Radius) *
471 radius.drad;*/
472 /* this includes covering fact, the effective vol,, and 1/r^2 dilution,
473 * dReff includes filling factor. the covering factor part is 1 for sphere,
474 * 1/2 for open */
475 /*radius.dVolOutwrd = outwrd*radius.Radius*radius.drad_x_fillfac/(radius.Radius +
476 2.*radius.drad);*/
477 /* dReff from above, includes filling factor */
479 ASSERT( radius.dVolOutwrd > 0. );
480
481 /* following will be used to "uncorrect" for this in lines when predicting continua
482 radius.GeoDil = radius.Radius/(radius.Radius + 2.*radius.drad);*/
483
484 /* this should multiply the line to become the net inward. geo part is 1/2 for
485 * open geometry, 0 for closed. for case of isotropic emitter only this and dVolOutwrd
486 * above are used */
488
489 if( geometry.lgSphere )
490 {
491 /* inward beams do not go in when lgSphere set since geometry symmetric */
492 radius.BeamInIn = 0.;
494 2.*radius.drad);
495 }
496 else
497 {
499
500 /* inward beams do not go out */
501 radius.BeamInOut = 0.;
502 }
503 /* factor for outwardly directed part of line */
505 2.*radius.drad);
506 return;
507}
508
509void ZoneEnd(void)
510{
511 long i;
512
513 DEBUG_ENTRY( "ZoneEnd()" );
514
515 /***********************************************************************
516 *
517 * correct rfield for attenuation from center of zone to inner edge
518 *
519 ***********************************************************************/
520
521 /* radius is outer radius of this zone, this resets continuum to
522 * flux at illuminated face of zone we have already computed. */
523
524 /* opac.tmn defined in ZoneStart */
525 /* undilute radiation so that rfield is at face of zone */
526 /* NB, upper limit of sum includes [nflux] to test unit verification cell */
527 for( i=0; i <= rfield.nflux; i++ )
528 {
530 rfield.flux_beam_time[i] /= opac.tmn[i];
531 rfield.flux_isotropic[i] /= opac.tmn[i];
534 /* >>chng 03 nov 08, update SummedCon here since flux changed */
535 rfield.SummedCon[i] = rfield.flux[0][i] + rfield.SummedDif[i];
536 }
537
538 /* do advection if enabled */
540 DynaEndZone();
541
542 if (0)
544
545 return;
546}
long int nzone
Definition: cddefines.cpp:14
FILE * ioQQQ
Definition: cddefines.cpp:7
long int iteration
Definition: cddefines.cpp:16
#define ASSERT(exp)
Definition: cddefines.h:578
T pow2(T a)
Definition: cddefines.h:931
#define MIN2
Definition: cddefines.h:761
#define POW2
Definition: cddefines.h:929
T sign(T x, T y)
Definition: cddefines.h:800
float realnum
Definition: cddefines.h:103
#define MAX2
Definition: cddefines.h:782
long max(int a, long b)
Definition: cddefines.h:775
NORETURN void TotalInsanity(void)
Definition: service.cpp:886
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:684
long min(int a, long b)
Definition: cddefines.h:723
t_conv conv
Definition: conv.cpp:5
void EdenChange(double EdenNew)
Definition: eden_change.cpp:12
t_dense dense
Definition: dense.cpp:24
t_DoppVel DoppVel
Definition: doppvel.cpp:5
t_dynamics dynamics
Definition: dynamics.cpp:44
void DynaEndZone(void)
Definition: dynamics.cpp:853
void DynaStartZone(void)
Definition: dynamics.cpp:401
t_geometry geometry
Definition: geometry.cpp:5
vector< diatomics * > diatoms
Definition: h2.cpp:8
vector< diatomics * >::iterator diatom_iter
Definition: h2.h:13
void mole_rk_bigchange(void)
t_opac opac
Definition: opacity.cpp:5
t_phycon phycon
Definition: phycon.cpp:6
UNUSED const double PI
Definition: physconst.h:29
UNUSED const double PI2
Definition: physconst.h:32
t_radius radius
Definition: radius.cpp:5
t_rfield rfield
Definition: rfield.cpp:8
t_struc struc
Definition: struc.cpp:6
realnum TurbVelLaw
Definition: doppvel.h:20
realnum TurbVel
Definition: doppvel.h:12
bool lgTurbLawOn
Definition: doppvel.h:24
realnum TurbVelZero
Definition: doppvel.h:16
void resetCountersZone()
Definition: conv.h:318
double eden
Definition: dense.h:190
char chDenseLaw[5]
Definition: dense.h:158
bool lgAdvection
Definition: dynamics.h:60
long int iEmissPower
Definition: geometry.h:61
realnum covaper
Definition: geometry.h:44
realnum covrt
Definition: geometry.h:51
realnum DirectionalCosin
Definition: geometry.h:15
bool lgSphere
Definition: geometry.h:24
realnum FillFac
Definition: geometry.h:19
realnum fiscal
Definition: geometry.h:21
realnum filpow
Definition: geometry.h:20
realnum covgeo
Definition: geometry.h:35
realnum * tmn
Definition: opacity.h:136
double * opacity_abs
Definition: opacity.h:95
double EdenProp
Definition: phycon.h:95
double te
Definition: phycon.h:11
double EdenInit
Definition: phycon.h:93
double TeProp
Definition: phycon.h:91
double TeInit
Definition: phycon.h:89
double BeamOutOut
Definition: radius.h:108
double CylindHigh
Definition: radius.h:120
double BeamInIn
Definition: radius.h:102
double drad
Definition: radius.h:31
double dr_min_last_iter
Definition: radius.h:176
double r1r0sq
Definition: radius.h:49
bool lgdR2Small
Definition: radius.h:112
realnum glbrad
Definition: radius.h:130
double depth_mid_zone
Definition: radius.h:41
double rinner
Definition: radius.h:22
double dr_max_last_iter
Definition: radius.h:177
double drad_x_fillfac
Definition: radius.h:71
double dVolOutwrd
Definition: radius.h:97
double dVeffAper
Definition: radius.h:87
realnum glbdst
Definition: radius.h:133
double Depth2Go
Definition: radius.h:44
double BeamInOut
Definition: radius.h:105
double darea_x_fillfac
Definition: radius.h:77
double * StopThickness
Definition: radius.h:55
double depth_x_fillfac
Definition: radius.h:74
double dVeffVol
Definition: radius.h:81
double drNext
Definition: radius.h:61
double dVolReflec
Definition: radius.h:98
double drad_mid_zone
Definition: radius.h:34
double dRNeff
Definition: radius.h:90
double Radius_mid_zone
Definition: radius.h:28
double Radius
Definition: radius.h:25
double depth
Definition: radius.h:38
double dRadSign
Definition: radius.h:68
realnum * SummedDif
Definition: rfield.h:172
realnum * flux_beam_time
Definition: rfield.h:92
realnum * flux_isotropic
Definition: rfield.h:89
realnum ** flux
Definition: rfield.h:86
double * SummedCon
Definition: rfield.h:171
long int nflux
Definition: rfield.h:43
realnum * flux_beam_const
Definition: rfield.h:92
realnum * testr
Definition: struc.h:25
realnum * ednstr
Definition: struc.h:30
bool lgPredNextTe
Definition: thermal.h:28
void TempChange(double TempNew, bool lgForceUpdate)
Definition: temp_change.cpp:51
t_thermal thermal
Definition: thermal.cpp:5
void ZoneStart(const char *chMode)
void ZoneEnd(void)
#define IOFF