cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
temp_change.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2022 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
3 /*tfidle update some temperature dependent variables */
4 /*tauff compute optical depth where cloud is thin to free-free and plasma freq */
5 #include "cddefines.h"
6 #include "conv.h"
7 #include "opacity.h"
8 #include "dense.h"
9 #include "phycon.h"
10 #include "stopcalc.h"
11 #include "trace.h"
12 #include "rfield.h"
13 #include "doppvel.h"
14 #include "radius.h"
15 #include "wind.h"
16 #include "thermal.h"
17 #include "vectorize.h"
18 #include "taulines.h"
19 
20 /*tauff compute optical depth where cloud is thin to free-free and plasma freq */
21 STATIC void tauff();
22 /* On first run, fill GauntFF with gaunt factors */
23 
27 STATIC void tfidle(
28  bool lgForceUpdate);
29 
33  double TempNew ,
34  /* option to force update of all variables */
35  bool lgForceUpdate)
36 {
37 
38  DEBUG_ENTRY( "TempChange()" );
39 
40  /* set new temperature */
41  if( TempNew > phycon.TEMP_LIMIT_HIGH )
42  {
43  /* temp is too high */
44  fprintf(ioQQQ,"\n\n PROBLEM DISASTER - the kinetic temperature, %.3eK,"
45  " is above the upper limit of the code, %.3eK.\n",
46  TempNew , phycon.TEMP_LIMIT_HIGH );
47  fprintf(ioQQQ," This calculation is aborting.\n Sorry.\n");
48 
49  TempNew = phycon.TEMP_LIMIT_HIGH*0.99999;
50  lgAbort = true;
51  }
52  else if( TempNew < phycon.TEMP_LIMIT_LOW )
53  {
54  /* temp is too low */
55  fprintf(ioQQQ,"\n\n PROBLEM DISASTER - the kinetic temperature, %.3eK,"
56  " is below the lower limit of the code, %.3eK.\n",
57  TempNew , phycon.TEMP_LIMIT_LOW );
58  fprintf(ioQQQ," Consider setting a lowest temperature with the SET TEMPERATURE FLOOR command.\n");
59  fprintf(ioQQQ," This calculation is aborting.\n Sorry.\n");
60  TempNew = phycon.TEMP_LIMIT_LOW*1.00001;
61  lgAbort = true;
62  }
63  else if( TempNew < StopCalc.TeFloor )
64  {
65  if( trace.lgTrace || trace.nTrConvg>=2 )
66  fprintf(ioQQQ,"temp_change: temp change floor hit, TempNew=%.3e TeFloor=%.3e, "
67  "setting constant temperature, nTotalIoniz=%li\n",
68  TempNew , StopCalc.TeFloor , conv.nTotalIoniz);
69  /* temperature floor option -
70  * go to constant temperature calculation if temperature
71  * falls below floor */
75  /*fprintf(ioQQQ,"DEBUG TempChange hit temp floor, setting const temp to %.3e\n",
76  phycon.te );*/
77  }
78  else
79  {
80  /* temp is within range */
81  phycon.te = TempNew;
82  }
83 
84  /* make helpful suggestion for how to debug this abort */
85  static bool lgPrinted = false;
86  if( lgAbort && !lgPrinted)
87  {
88  lgPrinted = true;
89  fprintf( ioQQQ, "\n This is often due to mixing luminosity/intensity commands, having"
90  " an unphysical SED, or too high a flux of cosmic rays.\n");
91  fprintf( ioQQQ, " To find the problem, try setting a constant temperature (and STOP ZONE 1), rerun the model,"
92  " and check the output to find what is happening.\n\n");
93  }
94 
95  /* now update related variables */
96  if ( conv.lgSearch )
97  {
98  /* in search phase, force species2.cpp level trimming to be
99  * re-tested */
100  for( long ipSpecies=0; ipSpecies<nSpecies; ++ipSpecies )
101  {
102  dBaseSpecies[ipSpecies].numLevels_local = dBaseSpecies[ipSpecies].numLevels_max;
103  }
104  }
105  tfidle( lgForceUpdate );
106  return;
107 }
112  double TempNew )
113 {
114 
115  DEBUG_ENTRY( "TempChange()" );
116 
117  /* set new temperature */
118  if( TempNew > phycon.TEMP_LIMIT_HIGH )
119  {
120  /* temp is too high */
121  fprintf(ioQQQ," PROBLEM DISASTER - the kinetic temperature, %.3eK,"
122  " is above the upper limit of the code, %.3eK.\n",
123  TempNew , phycon.TEMP_LIMIT_HIGH );
124  fprintf(ioQQQ," This calculation is aborting.\n Sorry.\n");
125 
126  TempNew = phycon.TEMP_LIMIT_HIGH*0.99999;
127  lgAbort = true;
128  }
129  else if( TempNew < phycon.TEMP_LIMIT_LOW )
130  {
131  /* temp is too low */
132  fprintf(ioQQQ," PROBLEM DISASTER - the kinetic temperature, %.3eK,"
133  " is below the lower limit of the code, %.3eK.\n",
134  TempNew , phycon.TEMP_LIMIT_LOW );
135  fprintf(ioQQQ," Consider setting a lowest temperature with the SET TEMPERATURE FLOOR command.\n");
136  fprintf(ioQQQ," This calculation is aborting.\n Sorry.\n");
137  TempNew = phycon.TEMP_LIMIT_LOW*1.00001;
138  lgAbort = true;
139  }
140  else
141  {
142  /* temp is within range */
143  phycon.te = TempNew;
144  }
145 
146  /* now update related variables */
147  tfidle( false );
148  return;
149 }
150 
151 void tfidle(
152  /* option to force update of all variables */
153  bool lgForceUpdate)
154 {
155  static double tgffused=-1.;
156  static double ttused = 0.;
157  static bool lgZLogSet = false;
158 
159  DEBUG_ENTRY( "tfidle()" );
160 
161  /* called with lgForceUpdate true in zero.c, when we must update everything */
162  if( lgForceUpdate )
163  {
164  ttused = -1.;
165  tgffused = -1.;
166  }
167 
168  /* check that eden not negative */
169  if( dense.eden <= 0. )
170  {
171  fprintf( ioQQQ, "tfidle called with a zero or negative electron density,%10.2e\n",
172  dense.eden );
173  TotalInsanity();
174  }
175 
176  /* check that temperature not negative */
177  if( phycon.te <= 0. )
178  {
179  fprintf( ioQQQ, "tfidle called with a negative electron temperature,%10.2e\n",
180  phycon.te );
181  TotalInsanity();
182  }
183 
184  /* one time only, set up array of logs of charge squared */
185  if( !lgZLogSet )
186  {
187  for( long nelem=0; nelem<LIMELM; ++nelem )
188  {
189  /* this array is used to modify the log temperature array
190  * defined below, for hydrogenic species of charge nelem+1 */
191  phycon.sqlogz[nelem] = log10( POW2(nelem+1.) );
192  }
193  lgZLogSet = true;
194  }
195 
196  if( ! fp_equal( phycon.te, ttused ) )
197  {
198  ttused = phycon.te;
200  /* current temperature in various units */
201  phycon.te_eV = phycon.te/EVDEGK;
202  phycon.te_ryd = phycon.te/TE1RYD;
203  phycon.te_wn = phycon.te / T1CM;
204 
206  phycon.sqrte = sqrt(phycon.te);
207  thermal.halfte = 0.5/phycon.te;
208  thermal.tsq1 = 1./phycon.tesqrd;
210  phycon.teinv = 1./phycon.te;
211 
212  phycon.alogte = log10(phycon.te);
213  phycon.alnte = log(phycon.te);
214 
215  phycon.telogn[0] = phycon.alogte;
216  for( int i=1; i < 7; i++ )
217  {
218  phycon.telogn[i] = phycon.telogn[i-1]*phycon.telogn[0];
219  }
220 
221  phycon.te10 = pow(phycon.te,0.10);
227 
228  phycon.te01 = pow(phycon.te,0.01);
234 
235  phycon.te001 = pow(phycon.te,0.001);
241  /*>>>chng 06 June 30 -Humeshkar Nemala*/
242  phycon.te0001 = pow(phycon.te ,0.0001);
248 
249  }
250 
251  /* >>>chng 99 nov 23, removed this line, so back to old method of h coll */
252  /* used for hydrogenic collisions */
253  /*
254  * following electron density has approximate correction for neutrals
255  * corr of hi*1.7e-4 accounts for col ion by HI;
256  * >>refer H0 correction for collisional contribution Drawin, H.W. 1969, Zs Phys 225, 483.
257  * also quoted in Dalgarno & McCray 1972
258  * extensive discussion of this in
259  *>>refer H0 collisions Lambert, D.L.
260  * used EdenHCorr instead
261  * edhi = eden + hi * 1.7e-4
262  */
264  /* dense.HCorrFac is unity by default and changed with the set HCOR command */
265  dense.xIonDense[ipHYDROGEN][0]*1.7e-4 * dense.HCorrFac;
267 
268  /*>>chng 93 jun 04,
269  * term with hi added June 4, 93, to account for warm pdr */
270  /* >>chng 05 jan 05, Will Henney noticed that 1.e-4 used here is not same as
271  * 1.7e-4 used for EdenHCorr, which had rewritten the expression.
272  * change so that edensqte uses EdenHCorr rather than reevaluating */
273  /*dense.edensqte = ((dense.eden + dense.xIonDense[ipHYDROGEN][0]/1e4)/phycon.sqrte);*/
275  dense.cdsqte = dense.edensqte*COLL_CONST;
276  dense.SqrtEden = sqrt(dense.eden);
277 
278  /* rest have to do with radiation field and frequency mesh which may not be defined yet */
279  if( !lgRfieldMalloced || !rfield.lgMeshSetUp() )
280  return;
281 
282  /* correction factors for induced recombination,
283  * also used as Boltzmann factors
284  * check for zero is because ContBoltz is zeroed out in initialization
285  * of code, its possible this is a constant density grid of models
286  * in which the code is called as a subroutine */
287  /* >>chng 01 aug 21, must also test on size of continuum nflux because
288  * conintitemp can increase nflux then call this routine, although
289  * temp may not have changed */
290  if( ! fp_equal(tgffused, phycon.te) || rfield.ContBoltz[0] <= 0. )
291  {
292  tgffused = phycon.te;
293  for( long i=0; i < rfield.nflux_with_check; ++i )
295  /* atom_level2 uses ContBoltz to see whether the rates will be significant.
296  * If the numbers did not agree then this test would be flawed, resulting in
297  * div by zero */
299  for( long i=0; i < rfield.nflux_with_check; ++i )
301  /* this is Boltzmann factor averaged over the width of the cell */
303  for( long i=0; i < rfield.nflux_with_check; ++i )
306  for( long i=0; i < rfield.nflux_with_check; ++i )
307  {
310  }
312  /* ipMaxBolt is number of non-zero cells, so non-zero up through ipMaxBolt-1 */
314  for( long i=rfield.nflux_with_check-1; i >= 0; --i )
315  {
316  if( rfield.ContBoltz[i] > 0. )
317  break;
318  rfield.ipMaxBolt = i;
319  }
320  }
321 
322  /* find frequency where thin to bremsstrahlung or plasma frequency */
323  tauff();
324 }
325 
326 /*tauff compute optical depth where cloud is thin to free-free and plasma freq */
327 STATIC void tauff()
328 {
329  realnum fac;
330 
331  /* simply return if space not yet allocated */
332  if( !lgOpacMalloced )
333  return;
334 
335  DEBUG_ENTRY( "tauff()" );
336 
337  if( !conv.nTotalIoniz )
339 
340  /* routine sets variable ipEnergyBremsThin, index for lowest cont cell that is optically thin */
341  /* find frequency where continuum thin to free-free */
342  while( rfield.ipEnergyBremsThin < rfield.nflux &&
344  {
346  }
347 
348  /* TFF will be frequency where cloud becomes optically thin to bremsstrahlung
349  * >>chng 96 may 7, had been 2, change as per Kevin Volk bug report */
351  {
352  /* tau can be zero when plasma frequency is within energy grid, */
355  fac = MAX2(fac,0.f);
357  }
358  else
359  {
360  rfield.EnergyBremsThin = 0.f;
361  }
362 
363  /* did not include plasma freq before
364  * function returns larger of these two frequencies */
366 
367  /* now increment ipEnergyBremsThin still further, until above plasma frequency */
368  while( rfield.ipEnergyBremsThin < rfield.nflux &&
370  {
372  }
373  return;
374 }
375 
377 {
378  ASSERT( massAMU > 0. );
379  // force a fairly conservative upper limit
380  ASSERT( massAMU < 10000. );
381 
382  /* usually TurbVel =0, reset with turbulence command
383  * cm/s here, but was entered in km/s with command */
384  double turb2 = POW2(DoppVel.TurbVel);
385 
386  /* this is option to dissipate the turbulence. DispScale is entered with
387  * dissipate keyword on turbulence command. The velocity is reduced here,
388  * by an assumed exponential scale, and also adds heat */
389  if( DoppVel.DispScale > 0. )
390  {
391  /* square of exp depth dependence */
392  turb2 *= sexp( 2.*radius.depth / DoppVel.DispScale );
393  }
394 
395  /* in case of D-Critical flow include initial velocity as
396  * a component of turbulence */
397  if( ! ( wind.lgBallistic() || wind.lgStatic() ) )
398  {
399  turb2 += POW2(wind.windv0);
400  }
401 
402  realnum width = (realnum)sqrt(2.*BOLTZMANN/ATOMIC_MASS_UNIT*phycon.te/massAMU+turb2);
403  ASSERT( width > 0.f );
404  return width;
405 }
406 
408 {
409 #if 0
410  /* usually TurbVel =0, reset with turbulence command
411  * cm/s here, but was entered in km/s with command */
412  double turb2 = POW2(DoppVel.TurbVel);
413 
414  /* this is option to dissipate the turbulence. DispScale is entered with
415  * dissipate keyword on turbulence command. The velocity is reduced here,
416  * by an assumed exponential scale, and also adds heat */
417  if( DoppVel.DispScale > 0. )
418  {
419  /* square of exp depth dependence */
420  turb2 *= sexp( 2.*radius.depth / DoppVel.DispScale );
421  }
422 
423  /* in case of D-Critical flow include initial velocity as
424  * a component of turbulence */
425  if( ! ( wind.lgBallistic() || wind.lgStatic() ) )
426  {
427  turb2 += POW2(wind.windv0);
428  }
429 #endif
430  DEBUG_ENTRY( "GetAveVelocity()" );
431 
432  /* this is average (NOT rms) particle speed for Maxwell distribution, Mihalas 70, 9-70 */
433  fixit("turbulence was included here for molecules but not ions. Now neither. Resolve.");
434  return (realnum)sqrt(8.*BOLTZMANN/PI/ATOMIC_MASS_UNIT*phycon.te/massAMU);
435 }
double depth
Definition: radius.h:31
vector< double, allocator_avx< double > > ContBoltz
Definition: rfield.h:124
double te007
Definition: phycon.h:58
t_thermal thermal
Definition: thermal.cpp:6
vector< double, allocator_avx< double > > vexp_arg
Definition: rfield.h:131
double te03
Definition: phycon.h:58
long int ipEnergyBremsThin
Definition: rfield.h:226
T * get_ptr(T *v)
Definition: cddefines.h:1109
NORETURN void TotalInsanity(void)
Definition: service.cpp:971
double te_update
Definition: thermal.h:148
bool lgOpacMalloced
Definition: cdinit.cpp:40
double widflx(size_t i) const
Definition: mesh.h:156
double EdenHCorr
Definition: dense.h:227
t_opac opac
Definition: opacity.cpp:5
realnum EnergyBremsThin
Definition: rfield.h:227
STATIC void tauff()
realnum windv0
Definition: wind.h:11
double cdsqte
Definition: dense.h:246
realnum HCorrFac
Definition: dense.h:121
double te02
Definition: phycon.h:58
long int ipMaxBolt
Definition: rfield.h:230
t_StopCalc StopCalc
Definition: stopcalc.cpp:7
t_conv conv
Definition: conv.cpp:5
double te0002
Definition: phycon.h:58
t_phycon phycon
Definition: phycon.cpp:6
realnum TurbVel
Definition: doppvel.h:21
vector< double, allocator_avx< double > > ContBoltzHelp2
Definition: rfield.h:128
sys_float sexp(sys_float x)
Definition: service.cpp:999
bool lgTemperatureConstant
Definition: thermal.h:44
vector< double, allocator_avx< double > > ContBoltzAvg
Definition: rfield.h:135
FILE * ioQQQ
Definition: cddefines.cpp:7
double sqlogz[LIMELM]
Definition: phycon.h:86
t_DoppVel DoppVel
Definition: doppvel.cpp:5
double te_eV
Definition: phycon.h:24
double anu(size_t i) const
Definition: mesh.h:120
long int nSpecies
Definition: taulines.cpp:22
void TempChange(double TempNew, bool lgForceUpdate)
Definition: temp_change.cpp:32
STATIC void tfidle(bool lgForceUpdate)
t_dense dense
Definition: global.cpp:15
double te005
Definition: phycon.h:58
double te003
Definition: phycon.h:58
void vexp(const double x[], double y[], long nlo, long nhi)
long int nflux_with_check
Definition: rfield.h:49
double te004
Definition: phycon.h:58
double edensqte
Definition: dense.h:241
const double TEMP_LIMIT_LOW
Definition: phycon.h:121
Wind wind
Definition: wind.cpp:5
double xIonDense[LIMELM][LIMELM+1]
Definition: dense.h:135
bool lgSearch
Definition: conv.h:168
t_trace trace
Definition: trace.cpp:5
bool fp_equal(sys_float x, sys_float y, int n=3)
Definition: cddefines.h:854
double te07
Definition: phycon.h:58
bool lgBallistic(void) const
Definition: wind.h:31
double te01
Definition: phycon.h:58
#define POW2
Definition: cddefines.h:979
bool lgMeshSetUp() const
Definition: mesh.h:84
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
double tsq1
Definition: thermal.h:142
double te001
Definition: phycon.h:58
double teinv
Definition: phycon.h:33
double te0005
Definition: phycon.h:58
double te0004
Definition: phycon.h:58
const double TEMP_LIMIT_HIGH
Definition: phycon.h:123
t_rfield rfield
Definition: rfield.cpp:9
realnum DispScale
Definition: doppvel.h:51
float realnum
Definition: cddefines.h:124
double alnte
Definition: phycon.h:95
realnum GetDopplerWidth(realnum massAMU)
realnum GetAveVelocity(realnum massAMU)
double te05
Definition: phycon.h:58
int nTrConvg
Definition: trace.h:27
double halfte
Definition: thermal.h:142
t_radius radius
Definition: radius.cpp:5
double anumin(size_t i) const
Definition: mesh.h:148
double te0007
Definition: phycon.h:58
long int nTotalIoniz
Definition: conv.h:159
#define ASSERT(exp)
Definition: cddefines.h:613
double te0001
Definition: phycon.h:58
realnum ConstTemp
Definition: thermal.h:56
bool lgRfieldMalloced
Definition: cdinit.cpp:39
const int LIMELM
Definition: cddefines.h:308
double te0003
Definition: phycon.h:58
double te40
Definition: phycon.h:58
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
double te_ryd
Definition: phycon.h:27
double te10
Definition: phycon.h:58
realnum EdenHCorr_f
Definition: dense.h:229
double te20
Definition: phycon.h:58
double te002
Definition: phycon.h:58
vector< double, allocator_avx< double > > ContBoltzHelp1
Definition: rfield.h:127
vector< species > dBaseSpecies
Definition: taulines.cpp:15
double eden
Definition: dense.h:201
realnum ** TauAbsGeo
Definition: opacity.h:91
#define MAX2(a, b)
Definition: cddefines.h:824
double TeFloor
Definition: stopcalc.h:33
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
double te70
Definition: phycon.h:58
double te90
Definition: phycon.h:58
double alogte
Definition: phycon.h:92
double telogn[7]
Definition: phycon.h:86
double SqrtEden
Definition: dense.h:223
bool lgStatic(void) const
Definition: wind.h:24
double te_wn
Definition: phycon.h:30
double sqrte
Definition: phycon.h:58
#define fixit(a)
Definition: cddefines.h:417
double te
Definition: phycon.h:21
const int ipHYDROGEN
Definition: cddefines.h:349
void vexpm1(const double x[], double y[], long nlo, long nhi)
double te04
Definition: phycon.h:58
realnum plsfrq
Definition: rfield.h:430
long int nflux
Definition: rfield.h:46
double te30
Definition: phycon.h:58
double te32
Definition: phycon.h:58
bool lgAbort
Definition: cddefines.cpp:10
double tesqrd
Definition: phycon.h:36