Cantera  2.1.2
OneDim.cpp
Go to the documentation of this file.
1 //! @file OneDim.cpp
4 #include "cantera/oneD/OneDim.h"
5 
7 #include "cantera/base/ctml.h"
8 
9 #include <fstream>
10 
11 using namespace ctml;
12 using namespace std;
13 
14 namespace Cantera
15 {
16 
17 OneDim::OneDim()
18  : m_tmin(1.0e-16), m_tmax(10.0), m_tfactor(0.5),
19  m_jac(0), m_newt(0),
20  m_rdt(0.0), m_jac_ok(false),
21  m_nd(0), m_bw(0), m_size(0),
22  m_init(false),
23  m_ss_jac_age(10), m_ts_jac_age(20),
24  m_interrupt(0), m_nevals(0), m_evaltime(0.0)
25 {
26  //writelog("OneDim default constructor\n");
27  m_newt = new MultiNewton(1);
28  //m_solve_time = 0.0;
29 }
30 
31 OneDim::OneDim(vector<Domain1D*> domains) :
32  m_tmin(1.0e-16), m_tmax(10.0), m_tfactor(0.5),
33  m_jac(0), m_newt(0),
34  m_rdt(0.0), m_jac_ok(false),
35  m_nd(0), m_bw(0), m_size(0),
36  m_init(false),
37  m_ss_jac_age(10), m_ts_jac_age(20),
38  m_interrupt(0), m_nevals(0), m_evaltime(0.0)
39 {
40  //writelog("OneDim constructor\n");
41 
42  // create a Newton iterator, and add each domain.
43  m_newt = new MultiNewton(1);
44  int nd = static_cast<int>(domains.size());
45  int i;
46  for (i = 0; i < nd; i++) {
47  addDomain(domains[i]);
48  }
49  init();
50  resize();
51 }
52 
53 size_t OneDim::domainIndex(const std::string& name)
54 {
55  for (size_t n = 0; n < m_nd; n++) {
56  if (domain(n).id() == name) {
57  return n;
58  }
59  }
60  throw CanteraError("OneDim::domainIndex","no domain named >>"+name+"<<");
61  return npos;
62 }
63 
65 {
66  // if 'd' is not the first domain, link it to the last domain
67  // added (the rightmost one)
68  int n = static_cast<int>(m_dom.size());
69  if (n > 0) {
70  m_dom.back()->append(d);
71  }
72 
73  // every other domain is a connector
74  if (2*(n/2) == n) {
75  m_connect.push_back(d);
76  } else {
77  m_bulk.push_back(d);
78  }
79 
80  // add it also to the global domain list, and set its
81  // container and position
82  m_dom.push_back(d);
83  d->setContainer(this, m_nd);
84  m_nd++;
85  resize();
86 }
87 
88 OneDim::~OneDim()
89 {
90  delete m_jac;
91  delete m_newt;
92 }
93 
95 {
96  return *m_jac;
97 }
99 {
100  return *m_newt;
101 }
102 
103 void OneDim::writeStats(int printTime)
104 {
105  saveStats();
106  char buf[100];
107  sprintf(buf,"\nStatistics:\n\n Grid Functions Time Jacobians Time \n");
108  writelog(buf);
109  size_t n = m_gridpts.size();
110  for (size_t i = 0; i < n; i++) {
111  if (printTime) {
112  sprintf(buf,"%5s %5i %9.4f %5i %9.4f \n",
113  int2str(m_gridpts[i]).c_str(), m_funcEvals[i], m_funcElapsed[i],
114  m_jacEvals[i], m_jacElapsed[i]);
115  } else {
116  sprintf(buf,"%5s %5i NA %5i NA \n",
117  int2str(m_gridpts[i]).c_str(), m_funcEvals[i], m_jacEvals[i]);
118  }
119  writelog(buf);
120  }
121 }
122 
124 {
125  if (m_jac) {
126  int nev = m_jac->nEvals();
127  if (nev > 0 && m_nevals > 0) {
128  m_gridpts.push_back(m_pts);
129  m_jacEvals.push_back(m_jac->nEvals());
130  m_jacElapsed.push_back(m_jac->elapsedTime());
131  m_funcEvals.push_back(m_nevals);
132  m_nevals = 0;
133  m_funcElapsed.push_back(m_evaltime);
134  m_evaltime = 0.0;
135  }
136  }
137 }
138 
140 {
141  m_bw = 0;
142  std::vector<size_t> nvars, loc;
143  size_t lc = 0;
144 
145  // save the statistics for the last grid
146  saveStats();
147  m_pts = 0;
148  for (size_t i = 0; i < m_nd; i++) {
149  Domain1D* d = m_dom[i];
150 
151  size_t np = d->nPoints();
152  size_t nv = d->nComponents();
153  for (size_t n = 0; n < np; n++) {
154  nvars.push_back(nv);
155  loc.push_back(lc);
156  lc += nv;
157  m_pts++;
158  }
159 
160  // update the Jacobian bandwidth
161  size_t bw1, bw2 = 0;
162 
163  // bandwidth of the local block
164  bw1 = d->bandwidth();
165  if (bw1 == npos) {
166  bw1 = 2*d->nComponents() - 1;
167  }
168 
169  // bandwidth of the block coupling the first point of this
170  // domain to the last point of the previous domain
171  if (i > 0) {
172  bw2 = m_dom[i-1]->bandwidth();
173  if (bw2 == npos) {
174  bw2 = m_dom[i-1]->nComponents();
175  }
176  bw2 += d->nComponents() - 1;
177  }
178  if (bw1 > m_bw) {
179  m_bw = bw1;
180  }
181  if (bw2 > m_bw) {
182  m_bw = bw2;
183  }
184 
185  m_size = d->loc() + d->size();
186  }
187  m_nvars = nvars;
188  m_loc = loc;
189 
190  m_newt->resize(size());
191  m_mask.resize(size());
192 
193  // delete the current Jacobian evaluator and create a new one
194  delete m_jac;
195  m_jac = new MultiJac(*this);
196  m_jac_ok = false;
197 
198  for (size_t i = 0; i < m_nd; i++) {
199  m_dom[i]->setJac(m_jac);
200  }
201 }
202 
203 int OneDim::solve(doublereal* x, doublereal* xnew, int loglevel)
204 {
205  if (!m_jac_ok) {
206  eval(npos, x, xnew, 0.0, 0);
207  m_jac->eval(x, xnew, 0.0);
208  m_jac->updateTransient(m_rdt, DATA_PTR(m_mask));
209  m_jac_ok = true;
210  }
211  return m_newt->solve(x, xnew, *this, *m_jac, loglevel);
212 }
213 
214 void OneDim::evalSSJacobian(doublereal* x, doublereal* xnew)
215 {
216  doublereal rdt_save = m_rdt;
217  m_jac_ok = false;
218  setSteadyMode();
219  eval(npos, x, xnew, 0.0, 0);
220  m_jac->eval(x, xnew, 0.0);
221  m_rdt = rdt_save;
222 }
223 
225 {
226  Domain1D* d = right();
227  while (d) {
228  if (d->loc() <= i) {
229  return d;
230  }
231  d = d->left();
232  }
233  return 0;
234 }
235 
236 void OneDim::eval(size_t j, double* x, double* r, doublereal rdt, int count)
237 {
238  clock_t t0 = clock();
239  if (m_interrupt) {
240  m_interrupt->eval(m_nevals);
241  }
242  fill(r, r + m_size, 0.0);
243  fill(m_mask.begin(), m_mask.end(), 0);
244  if (rdt < 0.0) {
245  rdt = m_rdt;
246  }
247  // int nn;
248  vector<Domain1D*>::iterator d;
249 
250  // iterate over the bulk domains first
251  for (d = m_bulk.begin(); d != m_bulk.end(); ++d) {
252  (*d)->eval(j, x, r, DATA_PTR(m_mask), rdt);
253  }
254 
255  // then over the connector domains
256  for (d = m_connect.begin(); d != m_connect.end(); ++d) {
257  (*d)->eval(j, x, r, DATA_PTR(m_mask), rdt);
258  }
259 
260  // increment counter and time
261  if (count) {
262  clock_t t1 = clock();
263  m_evaltime += double(t1 - t0)/CLOCKS_PER_SEC;
264  m_nevals++;
265  }
266 }
267 
268 doublereal OneDim::ssnorm(doublereal* x, doublereal* r)
269 {
270  eval(npos, x, r, 0.0, 0);
271  doublereal ss = 0.0;
272  for (size_t i = 0; i < m_size; i++) {
273  ss = std::max(fabs(r[i]),ss);
274  }
275  return ss;
276 }
277 
278 void OneDim::initTimeInteg(doublereal dt, doublereal* x)
279 {
280  doublereal rdt_old = m_rdt;
281  m_rdt = 1.0/dt;
282 
283  // if the stepsize has changed, then update the transient
284  // part of the Jacobian
285  if (fabs(rdt_old - m_rdt) > Tiny) {
286  m_jac->updateTransient(m_rdt, DATA_PTR(m_mask));
287  }
288 
289  // iterate over all domains, preparing each one to begin
290  // time stepping
291  Domain1D* d = left();
292  while (d) {
293  d->initTimeInteg(dt, x);
294  d = d->right();
295  }
296 }
297 
299 {
300  m_rdt = 0.0;
301  m_jac->updateTransient(m_rdt, DATA_PTR(m_mask));
302 
303  // iterate over all domains, preparing them for steady-state solution
304  Domain1D* d = left();
305  while (d) {
306  d->setSteadyMode();
307  d = d->right();
308  }
309 }
310 
312 {
313  if (!m_init) {
314  Domain1D* d = left();
315  while (d) {
316  d->init();
317  d = d->right();
318  }
319  }
320  m_init = true;
321 }
322 
324 {
325  if (m_container) {
326  m_container->jacobian().setAge(10000);
327  m_container->saveStats();
328  }
329 }
330 
331 doublereal OneDim::timeStep(int nsteps, doublereal dt, doublereal* x,
332  doublereal* r, int loglevel)
333 {
334  // set the Jacobian age parameter to the transient value
335  newton().setOptions(m_ts_jac_age);
336 
337  writelog("\n\n step size (s) log10(ss) \n", loglevel);
338  writelog("===============================\n", loglevel);
339 
340  int n = 0, m;
341  doublereal ss;
342  char str[80];
343  while (n < nsteps) {
344  if (loglevel > 0) {
345  ss = ssnorm(x, r);
346  sprintf(str, " %4d %10.4g %10.4g" , n,dt,log10(ss));
347  writelog(str);
348  }
349 
350  // set up for time stepping with stepsize dt
351  initTimeInteg(dt,x);
352 
353  // solve the transient problem
354  m = solve(x, r, loglevel-1);
355 
356  // successful time step. Copy the new solution in r to
357  // the current solution in x.
358  if (m >= 0) {
359  n += 1;
360  writelog("\n", loglevel);
361  copy(r, r + m_size, x);
362  if (m == 100) {
363  dt *= 1.5;
364  }
365  // else dt /= 1.5;
366  if (dt > m_tmax) {
367  dt = m_tmax;
368  }
369  }
370 
371  // No solution could be found with this time step.
372  // Decrease the stepsize and try again.
373  else {
374  writelog("...failure.\n", loglevel);
375  dt *= m_tfactor;
376  if (dt < m_tmin)
377  throw CanteraError("OneDim::timeStep",
378  "Time integration failed.");
379  }
380  }
381 
382  // Prepare to solve the steady problem.
383  setSteadyMode();
384  newton().setOptions(m_ss_jac_age);
385 
386  // return the value of the last stepsize, which may be smaller
387  // than the initial stepsize
388  return dt;
389 }
390 
391 void OneDim::save(const std::string& fname, std::string id,
392  const std::string& desc, doublereal* sol,
393  int loglevel)
394 {
395  struct tm* newtime;
396  time_t aclock;
397  ::time(&aclock); /* Get time in seconds */
398  newtime = localtime(&aclock); /* Convert time to struct tm form */
399 
400  XML_Node root("doc");
401  ifstream fin(fname.c_str());
402  XML_Node* ct;
403  if (fin) {
404  root.build(fin);
405  const XML_Node* same_ID = root.findID(id);
406  int jid = 1;
407  string idnew = id;
408  while (same_ID != 0) {
409  idnew = id + "_" + int2str(jid);
410  jid++;
411  same_ID = root.findID(idnew);
412  }
413  id = idnew;
414  fin.close();
415  ct = &root.child("ctml");
416  } else {
417  ct = &root.addChild("ctml");
418  }
419  XML_Node& sim = (XML_Node&)ct->addChild("simulation");
420  sim.addAttribute("id",id);
421  addString(sim,"timestamp",asctime(newtime));
422  if (desc != "") {
423  addString(sim,"description",desc);
424  }
425 
426  Domain1D* d = left();
427  while (d) {
428  d->save(sim, sol);
429  d = d->right();
430  }
431  ofstream s(fname.c_str());
432  if (!s) {
433  throw CanteraError("save","could not open file "+fname);
434  }
435  ct->write(s);
436  s.close();
437  writelog("Solution saved to file "+fname+" as solution "+id+".\n", loglevel);
438 }
439 
440 void Domain1D::setGrid(size_t n, const doublereal* z)
441 {
442  m_z.resize(n);
443  m_points = n;
444  for (size_t j = 0; j < m_points; j++) {
445  m_z[j] = z[j];
446  }
447 }
448 
449 }
size_t m_nd
number of domains
Definition: OneDim.h:266
std::string int2str(const int n, const std::string &fmt)
Convert an int to a string using a format converter.
Definition: stringUtils.cpp:40
Domain1D * left()
Pointer to left-most domain (first added).
Definition: OneDim.h:88
void addDomain(Domain1D *d)
Add a domain. Domains are added left-to-right.
Definition: OneDim.cpp:64
size_t nPoints() const
Number of grid points in this domain.
Definition: Domain1D.h:186
Domain1D * left() const
Return a pointer to the left neighbor.
Definition: Domain1D.h:502
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
int nEvals() const
Number of Jacobian evaluations.
Definition: MultiJac.h:45
double timeStep(int nsteps, double dt, double *x, double *r, int loglevel)
Definition: OneDim.cpp:331
void resize()
Call after one or more grids has been refined.
Definition: OneDim.cpp:139
void setSteadyMode()
Definition: Domain1D.h:329
doublereal ssnorm(doublereal *x, doublereal *r)
Steady-state max norm (infinity norm) of the residual evaluated using solution x. ...
Definition: OneDim.cpp:268
void eval(size_t j, double *x, double *r, doublereal rdt=-1.0, int count=1)
Evaluate the multi-domain residual function.
Definition: OneDim.cpp:236
MultiJac & jacobian()
Return a reference to the Jacobian evaluator.
Definition: OneDim.cpp:94
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:173
virtual size_t loc(size_t j=0) const
Location of the start of the local solution vector in the global solution vector,.
Definition: Domain1D.h:461
int solve(doublereal *x0, doublereal *x1, int loglevel)
Solve F(x) = 0, where F(x) is the multi-domain residual function.
Definition: OneDim.cpp:203
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:100
void setContainer(OneDim *c, size_t index)
Definition: Domain1D.h:91
size_t loc(size_t jg)
Location in the solution vector of the first component of global point jg.
Definition: OneDim.h:106
void addString(Cantera::XML_Node &node, const std::string &titleString, const std::string &valueString, const std::string &typeString)
This function adds a child node with the name string with a string value to the current node...
Definition: ctml.cpp:141
MultiNewton & newton()
Return a reference to the Newton iterator.
Definition: OneDim.cpp:98
virtual doublereal eval(doublereal t) const
Evaluate the function.
Definition: Func1.cpp:62
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:584
size_t nComponents() const
Number of components at each grid point.
Definition: Domain1D.h:164
Base class for one-dimensional domains.
Definition: Domain1D.h:37
void setAge(int age)
Set the Jacobian age.
Definition: MultiJac.h:63
int solve(doublereal *x0, doublereal *x1, OneDim &r, MultiJac &jac, int loglevel)
Find the solution to F(X) = 0 by damped Newton iteration.
size_t bandwidth()
Set the Jacobian bandwidth for this domain.
Definition: Domain1D.h:116
Domain1D & domain(size_t i) const
Return a reference to domain i.
Definition: OneDim.h:54
void setSteadyMode()
Definition: OneDim.cpp:298
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:68
void resize(size_t points)
Change the problem size.
void saveStats()
Save statistics on function and Jacobian evaluation, and reset the counters.
Definition: OneDim.cpp:123
Domain1D * right() const
Return a pointer to the right neighbor.
Definition: Domain1D.h:507
void eval(doublereal *x0, doublereal *resid0, double rdt)
Evaluate the Jacobian at x0.
Definition: MultiJac.cpp:48
void writeStats(int printTime=1)
Write statistics about the number of iterations and Jacobians at each grid level. ...
Definition: OneDim.cpp:103
void addAttribute(const std::string &attrib, const std::string &value)
Add or modify an attribute of the current node.
Definition: xml.cpp:518
Class MultiJac evaluates the Jacobian of a system of equations defined by a residual function supplie...
Definition: MultiJac.h:26
void initTimeInteg(doublereal dt, doublereal *x)
Prepare for time stepping beginning with solution x and timestep dt.
Definition: OneDim.cpp:278
const doublereal Tiny
Small number to compare differences of mole fractions against.
Definition: ct_defs.h:155
XML_Node * findID(const std::string &id, const int depth=100) const
This routine carries out a recursive search for an XML node based on the xml element attribute...
Definition: xml.cpp:697
doublereal elapsedTime() const
Elapsed CPU time spent computing the Jacobian.
Definition: MultiJac.h:40
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
Func1 * m_interrupt
Function called at the start of every call to eval.
Definition: OneDim.h:284
Newton iterator for multi-domain, one-dimensional problems.
Definition: MultiNewton.h:22
void writelog(const std::string &msg)
Write a message to the screen.
Definition: global.cpp:43
Domain1D * right()
Pointer to right-most domain (last added).
Definition: OneDim.h:93
void needJacUpdate()
Definition: OneDim.cpp:323
size_t size() const
Total solution vector length;.
Definition: OneDim.h:83
Domain1D * pointDomain(size_t i)
Return a pointer to the domain global point i belongs to.
Definition: OneDim.cpp:224
void build(std::istream &f)
Main routine to create an tree-like representation of an XML file.
Definition: xml.cpp:776
void setOptions(int maxJacAge=5)
Set options.
Definition: MultiNewton.h:70
virtual void init()
Definition: Domain1D.h:125
void initTimeInteg(doublereal dt, const doublereal *x0)
Definition: Domain1D.h:320