cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
mole_eval_balance.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 /*CO_step fills in matrix for heavy elements molecular routines */
4 #include "cddefines.h"
5 #include "dense.h"
6 #include "trace.h"
7 #include "atmdat.h"
8 #include "mole_priv.h"
9 #include "mole.h"
10 /* Nick Abel between July and October of 2003 assisted Dr. Ferland in improving the heavy element
11  * molecular network in Cloudy. Before this routine would predict negative abundances if
12  * the fraction of carbon in the form of molecules came close to 100%. A reorganizing of
13  * the reaction network detected several bugs. Treatment of "coupled reactions",
14  * in which both densities in the reaction rate were being predicted by Cloudy, were also
15  * added. Due to these improvements, Cloudy can now perform calculations
16  * where 100% of the carbon is in the form of CO without predicting negative abundances
17  *
18  * Additional changes were made in November of 2003 so that our reaction
19  * network would include all reactions from the TH85 paper. This involved
20  * adding silicon to the chemical network. Also the reaction rates were
21  * labeled to make identification with the reaction easier and the matrix
22  * elements of atomic C, O, and Si are now done in a loop, which makes
23  * the addition of future chemical species (like N or S) easy.
24  * */
25 /* Robin Williams in August 2006 onwards reorganized the coding to cut down repetitions.
26  * This isolated several further bugs, and allows a sigificant number of lines of
27  * code to be eliminated. The balance of S2/S2+ amd ClO/ClO+ seems highly sensitive
28  * (with small log scale results varying significantly if the order of arithmetic
29  * operations is changed) -- I suspect this may imply a bug somewhere.
30  * */
31 /*lint -e778 constant expression evaluatess to 0 in operation '-' */
32 /*=================================================================*/
33 
35 
36 void mole_eval_balance(long int num_total, double *b, bool lgJac, multi_arr<double,2> &c)
37 {
38  long int i, j;
39  mole_reaction *rate;
40  double rate_tot, rate_deriv[MAXREACTANTS], rk;
41  molecule *sp;
42 
43  DEBUG_ENTRY( "mole_eval_balance()" );
44  /* zero out array used for formation rates */
45  for( i=0; i < num_total; i++ )
46  {
47  b[i] = 0.;
48  }
49  if (lgJac)
50  c.zero();
51 
53  {
54  for(long mol=0;mol<mole_global.num_calc;mol++)
55  {
56  fprintf(ioQQQ," TrChemSp %20.13g %s\n",mole.species[mol].den,
57  mole_global.list[mol]->label.c_str());
58  }
59  for(mole_reaction_i p=mole_priv::reactab.begin();
60  p != mole_priv::reactab.end(); ++p)
61  {
62  rate = &(*p->second);
63  rk = mole.reaction_rks[ rate->index ];
64  fprintf(ioQQQ," TrChem %20.13g %s\n",rk,rate->label.c_str());
65  }
66  }
67 
68 
69  for(mole_reaction_i p=mole_priv::reactab.begin();
70  p != mole_priv::reactab.end(); ++p)
71  {
72  rate = &(*p->second);
73  rk = mole.reaction_rks[ rate->index ];
74 
75  rate_tot = rk;
76  for(i=0;i<rate->nreactants;i++)
77  {
78  rate_tot *= mole.species[ rate->reactants[i]->index ].den;
79  }
80 
82  {
83  fprintf(ioQQQ," TrChemTot %20.13g %20.13g %s\n",rate_tot,rk,rate->label.c_str());
84  for(i=0;i<rate->nreactants;i++)
85  {
86  fprintf(ioQQQ, " %20.13g", mole.species[ rate->reactants[i]->index ].den);
87  }
88  fprintf(ioQQQ,"\n");
89  }
90  for(i=0;i<rate->nreactants;i++)
91  {
92  sp = rate->reactants[i];
93  if (rate->rvector[i] == NULL)
94  {
95  b[sp->index] -= rate_tot;
96  }
97  }
98 
99  for(i=0;i<rate->nproducts;i++)
100  {
101  sp = rate->products[i];
102  if (rate->pvector[i] == NULL)
103  {
104  b[sp->index] += rate_tot;
105  }
106  }
107 
108  if (lgJac)
109  {
110  for(i=0;i<rate->nreactants;i++)
111  {
112  rate_deriv[i] = rk;
113  for(j=0;j<rate->nreactants;j++)
114  {
115  if(i!=j)
116  {
117  rate_deriv[i] *= mole.species[ rate->reactants[j]->index ].den;
118  }
119  }
120  }
121  for(j=0;j<rate->nreactants;j++)
122  {
123  sp = rate->reactants[j];
124  const double rated = rate_deriv[j];
125  for(i=0;i<rate->nreactants;i++)
126  {
127  if (rate->rvector[i] == NULL)
128  c[sp->index][rate->reactants[i]->index] -= rated;
129  }
130  for(i=0;i<rate->nproducts;i++)
131  {
132  if (rate->pvector[i] == NULL)
133  c[sp->index][rate->products[i]->index] += rated;
134  }
135  }
136  }
137  }
138 
139  if (lgJac)
140  {
142  // Make sure there appears to be a call-point
143  // even when NDEBUG is off
144  if (0) { void(lgNucleiConserved(c)); };
145  }
146 
147  //mole_dominant_rates(findspecies("H+"),ioQQQ);
148  return;
149 }
150 
151 void mole_eval_sources(long int num_total)
152 {
153  long int i, j, ion, ion2;
154  mole_reaction *rate;
155  double rate_tot, rate_deriv[MAXREACTANTS], rk;
156  molecule *sp;
157 
158  DEBUG_ENTRY( "mole_eval_sources()" );
159  /* zero out array used for formation rates */
160  for( i=0; i < num_total; i++ )
161  {
162  mole.species[i].src = mole.species[i].snk = 0.;
163  }
164 
165  for( long nelem=0; nelem< LIMELM; ++nelem )
166  {
167  /* these have one more ion than above */
168  for( ion=0; ion<nelem+2; ++ion )
169  {
170  /* zero out the transfer array */
171  for( ion2=0; ion2<nelem+2; ++ion2 )
172  {
173  mole.xMoleChTrRate[nelem][ion][ion2] = 0.;
174  }
175  }
176  }
177 
178  for(mole_reaction_i p=mole_priv::reactab.begin();
179  p != mole_priv::reactab.end(); ++p)
180  {
181  rate = &(*p->second);
182  rk = mole.reaction_rks[ rate->index ];
183 
184  for(i=0;i<rate->nreactants;i++)
185  {
186  rate_deriv[i] = rk;
187  for(j=0;j<rate->nreactants;j++)
188  {
189  if(i!=j)
190  {
191  rate_deriv[i] *= mole.species[ rate->reactants[j]->index ].den;
192  }
193  }
194  }
195 
196  rate_tot = rate_deriv[0] * mole.species[ rate->reactants[0]->index ].den;
197 
198  for(i=0;i<rate->nreactants;i++)
199  {
200  sp = rate->reactants[i];
201  if (rate->rvector[i] == NULL)
202  {
203  mole.species[sp->index].snk += rate_deriv[i];
204  }
205  else
206  {
207  if ( atmdat.lgCTOn )
208  {
209  for( molecule::nNucsMap::iterator nuc_i = sp->nNuclide.begin();
210  nuc_i != sp->nNuclide.end(); ++nuc_i)
211  {
212  if (! nuc_i->first->lgHasLinkedIon() )
213  continue;
214  ASSERT(nuc_i->second != 0);
215  if (rate->rvector[i]->charge != sp->charge)
216  {
217  long nelem = nuc_i->first->el->Z-1;
218  mole.xMoleChTrRate[nelem][sp->charge][rate->rvector[i]->charge] +=
219  (realnum) rate_deriv[i];
220  break;
221  }
222  }
223  }
224  }
225  }
226 
227  for(i=0;i<rate->nproducts;i++)
228  {
229  sp = rate->products[i];
230  if (rate->pvector[i] == NULL)
231  {
232  mole.species[sp->index].src += rate_tot;
233  }
234  }
235  }
236 
237  for (ChemNuclideList::iterator atom = nuclide_list.begin();
238  atom != nuclide_list.end(); ++atom)
239  {
240  if (!(*atom)->lgHasLinkedIon())
241  continue;
242  const long int nelem=(*atom)->el->Z-1;
243  if( !dense.lgElmtOn[nelem] )
244  continue;
245 
246  for (long int ion=0;ion<nelem+2;ion++)
247  {
248  if ((*atom)->ipMl[ion] != -1)
249  {
250  mole.source[nelem][ion] = mole.species[(*atom)->ipMl[ion]].src;
251  mole.sink[nelem][ion] = mole.species[(*atom)->ipMl[ion]].snk;
252  }
253  else
254  {
255  mole.source[nelem][ion] = 0.0;
256  mole.sink[nelem][ion] = 0.0;
257  }
258  }
259  }
260 
261  //mole_dominant_rates(findspecies("H+"),ioQQQ);
262  return;
263 }
264 
266 {
267  DEBUG_ENTRY ("lgNucleiConserved()" );
268  bool checkAllOK = true;
269 
270  for (unsigned long j=0; j<nuclide_list.size(); ++j )
271  {
272  nuclide_list[j]->index = j;
273  }
274 
275  size_t size1=0;
276  for (long j=0;j<mole_global.num_calc;j++)
277  {
278  size1 += mole_global.list[j]->nNuclide.size();
279  }
280  vector<long> natoms(size1), nNucs(size1), jend(mole_global.num_calc);
281  for (long j=0,i=0;j<mole_global.num_calc;j++)
282  {
283  for (molecule::nNucsMap::const_iterator el = mole_global.list[j]->nNuclide.begin();
284  el != mole_global.list[j]->nNuclide.end(); ++el)
285  {
286  natoms[i] = el->first->index;
287  nNucs[i] = el->second;
288  ++i;
289  }
290  jend[j] = long(i);
291  }
292 
293  vector<double> test(nuclide_list.size()),
294  tot(nuclide_list.size());
295  for (long i=0;i<mole_global.num_calc;i++)
296  {
297  for( unsigned long natom=0; natom < nuclide_list.size(); ++natom)
298  {
299  test[natom] = tot[natom] = 0.0;
300  }
301  for (long j=0,jstart=0;j<mole_global.num_calc;j++)
302  {
303  long lim = jend[j];
304  if (c[i][j] != 0.0)
305  {
306  for (long pos=jstart; pos < lim; ++pos)
307  {
308  const long natom = natoms[pos];
309  const int nNuclidej = nNucs[pos];
310  const double term = c[i][j] * nNuclidej;
311  test[natom] += term;
312  tot[natom] += fabs(term);
313  }
314  }
315  jstart = lim;
316  }
317 
318  for( unsigned long natom=0; natom < nuclide_list.size(); ++natom)
319  {
320  /*
321  * The following is Robin's response to a request to adjust the limit below
322  * (to 2e-9, from 1e-9) in order for a failing sim to complete successfully
323  * (Cloudy bailed with a conservation error in the Li H chemical network).
324  * The initial e-mail was sent on June 1, 2015, under the subject 'molecular
325  * balance tolerance', and Robin's response was posted the following day.
326  * The thread may be found on the cloudy-dev Google group.
327  *
328  * "Where this happens, there is generally a very rapid equilibrium reaction
329  * between two species, so where all the destruction rates are added on the
330  * diagonal of the Jacobian, you lose significance in the value of the slower
331  * secular rate.
332  *
333  * "When the elements are added to make the total consistency check, generally
334  * in a different order, this suffers from the problem that
335  * ((A+B)-A)-B != (A+B)-(A+B) != 0. in finite precision mathematics.
336  *
337  * "As the size of the differences in rates can be very large, I used to worry
338  * that this test was going to gradually loosen into meaninglessness. On a
339  * previous occasion, a version of the test was disabled because it was more
340  * convenient to do this than deal with the real errors it was demonstrating,
341  * and it took quite a while to pull things back. But this has been stable
342  * for long enough now that I don't think that loosening by a factor of 2
343  * would be a major problem.
344  *
345  * "The only numerically robust way around this would be to ensure you always
346  * add the rates in order from the smallest to the largest amplitude -- but
347  * I'm not sure how to do this without effectively pushing the test back to
348  * being one on the incoming rates, and generally making it too complex to
349  * be a robust test. [Cf. the testing of the HST mirror: this was figured
350  * to a test which was very accurate but complex -- and in the end broken;
351  * a simpler, more robust test which would have demonstrated the problem was
352  * (reportedly) not used because they couldn't see the point in running a less
353  * accurate test.]"
354  *
355  */
356  /*>>chng 16 dec 16, from 2e-9 to 3e-9. igm_primal fails with this print:
357  * PROBLEM Network conservation error Li H 7.10467e-41 2.46016e-09 1.56344e-05 3.65606e-07
358  * this occurred after changing iso n-changing to P&R. Network is very sensitive
359  * to small changes for this sim, probably due to the small Li abundance?
360  * This first fail was in default gcc compile on radegund / gcc 6.2. Fault is
361  * currently on gcc 6.2, all double, with default arith OK, and on icc
362  */
363  const bool checkOK =
364  ( fabs(test[natom]) <= MAX2(3e-9*tot[natom], 1e10*DBL_MIN) );
365  if ( UNLIKELY(!checkOK) )
366  {
367  chem_nuclide *atom = nuclide_list[natom].get_ptr();
368  fprintf( ioQQQ, " PROBLEM Network conservation error %s %s %g %g %g %g\n",
369  atom->label().c_str(),
370  mole_global.list[i]->label.c_str(),
371  test[natom],
372  test[natom]/tot[natom],
373  mole.species[atom->ipMl[0]].den,
374  mole.species[atom->ipMl[1]].den);
375  //fprintf(stdout,"Problem at %s\n",rate->label);
376  checkAllOK = false;
377  }
378  }
379  }
380  return checkAllOK;
381 }
382 
molecule * reactants[MAXREACTANTS]
Definition: mole_priv.h:53
t_mole_global mole_global
Definition: mole.cpp:7
nNucsMap nNuclide
Definition: mole.h:157
vector< double > reaction_rks
Definition: mole.h:470
t_atmdat atmdat
Definition: atmdat.cpp:6
int num_calc
Definition: mole.h:362
bool lgTraceMole
Definition: trace.h:55
int nreactants
Definition: mole_priv.h:52
#define MAXREACTANTS
Definition: mole_priv.h:45
STATIC bool lgNucleiConserved(const multi_arr< double, 2 > &c)
map< string, count_ptr< mole_reaction > > reactab
FILE * ioQQQ
Definition: cddefines.cpp:7
ChemNuclideList nuclide_list
Definition: mole.h:142
t_dense dense
Definition: global.cpp:15
vector< int > ipMl
Definition: mole.h:50
t_trace trace
Definition: trace.cpp:5
double ** sink
Definition: mole.h:464
molecule * products[MAXPRODUCTS]
Definition: mole_priv.h:56
string label
Definition: mole_priv.h:51
molecule * rvector[MAXREACTANTS]
Definition: mole_priv.h:54
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
double ** source
Definition: mole.h:464
t_mole_local mole
Definition: mole.cpp:8
map< string, count_ptr< mole_reaction > >::iterator mole_reaction_i
Definition: mole_priv.h:38
realnum *** xMoleChTrRate
Definition: mole.h:466
float realnum
Definition: cddefines.h:124
valarray< class molezone > species
Definition: mole.h:468
void mole_eval_sources(long int num_total)
int index
Definition: mole.h:194
bool lgElmtOn[LIMELM]
Definition: dense.h:160
#define ASSERT(exp)
Definition: cddefines.h:613
molecule * pvector[MAXPRODUCTS]
Definition: mole_priv.h:57
void mole_eval_balance(long int num_total, double *b, bool lgJac, multi_arr< double, 2 > &c)
const int LIMELM
Definition: cddefines.h:308
int charge
Definition: mole.h:158
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
#define UNLIKELY(x)
Definition: cpu.h:473
#define MAX2(a, b)
Definition: cddefines.h:824
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
MoleculeList list
Definition: mole.h:365
bool lgCTOn
Definition: atmdat.h:325
string label(void) const
Definition: mole.h:66