Cantera  2.2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
solveSP.cpp
1 /*
2  * @file: solveSP.cpp Implicit surface site concentration solver
3  */
4 /*
5  * Copyright 2004 Sandia Corporation. Under the terms of Contract
6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7  * retains certain rights in this software.
8  * See file License.txt for licensing information.
9  */
10 
14 
15 #include <cstdio>
16 
17 using namespace std;
18 namespace Cantera
19 {
20 
21 /***************************************************************************
22  * STATIC ROUTINES DEFINED IN THIS FILE
23  ***************************************************************************/
24 
25 static doublereal calc_damping(doublereal* x, doublereal* dx, size_t dim, int*);
26 static doublereal calcWeightedNorm(const doublereal [], const doublereal dx[], size_t);
27 
28 /***************************************************************************
29  * solveSP Class Definitions
30  ***************************************************************************/
31 
32 solveSP::solveSP(ImplicitSurfChem* surfChemPtr, int bulkFunc) :
33  m_SurfChemPtr(surfChemPtr),
34  m_objects(surfChemPtr->getObjects()),
35  m_neq(0),
36  m_bulkFunc(bulkFunc),
37  m_numSurfPhases(0),
38  m_numTotSurfSpecies(0),
39  m_numBulkPhasesSS(0),
40  m_numTotBulkSpeciesSS(0),
41  m_atol(1.0E-15),
42  m_rtol(1.0E-4),
43  m_maxstep(1000),
44  m_maxTotSpecies(0),
45  m_ioflag(0)
46 {
47  m_numSurfPhases = 0;
48  size_t numPossibleSurfPhases = m_objects.size();
49  for (size_t n = 0; n < numPossibleSurfPhases; n++) {
50  InterfaceKinetics* m_kin = m_objects[n];
51  size_t surfPhaseIndex = m_kin->surfacePhaseIndex();
52  if (surfPhaseIndex != npos) {
54  m_indexKinObjSurfPhase.push_back(n);
55  m_kinObjPhaseIDSurfPhase.push_back(surfPhaseIndex);
56  } else {
57  throw CanteraError("solveSP",
58  "InterfaceKinetics object has no surface phase");
59  }
60  ThermoPhase* tp = &(m_kin->thermo(surfPhaseIndex));
61  SurfPhase* sp = dynamic_cast<SurfPhase*>(tp);
62  if (!sp) {
63  throw CanteraError("solveSP",
64  "Inconsistent ThermoPhase object within "
65  "InterfaceKinetics object");
66  }
67 
68  m_ptrsSurfPhase.push_back(sp);
69  size_t nsp = sp->nSpecies();
70  m_nSpeciesSurfPhase.push_back(nsp);
71  m_numTotSurfSpecies += nsp;
72 
73  }
74  /*
75  * We rely on ordering to figure things out
76  */
78 
79  if (bulkFunc == BULK_DEPOSITION) {
81  } else {
83  }
84 
85  m_maxTotSpecies = 0;
86  for (size_t n = 0; n < m_numSurfPhases; n++) {
87  size_t tsp = m_objects[n]->nTotalSpecies();
88  m_maxTotSpecies = std::max(m_maxTotSpecies, tsp);
89  }
91 
92 
94  m_numEqn1.resize(m_maxTotSpecies, 0.0);
95  m_numEqn2.resize(m_maxTotSpecies, 0.0);
96  m_XMolKinSpecies.resize(m_maxTotSpecies, 0.0);
97  m_CSolnSave.resize(m_neq, 0.0);
98 
99  m_spSurfLarge.resize(m_numSurfPhases, 0);
100 
103  m_eqnIndexStartSolnPhase.resize(m_numSurfPhases + m_numBulkPhasesSS, 0);
104 
105  size_t kindexSP = 0;
106  size_t isp, k, nsp, kstart;
107  for (isp = 0; isp < m_numSurfPhases; isp++) {
108  size_t iKinObject = m_indexKinObjSurfPhase[isp];
109  InterfaceKinetics* m_kin = m_objects[iKinObject];
110  size_t surfPhaseIndex = m_kinObjPhaseIDSurfPhase[isp];
111  kstart = m_kin->kineticsSpeciesIndex(0, surfPhaseIndex);
112  nsp = m_nSpeciesSurfPhase[isp];
113  m_eqnIndexStartSolnPhase[isp] = kindexSP;
114  for (k = 0; k < nsp; k++, kindexSP++) {
115  m_kinSpecIndex[kindexSP] = kstart + k;
116  m_kinObjIndex[kindexSP] = isp;
117  }
118  }
119 
120  // Dimension solution vector
121  size_t dim1 = std::max<size_t>(1, m_neq);
122  m_CSolnSP.resize(dim1, 0.0);
123  m_CSolnSPInit.resize(dim1, 0.0);
124  m_CSolnSPOld.resize(dim1, 0.0);
125  m_wtResid.resize(dim1, 0.0);
126  m_wtSpecies.resize(dim1, 0.0);
127  m_resid.resize(dim1, 0.0);
128  m_Jac.resize(dim1, dim1, 0.0);
129 }
130 
131 int solveSP::solveSurfProb(int ifunc, doublereal time_scale, doublereal TKelvin,
132  doublereal PGas, doublereal reltol, doublereal abstol)
133 {
134  doublereal EXTRA_ACCURACY = 0.001;
135  if (ifunc == SFLUX_JACOBIAN) {
136  EXTRA_ACCURACY *= 0.001;
137  }
138  int info = 0;
139  int label_t=-1; /* Species IDs for time control */
140  int label_d = -1; /* Species IDs for damping control */
141  int label_t_old=-1;
142  doublereal label_factor = 1.0;
143  int iter=0; // iteration number on numlinear solver
144  int iter_max=1000; // maximum number of nonlinear iterations
145  doublereal deltaT = 1.0E-10; // Delta time step
146  doublereal damp=1.0, tmp;
147  // Weighted L2 norm of the residual. Currently, this is only
148  // used for IO purposes. It doesn't control convergence.
149  doublereal resid_norm;
150  doublereal inv_t = 0.0;
151  doublereal t_real = 0.0, update_norm = 1.0E6;
152 
153  bool do_time = false, not_converged = true;
154  m_ioflag = std::min(m_ioflag, 1);
155 
156  /*
157  * Set the initial value of the do_time parameter
158  */
159  if (ifunc == SFLUX_INITIALIZE || ifunc == SFLUX_TRANSIENT) {
160  do_time = true;
161  }
162 
163  /*
164  * Store the initial guess for the surface problem in the soln vector,
165  * CSoln, and in an separate vector CSolnInit.
166  */
167  size_t loc = 0;
168  for (size_t n = 0; n < m_numSurfPhases; n++) {
169  SurfPhase* sf_ptr = m_ptrsSurfPhase[n];
171  size_t nsp = m_nSpeciesSurfPhase[n];
172  for (size_t k = 0; k <nsp; k++) {
173  m_CSolnSP[loc] = m_numEqn1[k];
174  loc++;
175  }
176  }
177 
178  std::copy(m_CSolnSP.begin(), m_CSolnSP.end(), m_CSolnSPInit.begin());
179 
180  // Calculate the largest species in each phase
182 
183  if (m_ioflag) {
184  print_header(m_ioflag, ifunc, time_scale, true, reltol, abstol);
185  }
186 
187  /*
188  * Quick return when there isn't a surface problem to solve
189  */
190  if (m_neq == 0) {
191  not_converged = false;
192  update_norm = 0.0;
193  }
194 
195  /* ------------------------------------------------------------------
196  * Start of Newton's method
197  * ------------------------------------------------------------------
198  */
199  while (not_converged && iter < iter_max) {
200  iter++;
201  /*
202  * Store previous iteration's solution in the old solution vector
203  */
204  std::copy(m_CSolnSP.begin(), m_CSolnSP.end(), m_CSolnSPOld.begin());
205 
206  /*
207  * Evaluate the largest surface species for each surface phase every
208  * 5 iterations.
209  */
210  if (iter%5 == 4) {
212  }
213 
214  /*
215  * Calculate the value of the time step
216  * - heuristics to stop large oscillations in deltaT
217  */
218  if (do_time) {
219  /* don't hurry increase in time step at the same time as damping */
220  if (damp < 1.0) {
221  label_factor = 1.0;
222  }
225  &label_t, &label_t_old, &label_factor, m_ioflag);
226  if (iter < 10) {
227  inv_t = tmp;
228  } else if (tmp > 2.0*inv_t) {
229  inv_t = 2.0*inv_t;
230  } else {
231  inv_t = tmp;
232  }
233 
234  /*
235  * Check end condition
236  */
237 
238  if (ifunc == SFLUX_TRANSIENT) {
239  tmp = t_real + 1.0/inv_t;
240  if (tmp > time_scale) {
241  inv_t = 1.0/(time_scale - t_real);
242  }
243  }
244  } else {
245  /* make steady state calc a step of 1 million seconds to
246  prevent singular Jacobians for some pathological cases */
247  inv_t = 1.0e-6;
248  }
249  deltaT = 1.0/inv_t;
250 
251  /*
252  * Call the routine to numerically evaluation the Jacobian
253  * and residual for the current iteration.
254  */
256  DATA_PTR(m_CSolnSPOld), do_time, deltaT);
257 
258  /*
259  * Calculate the weights. Make sure the calculation is carried
260  * out on the first iteration.
261  */
262  if (iter%4 == 1) {
264  m_Jac, DATA_PTR(m_CSolnSP), abstol, reltol);
265  }
266 
267  /*
268  * Find the weighted norm of the residual
269  */
270  resid_norm = calcWeightedNorm(DATA_PTR(m_wtResid),
272 
273  /*
274  * Solve Linear system. The solution is in resid[]
275  */
276  info = m_Jac.factor();
277  if (info==0) {
278  m_Jac.solve(&m_resid[0]);
279  }
280  /*
281  * Force convergence if residual is small to avoid
282  * "nan" results from the linear solve.
283  */
284  else {
285  if (m_ioflag) {
286  printf("solveSurfSS: Zero pivot, assuming converged: %g (%d)\n",
287  resid_norm, info);
288  }
289  for (size_t jcol = 0; jcol < m_neq; jcol++) {
290  m_resid[jcol] = 0.0;
291  }
292 
293  /* print out some helpful info */
294  if (m_ioflag > 1) {
295  printf("-----\n");
296  printf("solveSurfProb: iter %d t_real %g delta_t %g\n\n",
297  iter,t_real, 1.0/inv_t);
298  printf("solveSurfProb: init guess, current concentration,"
299  "and prod rate:\n");
300  for (size_t jcol = 0; jcol < m_neq; jcol++) {
301  printf("\t%s %g %g %g\n", int2str(jcol).c_str(),
302  m_CSolnSPInit[jcol], m_CSolnSP[jcol],
304  }
305  printf("-----\n");
306  }
307  if (do_time) {
308  t_real += time_scale;
309  }
310  }
311 
312  /*
313  * Calculate the Damping factor needed to keep all unknowns
314  * between 0 and 1, and not allow too large a change (factor of 2)
315  * in any unknown.
316  */
317 
318  damp = calc_damping(DATA_PTR(m_CSolnSP), DATA_PTR(m_resid), m_neq, &label_d);
319 
320  /*
321  * Calculate the weighted norm of the update vector
322  * Here, resid is the delta of the solution, in concentration
323  * units.
324  */
325  update_norm = calcWeightedNorm(DATA_PTR(m_wtSpecies),
327  /*
328  * Update the solution vector and real time
329  * Crop the concentrations to zero.
330  */
331  for (size_t irow = 0; irow < m_neq; irow++) {
332  m_CSolnSP[irow] -= damp * m_resid[irow];
333  }
334  for (size_t irow = 0; irow < m_neq; irow++) {
335  m_CSolnSP[irow] = std::max(0.0, m_CSolnSP[irow]);
336  }
338 
339  if (do_time) {
340  t_real += damp/inv_t;
341  }
342 
343  if (m_ioflag) {
344  printIteration(m_ioflag, damp, label_d, label_t, inv_t, t_real, iter,
345  update_norm, resid_norm, do_time);
346  }
347 
348  if (ifunc == SFLUX_TRANSIENT) {
349  not_converged = (t_real < time_scale);
350  } else {
351  if (do_time) {
352  if (t_real > time_scale ||
353  (resid_norm < 1.0e-7 &&
354  update_norm*time_scale/t_real < EXTRA_ACCURACY)) {
355  do_time = false;
356  }
357  } else {
358  not_converged = ((update_norm > EXTRA_ACCURACY) ||
359  (resid_norm > EXTRA_ACCURACY));
360  }
361  }
362  } /* End of Newton's Method while statement */
363 
364  /*
365  * End Newton's method. If not converged, print error message and
366  * recalculate sdot's at equal site fractions.
367  */
368  if (not_converged) {
369  if (m_ioflag) {
370  printf("#$#$#$# Error in solveSP $#$#$#$ \n");
371  printf("Newton iter on surface species did not converge, "
372  "update_norm = %e \n", update_norm);
373  printf("Continuing anyway\n");
374  }
375  }
376 
377  /*
378  * Decide on what to return in the solution vector
379  * - right now, will always return the last solution
380  * no matter how bad
381  */
382  if (m_ioflag) {
384  false, deltaT);
385  resid_norm = calcWeightedNorm(DATA_PTR(m_wtResid),
387  printIteration(m_ioflag, damp, label_d, label_t, inv_t, t_real, iter,
388  update_norm, resid_norm, do_time, true);
389  }
390 
391  /*
392  * Return with the appropriate flag
393  */
394  if (update_norm > 1.0) {
395  return -1;
396  }
397  return 1;
398 }
399 
400 void solveSP::updateState(const doublereal* CSolnSP)
401 {
402  size_t loc = 0;
403  for (size_t n = 0; n < m_numSurfPhases; n++) {
404  m_ptrsSurfPhase[n]->setConcentrations(CSolnSP + loc);
405  loc += m_nSpeciesSurfPhase[n];
406  }
407 }
408 
409 void solveSP::updateMFSolnSP(doublereal* XMolSolnSP)
410 {
411  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
412  size_t keqnStart = m_eqnIndexStartSolnPhase[isp];
413  m_ptrsSurfPhase[isp]->getMoleFractions(XMolSolnSP + keqnStart);
414  }
415 }
416 
417 void solveSP::updateMFKinSpecies(doublereal* XMolKinSpecies, int isp)
418 {
419  InterfaceKinetics* m_kin = m_objects[isp];
420  size_t nph = m_kin->nPhases();
421  for (size_t iph = 0; iph < nph; iph++) {
422  size_t ksi = m_kin->kineticsSpeciesIndex(0, iph);
423  ThermoPhase& thref = m_kin->thermo(iph);
424  thref.getMoleFractions(XMolKinSpecies + ksi);
425  }
426 }
427 
428 void solveSP::evalSurfLarge(const doublereal* CSolnSP)
429 {
430  size_t kindexSP = 0;
431  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
432  size_t nsp = m_nSpeciesSurfPhase[isp];
433  doublereal Clarge = CSolnSP[kindexSP];
434  m_spSurfLarge[isp] = 0;
435  kindexSP++;
436  for (size_t k = 1; k < nsp; k++, kindexSP++) {
437  if (CSolnSP[kindexSP] > Clarge) {
438  Clarge = CSolnSP[kindexSP];
439  m_spSurfLarge[isp] = k;
440  }
441  }
442  }
443 }
444 
445 void solveSP::fun_eval(doublereal* resid, const doublereal* CSoln,
446  const doublereal* CSolnOld, const bool do_time,
447  const doublereal deltaT)
448 {
449  size_t isp, nsp, kstart, k, kindexSP, kins, kspecial;
450  doublereal lenScale = 1.0E-9;
451  doublereal sd = 0.0;
452  doublereal grRate;
453  if (m_numSurfPhases > 0) {
454  /*
455  * update the surface concentrations with the input surface
456  * concentration vector
457  */
458  updateState(CSoln);
459  /*
460  * Get the net production rates of all of the species in the
461  * surface kinetics mechanism
462  *
463  * HKM Should do it here for all kinetics objects so that
464  * bulk will eventually work.
465  */
466 
467  if (do_time) {
468  kindexSP = 0;
469  for (isp = 0; isp < m_numSurfPhases; isp++) {
470  nsp = m_nSpeciesSurfPhase[isp];
471  InterfaceKinetics* kinPtr = m_objects[isp];
472  size_t surfIndex = kinPtr->surfacePhaseIndex();
473  kstart = kinPtr->kineticsSpeciesIndex(0, surfIndex);
474  kins = kindexSP;
476  for (k = 0; k < nsp; k++, kindexSP++) {
477  resid[kindexSP] =
478  (CSoln[kindexSP] - CSolnOld[kindexSP]) / deltaT
479  - m_netProductionRatesSave[kstart + k];
480  }
481 
482  kspecial = kins + m_spSurfLarge[isp];
483  sd = m_ptrsSurfPhase[isp]->siteDensity();
484  resid[kspecial] = sd;
485  for (k = 0; k < nsp; k++) {
486  resid[kspecial] -= CSoln[kins + k];
487  }
488  }
489  } else {
490  kindexSP = 0;
491  for (isp = 0; isp < m_numSurfPhases; isp++) {
492  nsp = m_nSpeciesSurfPhase[isp];
493  InterfaceKinetics* kinPtr = m_objects[isp];
494  size_t surfIndex = kinPtr->surfacePhaseIndex();
495  kstart = kinPtr->kineticsSpeciesIndex(0, surfIndex);
496  kins = kindexSP;
498  for (k = 0; k < nsp; k++, kindexSP++) {
499  resid[kindexSP] = - m_netProductionRatesSave[kstart + k];
500  }
501  kspecial = kins + m_spSurfLarge[isp];
502  sd = m_ptrsSurfPhase[isp]->siteDensity();
503  resid[kspecial] = sd;
504  for (k = 0; k < nsp; k++) {
505  resid[kspecial] -= CSoln[kins + k];
506  }
507  }
508  }
509 
510  if (m_bulkFunc == BULK_DEPOSITION) {
511  kindexSP = m_numTotSurfSpecies;
512  for (isp = 0; isp < m_numBulkPhasesSS; isp++) {
513  doublereal* XBlk = DATA_PTR(m_numEqn1);
514  nsp = m_nSpeciesSurfPhase[isp];
515  size_t surfPhaseIndex = m_indexKinObjSurfPhase[isp];
516  InterfaceKinetics* m_kin = m_objects[isp];
517  grRate = 0.0;
518  kstart = m_kin->kineticsSpeciesIndex(0, surfPhaseIndex);
519  for (k = 0; k < nsp; k++) {
520  if (m_netProductionRatesSave[kstart + k] > 0.0) {
521  grRate += m_netProductionRatesSave[kstart + k];
522  }
523  }
524  resid[kindexSP] = m_bulkPhasePtrs[isp]->molarDensity();
525  for (k = 0; k < nsp; k++) {
526  resid[kindexSP] -= CSoln[kindexSP + k];
527  }
528  if (grRate > 0.0) {
529  for (k = 1; k < nsp; k++) {
530  if (m_netProductionRatesSave[kstart + k] > 0.0) {
531  resid[kindexSP + k] = XBlk[k] * grRate
532  - m_netProductionRatesSave[kstart + k];
533  } else {
534  resid[kindexSP + k] = XBlk[k] * grRate;
535  }
536  }
537  } else {
538  grRate = 1.0E-6;
539  grRate += fabs(m_netProductionRatesSave[kstart + k]);
540  for (k = 1; k < nsp; k++) {
541  resid[kindexSP + k] = grRate * (XBlk[k] - 1.0/nsp);
542  }
543  }
544  if (do_time) {
545  for (k = 1; k < nsp; k++) {
546  resid[kindexSP + k] +=
547  lenScale / deltaT *
548  (CSoln[kindexSP + k]- CSolnOld[kindexSP + k]);
549  }
550  }
551  kindexSP += nsp;
552  }
553  }
554  }
555 }
556 
558  doublereal resid[], doublereal CSoln[],
559  const doublereal CSolnOld[], const bool do_time,
560  const doublereal deltaT)
561 {
562  size_t kColIndex = 0, nsp, jsp, i, kCol;
563  doublereal dc, cSave, sd;
564  /*
565  * Calculate the residual
566  */
567  fun_eval(resid, CSoln, CSolnOld, do_time, deltaT);
568  /*
569  * Now we will look over the columns perturbing each unknown.
570  */
571  for (jsp = 0; jsp < m_numSurfPhases; jsp++) {
572  nsp = m_nSpeciesSurfPhase[jsp];
573  sd = m_ptrsSurfPhase[jsp]->siteDensity();
574  for (kCol = 0; kCol < nsp; kCol++) {
575  cSave = CSoln[kColIndex];
576  dc = std::max(1.0E-10 * sd, fabs(cSave) * 1.0E-7);
577  CSoln[kColIndex] += dc;
578  fun_eval(DATA_PTR(m_numEqn2), CSoln, CSolnOld, do_time, deltaT);
579  for (i = 0; i < m_neq; i++) {
580  jac(i, kColIndex) = (m_numEqn2[i] - resid[i])/dc;
581  }
582  CSoln[kColIndex] = cSave;
583  kColIndex++;
584  }
585  }
586 
587  if (m_bulkFunc == BULK_DEPOSITION) {
588  for (jsp = 0; jsp < m_numBulkPhasesSS; jsp++) {
589  nsp = m_numBulkSpecies[jsp];
590  sd = m_bulkPhasePtrs[jsp]->molarDensity();
591  for (kCol = 0; kCol < nsp; kCol++) {
592  cSave = CSoln[kColIndex];
593  dc = std::max(1.0E-10 * sd, fabs(cSave) * 1.0E-7);
594  CSoln[kColIndex] += dc;
595  fun_eval(DATA_PTR(m_numEqn2), CSoln, CSolnOld, do_time, deltaT);
596  for (i = 0; i < m_neq; i++) {
597  jac(i, kColIndex) = (m_numEqn2[i] - resid[i])/dc;
598  }
599  CSoln[kColIndex] = cSave;
600  kColIndex++;
601  }
602  }
603  }
604 }
605 
606 /*!
607  * This function calculates a damping factor for the Newton iteration update
608  * vector, dxneg, to insure that all site and bulk fractions, x, remain
609  * bounded between zero and one.
610  *
611  * dxneg[] = negative of the update vector.
612  *
613  * The constant "APPROACH" sets the fraction of the distance to the boundary
614  * that the step can take. If the full step would not force any fraction
615  * outside of 0-1, then Newton's method is allowed to operate normally.
616  */
617 static doublereal calc_damping(doublereal x[], doublereal dxneg[], size_t dim, int* label)
618 {
619  const doublereal APPROACH = 0.80;
620  doublereal damp = 1.0, xnew, xtop, xbot;
621  static doublereal damp_old = 1.0;
622 
623  *label = -1;
624 
625  for (size_t i = 0; i < dim; i++) {
626 
627  /*
628  * Calculate the new suggested new value of x[i]
629  */
630 
631  xnew = x[i] - damp * dxneg[i];
632 
633  /*
634  * Calculate the allowed maximum and minimum values of x[i]
635  * - Only going to allow x[i] to converge to zero by a
636  * single order of magnitude at a time
637  */
638 
639  xtop = 1.0 - 0.1*fabs(1.0-x[i]);
640  xbot = fabs(x[i]*0.1) - 1.0e-16;
641  if (xnew > xtop) {
642  damp = - APPROACH * (1.0 - x[i]) / dxneg[i];
643  *label = int(i);
644  } else if (xnew < xbot) {
645  damp = APPROACH * x[i] / dxneg[i];
646  *label = int(i);
647  } else if (xnew > 3.0*std::max(x[i], 1.0E-10)) {
648  damp = - 2.0 * std::max(x[i], 1.0E-10) / dxneg[i];
649  *label = int(i);
650  }
651  }
652  damp = std::max(damp, 1e-2);
653  /*
654  * Only allow the damping parameter to increase by a factor of three each
655  * iteration. Heuristic to avoid oscillations in the value of damp
656  */
657 
658  if (damp > damp_old*3) {
659  damp = damp_old*3;
660  *label = -1;
661  }
662 
663  /*
664  * Save old value of the damping parameter for use
665  * in subsequent calls.
666  */
667 
668  damp_old = damp;
669  return damp;
670 
671 } /* calc_damping */
672 
673 /*
674  * This function calculates the norm of an update, dx[],
675  * based on the weighted values of x.
676  */
677 static doublereal calcWeightedNorm(const doublereal wtX[], const doublereal dx[], size_t dim)
678 {
679  doublereal norm = 0.0;
680  doublereal tmp;
681  if (dim == 0) {
682  return 0.0;
683  }
684  for (size_t i = 0; i < dim; i++) {
685  tmp = dx[i] / wtX[i];
686  norm += tmp * tmp;
687  }
688  return sqrt(norm/dim);
689 }
690 
691 void solveSP::calcWeights(doublereal wtSpecies[], doublereal wtResid[],
692  const Array2D& Jac, const doublereal CSoln[],
693  const doublereal abstol, const doublereal reltol)
694 {
695  size_t k, jcol, kindex, isp, nsp;
696  doublereal sd;
697  /*
698  * First calculate the weighting factor for the concentrations of
699  * the surface species and bulk species.
700  */
701  kindex = 0;
702  for (isp = 0; isp < m_numSurfPhases; isp++) {
703  nsp = m_nSpeciesSurfPhase[isp];
704  sd = m_ptrsSurfPhase[isp]->siteDensity();
705  for (k = 0; k < nsp; k++, kindex++) {
706  wtSpecies[kindex] = abstol * sd + reltol * fabs(CSoln[kindex]);
707  }
708  }
709  if (m_bulkFunc == BULK_DEPOSITION) {
710  for (isp = 0; isp < m_numBulkPhasesSS; isp++) {
711  nsp = m_numBulkSpecies[isp];
712  sd = m_bulkPhasePtrs[isp]->molarDensity();
713  for (k = 0; k < nsp; k++, kindex++) {
714  wtSpecies[kindex] = abstol * sd + reltol * fabs(CSoln[kindex]);
715  }
716  }
717  }
718  /*
719  * Now do the residual Weights. Since we have the Jacobian, we
720  * will use it to generate a number based on the what a significant
721  * change in a solution variable does to each residual.
722  * This is a row sum scale operation.
723  */
724  for (k = 0; k < m_neq; k++) {
725  wtResid[k] = 0.0;
726  for (jcol = 0; jcol < m_neq; jcol++) {
727  wtResid[k] += fabs(Jac(k,jcol) * wtSpecies[jcol]);
728  }
729  }
730 }
731 
732 doublereal solveSP::calc_t(doublereal netProdRateSolnSP[],
733  doublereal XMolSolnSP[],
734  int* label, int* label_old,
735  doublereal* label_factor, int ioflag)
736 {
737  size_t k, isp, nsp, kstart;
738  doublereal inv_timeScale = 1.0E-10;
739  doublereal sden, tmp;
740  size_t kindexSP = 0;
741  *label = 0;
742  updateMFSolnSP(XMolSolnSP);
743  for (isp = 0; isp < m_numSurfPhases; isp++) {
744  nsp = m_nSpeciesSurfPhase[isp];
745 
746  // Get the interface kinetics associated with this surface
747  InterfaceKinetics* m_kin = m_objects[isp];
748 
749  // Calculate the start of the species index for surfaces within
750  // the InterfaceKinetics object
751  size_t surfIndex = m_kin->surfacePhaseIndex();
752  kstart = m_kin->kineticsSpeciesIndex(0, surfIndex);
753  ThermoPhase& THref = m_kin->thermo(surfIndex);
754 
756 
757  sden = THref.molarDensity();
758  for (k = 0; k < nsp; k++, kindexSP++) {
759  size_t kspindex = kstart + k;
760  netProdRateSolnSP[kindexSP] = m_numEqn1[kspindex];
761  if (XMolSolnSP[kindexSP] <= 1.0E-10) {
762  tmp = 1.0E-10;
763  } else {
764  tmp = XMolSolnSP[kindexSP];
765  }
766  tmp *= sden;
767  tmp = fabs(netProdRateSolnSP[kindexSP]/ tmp);
768  if (netProdRateSolnSP[kindexSP]> 0.0) {
769  tmp /= 100.;
770  }
771  if (tmp > inv_timeScale) {
772  inv_timeScale = tmp;
773  *label = int(kindexSP);
774  }
775  }
776  }
777 
778  /*
779  * Increase time step exponentially as same species repeatedly
780  * controls time step
781  */
782  if (*label == *label_old) {
783  *label_factor *= 1.5;
784  } else {
785  *label_old = *label;
786  *label_factor = 1.0;
787  }
788  return inv_timeScale / *label_factor;
789 } /* calc_t */
790 
791 void solveSP::print_header(int ioflag, int ifunc, doublereal time_scale,
792  int damping, doublereal reltol, doublereal abstol)
793 {
794  if (ioflag) {
795  printf("\n================================ SOLVESP CALL SETUP "
796  "========================================\n");
797  if (ifunc == SFLUX_INITIALIZE) {
798  printf("\n SOLVESP Called with Initialization turned on\n");
799  printf(" Time scale input = %9.3e\n", time_scale);
800  } else if (ifunc == SFLUX_RESIDUAL) {
801  printf("\n SOLVESP Called to calculate steady state residual\n");
802  printf(" from a good initial guess\n");
803  } else if (ifunc == SFLUX_JACOBIAN) {
804  printf("\n SOLVESP Called to calculate steady state Jacobian\n");
805  printf(" from a good initial guess\n");
806  } else if (ifunc == SFLUX_TRANSIENT) {
807  printf("\n SOLVESP Called to integrate surface in time\n");
808  printf(" for a total of %9.3e sec\n", time_scale);
809  } else {
810  throw CanteraError("solveSP::print_header",
811  "Unknown ifunc flag = " + int2str(ifunc));
812  }
813 
814  if (m_bulkFunc == BULK_DEPOSITION) {
815  printf(" The composition of the Bulk Phases will be calculated\n");
816  } else if (m_bulkFunc == BULK_ETCH) {
817  printf(" Bulk Phases have fixed compositions\n");
818  } else {
819  throw CanteraError("solveSP::print_header",
820  "Unknown bulkFunc flag = " + int2str(m_bulkFunc));
821  }
822 
823  if (damping) {
824  printf(" Damping is ON \n");
825  } else {
826  printf(" Damping is OFF \n");
827  }
828 
829  printf(" Reltol = %9.3e, Abstol = %9.3e\n", reltol, abstol);
830  }
831 
832  if (ioflag == 1) {
833  printf("\n\n\t Iter Time Del_t Damp DelX "
834  " Resid Name-Time Name-Damp\n");
835  printf("\t -----------------------------------------------"
836  "------------------------------------\n");
837  }
838 }
839 
840 void solveSP::printIteration(int ioflag, doublereal damp, int label_d,
841  int label_t, doublereal inv_t, doublereal t_real,
842  size_t iter, doublereal update_norm,
843  doublereal resid_norm, bool do_time, bool final)
844 {
845  size_t i, k;
846  string nm;
847  if (ioflag == 1) {
848  if (final) {
849  printf("\tFIN%3s ", int2str(iter).c_str());
850  } else {
851  printf("\t%6s ", int2str(iter).c_str());
852  }
853  if (do_time) {
854  printf("%9.4e %9.4e ", t_real, 1.0/inv_t);
855  } else
856  for (i = 0; i < 22; i++) {
857  printf(" ");
858  }
859  if (damp < 1.0) {
860  printf("%9.4e ", damp);
861  } else
862  for (i = 0; i < 11; i++) {
863  printf(" ");
864  }
865  printf("%9.4e %9.4e", update_norm, resid_norm);
866  if (do_time) {
867  k = m_kinSpecIndex[label_t];
868  size_t isp = m_kinObjIndex[label_t];
869  InterfaceKinetics* m_kin = m_objects[isp];
870  nm = m_kin->kineticsSpeciesName(k);
871  printf(" %-16s", nm.c_str());
872  } else {
873  for (i = 0; i < 16; i++) {
874  printf(" ");
875  }
876  }
877  if (label_d >= 0) {
878  k = m_kinSpecIndex[label_d];
879  size_t isp = m_kinObjIndex[label_d];
880  InterfaceKinetics* m_kin = m_objects[isp];
881  nm = m_kin->kineticsSpeciesName(k);
882  printf(" %-16s", nm.c_str());
883  }
884  if (final) {
885  printf(" -- success");
886  }
887  printf("\n");
888  }
889 } /* printIteration */
890 
891 }
void printIteration(int ioflag, doublereal damp, int label_d, int label_t, doublereal inv_t, doublereal t_real, size_t iter, doublereal update_norm, doublereal resid_norm, bool do_time, bool final=false)
Printing routine that gets called after every iteration.
Definition: solveSP.cpp:840
std::string int2str(const int n, const std::string &fmt)
Convert an int to a string using a format converter.
Definition: stringUtils.cpp:39
int m_bulkFunc
This variable determines how the bulk phases are to be handled.
Definition: solveSP.h:340
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase, assuming an ideal solution model (see Thermodynamic Properties and class SurfPhase).
std::vector< InterfaceKinetics * > & m_objects
Vector of interface kinetics objects.
Definition: solveSP.h:328
size_t m_neq
Total number of equations to solve in the implicit problem.
Definition: solveSP.h:334
thermo_t & thermo(size_t n=0)
This method returns a reference to the nth ThermoPhase object defined in this kinetics mechanism...
Definition: Kinetics.h:285
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:165
std::string kineticsSpeciesName(size_t k) const
Return the name of the kth species in the kinetics manager.
Definition: Kinetics.cpp:359
vector_fp m_CSolnSPInit
Saved initial solution vector. length MAX(1, m_neq)
Definition: solveSP.h:488
size_t m_numBulkPhasesSS
Total number of volumetric condensed phases included in the steady state problem handled by this rout...
Definition: solveSP.h:411
void updateMFSolnSP(doublereal *XMolSolnSP)
Update mole fraction vector consisting of unknowns in surface problem.
Definition: solveSP.cpp:409
vector_fp m_CSolnSP
Solution vector. length MAX(1, m_neq)
Definition: solveSP.h:485
doublereal molarDensity() const
Molar density (kmol/m^3).
Definition: Phase.cpp:663
const int BULK_ETCH
Etching of a bulk phase is to be expected.
Definition: solveSP.h:59
void getMoleFractions(doublereal *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:556
void getConcentrations(doublereal *const c) const
Get the species concentrations (kmol/m^3).
Definition: Phase.cpp:609
A class for 2D arrays stored in column-major (Fortran-compatible) form.
Definition: Array.h:29
virtual void getNetProductionRates(doublereal *wdot)
Species net production rates [kmol/m^3/s or kmol/m^2/s].
Definition: Kinetics.cpp:501
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:97
void evalSurfLarge(const doublereal *CSolnSP)
Update the vector that keeps track of the largest species in each surface phase.
Definition: solveSP.cpp:428
A simple thermodynamic model for a surface phase, assuming an ideal solution model.
Definition: SurfPhase.h:143
size_t surfacePhaseIndex()
This returns the integer index of the phase which has ThermoPhase type cSurf.
Definition: Kinetics.h:260
size_t m_numTotSurfSpecies
Total number of surface species in all surface phases.
Definition: solveSP.h:355
void print_header(int ioflag, int ifunc, doublereal time_scale, int damping, doublereal reltol, doublereal abstol)
Printing routine that optionally gets called at the start of every invocation.
Definition: solveSP.cpp:791
int solveSurfProb(int ifunc, doublereal time_scale, doublereal TKelvin, doublereal PGas, doublereal reltol, doublereal abstol)
Main routine that actually calculates the pseudo steady state of the surface problem.
Definition: solveSP.cpp:131
const int SFLUX_INITIALIZE
This assumes that the initial guess supplied to the routine is far from the correct one...
Definition: solveSP.h:24
size_t nPhases() const
The number of phases participating in the reaction mechanism.
Definition: Kinetics.h:225
std::vector< size_t > m_eqnIndexStartSolnPhase
Index of the start of the unknowns for each solution phase.
Definition: solveSP.h:389
A kinetics manager for heterogeneous reaction mechanisms.
vector_fp m_wtResid
Weights for the residual norm calculation. length MAX(1, m_neq)
Definition: solveSP.h:494
const int SFLUX_RESIDUAL
Need to solve the surface problem in order to calculate the surface fluxes of gas-phase species...
Definition: solveSP.h:31
vector_fp m_XMolKinSpecies
Vector of mole fractions. length m_maxTotSpecies.
Definition: solveSP.h:516
int solve(doublereal *b, size_t nrhs=1, size_t ldb=0)
Solves the Ax = b system returning x in the b spot.
doublereal calc_t(doublereal netProdRateSolnSP[], doublereal XMolSolnSP[], int *label, int *label_old, doublereal *label_factor, int ioflag)
Calculate a conservative delta T to use in a pseudo-steady state algorithm.
Definition: solveSP.cpp:732
vector_fp m_numEqn1
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:476
const int BULK_DEPOSITION
Deposition of a bulk phase is to be expected.
Definition: solveSP.h:54
A class for full (non-sparse) matrices with Fortran-compatible data storage.
Definition: SquareMatrix.h:26
const int SFLUX_JACOBIAN
Calculation of the surface problem is due to the need for a numerical Jacobian for the gas-problem...
Definition: solveSP.h:37
std::vector< size_t > m_spSurfLarge
Vector containing the indices of the largest species in each surface phase.
Definition: solveSP.h:457
vector_fp m_CSolnSave
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:482
std::vector< size_t > m_nSpeciesSurfPhase
Vector of length number of surface phases containing the number of surface species in each phase...
Definition: solveSP.h:370
int factor()
Factors the A matrix, overwriting A.
void updateMFKinSpecies(doublereal *XMolKinSp, int isp)
Update the mole fraction vector for a specific kinetic species vector corresponding to one InterfaceK...
Definition: solveSP.cpp:417
void resjac_eval(SquareMatrix &jac, doublereal *resid, doublereal *CSolnSP, const doublereal *CSolnSPOld, const bool do_time, const doublereal deltaT)
Main routine that calculates the current residual and Jacobian.
Definition: solveSP.cpp:557
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:99
vector_fp m_numEqn2
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:479
const int SFLUX_TRANSIENT
The transient calculation is performed here for an amount of time specified by "time_scale".
Definition: solveSP.h:44
Declarations for the implicit integration of surface site density equations (see Kinetics Managers an...
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition: Kinetics.h:323
std::vector< SurfPhase * > m_ptrsSurfPhase
Vector of surface phase pointers.
Definition: solveSP.h:377
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:265
std::vector< size_t > m_kinObjPhaseIDSurfPhase
Phase ID in the InterfaceKinetics object of the surface phase.
Definition: solveSP.h:398
vector_fp m_netProductionRatesSave
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:473
std::vector< size_t > m_kinObjIndex
Index between the equation index and the index of the InterfaceKinetics object.
Definition: solveSP.h:448
Advances the surface coverages of the associated set of SurfacePhase objects in time.
size_t m_numSurfPhases
Number of surface phases in the surface problem.
Definition: solveSP.h:347
std::vector< ThermoPhase * > m_bulkPhasePtrs
Vector of bulk phase pointers, length is equal to m_numBulkPhases.
Definition: solveSP.h:431
std::vector< size_t > m_numBulkSpecies
Vector of number of species in the m_numBulkPhases phases.
Definition: solveSP.h:417
vector_fp m_CSolnSPOld
Saved solution vector at the old time step. length MAX(1, m_neq)
Definition: solveSP.h:491
vector_fp m_resid
Residual for the surface problem.
Definition: solveSP.h:513
Header file for implicit surface problem solver (see Chemical Kinetics and class solveSP).
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
std::vector< size_t > m_kinSpecIndex
Index between the equation index and the position in the kinetic species array for the appropriate ki...
Definition: solveSP.h:441
size_t m_maxTotSpecies
Maximum number of species in any single kinetics operator -> also maxed wrt the total # of solution s...
Definition: solveSP.h:470
std::vector< size_t > m_indexKinObjSurfPhase
Mapping between the surface phases and the InterfaceKinetics objects.
Definition: solveSP.h:363
vector_fp m_wtSpecies
Weights for the species concentrations norm calculation.
Definition: solveSP.h:500
SquareMatrix m_Jac
Jacobian.
Definition: solveSP.h:520
void fun_eval(doublereal *resid, const doublereal *CSolnSP, const doublereal *CSolnOldSP, const bool do_time, const doublereal deltaT)
Main Function evaluation.
Definition: solveSP.cpp:445
size_t m_numTotBulkSpeciesSS
Total number of species in all bulk phases.
Definition: solveSP.h:428
void updateState(const doublereal *cSurfSpec)
Update the surface states of the surface phases.
Definition: solveSP.cpp:400
void calcWeights(doublereal wtSpecies[], doublereal wtResid[], const Array2D &Jac, const doublereal CSolnSP[], const doublereal abstol, const doublereal reltol)
Calculate the solution and residual weights.
Definition: solveSP.cpp:691