Cantera  2.0
vcs_util.cpp
Go to the documentation of this file.
1 /**
2  * @file vcs_util.cpp
3  * Internal definitions for utility functions for the VCSnonideal package
4  */
5 /*
6  * Copyright (2005) Sandia Corporation. Under the terms of
7  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
8  * U.S. Government retains certain rights in this software.
9  */
10 #include <cstdlib>
11 #include <cmath>
12 #include <cassert>
13 
15 #include <cstring>
16 #include <cstdlib>
17 
18 using namespace std;
19 
20 namespace VCSnonideal
21 {
22 
23 /***************************************************************************/
24 /***************************************************************************/
25 /***************************************************************************/
26 #ifndef USE_MEMSET
27 void vcs_dzero(double* vector, int length)
28 
29 /**************************************************************************
30  *
31  * vcs_dzero:
32  *
33  * Zeroes a double vector
34  *************************************************************************/
35 {
36  int i;
37  for (i = 0; i < length; i++) {
38  vector[i] = 0.0;
39  }
40 } /* vcs_dzero() ***********************************************************/
41 #endif
42 /***************************************************************************/
43 /***************************************************************************/
44 /***************************************************************************/
45 #ifndef USE_MEMSET
46 void vcs_izero(int* vector, int length)
47 
48 /**************************************************************************
49  *
50  * vcs_izero:
51  *
52  * Zeroes an int vector
53  *************************************************************************/
54 {
55  int i;
56  for (i = 0; i < length; i++) {
57  vector[i] = 0;
58  }
59 } /* vcs_izero() ***********************************************************/
60 #endif
61 /***************************************************************************/
62 /***************************************************************************/
63 /***************************************************************************/
64 
65 #ifndef USE_MEMSET
66 void vcs_dcopy(double* const vec_to, const double* const vec_from, int length)
67 
68 /**************************************************************************
69  *
70  * vcs_dcopy:
71  *
72  * Copies a double vector
73  ***************************************************************************/
74 {
75  int i;
76  for (i = 0; i < length; i++) {
77  vec_to[i] = vec_from[i];
78  }
79 } /* vcs_dzero() *************************************************************/
80 #endif
81 /*****************************************************************************/
82 /*****************************************************************************/
83 /*****************************************************************************/
84 
85 #ifndef USE_MEMSET
86 void vcs_icopy(int* vec_to, int* vec_from, int length)
87 
88 /**************************************************************************
89  *
90  * vcs_icopy:
91  *
92  * copies an int vector
93  ***************************************************************************/
94 {
95  int i;
96  for (i = 0; i < length; i++) {
97  vec_to[i] = vec_from[i];
98  }
99 } /* vcs_dzero() *************************************************************/
100 #endif
101 
102 /*****************************************************************************/
103 /*****************************************************************************/
104 /*****************************************************************************/
105 
106 #ifndef USE_MEMSET
107 /*
108  * vcs_vdzero
109  *
110  * zeroes a double vector
111  */
112 void vcs_vdzero(std::vector<double> &vvv, int len)
113 {
114  if (len < 0) {
115  std::fill(vvv.begin(), vvv.end(), 0.0);
116  } else {
117  std::fill_n(vvv.begin(), len, 0.0);
118  }
119 }
120 #endif
121 
122 double vcs_l2norm(const std::vector<double> vec)
123 {
124  size_t len = vec.size();
125  if (len == 0) {
126  return 0.0;
127  }
128  double sum = 0.0;
129  std::vector<double>::const_iterator pos;
130  for (pos = vec.begin(); pos != vec.end(); ++pos) {
131  sum += (*pos) * (*pos);
132  }
133  return std::sqrt(sum/len);
134 }
135 
136 /*****************************************************************************/
137 /*****************************************************************************/
138 /*****************************************************************************/
139 
140 #ifndef USE_MEMSET
141 /*
142  * vcs_vizero
143  *
144  * zeroes a double vector
145  */
146 void vcs_vizero(std::vector<int> &vvv, int len)
147 {
148  if (len < 0) {
149  std::fill(vvv.begin(), vvv.end(), 0.0);
150  } else {
151  std::fill_n(vvv.begin(), len, 0.0);
152  }
153 }
154 #endif
155 
156 
157 
158 #ifndef USE_MEMSET
159 /*
160  * vcs_vdcopy
161  *
162  * copies a vector of doubles to another vector of doubles
163  *
164  * @param vec_to Vector to be copied to
165  * @param vec_from Vector to be copied from
166  * @param length Length of the copy
167  */
168 void vcs_vdcopy(std::vector<double> &vec_to,
169  const std::vector<double> & vec_from, int length)
170 {
171  std::copy(vec_from.begin(), vec_from.begin() + length, vec_to.begin());
172 }
173 #endif
174 
175 
176 #ifndef USE_MEMSET
177 /*
178  * vcs_vicopy
179  *
180  * copies a vector to another vector
181  *
182  * @param vec_to Vector to be copied to
183  * @param vec_from Vector to be copied from
184  * @param length Length of the copy
185  */
186 void vcs_vicopy(std::vector<int> &vec_to,
187  const std::vector<int> & vec_from, int length)
188 {
189  std::copy(vec_from.begin(), vec_from.begin() + length, vec_to.begin());
190 }
191 #endif
192 
193 /*
194  *
195  * Finds the location of the maximum component in a double vector
196  * INPUT
197  * x(*) - Vector to search
198  * xSize(*) if nonnull, this is the multiplier vector to be
199  * multiplied into x(*) before making the decision.
200  * j <= i < n : i is the range of indices to search in X(*)
201  *
202  * RETURN
203  * return index of the greatest value on X(*) searched
204  */
205 size_t vcs_optMax(const double* x, const double* xSize, size_t j, size_t n)
206 {
207  size_t i;
208  size_t largest = j;
209  double big = x[j];
210  if (xSize) {
211  assert(xSize[j] > 0.0);
212  big *= xSize[j];
213  for (i = j + 1; i < n; ++i) {
214  assert(xSize[i] > 0.0);
215  if ((x[i]*xSize[i]) > big) {
216  largest = i;
217  big = x[i]*xSize[i];
218  }
219  }
220  } else {
221  for (i = j + 1; i < n; ++i) {
222  if (x[i] > big) {
223  largest = i;
224  big = x[i];
225  }
226  }
227  }
228  return largest;
229 }
230 
231 int vcs_max_int(const int* vector, int length)
232 
233 /**************************************************************************
234  *
235  * vcs_max_int:
236  *
237  * returns the maximum integer in a list.
238  ***************************************************************************/
239 {
240  int i, retn;
241  if (vector == NULL || length <= 0) {
242  return 0;
243  }
244  retn = vector[0];
245  for (i = 1; i < length; i++) {
246  retn = std::max(retn, vector[i]);
247  }
248  return retn;
249 }
250 
251 
252 //====================================================================================================================
253 #ifdef DEBUG_HKM
254 static void mlequ_matrixDump(double* c, int idem, int n)
255 {
256  int i, j;
257  printf("vcsUtil_mlequ() MATRIX DUMP --------------------------------------------------\n");
258  printf(" ");
259  for (j = 0; j < n; ++j) {
260  printf(" % 3d ", j);
261  }
262  printf("\n");
263  for (j = 0; j < n; ++j) {
264  printf("-----------");
265  }
266  printf("\n");
267  for (i = 0; i < n; ++i) {
268  printf(" %3d | ", i);
269  for (j = 0; j < n; ++j) {
270  printf("% 10.3e ", c[i + j * idem]);
271  }
272  printf("\n");
273  }
274  for (j = 0; j < n; ++j) {
275  printf("-----------");
276  }
277  printf("\n");
278  printf("vcsUtil_mlequ() END MATRIX DUMP --------------------------------------------------\n");
279 
280 }
281 #endif
282 //====================================================================================================================
283 //! Swap rows in the c matrix and the b rhs matrix
284 /*!
285  * @param c Matrix of size nxn, row first
286  * @param idem C storage dimension for the number of rows
287  * @param n Size of the matrix
288  * @param b RHS of the Ax=b problem to solve
289  * @param m Number of rhs to solve
290  * @param irowa first row to swap
291  * @param irowb second row to swap
292  */
293 static void vcsUtil_swapRows(double* c, size_t idem, size_t n, double* b, size_t m, size_t irowa, size_t irowb)
294 {
295  if (irowa == irowb) {
296  return;
297  }
298  for (size_t j = 0; j < n; j++) {
299  std::swap(c[irowa + j * idem], c[irowb + j * idem]);
300  }
301  for (size_t j = 0; j < m; j++) {
302  std::swap(b[irowa + j * idem], b[irowb + j * idem]);
303  }
304 }
305 //====================================================================================================================
306 //! Swap rows in the c matrix and the b rhs matrix to lower the condition number of the matrix
307 /*!
308  * @param c Matrix of size nxn, row first
309  * @param idem C storage dimension for the number of rows
310  * @param n Size of the matrix
311  * @param b RHS of the Ax=b problem to solve
312  * @param m Number of rhs to solve
313  */
314 static void vcsUtil_mlequ_preprocess(double* c, size_t idem, size_t n, double* b, size_t m)
315 {
316  size_t j = 0;
317  std::vector<int> irowUsed(n, 0);
318 
319  for (j = 0; j < n; j++) {
320  int numNonzero = 0;
321  size_t inonzero = npos;
322  for (size_t i = 0; i < n; i++) {
323  if (c[i + j * idem] != 0.0) {
324  numNonzero++;
325  inonzero = i;
326  }
327  }
328  if (numNonzero == 1) {
329  if (inonzero != j) {
330  if (irowUsed[inonzero] == 0) {
331  vcsUtil_swapRows(c, idem, n, b, m, j, inonzero);
332 #ifdef DEBUG_HKM
333  // mlequ_matrixDump(c, idem, n);
334 #endif
335  }
336  }
337  irowUsed[j] = 1;
338  }
339  }
340 
341  for (j = 0; j < n; j++) {
342  if (c[j + j * idem] == 0.0) {
343  int numNonzero = 0;
344  size_t inonzero = npos;
345  for (size_t i = 0; i < n; i++) {
346  if (! irowUsed[i]) {
347  if (c[i + j * idem] != 0.0) {
348  if ((c[i + i * idem] == 0.0) || (c[j + i * idem] != 0.0)) {
349  numNonzero++;
350  inonzero = i;
351  }
352  }
353  }
354  }
355  if (numNonzero == 1) {
356  if (inonzero != j) {
357  if (irowUsed[inonzero] == 0) {
358  vcsUtil_swapRows(c, idem, n, b, m, j, inonzero);
359 #ifdef DEBUG_HKM
360  // mlequ_matrixDump(c, idem, n);
361 #endif
362  }
363  }
364  irowUsed[j] = 1;
365  }
366  }
367  }
368 
369  for (j = 0; j < n; j++) {
370  if (c[j + j * idem] == 0.0) {
371  int numNonzero = 0;
372  size_t inonzero = npos;
373  for (size_t i = 0; i < n; i++) {
374  if (! irowUsed[i]) {
375  if (c[i + j * idem] != 0.0) {
376  if ((c[i + i * idem] == 0.0) || (c[j + i * idem] != 0.0)) {
377  numNonzero++;
378  inonzero = i;
379  }
380  }
381  }
382  }
383  if (inonzero != npos) {
384  if (inonzero != j) {
385  if (irowUsed[inonzero] == 0) {
386  vcsUtil_swapRows(c, idem, n, b, m, j, inonzero);
387 #ifdef DEBUG_HKM
388  // mlequ_matrixDump(c, idem, n);
389 #endif
390  }
391  }
392  }
393  }
394  }
395 }
396 //====================================================================================================================
397 // Invert an n x n matrix and solve m rhs's
398 /*
399  * Solve a square matrix with multiple right hand sides
400  *
401  * \f[
402  * C X + B = 0;
403  * \f]
404  *
405  * This routine uses Gauss elimination and is optimized for the solution
406  * of lots of rhs's. A crude form of row pivoting is used here.
407  * The matrix C is destroyed.
408  *
409  * @return Routine returns an integer representing success:
410  * - 1 : Matrix is singular
411  * - 0 : solution is OK
412  * The solution x[] is returned in the matrix b.
413  *
414  * @param c Matrix to be inverted. c is in fortran format, i.e., rows
415  * are the inner loop. Row numbers equal to idem.
416  * c[i+j*idem] = c_i_j = Matrix to be inverted: i = row number
417  * j = column number
418  * @param idem number of row dimensions in c
419  * @param n Number of rows and columns in c
420  * @param b Multiple RHS. Note, b is actually the negative of
421  * most formulations. Row numbers equal to idem.
422  * b[i+j*idem] = b_i_j = vectors of rhs's: i = row number
423  * j = column number
424  * (each column is a new rhs)
425  * @param m number of rhs's
426  */
427 int vcsUtil_mlequ(double* c, size_t idem, size_t n, double* b, size_t m)
428 {
429  size_t k;
430 #ifdef DEBUG_HKM
431  // mlequ_matrixDump(c, idem, n);
432 #endif
433  vcsUtil_mlequ_preprocess(c, idem, n, b, m);
434 #ifdef DEBUG_HKM
435  // mlequ_matrixDump(c, idem, n);
436  static int s_numCalls = 0;
437  s_numCalls++;
438 #endif
439 
440  double R;
441  if (n > idem || n <= 0) {
442  plogf("vcsUtil_mlequ ERROR: badly dimensioned matrix: %d %d\n", n, idem);
443  return 1;
444  }
445 
446 #ifdef DEBUG_HKM
447  int dmatrix = 0;
448  for (size_t i = 0; i < n; ++i) {
449  bool notFound = true;
450  for (size_t j = 0; j < n; ++j) {
451  if (c[i + j * idem] != 0.0) {
452  notFound = false;
453  }
454  }
455  if (notFound) {
456  printf(" vcsUtil_mlequ ERROR(): row %d is identically zero\n", i);
457  }
458  }
459  for (size_t j = 0; j < n; ++j) {
460  bool notFound = true;
461  for (size_t i = 0; i < n; ++i) {
462  if (c[i + j * idem] != 0.0) {
463  notFound = false;
464  }
465  }
466  if (notFound) {
467  printf(" vcsUtil_mlequ ERROR(): column %d is identically zero\n", j);
468  }
469  }
470  // if (s_numCalls >= 32) {
471  // printf("vcsUtil_mlequ: we are here\n");
472  // dmatrix = 1;
473  // }
474 
475  if (dmatrix) {
476  mlequ_matrixDump(c, idem, n);
477  }
478 #endif
479  /*
480  * Loop over the rows
481  * -> At the end of each loop, the only nonzero entry in the column
482  * will be on the diagonal. We can therfore just invert the
483  * diagonal at the end of the program to solve the equation system.
484  */
485  for (size_t i = 0; i < n; ++i) {
486  if (c[i + i * idem] == 0.0) {
487  /*
488  * Do a simple form of row pivoting to find a non-zero pivot
489  */
490  for (k = i + 1; k < n; ++k) {
491  if (c[k + i * idem] != 0.0) {
492  goto FOUND_PIVOT;
493  }
494  }
495  plogf("vcsUtil_mlequ ERROR: Encountered a zero column: %d\n", i);
496 #ifdef DEBUG_HKM
497  plogf(" call # %d\n", s_numCalls);
498 #endif
499 #ifdef DEBUG_HKM
500  mlequ_matrixDump(c, idem, n);
501 #endif
502  return 1;
503 FOUND_PIVOT:
504  ;
505  for (size_t j = 0; j < n; ++j) {
506  c[i + j * idem] += c[k + j * idem];
507  }
508  for (size_t j = 0; j < m; ++j) {
509  b[i + j * idem] += b[k + j * idem];
510  }
511  }
512 
513  for (size_t l = 0; l < n; ++l) {
514  if (l != i && c[l + i * idem] != 0.0) {
515  R = c[l + i * idem] / c[i + i * idem];
516  c[l + i * idem] = 0.0;
517  for (size_t j = i+1; j < n; ++j) {
518  c[l + j * idem] -= c[i + j * idem] * R;
519  }
520  for (size_t j = 0; j < m; ++j) {
521  b[l + j * idem] -= b[i + j * idem] * R;
522  }
523  }
524  }
525  }
526  /*
527  * The negative in the last expression is due to the form of B upon
528  * input
529  */
530  for (size_t i = 0; i < n; ++i) {
531  for (size_t j = 0; j < m; ++j) {
532  b[i + j * idem] = -b[i + j * idem] / c[i + i*idem];
533  }
534  }
535  return 0;
536 }
537 //====================================================================================================================
538 // Linear equation solution by Gauss-Jordan elimination for multiple rhs vectors
539 /*
540  * Solve a square matrix with multiple right hand sides
541  *
542  * \f[
543  * C X + B = 0;
544  * \f]
545  *
546  * This routine uses Gauss-Jordan elimination with full pivoting and is optimized for the solution
547  * of lots of rhs's.
548  *
549  * @return Routine returns an integer representing success:
550  * - 1 : Matrix is singular
551  * - 0 : solution is OK
552  * The solution x[] is returned in the matrix b.
553  *
554  * @param c Matrix to be inverted. c is in fortran format, i.e., rows
555  * are the inner loop. Row numbers equal to idem.
556  * c[i+j*idem] = c_i_j = Matrix to be inverted: i = row number
557  * j = column number
558  * @param idem number of row dimensions in c
559  * @param n Number of rows and columns in c
560  * @param b Multiple RHS. Note, b is actually the negative of
561  * most formulations. Row numbers equal to idem.
562  * b[i+j*idem] = b_i_j = vectors of rhs's: i = row number
563  * j = column number
564  * (each column is a new rhs)
565  * @param m number of rhs's
566  */
567 int vcsUtil_gaussj(double* c, size_t idem, size_t n, double* b, size_t m)
568 {
569 
570  size_t i, j, k, l, ll;
571  size_t irow = npos;
572  size_t icol = npos;
573  bool needInverse = false;
574  double pivinv;
575 #ifdef DEBUG_HKM
576  static int s_numCalls = 0;
577  s_numCalls++;
578 #endif
579 #ifdef DEBUG_HKM
580  // mlequ_matrixDump(c, idem, n);
581 #endif
582  /*
583  * Preprocess the problem
584  */
585  vcsUtil_mlequ_preprocess(c, idem, n, b, m);
586 
587 #ifdef DEBUG_HKM
588  // mlequ_matrixDump(c, idem, n);
589 #endif
590 
591  std::vector<size_t> indxc(n);
592  std::vector<size_t> indxr(n);
593  std::vector<int> ipiv(n, 0);
594  doublereal big = 0.0;
595  /*
596  * This is the main loop over the columns to be reduced.
597  */
598  for (i = 0; i < n; i++) {
599  big = 0.0;
600  for (j = 0; j < n; j++) {
601  if (ipiv[j] != 1) {
602  for (k = 0; k < n; k++) {
603  if (ipiv[k] == 0) {
604  if (fabs(c[j + idem * k]) >= big) {
605  big = fabs(c[j + idem * k]);
606  irow = j;
607  icol = k;
608  }
609  }
610  }
611  }
612  }
613  ++(ipiv[icol]);
614  if (irow != icol) {
615  vcsUtil_swapRows(c, idem, n, b, m, irow, icol);
616  }
617  indxr[i] = irow;
618  indxc[i] = icol;
619  if (c[icol + idem * icol] == 0.0) {
620  plogf("vcsUtil_gaussj ERROR: Encountered a zero column: %d\n", i);
621  return 1;
622  }
623  pivinv = 1.0 / c[icol + idem * icol];
624  c[icol + idem * icol] = 1.0;
625  for (l = 0; l < n; l++) {
626  c[icol + idem * l] *= pivinv;
627  }
628  for (l = 0; l < m; l++) {
629  b[icol + idem * l] *= pivinv;
630  }
631  for (ll = 0; ll < n; ll++) {
632  if (ll != icol) {
633  double dum = c[ll + idem * icol];
634  c[ll + idem * icol] = 0;
635  for (l = 0; l < n; l++) {
636  c[ll + idem * l] -= c[icol + idem * l] * dum;
637  }
638  for (l = 0; l < m; l++) {
639  b[ll + idem * l] -= b[icol + idem * l] * dum;
640  }
641  }
642  }
643  }
644  if (needInverse) {
645  for (l = n-1; l != npos; l--) {
646  if (indxr[l] != indxc[l]) {
647  for (k = 0; k < n; k++) {
648  std::swap(c[k + idem * indxr[l]], c[k + idem * indxr[l]]);
649  }
650  }
651  }
652  }
653 
654 
655  /*
656  * The negative in the last expression is due to the form of B upon
657  * input
658  */
659  for (i = 0; i < n; ++i) {
660  for (j = 0; j < m; ++j) {
661  b[i + j * idem] = -b[i + j * idem];
662  }
663  }
664  return 0;
665 }
666 //====================================================================================================================
667 
668 // Returns the value of the gas constant in the units specified by a parameter
669 /*
670  * @param mu_units Specifies the units.
671  * - VCS_UNITS_KCALMOL: kcal gmol-1 K-1
672  * - VCS_UNITS_UNITLESS: 1.0 K-1
673  * - VCS_UNITS_KJMOL: kJ gmol-1 K-1
674  * - VCS_UNITS_KELVIN: 1.0 K-1
675  * - VCS_UNITS_MKS: joules kmol-1 K-1 = kg m2 s-2 kmol-1 K-1
676  */
677 double vcsUtil_gasConstant(int mu_units)
678 {
679  double r;
680  switch (mu_units) {
681  case VCS_UNITS_KCALMOL:
682  r = Cantera::GasConst_cal_mol_K * 1e-3;
683  break;
684  case VCS_UNITS_UNITLESS:
685  r = 1.0;
686  break;
687  case VCS_UNITS_KJMOL:
688  r = Cantera::GasConstant * 1e-6;
689  break;
690  case VCS_UNITS_KELVIN:
691  r = 1.0;
692  break;
693  case VCS_UNITS_MKS:
694  /* joules / kg-mol K = kg m2 / s2 kg-mol K */
696  break;
697  default:
698  plogf("vcs_gasConstant error: uknown units: %d\n",
699  mu_units);
700  exit(EXIT_FAILURE);
701  }
702  return r;
703 }
704 
705 
706 
707 void vcs_print_line(const char* string, int num)
708 
709 /**************************************************************************
710  *
711  * vcs_print_char:
712  *
713  * Print a line consisting of a multiple of the same string
714  *
715  ***************************************************************************/
716 {
717  if (string) {
718  for (int j = 0; j < num; j++) {
719  plogf("%s", string);
720  }
721  }
722  plogendl();
723 }
724 
725 
726 const char* vcs_speciesType_string(int speciesStatus, int length)
727 {
728  const char* sss;
729  switch (speciesStatus) {
731  sss = "Component Species";
732  break;
733  case VCS_SPECIES_MAJOR:
734  sss ="Major Species";
735  break;
736  case VCS_SPECIES_MINOR:
737  sss ="Minor Species";
738  break;
740  if (length < 48) {
741  sss = "Set Zeroed-Phase";
742  } else {
743  sss ="Purposely Zeroed-Phase Species (not in problem)";
744  }
745  break;
747  if (length < 23) {
748  sss = "Zeroed-MS Phase";
749  } else {
750  sss ="Zeroed-MS Phase Species";
751  }
752  break;
754  if (length < 23) {
755  sss = "Zeroed-SS Phase";
756  } else {
757  sss ="Zeroed-SS Phase Species";
758  }
759  break;
760  case VCS_SPECIES_DELETED:
761  if (length < 22) {
762  sss = "Deleted Species";
763  } else if (length < 40) {
764  sss = "Deleted-Small Species";
765  } else {
766  sss ="Deleted-Small Species in a MS phase";
767  }
768  break;
770  if (length < 47) {
771  sss = "Tmp Zeroed in MS";
772  } else {
773  sss ="Zeroed Species in an active MS phase (tmp)";
774  }
775  break;
777  if (length < 56) {
778  sss = "Stoich Zeroed in MS";
779  } else {
780  sss ="Zeroed Species in an active MS phase (Stoich Constraint)";
781  }
782  break;
784  if (length < 29) {
785  sss = "InterfaceVoltage";
786  } else {
787  sss ="InterfaceVoltage Species";
788  }
789  break;
790  default:
791  sss = "unknown species type";
792  }
793  return sss;
794 }
795 
796 
797 /************************************************************************ **/
798 
799 void vcs_print_stringTrunc(const char* str, size_t space, int alignment)
800 
801 /***********************************************************************
802  * vcs_print_stringTrunc():
803  *
804  * Print a string within a given space limit. This routine
805  * limits the amount of the string that will be printed to a
806  * maximum of "space" characters.
807  *
808  * str = String -> must be null terminated.
809  * space = space limit for the printing.
810  * alignment = 0 centered
811  * 1 right aligned
812  * 2 left aligned
813  ***********************************************************************/
814 {
815  size_t i, ls=0, rs=0;
816  size_t len = strlen(str);
817  if ((len) >= space) {
818  for (i = 0; i < space; i++) {
819  plogf("%c", str[i]);
820  }
821  } else {
822  if (alignment == 1) {
823  ls = space - len;
824  } else if (alignment == 2) {
825  rs = space - len;
826  } else {
827  ls = (space - len) / 2;
828  rs = space - len - ls;
829  }
830  if (ls != 0) {
831  for (i = 0; i < ls; i++) {
832  plogf(" ");
833  }
834  }
835  plogf("%s", str);
836  if (rs != 0) {
837  for (i = 0; i < rs; i++) {
838  plogf(" ");
839  }
840  }
841  }
842 }
843 
844 /*****************************************************************************/
845 /*****************************************************************************/
846 /*****************************************************************************/
847 
848 bool vcs_doubleEqual(double d1, double d2)
849 
850 /*************************************************************************
851  * vcs_doubleEqual()
852  *
853  * Simple routine to check whether two doubles are equal up to
854  * roundoff error. Currently it's set to check for 10 digits of
855  * accuracy.
856  *************************************************************************/
857 {
858  double denom = fabs(d1) + fabs(d2) + 1.0;
859  double fac = fabs(d1 - d2) / denom;
860  if (fac > 1.0E-10) {
861  return false;
862  }
863  return true;
864 }
865 
866 }