Cantera  2.0
OneDim.cpp
3 #include "cantera/oneD/OneDim.h"
4 
5 #include "cantera/base/ctml.h"
6 
7 #include <fstream>
8 
9 using namespace ctml;
10 using namespace std;
11 
12 namespace Cantera
13 {
14 
15 /**
16  * Default constructor. Create an empty object.
17  */
18 OneDim::OneDim()
19  : m_tmin(1.0e-16), m_tmax(10.0), m_tfactor(0.5),
20  m_jac(0), m_newt(0),
21  m_rdt(0.0), m_jac_ok(false),
22  m_nd(0), m_bw(0), m_size(0),
23  m_init(false),
24  m_ss_jac_age(10), m_ts_jac_age(20),
25  m_nevals(0), m_evaltime(0.0)
26 {
27  //writelog("OneDim default constructor\n");
28  m_newt = new MultiNewton(1);
29  //m_solve_time = 0.0;
30 }
31 
32 
33 /**
34  * Construct a OneDim container for the domains pointed at by the
35  * input vector of pointers.
36 */
37 OneDim::OneDim(vector<Domain1D*> domains) :
38  m_tmin(1.0e-16), m_tmax(10.0), m_tfactor(0.5),
39  m_jac(0), m_newt(0),
40  m_rdt(0.0), m_jac_ok(false),
41  m_nd(0), m_bw(0), m_size(0),
42  m_init(false),
43  m_ss_jac_age(10), m_ts_jac_age(20),
44  m_nevals(0), m_evaltime(0.0)
45 {
46  //writelog("OneDim constructor\n");
47 
48  // create a Newton iterator, and add each domain.
49  m_newt = new MultiNewton(1);
50  int nd = static_cast<int>(domains.size());
51  int i;
52  for (i = 0; i < nd; i++) {
53  addDomain(domains[i]);
54  }
55  init();
56  resize();
57 }
58 
59 
60 size_t OneDim::domainIndex(string name)
61 {
62  for (size_t n = 0; n < m_nd; n++) {
63  if (domain(n).id() == name) {
64  return n;
65  }
66  }
67  throw CanteraError("OneDim::domainIndex","no domain named >>"+name+"<<");
68  return npos;
69 }
70 
71 
72 /**
73  * Domains are added left-to-right.
74  */
76 {
77 
78  // if 'd' is not the first domain, link it to the last domain
79  // added (the rightmost one)
80  int n = static_cast<int>(m_dom.size());
81  if (n > 0) {
82  m_dom.back()->append(d);
83  }
84 
85  // every other domain is a connector
86  if (2*(n/2) == n) {
87  m_connect.push_back(d);
88  } else {
89  m_bulk.push_back(d);
90  }
91 
92  // add it also to the global domain list, and set its
93  // container and position
94  m_dom.push_back(d);
95  d->setContainer(this, m_nd);
96  m_nd++;
97  resize();
98 }
99 
100 
102 {
103  delete m_jac;
104  delete m_newt;
105 }
106 
108 {
109  return *m_jac;
110 }
112 {
113  return *m_newt;
114 }
115 
116 //==============================================================================================================
117 void OneDim::writeStats(int printTime)
118 {
119  saveStats();
120  char buf[100];
121  sprintf(buf,"\nStatistics:\n\n Grid Functions Time Jacobians Time \n");
122  writelog(buf);
123  size_t n = m_gridpts.size();
124  for (size_t i = 0; i < n; i++) {
125  if (printTime) {
126  sprintf(buf,"%5s %5i %9.4f %5i %9.4f \n",
127  int2str(m_gridpts[i]).c_str(), m_funcEvals[i], m_funcElapsed[i],
128  m_jacEvals[i], m_jacElapsed[i]);
129  } else {
130  sprintf(buf,"%5s %5i NA %5i NA \n",
131  int2str(m_gridpts[i]).c_str(), m_funcEvals[i], m_jacEvals[i]);
132  }
133  writelog(buf);
134  }
135 }
136 //==============================================================================================================
137 
138 /**
139  * Save statistics on function and Jacobiab evaulation, and reset
140  * the counters. Statistics are saved only if the number of
141  * Jacobian evaluations is greater than zero. The statistics saved
142  * are
143  *
144  * - number of grid points
145  * - number of Jacobian evaluations
146  * - CPU time spent evaluating Jacobians
147  * - number of non-Jacobian function evaluations
148  * - CPU time spent evaluating functions
149  */
151 {
152  if (m_jac) {
153  int nev = m_jac->nEvals();
154  if (nev > 0 && m_nevals > 0) {
155  m_gridpts.push_back(m_pts);
156  m_jacEvals.push_back(m_jac->nEvals());
157  m_jacElapsed.push_back(m_jac->elapsedTime());
158  m_funcEvals.push_back(m_nevals);
159  m_nevals = 0;
160  m_funcElapsed.push_back(m_evaltime);
161  m_evaltime = 0.0;
162  }
163  }
164 }
165 
166 
167 /**
168  * Call after one or more grids has been refined.
169  */
171 {
172  m_bw = 0;
173  std::vector<size_t> nvars, loc;
174  size_t lc = 0;
175 
176  // save the statistics for the last grid
177  saveStats();
178  m_pts = 0;
179  for (size_t i = 0; i < m_nd; i++) {
180  Domain1D* d = m_dom[i];
181 
182  size_t np = d->nPoints();
183  size_t nv = d->nComponents();
184  for (size_t n = 0; n < np; n++) {
185  nvars.push_back(nv);
186  loc.push_back(lc);
187  lc += nv;
188  m_pts++;
189  }
190 
191  // update the Jacobian bandwidth
192  size_t bw1, bw2 = 0;
193 
194  // bandwidth of the local block
195  bw1 = d->bandwidth();
196  if (bw1 == npos) {
197  bw1 = 2*d->nComponents() - 1;
198  }
199 
200  // bandwidth of the block coupling the first point of this
201  // domain to the last point of the previous domain
202  if (i > 0) {
203  bw2 = m_dom[i-1]->bandwidth();
204  if (bw2 == npos) {
205  bw2 = m_dom[i-1]->nComponents();
206  }
207  bw2 += d->nComponents() - 1;
208  }
209  if (bw1 > m_bw) {
210  m_bw = bw1;
211  }
212  if (bw2 > m_bw) {
213  m_bw = bw2;
214  }
215 
216  m_size = d->loc() + d->size();
217  }
218  m_nvars = nvars;
219  m_loc = loc;
220 
221  m_newt->resize(size());
222  m_mask.resize(size());
223 
224  // delete the current Jacobian evaluator and create a new one
225  delete m_jac;
226  m_jac = new MultiJac(*this);
227  m_jac_ok = false;
228 
229  for (size_t i = 0; i < m_nd; i++) {
230  m_dom[i]->setJac(m_jac);
231  }
232 }
233 
234 
235 int OneDim::solve(doublereal* x, doublereal* xnew, int loglevel)
236 {
237  if (!m_jac_ok) {
238  eval(npos, x, xnew, 0.0, 0);
239  m_jac->eval(x, xnew, 0.0);
240  m_jac->updateTransient(m_rdt, DATA_PTR(m_mask));
241  m_jac_ok = true;
242  }
243  int m = m_newt->solve(x, xnew, *this, *m_jac, loglevel);
244  return m;
245 }
246 
247 void OneDim::evalSSJacobian(doublereal* x, doublereal* xnew)
248 {
249  doublereal rdt_save = m_rdt;
250  m_jac_ok = false;
251  setSteadyMode();
252  eval(npos, x, xnew, 0.0, 0);
253  m_jac->eval(x, xnew, 0.0);
254  m_rdt = rdt_save;
255 }
256 
257 /**
258  * Return a pointer to the domain that contains component i of the
259  * global solution vector. The domains are scanned right-to-left,
260  * and the first one with starting location less or equal to i is
261  * returned.
262  *
263  * 8/26/02 changed '<' to '<=' DGG
264  *
265  */
267 {
268  Domain1D* d = right();
269  while (d) {
270  if (d->loc() <= i) {
271  return d;
272  }
273  d = d->left();
274  }
275  return 0;
276 }
277 
278 
279 /**
280  * Evaluate the multi-domain residual function, and return the
281  * result in array r.
282  */
283 void OneDim::eval(size_t j, double* x, double* r, doublereal rdt, int count)
284 {
285  clock_t t0 = clock();
286  fill(r, r + m_size, 0.0);
287  fill(m_mask.begin(), m_mask.end(), 0);
288  if (rdt < 0.0) {
289  rdt = m_rdt;
290  }
291  // int nn;
292  vector<Domain1D*>::iterator d;
293 
294  // iterate over the bulk domains first
295  for (d = m_bulk.begin(); d != m_bulk.end(); ++d) {
296  (*d)->eval(j, x, r, DATA_PTR(m_mask), rdt);
297  }
298 
299  // then over the connector domains
300  for (d = m_connect.begin(); d != m_connect.end(); ++d) {
301  (*d)->eval(j, x, r, DATA_PTR(m_mask), rdt);
302  }
303 
304  // increment counter and time
305  if (count) {
306  clock_t t1 = clock();
307  m_evaltime += double(t1 - t0)/CLOCKS_PER_SEC;
308  m_nevals++;
309  }
310 }
311 
312 
313 /**
314  * The 'infinity' (maximum magnitude) norm of the steady-state
315  * residual. Used only for diagnostic output.
316  */
317 doublereal OneDim::ssnorm(doublereal* x, doublereal* r)
318 {
319  eval(npos, x, r, 0.0, 0);
320  doublereal ss = 0.0;
321  for (size_t i = 0; i < m_size; i++) {
322  ss = std::max(fabs(r[i]),ss);
323  }
324  return ss;
325 }
326 
327 
328 /**
329  * Prepare for time stepping with timestep dt.
330  */
331 void OneDim::initTimeInteg(doublereal dt, doublereal* x)
332 {
333  doublereal rdt_old = m_rdt;
334  m_rdt = 1.0/dt;
335 
336  // if the stepsize has changed, then update the transient
337  // part of the Jacobian
338  if (fabs(rdt_old - m_rdt) > Tiny) {
339  m_jac->updateTransient(m_rdt, DATA_PTR(m_mask));
340  }
341 
342  // iterate over all domains, preparing each one to begin
343  // time stepping
344  Domain1D* d = left();
345  while (d) {
346  d->initTimeInteg(dt, x);
347  d = d->right();
348  }
349 }
350 
351 
352 /**
353  * Prepare to solve the steady-state problem. Set the reciprocal
354  * of the time step to zero, and, if it was previously non-zero,
355  * signal that a new Jacobian will be needed.
356  */
358 {
359  m_rdt = 0.0;
360  m_jac->updateTransient(m_rdt, DATA_PTR(m_mask));
361 
362  // iterate over all domains, preparing them for steady-state solution
363  Domain1D* d = left();
364  while (d) {
365  d->setSteadyMode();
366  d = d->right();
367  }
368 }
369 
370 /**
371  * Initialize all domains. On the first call, this methods calls
372  * the init method of each domain, proceeding from left to right.
373  * Subsequent calls do nothing.
374  */
376 {
377  if (!m_init) {
378  Domain1D* d = left();
379  while (d) {
380  d->init();
381  d = d->right();
382  }
383  }
384  m_init = true;
385 }
386 
387 
388 /**
389  * Signal that the current Jacobian is no longer valid.
390  */
392 {
393  if (m_container) {
394  m_container->jacobian().setAge(10000);
395  m_container->saveStats();
396  }
397 }
398 
399 
400 /**
401  * Take time steps using Backward Euler.
402  *
403  * nsteps -- number of steps
404  * dt -- initial step size
405  * loglevel -- controls amount of printed diagnostics
406  */
407 doublereal OneDim::timeStep(int nsteps, doublereal dt, doublereal* x,
408  doublereal* r, int loglevel)
409 {
410 
411  // set the Jacobian age parameter to the transient value
412  newton().setOptions(m_ts_jac_age);
413 
414  if (loglevel > 0) {
415  //writelog("Begin time stepping.\n\n");
416  writelog("\n\n step size (s) log10(ss) \n");
417  writelog("===============================\n");
418  }
419 
420  int n = 0, m;
421  doublereal ss;
422  char str[80];
423  while (n < nsteps) {
424  if (loglevel > 0) {
425  ss = ssnorm(x, r);
426  sprintf(str, " %4d %10.4g %10.4g" , n,dt,log10(ss));
427  writelog(str);
428  }
429 
430  // set up for time stepping with stepsize dt
431  initTimeInteg(dt,x);
432 
433  // solve the transient problem
434  m = solve(x, r, loglevel-1);
435 
436  // successful time step. Copy the new solution in r to
437  // the current solution in x.
438  if (m >= 0) {
439  n += 1;
440  if (loglevel > 0) {
441  writelog("\n");
442  }
443  copy(r, r + m_size, x);
444  if (m == 100) {
445  dt *= 1.5;
446  }
447  // else dt /= 1.5;
448  if (dt > m_tmax) {
449  dt = m_tmax;
450  }
451  }
452 
453  // No solution could be found with this time step.
454  // Decrease the stepsize and try again.
455  else {
456  if (loglevel > 0) {
457  writelog("...failure.\n");
458  }
459  dt *= m_tfactor;
460  if (dt < m_tmin)
461  throw CanteraError("OneDim::timeStep",
462  "Time integration failed.");
463  }
464  }
465 
466  // Prepare to solve the steady problem.
467  setSteadyMode();
468  newton().setOptions(m_ss_jac_age);
469 
470  // return the value of the last stepsize, which may be smaller
471  // than the initial stepsize
472  return dt;
473 }
474 
475 
476 void OneDim::save(string fname, string id, string desc, doublereal* sol)
477 {
478 
479  struct tm* newtime;
480  time_t aclock;
481  ::time(&aclock); /* Get time in seconds */
482  newtime = localtime(&aclock); /* Convert time to struct tm form */
483 
484  XML_Node root("doc");
485  ifstream fin(fname.c_str());
486  XML_Node* ct;
487  if (fin) {
488  root.build(fin);
489  const XML_Node* same_ID = root.findID(id);
490  int jid = 1;
491  string idnew = id;
492  while (same_ID != 0) {
493  idnew = id + "_" + int2str(jid);
494  jid++;
495  same_ID = root.findID(idnew);
496  }
497  id = idnew;
498  fin.close();
499  ct = &root.child("ctml");
500  } else {
501  ct = &root.addChild("ctml");
502  }
503  XML_Node& sim = (XML_Node&)ct->addChild("simulation");
504  sim.addAttribute("id",id);
505  addString(sim,"timestamp",asctime(newtime));
506  if (desc != "") {
507  addString(sim,"description",desc);
508  }
509 
510  Domain1D* d = left();
511  while (d) {
512  d->save(sim, sol);
513  d = d->right();
514  }
515  ofstream s(fname.c_str());
516  if (!s) {
517  throw CanteraError("save","could not open file "+fname);
518  }
519  ct->write(s);
520  s.close();
521  writelog("Solution saved to file "+fname+" as solution "+id+".\n");
522 }
523 
524 
525 void Domain1D::setGrid(size_t n, const doublereal* z)
526 {
527  m_z.resize(n);
528  m_points = n;
529  for (size_t j = 0; j < m_points; j++) {
530  m_z[j] = z[j];
531  }
532 }
533 
534 }