Cantera  3.1.0a1
solveSP.cpp
Go to the documentation of this file.
1 /**
2  * @file: solveSP.cpp Implicit surface site concentration solver
3  */
4 
5 // This file is part of Cantera. See License.txt in the top-level directory or
6 // at https://cantera.org/license.txt for license and copyright information.
7 
11 
12 namespace Cantera
13 {
14 
15 // STATIC ROUTINES DEFINED IN THIS FILE
16 
17 static double calc_damping(double* x, double* dx, size_t dim, int*);
18 static double calcWeightedNorm(const double [], const double dx[], size_t);
19 
20 // solveSP Class Definitions
21 
22 solveSP::solveSP(ImplicitSurfChem* surfChemPtr, int bulkFunc) :
23  m_objects(surfChemPtr->getObjects()),
24  m_bulkFunc(bulkFunc)
25 {
26  for (size_t n = 0; n < m_objects.size(); n++) {
27  InterfaceKinetics* kin = m_objects[n];
28  SurfPhase* sp = dynamic_cast<SurfPhase*>(&kin->thermo(0));
29  if (sp == nullptr) {
30  throw CanteraError("solveSP::solveSP",
31  "InterfaceKinetics object has no surface phase");
32  }
33 
35  m_indexKinObjSurfPhase.push_back(n);
36 
37  m_ptrsSurfPhase.push_back(sp);
38  size_t nsp = sp->nSpecies();
39  m_nSpeciesSurfPhase.push_back(nsp);
40  m_numTotSurfSpecies += nsp;
41  }
42 
43  if (bulkFunc == BULK_DEPOSITION) {
45  } else {
47  }
48 
49  for (size_t n = 0; n < m_numSurfPhases; n++) {
50  size_t tsp = m_objects[n]->nTotalSpecies();
51  m_maxTotSpecies = std::max(m_maxTotSpecies, tsp);
52  }
54 
56  m_numEqn1.resize(m_maxTotSpecies, 0.0);
57  m_numEqn2.resize(m_maxTotSpecies, 0.0);
58  m_XMolKinSpecies.resize(m_maxTotSpecies, 0.0);
59  m_CSolnSave.resize(m_neq, 0.0);
60  m_spSurfLarge.resize(m_numSurfPhases, 0);
64 
65  size_t kindexSP = 0;
66  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
67  size_t iKinObject = m_indexKinObjSurfPhase[isp];
68  InterfaceKinetics* kin = m_objects[iKinObject];
69  size_t kstart = kin->kineticsSpeciesIndex(0, 0);
70  size_t nsp = m_nSpeciesSurfPhase[isp];
71  m_eqnIndexStartSolnPhase[isp] = kindexSP;
72  for (size_t k = 0; k < nsp; k++, kindexSP++) {
73  m_kinSpecIndex[kindexSP] = kstart + k;
74  m_kinObjIndex[kindexSP] = isp;
75  }
76  }
77 
78  // Dimension solution vector
79  size_t dim1 = std::max<size_t>(1, m_neq);
80  m_CSolnSP.resize(dim1, 0.0);
81  m_CSolnSPInit.resize(dim1, 0.0);
82  m_CSolnSPOld.resize(dim1, 0.0);
83  m_wtResid.resize(dim1, 0.0);
84  m_wtSpecies.resize(dim1, 0.0);
85  m_resid.resize(dim1, 0.0);
86  m_Jac.resize(dim1, dim1, 0.0);
87 }
88 
89 int solveSP::solveSurfProb(int ifunc, double time_scale, double TKelvin,
90  double PGas, double reltol, double abstol)
91 {
92  double EXTRA_ACCURACY = 0.001;
93  if (ifunc == SFLUX_JACOBIAN) {
94  EXTRA_ACCURACY *= 0.001;
95  }
96  int label_t=-1; // Species IDs for time control
97  int label_d = -1; // Species IDs for damping control
98  int label_t_old=-1;
99  double label_factor = 1.0;
100  int iter=0; // iteration number on nonlinear solver
101  int iter_max=1000; // maximum number of nonlinear iterations
102  double deltaT = 1.0E-10; // Delta time step
103  double damp=1.0;
104  double inv_t = 0.0;
105  double t_real = 0.0, update_norm = 1.0E6;
106  bool do_time = false, not_converged = true;
107  m_ioflag = std::min(m_ioflag, 1);
108 
109  // Set the initial value of the do_time parameter
110  if (ifunc == SFLUX_INITIALIZE || ifunc == SFLUX_TRANSIENT) {
111  do_time = true;
112  }
113 
114  // Store the initial guess for the surface problem in the soln vector,
115  // CSoln, and in an separate vector CSolnInit.
116  size_t loc = 0;
117  for (size_t n = 0; n < m_numSurfPhases; n++) {
118  m_ptrsSurfPhase[n]->getConcentrations(m_numEqn1.data());
119  for (size_t k = 0; k < m_nSpeciesSurfPhase[n]; k++) {
120  m_CSolnSP[loc] = m_numEqn1[k];
121  loc++;
122  }
123  }
124 
126 
127  // Calculate the largest species in each phase
128  evalSurfLarge(m_CSolnSP.data());
129 
130  if (m_ioflag) {
131  print_header(m_ioflag, ifunc, time_scale, true, reltol, abstol);
132  }
133 
134  // Quick return when there isn't a surface problem to solve
135  if (m_neq == 0) {
136  not_converged = false;
137  update_norm = 0.0;
138  }
139 
140  // Start of Newton's method
141  while (not_converged && iter < iter_max) {
142  iter++;
143  // Store previous iteration's solution in the old solution vector
145 
146  // Evaluate the largest surface species for each surface phase every
147  // 5 iterations.
148  if (iter%5 == 4) {
149  evalSurfLarge(m_CSolnSP.data());
150  }
151 
152  // Calculate the value of the time step
153  // - heuristics to stop large oscillations in deltaT
154  if (do_time) {
155  // don't hurry increase in time step at the same time as damping
156  if (damp < 1.0) {
157  label_factor = 1.0;
158  }
159  double tmp = calc_t(m_netProductionRatesSave.data(),
160  m_XMolKinSpecies.data(),
161  &label_t, &label_t_old, &label_factor, m_ioflag);
162  if (iter < 10) {
163  inv_t = tmp;
164  } else if (tmp > 2.0*inv_t) {
165  inv_t = 2.0*inv_t;
166  } else {
167  inv_t = tmp;
168  }
169 
170  // Check end condition
171  if (ifunc == SFLUX_TRANSIENT) {
172  tmp = t_real + 1.0/inv_t;
173  if (tmp > time_scale) {
174  inv_t = 1.0/(time_scale - t_real);
175  }
176  }
177  } else {
178  // make steady state calc a step of 1 million seconds to prevent
179  // singular Jacobians for some pathological cases
180  inv_t = 1.0e-6;
181  }
182  deltaT = 1.0/inv_t;
183 
184  // Call the routine to numerically evaluation the Jacobian and residual
185  // for the current iteration.
186  resjac_eval(m_Jac, m_resid.data(), m_CSolnSP.data(),
187  m_CSolnSPOld.data(), do_time, deltaT);
188 
189  // Calculate the weights. Make sure the calculation is carried out on
190  // the first iteration.
191  if (iter%4 == 1) {
192  calcWeights(m_wtSpecies.data(), m_wtResid.data(),
193  m_Jac, m_CSolnSP.data(), abstol, reltol);
194  }
195 
196  // Find the weighted norm of the residual
197  double resid_norm = calcWeightedNorm(m_wtResid.data(), m_resid.data(), m_neq);
198 
199  // Solve Linear system. The solution is in m_resid
200  solve(m_Jac, m_resid.data());
201 
202  // Calculate the Damping factor needed to keep all unknowns between 0
203  // and 1, and not allow too large a change (factor of 2) in any unknown.
204  damp = calc_damping(m_CSolnSP.data(), m_resid.data(), m_neq, &label_d);
205 
206  // Calculate the weighted norm of the update vector Here, resid is the
207  // delta of the solution, in concentration units.
208  update_norm = calcWeightedNorm(m_wtSpecies.data(),
209  m_resid.data(), m_neq);
210 
211  // Update the solution vector and real time Crop the concentrations to
212  // zero.
213  for (size_t irow = 0; irow < m_neq; irow++) {
214  m_CSolnSP[irow] -= damp * m_resid[irow];
215  }
216  for (size_t irow = 0; irow < m_neq; irow++) {
217  m_CSolnSP[irow] = std::max(0.0, m_CSolnSP[irow]);
218  }
219  updateState(m_CSolnSP.data());
220 
221  if (do_time) {
222  t_real += damp/inv_t;
223  }
224 
225  if (m_ioflag) {
226  printIteration(m_ioflag, damp, label_d, label_t, inv_t, t_real, iter,
227  update_norm, resid_norm, do_time);
228  }
229 
230  if (ifunc == SFLUX_TRANSIENT) {
231  not_converged = (t_real < time_scale);
232  } else {
233  if (do_time) {
234  if (t_real > time_scale ||
235  (resid_norm < 1.0e-7 &&
236  update_norm*time_scale/t_real < EXTRA_ACCURACY)) {
237  do_time = false;
238  }
239  } else {
240  not_converged = ((update_norm > EXTRA_ACCURACY) ||
241  (resid_norm > EXTRA_ACCURACY));
242  }
243  }
244  } // End of Newton's Method while statement
245 
246  // End Newton's method. If not converged, print error message and
247  // recalculate sdot's at equal site fractions.
248  if (not_converged && m_ioflag) {
249  writelog("#$#$#$# Error in solveSP $#$#$#$ \n");
250  writelogf("Newton iter on surface species did not converge, "
251  "update_norm = %e \n", update_norm);
252  writelog("Continuing anyway\n");
253  }
254 
255  // Decide on what to return in the solution vector. Right now, will always
256  // return the last solution no matter how bad
257  if (m_ioflag) {
258  fun_eval(m_resid.data(), m_CSolnSP.data(), m_CSolnSPOld.data(),
259  false, deltaT);
260  double resid_norm = calcWeightedNorm(m_wtResid.data(), m_resid.data(), m_neq);
261  printIteration(m_ioflag, damp, label_d, label_t, inv_t, t_real, iter,
262  update_norm, resid_norm, do_time, true);
263  }
264 
265  // Return with the appropriate flag
266  if (update_norm > 1.0) {
267  return -1;
268  }
269  return 1;
270 }
271 
272 void solveSP::updateState(const double* CSolnSP)
273 {
274  vector<double> X;
275  size_t loc = 0;
276  for (size_t n = 0; n < m_numSurfPhases; n++) {
277  X.resize(m_nSpeciesSurfPhase[n]);
278  for (size_t k = 0; k < X.size(); k++) {
279  X[k] = CSolnSP[loc + k] / m_ptrsSurfPhase[n]->siteDensity();
280  }
281  m_ptrsSurfPhase[n]->setMoleFractions_NoNorm(X.data());
282  loc += m_nSpeciesSurfPhase[n];
283  }
284 }
285 
286 void solveSP::updateMFSolnSP(double* XMolSolnSP)
287 {
288  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
289  size_t keqnStart = m_eqnIndexStartSolnPhase[isp];
290  m_ptrsSurfPhase[isp]->getMoleFractions(XMolSolnSP + keqnStart);
291  }
292 }
293 
294 void solveSP::updateMFKinSpecies(double* XMolKinSpecies, int isp)
295 {
296  InterfaceKinetics* kin = m_objects[isp];
297  for (size_t iph = 0; iph < kin->nPhases(); iph++) {
298  size_t ksi = kin->kineticsSpeciesIndex(0, iph);
299  kin->thermo(iph).getMoleFractions(XMolKinSpecies + ksi);
300  }
301 }
302 
303 void solveSP::evalSurfLarge(const double* CSolnSP)
304 {
305  size_t kindexSP = 0;
306  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
307  double Clarge = CSolnSP[kindexSP];
308  m_spSurfLarge[isp] = 0;
309  kindexSP++;
310  for (size_t k = 1; k < m_nSpeciesSurfPhase[isp]; k++, kindexSP++) {
311  if (CSolnSP[kindexSP] > Clarge) {
312  Clarge = CSolnSP[kindexSP];
313  m_spSurfLarge[isp] = k;
314  }
315  }
316  }
317 }
318 
319 void solveSP::fun_eval(double* resid, const double* CSoln, const double* CSolnOld,
320  const bool do_time, const double deltaT)
321 {
322  size_t k;
323  double lenScale = 1.0E-9;
324  if (m_numSurfPhases > 0) {
325  // update the surface concentrations with the input surface
326  // concentration vector
327  updateState(CSoln);
328 
329  // Get the net production rates of all of the species in the
330  // surface kinetics mechanism
331  //
332  // HKM Should do it here for all kinetics objects so that
333  // bulk will eventually work.
334  if (do_time) {
335  size_t kindexSP = 0;
336  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
337  size_t nsp = m_nSpeciesSurfPhase[isp];
338  InterfaceKinetics* kinPtr = m_objects[isp];
339  size_t kins = kindexSP;
341  for (k = 0; k < nsp; k++, kindexSP++) {
342  resid[kindexSP] =
343  (CSoln[kindexSP] - CSolnOld[kindexSP]) / deltaT
345  }
346 
347  size_t kspecial = kins + m_spSurfLarge[isp];
348  double sd = m_ptrsSurfPhase[isp]->siteDensity();
349  resid[kspecial] = sd;
350  for (k = 0; k < nsp; k++) {
351  resid[kspecial] -= CSoln[kins + k];
352  }
353  }
354  } else {
355  size_t kindexSP = 0;
356  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
357  size_t nsp = m_nSpeciesSurfPhase[isp];
358  InterfaceKinetics* kinPtr = m_objects[isp];
359  size_t kins = kindexSP;
361  for (k = 0; k < nsp; k++, kindexSP++) {
362  resid[kindexSP] = - m_netProductionRatesSave[k];
363  }
364  size_t kspecial = kins + m_spSurfLarge[isp];
365  double sd = m_ptrsSurfPhase[isp]->siteDensity();
366  resid[kspecial] = sd;
367  for (k = 0; k < nsp; k++) {
368  resid[kspecial] -= CSoln[kins + k];
369  }
370  }
371  }
372 
373  if (m_bulkFunc == BULK_DEPOSITION) {
374  size_t kindexSP = m_numTotSurfSpecies;
375  for (size_t isp = 0; isp < m_numBulkPhasesSS; isp++) {
376  double* XBlk = m_numEqn1.data();
377  size_t nsp = m_nSpeciesSurfPhase[isp];
378  double grRate = 0.0;
379  for (k = 0; k < nsp; k++) {
380  if (m_netProductionRatesSave[k] > 0.0) {
381  grRate += m_netProductionRatesSave[k];
382  }
383  }
384  resid[kindexSP] = m_bulkPhasePtrs[isp]->molarDensity();
385  for (k = 0; k < nsp; k++) {
386  resid[kindexSP] -= CSoln[kindexSP + k];
387  }
388  if (grRate > 0.0) {
389  for (k = 1; k < nsp; k++) {
390  if (m_netProductionRatesSave[k] > 0.0) {
391  resid[kindexSP + k] = XBlk[k] * grRate
393  } else {
394  resid[kindexSP + k] = XBlk[k] * grRate;
395  }
396  }
397  } else {
398  grRate = 1.0E-6;
399  //! @todo the appearance of k in this formula is suspicious
400  grRate += fabs(m_netProductionRatesSave[k]);
401  for (k = 1; k < nsp; k++) {
402  resid[kindexSP + k] = grRate * (XBlk[k] - 1.0/nsp);
403  }
404  }
405  if (do_time) {
406  for (k = 1; k < nsp; k++) {
407  resid[kindexSP + k] +=
408  lenScale / deltaT *
409  (CSoln[kindexSP + k]- CSolnOld[kindexSP + k]);
410  }
411  }
412  kindexSP += nsp;
413  }
414  }
415  }
416 }
417 
418 void solveSP::resjac_eval(DenseMatrix& jac, double resid[], double CSoln[],
419  const double CSolnOld[], const bool do_time,
420  const double deltaT)
421 {
422  size_t kColIndex = 0;
423  // Calculate the residual
424  fun_eval(resid, CSoln, CSolnOld, do_time, deltaT);
425  // Now we will look over the columns perturbing each unknown.
426  for (size_t jsp = 0; jsp < m_numSurfPhases; jsp++) {
427  size_t nsp = m_nSpeciesSurfPhase[jsp];
428  double sd = m_ptrsSurfPhase[jsp]->siteDensity();
429  for (size_t kCol = 0; kCol < nsp; kCol++) {
430  double cSave = CSoln[kColIndex];
431  double dc = std::max(1.0E-10 * sd, fabs(cSave) * 1.0E-7);
432  CSoln[kColIndex] += dc;
433  fun_eval(m_numEqn2.data(), CSoln, CSolnOld, do_time, deltaT);
434  for (size_t i = 0; i < m_neq; i++) {
435  jac(i, kColIndex) = (m_numEqn2[i] - resid[i])/dc;
436  }
437  CSoln[kColIndex] = cSave;
438  kColIndex++;
439  }
440  }
441 
442  if (m_bulkFunc == BULK_DEPOSITION) {
443  for (size_t jsp = 0; jsp < m_numBulkPhasesSS; jsp++) {
444  size_t nsp = m_numBulkSpecies[jsp];
445  double sd = m_bulkPhasePtrs[jsp]->molarDensity();
446  for (size_t kCol = 0; kCol < nsp; kCol++) {
447  double cSave = CSoln[kColIndex];
448  double dc = std::max(1.0E-10 * sd, fabs(cSave) * 1.0E-7);
449  CSoln[kColIndex] += dc;
450  fun_eval(m_numEqn2.data(), CSoln, CSolnOld, do_time, deltaT);
451  for (size_t i = 0; i < m_neq; i++) {
452  jac(i, kColIndex) = (m_numEqn2[i] - resid[i])/dc;
453  }
454  CSoln[kColIndex] = cSave;
455  kColIndex++;
456  }
457  }
458  }
459 }
460 
461 /**
462  * This function calculates a damping factor for the Newton iteration update
463  * vector, dxneg, to insure that all site and bulk fractions, x, remain
464  * bounded between zero and one.
465  *
466  * dxneg[] = negative of the update vector.
467  *
468  * The constant "APPROACH" sets the fraction of the distance to the boundary
469  * that the step can take. If the full step would not force any fraction
470  * outside of 0-1, then Newton's method is allowed to operate normally.
471  */
472 static double calc_damping(double x[], double dxneg[], size_t dim, int* label)
473 {
474  const double APPROACH = 0.80;
475  double damp = 1.0;
476  static double damp_old = 1.0; //! @todo this variable breaks thread safety
477  *label = -1;
478 
479  for (size_t i = 0; i < dim; i++) {
480  // Calculate the new suggested new value of x[i]
481  double xnew = x[i] - damp * dxneg[i];
482 
483  // Calculate the allowed maximum and minimum values of x[i]
484  // - Only going to allow x[i] to converge to zero by a
485  // single order of magnitude at a time
486  double xtop = 1.0 - 0.1*fabs(1.0-x[i]);
487  double xbot = fabs(x[i]*0.1) - 1.0e-16;
488  if (xnew > xtop) {
489  damp = - APPROACH * (1.0 - x[i]) / dxneg[i];
490  *label = int(i);
491  } else if (xnew < xbot) {
492  damp = APPROACH * x[i] / dxneg[i];
493  *label = int(i);
494  } else if (xnew > 3.0*std::max(x[i], 1.0E-10)) {
495  damp = - 2.0 * std::max(x[i], 1.0E-10) / dxneg[i];
496  *label = int(i);
497  }
498  }
499  damp = std::max(damp, 1e-2);
500 
501  // Only allow the damping parameter to increase by a factor of three each
502  // iteration. Heuristic to avoid oscillations in the value of damp
503  if (damp > damp_old*3) {
504  damp = damp_old*3;
505  *label = -1;
506  }
507 
508  // Save old value of the damping parameter for use in subsequent calls.
509  damp_old = damp;
510  return damp;
511 
512 } /* calc_damping */
513 
514 /**
515  * This function calculates the norm of an update, dx[], based on the
516  * weighted values of x.
517  */
518 static double calcWeightedNorm(const double wtX[], const double dx[], size_t dim)
519 {
520  double norm = 0.0;
521  if (dim == 0) {
522  return 0.0;
523  }
524  for (size_t i = 0; i < dim; i++) {
525  norm += pow(dx[i] / wtX[i], 2);
526  }
527  return sqrt(norm/dim);
528 }
529 
530 void solveSP::calcWeights(double wtSpecies[], double wtResid[],
531  const Array2D& Jac, const double CSoln[],
532  const double abstol, const double reltol)
533 {
534  // First calculate the weighting factor for the concentrations of the
535  // surface species and bulk species.
536  size_t kindex = 0;
537  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
538  double sd = m_ptrsSurfPhase[isp]->siteDensity();
539  for (size_t k = 0; k < m_nSpeciesSurfPhase[isp]; k++, kindex++) {
540  wtSpecies[kindex] = abstol * sd + reltol * fabs(CSoln[kindex]);
541  }
542  }
543  if (m_bulkFunc == BULK_DEPOSITION) {
544  for (size_t isp = 0; isp < m_numBulkPhasesSS; isp++) {
545  double sd = m_bulkPhasePtrs[isp]->molarDensity();
546  for (size_t k = 0; k < m_numBulkSpecies[isp]; k++, kindex++) {
547  wtSpecies[kindex] = abstol * sd + reltol * fabs(CSoln[kindex]);
548  }
549  }
550  }
551 
552  // Now do the residual Weights. Since we have the Jacobian, we will use it
553  // to generate a number based on the what a significant change in a solution
554  // variable does to each residual. This is a row sum scale operation.
555  for (size_t k = 0; k < m_neq; k++) {
556  wtResid[k] = 0.0;
557  for (size_t jcol = 0; jcol < m_neq; jcol++) {
558  wtResid[k] += fabs(Jac(k,jcol) * wtSpecies[jcol]);
559  }
560  }
561 }
562 
563 double solveSP::calc_t(double netProdRateSolnSP[], double XMolSolnSP[], int* label,
564  int* label_old, double* label_factor, int ioflag)
565 {
566  double inv_timeScale = 1.0E-10;
567  size_t kindexSP = 0;
568  *label = 0;
569  updateMFSolnSP(XMolSolnSP);
570  for (size_t isp = 0; isp < m_numSurfPhases; isp++) {
571  // Get the interface kinetics associated with this surface
572  InterfaceKinetics* kin = m_objects[isp];
573 
574  kin->getNetProductionRates(m_numEqn1.data());
575  double sden = kin->thermo(0).molarDensity();
576  for (size_t k = 0; k < m_nSpeciesSurfPhase[isp]; k++, kindexSP++) {
577  netProdRateSolnSP[kindexSP] = m_numEqn1[k];
578  double tmp = std::max(XMolSolnSP[kindexSP], 1.0e-10);
579  tmp *= sden;
580  tmp = fabs(netProdRateSolnSP[kindexSP]/ tmp);
581  if (netProdRateSolnSP[kindexSP]> 0.0) {
582  tmp /= 100.;
583  }
584  if (tmp > inv_timeScale) {
585  inv_timeScale = tmp;
586  *label = int(kindexSP);
587  }
588  }
589  }
590 
591  // Increase time step exponentially as same species repeatedly controls time
592  // step
593  if (*label == *label_old) {
594  *label_factor *= 1.5;
595  } else {
596  *label_old = *label;
597  *label_factor = 1.0;
598  }
599  return inv_timeScale / *label_factor;
600 } // calc_t
601 
602 void solveSP::print_header(int ioflag, int ifunc, double time_scale,
603  int damping, double reltol, double abstol)
604 {
605  if (ioflag) {
606  writelog("\n================================ SOLVESP CALL SETUP "
607  "========================================\n");
608  if (ifunc == SFLUX_INITIALIZE) {
609  writelog("\n SOLVESP Called with Initialization turned on\n");
610  writelogf(" Time scale input = %9.3e\n", time_scale);
611  } else if (ifunc == SFLUX_RESIDUAL) {
612  writelog("\n SOLVESP Called to calculate steady state residual\n");
613  writelog(" from a good initial guess\n");
614  } else if (ifunc == SFLUX_JACOBIAN) {
615  writelog("\n SOLVESP Called to calculate steady state Jacobian\n");
616  writelog(" from a good initial guess\n");
617  } else if (ifunc == SFLUX_TRANSIENT) {
618  writelog("\n SOLVESP Called to integrate surface in time\n");
619  writelogf(" for a total of %9.3e sec\n", time_scale);
620  } else {
621  throw CanteraError("solveSP::print_header",
622  "Unknown ifunc flag = {}", ifunc);
623  }
624 
625  if (m_bulkFunc == BULK_DEPOSITION) {
626  writelog(" The composition of the Bulk Phases will be calculated\n");
627  } else if (m_bulkFunc == BULK_ETCH) {
628  writelog(" Bulk Phases have fixed compositions\n");
629  } else {
630  throw CanteraError("solveSP::print_header",
631  "Unknown bulkFunc flag = {}", m_bulkFunc);
632  }
633 
634  if (damping) {
635  writelog(" Damping is ON \n");
636  } else {
637  writelog(" Damping is OFF \n");
638  }
639 
640  writelogf(" Reltol = %9.3e, Abstol = %9.3e\n", reltol, abstol);
641  }
642 
643  if (ioflag == 1) {
644  writelog("\n\n\t Iter Time Del_t Damp DelX "
645  " Resid Name-Time Name-Damp\n");
646  writelog("\t -----------------------------------------------"
647  "------------------------------------\n");
648  }
649 }
650 
651 void solveSP::printIteration(int ioflag, double damp, int label_d,
652  int label_t, double inv_t, double t_real,
653  size_t iter, double update_norm,
654  double resid_norm, bool do_time, bool final)
655 {
656  if (ioflag == 1) {
657  if (final) {
658  writelogf("\tFIN%3d ", iter);
659  } else {
660  writelogf("\t%6d ", iter);
661  }
662  if (do_time) {
663  writelogf("%9.4e %9.4e ", t_real, 1.0/inv_t);
664  } else {
665  writeline(' ', 22, false);
666  }
667  if (damp < 1.0) {
668  writelogf("%9.4e ", damp);
669  } else {
670  writeline(' ', 11, false);
671  }
672  writelogf("%9.4e %9.4e", update_norm, resid_norm);
673  if (do_time) {
674  size_t k = m_kinSpecIndex[label_t];
675  size_t isp = m_kinObjIndex[label_t];
676  writelog(" %-16s", m_objects[isp]->kineticsSpeciesName(k));
677  } else {
678  writeline(' ', 16, false);
679  }
680  if (label_d >= 0) {
681  size_t k = m_kinSpecIndex[label_d];
682  size_t isp = m_kinObjIndex[label_d];
683  writelogf(" %-16s", m_objects[isp]->kineticsSpeciesName(k));
684  }
685  if (final) {
686  writelog(" -- success");
687  }
688  writelog("\n");
689  }
690 } // printIteration
691 
692 }
Declarations for the implicit integration of surface site density equations (see Kinetics Managers an...
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
A class for 2D arrays stored in column-major (Fortran-compatible) form.
Definition: Array.h:32
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:66
A class for full (non-sparse) matrices with Fortran-compatible data storage, which adds matrix operat...
Definition: DenseMatrix.h:55
void resize(size_t n, size_t m, double v=0.0) override
Resize the matrix.
Definition: DenseMatrix.cpp:60
Advances the surface coverages of the associated set of SurfacePhase objects in time.
A kinetics manager for heterogeneous reaction mechanisms.
size_t nPhases() const
The number of phases participating in the reaction mechanism.
Definition: Kinetics.h:184
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition: Kinetics.h:276
virtual void getNetProductionRates(double *wdot)
Species net production rates [kmol/m^3/s or kmol/m^2/s].
Definition: Kinetics.cpp:363
ThermoPhase & thermo(size_t n=0)
This method returns a reference to the nth ThermoPhase object defined in this kinetics mechanism.
Definition: Kinetics.h:242
virtual double molarDensity() const
Molar density (kmol/m^3).
Definition: Phase.cpp:576
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:231
void getMoleFractions(double *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:434
A simple thermodynamic model for a surface phase, assuming an ideal solution model.
Definition: SurfPhase.h:98
vector< size_t > m_numBulkSpecies
Vector of number of species in the m_numBulkPhases phases.
Definition: solveSP.h:391
vector< double > m_wtSpecies
Weights for the species concentrations norm calculation.
Definition: solveSP.h:461
size_t m_numTotSurfSpecies
Total number of surface species in all surface phases.
Definition: solveSP.h:338
void evalSurfLarge(const double *CSolnSP)
Update the vector that keeps track of the largest species in each surface phase.
Definition: solveSP.cpp:303
void updateState(const double *cSurfSpec)
Update the surface states of the surface phases.
Definition: solveSP.cpp:272
vector< size_t > m_indexKinObjSurfPhase
Mapping between the surface phases and the InterfaceKinetics objects.
Definition: solveSP.h:346
vector< size_t > m_kinObjIndex
Index between the equation index and the index of the InterfaceKinetics object.
Definition: solveSP.h:418
solveSP(ImplicitSurfChem *surfChemPtr, int bulkFunc=BULK_ETCH)
Constructor for the object.
Definition: solveSP.cpp:22
vector< double > m_resid
Residual for the surface problem.
Definition: solveSP.h:473
vector< ThermoPhase * > m_bulkPhasePtrs
Vector of bulk phase pointers, length is equal to m_numBulkPhases.
Definition: solveSP.h:401
vector< size_t > m_spSurfLarge
Vector containing the indices of the largest species in each surface phase.
Definition: solveSP.h:427
vector< double > m_netProductionRatesSave
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:434
double calc_t(double netProdRateSolnSP[], double XMolSolnSP[], int *label, int *label_old, double *label_factor, int ioflag)
Calculate a conservative delta T to use in a pseudo-steady state algorithm.
Definition: solveSP.cpp:563
int m_bulkFunc
This variable determines how the bulk phases are to be handled.
Definition: solveSP.h:323
vector< double > m_wtResid
Weights for the residual norm calculation. length MAX(1, m_neq)
Definition: solveSP.h:455
vector< InterfaceKinetics * > & m_objects
Vector of interface kinetics objects.
Definition: solveSP.h:311
void fun_eval(double *resid, const double *CSolnSP, const double *CSolnOldSP, const bool do_time, const double deltaT)
Main Function evaluation.
Definition: solveSP.cpp:319
vector< double > m_CSolnSave
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:443
void updateMFSolnSP(double *XMolSolnSP)
Update mole fraction vector consisting of unknowns in surface problem.
Definition: solveSP.cpp:286
vector< double > m_CSolnSPOld
Saved solution vector at the old time step. length MAX(1, m_neq)
Definition: solveSP.h:452
void printIteration(int ioflag, double damp, int label_d, int label_t, double inv_t, double t_real, size_t iter, double update_norm, double resid_norm, bool do_time, bool final=false)
Printing routine that gets called after every iteration.
Definition: solveSP.cpp:651
vector< double > m_CSolnSP
Solution vector. length MAX(1, m_neq)
Definition: solveSP.h:446
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:411
size_t m_numTotBulkSpeciesSS
Total number of species in all bulk phases.
Definition: solveSP.h:398
vector< double > m_numEqn1
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:437
vector< double > m_CSolnSPInit
Saved initial solution vector. length MAX(1, m_neq)
Definition: solveSP.h:449
vector< double > m_XMolKinSpecies
Vector of mole fractions. length m_maxTotSpecies.
Definition: solveSP.h:476
vector< double > m_numEqn2
Temporary vector with length equal to max m_maxTotSpecies.
Definition: solveSP.h:440
vector< size_t > m_eqnIndexStartSolnPhase
Index of the start of the unknowns for each solution phase.
Definition: solveSP.h:372
vector< size_t > m_nSpeciesSurfPhase
Vector of length number of surface phases containing the number of surface species in each phase.
Definition: solveSP.h:353
size_t m_numSurfPhases
Number of surface phases in the surface problem.
Definition: solveSP.h:330
void print_header(int ioflag, int ifunc, double time_scale, int damping, double reltol, double abstol)
Printing routine that optionally gets called at the start of every invocation.
Definition: solveSP.cpp:602
void updateMFKinSpecies(double *XMolKinSp, int isp)
Update the mole fraction vector for a specific kinetic species vector corresponding to one InterfaceK...
Definition: solveSP.cpp:294
DenseMatrix m_Jac
Jacobian.
Definition: solveSP.h:480
size_t m_numBulkPhasesSS
Total number of volumetric condensed phases included in the steady state problem handled by this rout...
Definition: solveSP.h:385
size_t m_neq
Total number of equations to solve in the implicit problem.
Definition: solveSP.h:317
vector< SurfPhase * > m_ptrsSurfPhase
Vector of surface phase pointers.
Definition: solveSP.h:360
size_t m_maxTotSpecies
Maximum number of species in any single kinetics operator -> also maxed wrt the total # of solution s...
Definition: solveSP.h:431
int solveSurfProb(int ifunc, double time_scale, double TKelvin, double PGas, double reltol, double abstol)
Main routine that actually calculates the pseudo steady state of the surface problem.
Definition: solveSP.cpp:89
void calcWeights(double wtSpecies[], double wtResid[], const Array2D &Jac, const double CSolnSP[], const double abstol, const double reltol)
Calculate the solution and residual weights.
Definition: solveSP.cpp:530
void resjac_eval(DenseMatrix &jac, double *resid, double *CSolnSP, const double *CSolnSPOld, const bool do_time, const double deltaT)
Main routine that calculates the current residual and Jacobian.
Definition: solveSP.cpp:418
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:195
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:175
const int BULK_ETCH
Etching of a bulk phase is to be expected.
Definition: solveSP.h:58
const int BULK_DEPOSITION
Deposition of a bulk phase is to be expected.
Definition: solveSP.h:53
const int SFLUX_TRANSIENT
The transient calculation is performed here for an amount of time specified by "time_scale".
Definition: solveSP.h:42
const int SFLUX_RESIDUAL
Need to solve the surface problem in order to calculate the surface fluxes of gas-phase species.
Definition: solveSP.h:29
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:35
const int SFLUX_INITIALIZE
This assumes that the initial guess supplied to the routine is far from the correct one.
Definition: solveSP.h:22
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:564
static double calcWeightedNorm(const double[], const double dx[], size_t)
This function calculates the norm of an update, dx[], based on the weighted values of x.
Definition: solveSP.cpp:518
int solve(DenseMatrix &A, double *b, size_t nrhs, size_t ldb)
Solve Ax = b. Array b is overwritten on exit with x.
Header file for implicit surface problem solver (see Chemical Kinetics and class solveSP).