cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
conv_eden_ioniz.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 /*ConvEdenIoniz called by ConvTempIonz, calls ConvIoniz solving for eden */
4 /*lgConvEden returns true if electron density is converged */
5 /*EdenError evaluate ConvIoniz() until ionization has converged and return error on eden */
6 #include "cddefines.h"
7 #include "dense.h"
8 #include "trace.h"
9 #include "conv.h"
10 #include "cooling.h"
11 #include "thermal.h"
12 #include "iter_track.h"
13 #include "rt.h"
14 
15 /*lgConvEden returns true if electron density is converged */
16 STATIC bool lgConvEden();
17 /*EdenError evaluate ConvIoniz() until ionization has converged and return error on eden */
18 STATIC double EdenError(double eden);
19 
20 /*ConvEdenIoniz called by ConvTempEdenIoniz, calls ConvIoniz solving for eden
21  * returns 0 if ok, 1 if abort */
22 int ConvEdenIoniz(void)
23 {
24  DEBUG_ENTRY( "ConvEdenIoniz()" );
25 
26  /* this routine is called by ConvTempEdenIoniz, it calls ConvIoniz
27  * and changes the electron density until it converges */
28 
29  if( trace.lgTrace )
30  {
31  fprintf( ioQQQ, "\n" );
32  fprintf( ioQQQ, " ConvEdenIoniz entered\n" );
33  }
34  if( trace.nTrConvg>=3 )
35  {
36  fprintf( ioQQQ,
37  " ConvEdenIoniz called, entering eden loop using solver %s.\n",
39  }
40 
41  /* save entry value of eden */
42  double EdenEntry = dense.eden;
43 
44  // this branch uses the van Wijngaarden-Dekker-Brent method
45  if( strcmp( conv.chSolverEden , "vWDB" )== 0 )
46  {
47  conv.lgConvEden = false;
48 
49  iter_track NeTrack;
50  double n1, error1, n2, error2;
51  // this is the maximum relative step in eden
52  double factor = 0.02;
53  bool lgHysteresis = false;
54 
55  for( int n=0; n < 3; ++n )
56  {
57  const int DEF_ITER = 10;
58  // if hysteresis is detected, we should lower the maximum
59  // step to avoid upsetting the lower solvers by large steps
60  if( lgHysteresis )
61  factor /= 5.;
62 
63  NeTrack.clear();
64 
65  // when dense.EdenTrue becomes negative, error1 > n1 (since n1 > 0.)
66  // a straight copy EdenTrue -> eden would then imply a step > 100% down
67  // all of the code below will cap that to n1*(1.-factor), and eden stays > 0.
68  // this is also asserted in EdenError, the ONLY place where dense.eden is set
69 
70  n1 = dense.eden;
71  error1 = EdenError( n1 );
72  NeTrack.add( n1, error1 );
73 
75  n2 = sqrt(dense.eden*dense.EdenTrue);
76  else if( abs(safe_div( error1, n1 )) < factor )
77  n2 = dense.EdenTrue;
78  else
79  n2 = ( error1 > 0. ) ? n1*(1.-factor) : n1*(1.+factor);
80 
81  // n1 == n2 will occur if SET EDEN command was given
82  if( !fp_equal( n1, n2 ) )
83  error2 = EdenError( n2 );
84  else
85  error2 = error1;
86  NeTrack.add( n2, error2 );
87 
88  int j = 0;
89 
90  // now hunt until we have bracketed the solution
91  while( error1*error2 > 0. && j++ < DEF_ITER )
92  {
93  n1 = n2;
94  error1 = error2;
95  double deriv = NeTrack.deriv(5);
96  // this can occur in fully stripped conditions where
97  // dense.EdenTrue is essentially independent of dense.eden
98  if( deriv == 0. )
99  deriv = 1.;
100  // the factor 1.2 creates 20% safety margin
101  double step = safe_div( -1.2*error1, deriv, 0. );
102  step = sign( min( abs(step), factor*n1 ), step );
103  n2 = n1 + step;
104  error2 = EdenError( n2 );
105  NeTrack.add( n2, error2 );
106  }
107 
108  if( error1*error2 > 0. && trace.nTrConvg >= 3 )
109  {
110  fprintf( ioQQQ, " ConvEdenIoniz: bracket failure 1 n1: %e %e n2: %e %e\n",
111  n1, error1, n2, error2 );
112  NeTrack.print_history();
113  }
114 
115  // using the derivative failed, so simply start hunting up or downwards
116  // we may need to take a big step, so max_iter should be big
117  while( error1*error2 > 0. && j++ < 20*DEF_ITER )
118  {
119  n1 = n2;
120  error1 = error2;
121  n2 = ( error1 > 0. ) ? n1*(1.-factor) : n1*(1.+factor);
122  error2 = EdenError( n2 );
123  NeTrack.add( n2, error2 );
124  }
125 
126  if( error1*error2 > 0. && trace.nTrConvg >= 3 )
127  {
128  fprintf( ioQQQ, " ConvEdenIoniz: bracket failure 2 n1: %e %e n2: %e %e\n",
129  n1, error1, n2, error2 );
130  NeTrack.print_history();
131  }
132 
133  NeTrack.clear();
134 
135  // the bracket should have been found, now set up the Brent solver
136  if( NeTrack.init_bracket( n1, error1, n2, error2 ) == 0 )
137  {
138  int nBound = 0;
139 
140  // set tolerance to 2 ulp; if bracket gets narrower than 3 ulp we declare
141  // a convergence failure to avoid changes getting lost in machine precision
142  NeTrack.set_tol(2.*DBL_EPSILON*n2);
143 
144  double NeNew = 0.5*(n1+n2);
145  for( int i = 0; i < (1<<(n/2))*DEF_ITER; i++ )
146  {
147  // check for convergence, as well as a pathologically narrow bracket
148  if( lgConvEden() || NeTrack.bracket_width() < 3.*DBL_EPSILON*n2 )
149  break;
150 
151  NeTrack.add( NeNew, EdenError( NeNew ) );
152  NeNew = NeTrack.next_val(factor);
153 
154  // this guards against hysteresis. the symptom of hysteresis is
155  // that EdenTrue ends up being outside the bracket consistently.
156  // if this happens several times in a row, break out of this loop
157  int nVal = NeTrack.in_bounds(dense.EdenTrue);
158  if( nVal == 0 )
159  nBound = 0;
160  else
161  nBound += nVal;
162  if( abs(nBound) >= 3 )
163  {
164  lgHysteresis = true;
165  if( trace.nTrConvg >= 3 )
166  fprintf( ioQQQ, " ConvEdenIoniz: hysteresis detected\n" );
167  break;
168  }
169  }
170  }
171 
172  if( conv.lgConvEden )
173  break;
174 
175  if( trace.nTrConvg >= 3 )
176  {
177  fprintf( ioQQQ, " ConvEdenIoniz: brent fails\n" );
178  NeTrack.print_history();
179  }
180  }
181 
182  }
183  else if( strcmp( conv.chSolverEden , "SECA" )== 0 )
184  {
185  conv.lgConvEden = false;
186 
187 
188  double n1=0., error1=0., n2=0., error2=0.;
189  const int MAX_ITER = 20;
190  for( int n=0; n < MAX_ITER; ++n )
191  {
192  if ( n == 0 )
193  {
194  n2 = dense.eden;
195  if ( dense.EdenTrue > 0. )
196  n2 = sqrt( n2*dense.EdenTrue );
197  }
198  else if ( n == 1 || (n1-n2)*(error1-error2) <= 0.0 )
199  {
200  n1 = n2;
201  error1 = error2;
202  if ( dense.EdenTrue > 0. )
203  n2 = sqrt(dense.EdenTrue*dense.eden);
204  else
205  n2 = dense.eden/2.;
206  }
207  else
208  {
209  double nt = (n1*error2 - n2*error1)/(error2-error1);
210  if (fabs(nt-n2) > 2*fabs(n2-n1))
211  nt = 3*n2 - 2*n1;
212  // fprintf(ioQQQ,"3 %lg %lg %lg\n",n1,n2,nt);
213  if (nt < 0.1*n2)
214  nt = 0.1*n2;
215  n1 = n2;
216  error1 = error2;
217  n2 = nt;
218  }
219  error2 = EdenError( n2 );
220 
221  if (0)
222  fprintf(ioQQQ,"LONG Nzone %ld Loop %d density %15.8g true %15.8g error %15.8g\n",
223  nzone, n, n2, dense.EdenTrue, error2);
224 
225  if( lgConvEden() )
226  break;
227 
228  }
229  }
230  else
231  {
232  fprintf( ioQQQ, "ConvEdenIoniz finds insane solver %s\n", conv.chSolverEden );
233  ShowMe();
234  }
235 
236  if( trace.lgTrace || trace.nTrConvg >= 3 )
237  {
238  fprintf( ioQQQ, " ConvEdenIoniz: entry eden %.4e -> %.4e rel chng %.2f%% accuracy %.2f%%\n",
239  EdenEntry, dense.eden, (safe_div(dense.eden,EdenEntry,1.)-1.)*100.,
240  (safe_div(dense.eden,dense.EdenTrue,1.)-1.)*100. );
241  fprintf( ioQQQ, " ConvEdenIoniz returns converged=%c reason %s\n",
243  }
244 
245  if (!lgConvBaseHeatTest)
246  {
247  //HeatZero();
248 
249  /* get total cooling, thermal.ctot = does not occur since passes as pointer. This can add heat.
250  * it calls coolSum at end to sum up the total cooling */
252 
253  HeatSum();
254  }
255 
256  return 0;
257 }
258 
259 /* returns true if electron density is converged */
261 {
262  conv.lgConvEden =
264  || ( dense.eden > 0 &&
265  abs(dense.eden-dense.EdenTrue) < 1e-15*dense.xNucleiTotal );
266  if( !conv.lgConvEden )
267  {
268  conv.setConvIonizFail( "Ne big chg" , dense.EdenTrue, dense.eden);
269  }
270  return conv.lgConvEden;
271 }
272 
273 /* evaluate ConvIoniz() until ionization has converged and return error on eden */
274 STATIC double EdenError(double eden)
275 {
276  // this is the only place where the new electron density is set
277  static ConvergenceCounter cctr=conv.register_("EDEN_CHANGES");
278  ++cctr;
279  EdenChange( eden );
280 
281  //RT_OTS();
282  double SumOTS;
283  RT_OTS_Update(&SumOTS);
284  RT_line_all_escape( NULL );
285 
286  for( int i=0; i < 5; ++i )
287  {
288  if( ConvIoniz() )
289  lgAbort = true;
290 
291  if( conv.lgConvIoniz() )
292  break;
293  }
294 
295  double error = dense.eden - dense.EdenTrue;
296 
297  if( trace.nTrConvg >= 3 )
298  fprintf( ioQQQ, " EdenError: eden %.4e EdenTrue %.4e rel. err. %.4e\n",
300 
301  return error;
302 }
303 
bool lgConvEden
Definition: conv.h:195
void RT_OTS_Update(double *SumOTS)
Definition: rt_ots.cpp:476
t_thermal thermal
Definition: thermal.cpp:6
double EdenErrorAllowed
Definition: conv.h:263
double bracket_width() const
Definition: iter_track.h:85
double next_val()
Definition: iter_track.cpp:57
void add(double x, double fx)
Definition: iter_track.h:120
const realnum SMALLFLOAT
Definition: cpu.h:246
void print_history() const
Definition: iter_track.h:186
realnum xNucleiTotal
Definition: dense.h:114
ConvergenceCounter register_(const string name)
Definition: conv.cpp:87
char TorF(bool l)
Definition: cddefines.h:749
t_conv conv
Definition: conv.cpp:5
T sign(T x, T y)
Definition: cddefines.h:842
void CoolEvaluate(double *tot)
Definition: cool_eval.cpp:58
FILE * ioQQQ
Definition: cddefines.cpp:7
long int nzone
Definition: cddefines.cpp:14
bool lgConvIoniz() const
Definition: conv.h:108
STATIC double EdenError(double eden)
void RT_line_all_escape(realnum *error)
Definition: rt_line_all.cpp:21
t_dense dense
Definition: global.cpp:15
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
const char * chConvIoniz() const
Definition: conv.h:112
int init_bracket(double x1, double fx1, double x2, double fx2)
Definition: iter_track.h:104
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
double EdenTrue
Definition: dense.h:232
long min(int a, long b)
Definition: cddefines.h:762
sys_float safe_div(sys_float x, sys_float y, sys_float res_0by0)
Definition: cddefines.h:1011
int nTrConvg
Definition: trace.h:27
STATIC bool lgConvEden()
void clear()
Definition: iter_track.h:76
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
double eden
Definition: dense.h:201
int ConvIoniz(void)
Definition: conv_ioniz.cpp:11
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
double deriv(int n, double &sigma) const
Definition: iter_track.cpp:184
void setConvIonizFail(const char *reason, double oldval, double newval)
Definition: conv.h:100
int in_bounds(double x) const
Definition: iter_track.h:170
void EdenChange(double EdenNew)
Definition: eden_change.cpp:11
void ShowMe(void)
Definition: service.cpp:205
char chSolverEden[20]
Definition: conv.h:238
void set_tol(double tol)
Definition: iter_track.h:81
int ConvEdenIoniz(void)
void HeatSum(void)
Definition: heat_sum.cpp:498
bool lgAbort
Definition: cddefines.cpp:10
double ctot
Definition: thermal.h:130
const bool lgConvBaseHeatTest
Definition: cooling.h:7