Cantera  2.0
vcs_elem_rearrange.cpp
Go to the documentation of this file.
1 /**
2  * @file vcs_elem_rearrange.cpp
3  * Contains implementations for rearranging the element columns, and
4  * it contains the algorithm for choosing the rearrangement.
5  */
6 /*
7  * Copyright (2005) Sandia Corporation. Under the terms of
8  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
9  * U.S. Government retains certain rights in this software.
10  */
11 
15 
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cmath>
19 
20 namespace VCSnonideal
21 {
22 
23 // Rearrange the constraint equations represented by the Formula
24 // Matrix so that the operational ones are in the front
25 /*
26  *
27  * This subroutine handles the rearrangement of the constraint
28  * equations represented by the Formula Matrix. Rearrangement is only
29  * necessary when the number of components is less than the number of
30  * elements. For this case, some constraints can never be satisfied
31  * exactly, because the range space represented by the Formula
32  * Matrix of the components can't span the extra space. These
33  * constraints, which are out of the range space of the component
34  * Formula matrix entries, are migrated to the back of the Formula
35  * matrix.
36  *
37  * A prototypical example is an extra element column in
38  * FormulaMatrix[],
39  * which is identically zero. For example, let's say that argon is
40  * has an element column in FormulaMatrix[], but no species in the
41  * mechanism
42  * actually contains argon. Then, nc < ne. Also, without perturbation
43  * of FormulaMatrix[] vcs_basopt[] would produce a zero pivot
44  * because the matrix
45  * would be singular (unless the argon element column was already the
46  * last column of FormulaMatrix[].
47  * This routine borrows heavily from vcs_basopt's algorithm. It
48  * finds nc constraints which span the range space of the Component
49  * Formula matrix, and assigns them as the first nc components in the
50  * formula matrix. This guarantees that vcs_basopt[] has a
51  * nonsingular matrix to invert.
52  *
53  * Other Variables
54  * aw[i] = Mole fraction work space (ne in length)
55  * sa[j] = Gramm-Schmidt orthog work space (ne in length)
56  * ss[j] = Gramm-Schmidt orthog work space (ne in length)
57  * sm[i+j*ne] = QR matrix work space (ne*ne in length)
58  *
59  */
60 int VCS_SOLVE::vcs_elem_rearrange(double* const aw, double* const sa,
61  double* const sm, double* const ss)
62 {
63  size_t j, k, l, i, jl, ml, jr, ielem;
64  bool lindep;
65  size_t ncomponents = m_numComponents;
66  double test = -1.0E10;
67 #ifdef DEBUG_MODE
68  if (m_debug_print_lvl >= 2) {
69  plogf(" ");
70  for (i=0; i<77; i++) {
71  plogf("-");
72  }
73  plogf("\n");
74  plogf(" --- Subroutine elem_rearrange() called to ");
75  plogf("check stoich. coefficient matrix\n");
76  plogf(" --- and to rearrange the element ordering once");
77  plogendl();
78  }
79 #endif
80 
81  /*
82  * Use a temporary work array for the element numbers
83  * Also make sure the value of test is unique.
84  */
85  lindep = false;
86  do {
87  lindep = false;
88  for (i = 0; i < m_numElemConstraints; ++i) {
89  test -= 1.0;
90  aw[i] = m_elemAbundancesGoal[i];
91  if (test == aw[i]) {
92  lindep = true;
93  }
94  }
95  } while (lindep);
96 
97  /*
98  * Top of a loop of some sort based on the index JR. JR is the
99  * current number independent elements found.
100  */
101  jr = npos;
102  do {
103  ++jr;
104  /*
105  * Top of another loop point based on finding a linearly
106  * independent species
107  */
108  do {
109  /*
110  * Search the remaining part of the mole fraction vector, AW,
111  * for the largest remaining species. Return its identity in K.
112  */
114  for (ielem = jr; ielem < m_numElemConstraints; ielem++) {
115  if (m_elementActive[ielem]) {
116  if (aw[ielem] != test) {
117  k = ielem;
118  break;
119  }
120  }
121  }
122  if (k == m_numElemConstraints) {
123  plogf("vcs_elem_rearrange::Shouldn't be here. Algorithm misfired.");
124  plogendl();
125  exit(EXIT_FAILURE);
126  }
127 
128  /*
129  * Assign a large negative number to the element that we have
130  * just found, in order to take it out of further consideration.
131  */
132  aw[k] = test;
133 
134  /* *********************************************************** */
135  /* **** CHECK LINEAR INDEPENDENCE OF CURRENT FORMULA MATRIX */
136  /* **** LINE WITH PREVIOUS LINES OF THE FORMULA MATRIX ****** */
137  /* *********************************************************** */
138  /*
139  * Modified Gram-Schmidt Method, p. 202 Dalquist
140  * QR factorization of a matrix without row pivoting.
141  */
142  jl = jr;
143  /*
144  * Fill in the row for the current element, k, under consideration
145  * The row will contain the Formula matrix value for that element
146  * from the current component.
147  */
148  for (j = 0; j < ncomponents; ++j) {
149  sm[j + jr*ncomponents] = m_formulaMatrix[k][j];
150  }
151  if (jl > 0) {
152  /*
153  * Compute the coefficients of JA column of the
154  * the upper triangular R matrix, SS(J) = R_J_JR
155  * (this is slightly different than Dalquist)
156  * R_JA_JA = 1
157  */
158  for (j = 0; j < jl; ++j) {
159  ss[j] = 0.0;
160  for (i = 0; i < ncomponents; ++i) {
161  ss[j] += sm[i + jr*ncomponents] * sm[i + j*ncomponents];
162  }
163  ss[j] /= sa[j];
164  }
165  /*
166  * Now make the new column, (*,JR), orthogonal to the
167  * previous columns
168  */
169  for (j = 0; j < jl; ++j) {
170  for (l = 0; l < ncomponents; ++l) {
171  sm[l + jr*ncomponents] -= ss[j] * sm[l + j*ncomponents];
172  }
173  }
174  }
175 
176  /*
177  * Find the new length of the new column in Q.
178  * It will be used in the denominator in future row calcs.
179  */
180  sa[jr] = 0.0;
181  for (ml = 0; ml < ncomponents; ++ml) {
182  sa[jr] += SQUARE(sm[ml + jr*ncomponents]);
183  }
184  /* **************************************************** */
185  /* **** IF NORM OF NEW ROW .LT. 1E-6 REJECT ********** */
186  /* **************************************************** */
187  if (sa[jr] < 1.0e-6) {
188  lindep = true;
189  } else {
190  lindep = false;
191  }
192  } while (lindep);
193  /* ****************************************** */
194  /* **** REARRANGE THE DATA ****************** */
195  /* ****************************************** */
196  if (jr != k) {
197 #ifdef DEBUG_MODE
198  if (m_debug_print_lvl >= 2) {
199  plogf(" --- ");
200  plogf("%-2.2s", (m_elementName[k]).c_str());
201  plogf("(%9.2g) replaces ", m_elemAbundancesGoal[k]);
202  plogf("%-2.2s", (m_elementName[jr]).c_str());
203  plogf("(%9.2g) as element %3d", m_elemAbundancesGoal[jr], jr);
204  plogendl();
205  }
206 #endif
207  vcs_switch_elem_pos(jr, k);
208  std::swap(aw[jr], aw[k]);
209  }
210 
211  /*
212  * If we haven't found enough components, go back
213  * and find some more. (nc -1 is used below, because
214  * jr is counted from 0, via the C convention.
215  */
216  } while (jr < (ncomponents-1));
217  return VCS_SUCCESS;
218 }
219 
220 // Swaps the indices for all of the global data for two elements, ipos
221 // and jpos.
222 /*
223  * This function knows all of the element information with VCS_SOLVE, and
224  * can therefore switch element positions
225  *
226  * @param ipos first global element index
227  * @param jpos second global element index
228  */
229 void VCS_SOLVE::vcs_switch_elem_pos(size_t ipos, size_t jpos)
230 {
231  if (ipos == jpos) {
232  return;
233  }
234  size_t j;
235  vcs_VolPhase* volPhase;
236 #ifdef DEBUG_MODE
237  if (ipos > (m_numElemConstraints - 1) ||
238  jpos > (m_numElemConstraints - 1)) {
239  plogf("vcs_switch_elem_pos: ifunc = 0: inappropriate args: %d %d\n",
240  ipos, jpos);
241  plogendl();
242  exit(EXIT_FAILURE);
243  }
244 #endif
245  /*
246  * Change the element Global Index list in each vcs_VolPhase object
247  * to reflect the switch in the element positions.
248  */
249  for (size_t iph = 0; iph < m_numPhases; iph++) {
250  volPhase = m_VolPhaseList[iph];
251  for (size_t e = 0; e < volPhase->nElemConstraints(); e++) {
252  if (volPhase->elemGlobalIndex(e) == ipos) {
253  volPhase->setElemGlobalIndex(e, jpos);
254  }
255  if (volPhase->elemGlobalIndex(e) == jpos) {
256  volPhase->setElemGlobalIndex(e, ipos);
257  }
258  }
259  }
260  std::swap(m_elemAbundancesGoal[ipos], m_elemAbundancesGoal[jpos]);
261  std::swap(m_elemAbundances[ipos], m_elemAbundances[jpos]);
262  std::swap(m_elementMapIndex[ipos], m_elementMapIndex[jpos]);
263  std::swap(m_elType[ipos], m_elType[jpos]);
264  std::swap(m_elementActive[ipos], m_elementActive[jpos]);
265  for (j = 0; j < m_numSpeciesTot; ++j) {
266  std::swap(m_formulaMatrix[ipos][j], m_formulaMatrix[jpos][j]);
267  }
268  std::swap(m_elementName[ipos], m_elementName[jpos]);
269 }
270 }
271