MOOSE - Multiscale Object Oriented Simulation Environment
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
HinesMatrix.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 ** This program is part of 'MOOSE', the
3 ** Messaging Object Oriented Simulation Environment.
4 ** copyright (C) 2003-2007 Upinder S. Bhalla, Niraj Dudani and NCBS
5 ** It is made available under the terms of the
6 ** GNU Lesser General Public License version 2.1
7 ** See the file COPYING.LIB for the full notice.
8 **********************************************************************/
9 
10 #include "header.h"
11 #include "HinesMatrix.h"
12 #include <sstream>
13 #include <iomanip>
14 #include <stdexcept>
15 
17  :
18  nCompt_( 0 ),
19  dt_( 0.0 ),
20  stage_( -1 )
21 {
22  ;
23 }
24 
25 void HinesMatrix::setup( const vector< TreeNodeStruct >& tree, double dt )
26 {
27  clear();
28 
29  nCompt_ = tree.size();
30 
31 #if SANITY_CHECK
32  stringstream ss;
33  if(nCompt_ <= 0)
34  {
35  ss << "Horror, horror! Trying to create a matrix with size " << nCompt_
36  << endl;
37  dump(ss.str(), "ERROR");
38  throw range_error("Expected greater than 0.");
39  }
40 #endif /* ----- not STRICT_CHECK ----- */
41 
42  dt_ = dt;
43  tree_ = &tree;
44 
45  for ( unsigned int i = 0; i < nCompt_; i++ )
46  Ga_.push_back( 2.0 / tree[ i ].Ra );
47 
48  makeJunctions();
49  makeMatrix();
50  makeOperands();
51 }
52 
54 {
55  nCompt_ = 0;
56  dt_ = 0.0;
57  junction_.clear();
58  HS_.clear();
59  HJ_.clear();
60  HJCopy_.clear();
61  VMid_.clear();
62  operand_.clear();
63  backOperand_.clear();
64  stage_ = 0;
65 
66  tree_ = 0;
67  Ga_.clear();
68  coupled_.clear();
69  operandBase_.clear();
70  groupNumber_.clear();
71 }
72 
74  const vector< unsigned int >& A,
75  const vector< unsigned int >& B )
76 {
77  if ( A.empty() || B.empty() )
78  return 0;
79 
80  return A[ 0 ] < B[ 0 ];
81 }
82 
83 // Stage 3
85 {
86  // 3.1
87  for ( unsigned int i = 0; i < nCompt_; ++i )
88  {
89  const vector< unsigned int >& c = ( *tree_ )[ i ].children;
90 
91  if ( c.size() == 0 )
92  continue;
93 
94  if ( c.size() == 1 )
95  {
96  int diff = ( int )( c[ 0 ] ) - i;
97 
98  if ( diff == 1 || diff == -1 )
99  continue;
100  }
101 
102  // "coupled" contains a list of all children..
103  coupled_.push_back( c );
104  // ..and the parent compartment itself.
105  coupled_.back().push_back( i );
106  }
107 
108  // 3.2
109  vector< vector< unsigned int > >::iterator group;
110  for ( group = coupled_.begin(); group != coupled_.end(); ++group )
111  sort( group->begin(), group->end() );
112 
113  sort( coupled_.begin(), coupled_.end(), groupCompare );
114 
115  // 3.3
116  unsigned int index;
117  unsigned int rank;
118  for ( group = coupled_.begin(); group != coupled_.end(); ++group )
119  // Loop uptil penultimate compartment in group
120  for ( unsigned int c = 0; c < group->size() - 1; ++c )
121  {
122  index = ( *group )[ c ];
123  rank = group->size() - c - 1;
124  junction_.push_back( JunctionStruct( index, rank ) );
125 
126  groupNumber_[ index ] = group - coupled_.begin();
127  }
128 
129  sort( junction_.begin(), junction_.end() );
130 }
131 
132 // Stage 4
134 {
135  const vector< TreeNodeStruct >& node = *tree_;
136 
137  // Setting up HS
138  HS_.resize( 4 * nCompt_, 0.0 );
139  for ( unsigned int i = 0; i < nCompt_; ++i )
140  HS_[ 4 * i + 2 ] =
141  node[ i ].Cm / ( dt_ / 2.0 ) +
142  1.0 / node[ i ].Rm;
143 
144  double gi, gj, gij;
145  vector< JunctionStruct >::iterator junction = junction_.begin();
146  for ( unsigned int i = 0; i < nCompt_ - 1; ++i )
147  {
148  if ( !junction_.empty() &&
149  junction < junction_.end() &&
150  i == junction->index )
151  {
152  ++junction;
153  continue;
154  }
155 
156  gi = Ga_[ i ];
157  gj = Ga_[ i + 1 ];
158  gij = gi * gj / ( gi + gj );
159 
160  HS_[ 4 * i + 1 ] = -gij;
161  HS_[ 4 * i + 2 ] += gij;
162  HS_[ 4 * i + 6 ] += gij;
163  }
164 
165  vector< vector< unsigned int > >::iterator group;
166  vector< unsigned int >::iterator i;
167  for ( group = coupled_.begin(); group != coupled_.end(); ++group )
168  {
169  double gsum = 0.0;
170 
171  for ( i = group->begin(); i != group->end(); ++i )
172  gsum += Ga_[ *i ];
173 
174  for ( i = group->begin(); i != group->end(); ++i )
175  {
176  gi = Ga_[ *i ];
177 
178  HS_[ 4 * *i + 2 ] += gi * ( 1.0 - gi / gsum );
179  }
180  }
181 
182  // Setting up HJ
183  vector< unsigned int >::iterator j;
184  unsigned int size = 0;
185  unsigned int rank;
186  for ( group = coupled_.begin(); group != coupled_.end(); ++group )
187  {
188  rank = group->size() - 1;
189  size += rank * ( rank + 1 );
190  }
191 
192  HJ_.reserve( size );
193 
194  for ( group = coupled_.begin(); group != coupled_.end(); ++group )
195  {
196  double gsum = 0.0;
197 
198  for ( i = group->begin(); i != group->end(); ++i )
199  gsum += Ga_[ *i ];
200 
201  for ( i = group->begin(); i != group->end() - 1; ++i )
202  {
203  int base = HJ_.size();
204 
205  for ( j = i + 1; j != group->end(); ++j )
206  {
207  gij = Ga_[ *i ] * Ga_[ *j ] / gsum;
208 
209  HJ_.push_back( -gij );
210  HJ_.push_back( -gij );
211  }
212 
213  //~ operandBase_[ *i ] = &HJ_[ base ];
214  operandBase_[ *i ] = HJ_.begin() + base;
215  }
216  }
217 
218  // Copy diagonal elements into their final locations
219  for ( unsigned int i = 0; i < nCompt_; ++i )
220  HS_[ 4 * i ] = HS_[ 4 * i + 2 ];
221  // Create copy of HJ
222  HJCopy_.assign( HJ_.begin(), HJ_.end() );
223 }
224 
225 // Stage 5
227 {
228  unsigned int index;
229  unsigned int rank;
230  unsigned int farIndex;
231  vdIterator base;
232  vector< JunctionStruct >::iterator junction;
233 
234  // Allocate space in VMid. Needed, since we will store pointers to its
235  // elements below.
236  VMid_.resize( nCompt_ );
237 
238  // Operands for forward-elimination
239  for ( junction = junction_.begin(); junction != junction_.end(); ++junction )
240  {
241  index = junction->index;
242  rank = junction->rank;
243  base = operandBase_[ index ];
244 
245  // This is the list of compartments connected at a junction.
246  const vector< unsigned int >& group =
247  coupled_[ groupNumber_[ index ] ];
248 
249  if ( rank == 1 )
250  {
251  operand_.push_back( base );
252 
253  // Select last member.
254  farIndex = group[ group.size() - 1 ];
255  operand_.push_back( HS_.begin() + 4 * farIndex );
256  operand_.push_back( VMid_.begin() + farIndex );
257  }
258  else if ( rank == 2 )
259  {
260  operand_.push_back( base );
261 
262  // Select 2nd last member.
263  farIndex = group[ group.size() - 2 ];
264  operand_.push_back( HS_.begin() + 4 * farIndex );
265  operand_.push_back( VMid_.begin() + farIndex );
266 
267  // Select last member.
268  farIndex = group[ group.size() - 1 ];
269  operand_.push_back( HS_.begin() + 4 * farIndex );
270  operand_.push_back( VMid_.begin() + farIndex );
271  }
272  else
273  {
274  // Operations on diagonal elements and elements from B (as in Ax = B).
275  int start = group.size() - rank;
276  for ( unsigned int j = 0; j < rank; ++j )
277  {
278  farIndex = group[ start + j ];
279 
280  // Diagonal elements
281  operand_.push_back( HS_.begin() + 4 * farIndex );
282  operand_.push_back( base + 2 * j );
283  operand_.push_back( base + 2 * j + 1 );
284 
285  // Elements from B
286  operand_.push_back( HS_.begin() + 4 * farIndex + 3 );
287  operand_.push_back( HS_.begin() + 4 * index + 3 );
288  operand_.push_back( base + 2 * j + 1 );
289  }
290 
291  // Operations on off-diagonal elements.
292  vdIterator left;
293  vdIterator above;
294  vdIterator target;
295 
296  // Upper triangle elements
297  left = base + 1;
298  target = base + 2 * rank;
299  for ( unsigned int i = 1; i < rank; ++i )
300  {
301  above = base + 2 * i;
302  for ( unsigned int j = 0; j < rank - i; ++j )
303  {
304  operand_.push_back( target );
305  operand_.push_back( above );
306  operand_.push_back( left );
307 
308  above += 2;
309  target += 2;
310  }
311  left += 2;
312  }
313 
314  // Lower triangle elements
315  target = base + 2 * rank + 1;
316  above = base;
317  for ( unsigned int i = 1; i < rank; ++i )
318  {
319  left = base + 2 * i + 1;
320  for ( unsigned int j = 0; j < rank - i; ++j )
321  {
322  operand_.push_back( target );
323  operand_.push_back( above );
324  operand_.push_back( left );
325 
326  /*
327  * This check required because the MS VC++ compiler is
328  * paranoid about iterators going out of bounds, even if
329  * they are never used after that.
330  */
331  if ( i == rank - 1 && j == rank - i - 1 )
332  continue;
333 
334  target += 2;
335  left += 2;
336  }
337  above += 2;
338  }
339  }
340  }
341 
342  // Operands for backward substitution
343  for ( junction = junction_.begin(); junction != junction_.end(); ++junction )
344  {
345  if ( junction->rank < 3 )
346  continue;
347 
348  index = junction->index;
349  rank = junction->rank;
350  base = operandBase_[ index ];
351 
352  // This is the list of compartments connected at a junction.
353  const vector< unsigned int >& group =
354  coupled_[ groupNumber_[ index ] ];
355 
356  unsigned int start = group.size() - rank;
357  for ( unsigned int j = 0; j < rank; ++j )
358  {
359  farIndex = group[ start + j ];
360 
361  backOperand_.push_back( base + 2 * j );
362  backOperand_.push_back( VMid_.begin() + farIndex );
363  }
364  }
365 }
366 
368 // Public interface to matrix
370 unsigned int HinesMatrix::getSize() const
371 {
372  return nCompt_;
373 }
374 
375 double HinesMatrix::getA( unsigned int row, unsigned int col ) const
376 {
377  /*
378  * If forward elimination is done, or backward substitution is done, and
379  * if (row, col) is in the lower triangle, then return 0.
380  */
381  if ( ( stage_ == 1 || stage_ == 2 ) && row > col )
382  return 0.0;
383 
384  if ( row >= nCompt_ || col >= nCompt_ )
385  return 0.0;
386 
387  if ( row == col )
388  return HS_[ 4 * row ];
389 
390  unsigned int smaller = row < col ? row : col;
391  unsigned int bigger = row > col ? row : col;
392 
393  if ( groupNumber_.find( smaller ) == groupNumber_.end() )
394  {
395  if ( bigger - smaller == 1 )
396  return HS_[ 4 * smaller + 1 ];
397  else
398  return 0.0;
399  }
400  else
401  {
402  // We could use: groupNumber = groupNumber_[ smaller ], but this is a
403  // const function
404  unsigned int groupNumber = groupNumber_.find( smaller )->second;
405  const vector< unsigned int >& group = coupled_[ groupNumber ];
406  unsigned int location, size;
407  unsigned int smallRank, bigRank;
408 
409  if ( find( group.begin(), group.end(), bigger ) != group.end() )
410  {
411  location = 0;
412  for ( int i = 0; i < static_cast< int >( groupNumber ); ++i )
413  {
414  size = coupled_[ i ].size();
415  location += size * ( size - 1 );
416  }
417 
418  size = group.size();
419  smallRank = group.end() - find( group.begin(), group.end(), smaller ) - 1;
420  bigRank = group.end() - find( group.begin(), group.end(), bigger ) - 1;
421  location += size * ( size - 1 ) - smallRank * ( smallRank + 1 );
422  location += 2 * ( smallRank - bigRank - 1 );
423 
424  if ( row == smaller )
425  return HJ_[ location ];
426  else
427  return HJ_[ location + 1 ];
428  }
429  else
430  {
431  return 0.0;
432  }
433  }
434 }
435 
436 double HinesMatrix::getB( unsigned int row ) const
437 {
438  return HS_[ 4 * row + 3 ];
439 }
440 
441 double HinesMatrix::getVMid( unsigned int row ) const
442 {
443  return VMid_[ row ];
444 }
445 
447 // Inserting into a stream
449 ostream& operator <<( ostream& s, const HinesMatrix& m )
450 {
451  unsigned int size = m.getSize();
452 
453  s << "\nA:\n";
454  for ( unsigned int i = 0; i < size; i++ )
455  {
456  for ( unsigned int j = 0; j < size; j++ )
457  s << setw( 12 ) << setprecision( 5 ) << m.getA( i, j );
458  s << "\n";
459  }
460 
461  s << "\n" << "V:\n";
462  for ( unsigned int i = 0; i < size; i++ )
463  s << m.getVMid( i ) << "\n";
464 
465  s << "\n" << "B:\n";
466  for ( unsigned int i = 0; i < size; i++ )
467  s << m.getB( i ) << "\n";
468 
469  return s;
470 }
471 
473 
474 #ifdef DO_UNIT_TESTS
475 
476 #include "TestHSolve.h"
477 
478 void testHinesMatrix()
479 {
480  vector< int* > childArray;
481  vector< unsigned int > childArraySize;
482 
503  int childArray_1[ ] =
504  {
505  /* c0 */ -1,
506  /* c1 */ -1, 0,
507  /* c2 */ -1, 1,
508  /* c3 */ -1,
509  /* c4 */ -1, 3,
510  /* c5 */ -1,
511  /* c6 */ -1, 5,
512  /* c7 */ -1, 4, 6,
513  /* c8 */ -1, 7,
514  /* c9 */ -1, 8,
515  /* c10 */ -1,
516  /* c11 */ -1, 10,
517  /* c12 */ -1,
518  /* c13 */ -1, 12,
519  /* c14 */ -1, 11, 13,
520  /* c15 */ -1, 14, 16,
521  /* c16 */ -1, 17,
522  /* c17 */ -1, 2, 9, 18,
523  /* c18 */ -1, 19,
524  /* c19 */ -1,
525  };
526 
527  childArray.push_back( childArray_1 );
528  childArraySize.push_back( sizeof( childArray_1 ) / sizeof( int ) );
529 
542  int childArray_2[ ] =
543  {
544  /* c0 */ -1,
545  /* c1 */ -1,
546  /* c2 */ -1, 0, 1, 3,
547  /* c3 */ -1,
548  };
549 
550  childArray.push_back( childArray_2 );
551  childArraySize.push_back( sizeof( childArray_2 ) / sizeof( int ) );
552 
565  int childArray_3[ ] =
566  {
567  /* c0 */ -1, 2,
568  /* c1 */ -1,
569  /* c2 */ -1, 1, 3,
570  /* c3 */ -1,
571  };
572 
573  childArray.push_back( childArray_3 );
574  childArraySize.push_back( sizeof( childArray_3 ) / sizeof( int ) );
575 
588  int childArray_4[ ] =
589  {
590  /* c0 */ -1,
591  /* c1 */ -1,
592  /* c2 */ -1, 0, 1,
593  /* c3 */ -1, 2,
594  };
595 
596  childArray.push_back( childArray_4 );
597  childArraySize.push_back( sizeof( childArray_4 ) / sizeof( int ) );
598 
612  int childArray_5[ ] =
613  {
614  /* c0 */ -1,
615  /* c1 */ -1, 2,
616  /* c2 */ -1, 0, 4,
617  /* c3 */ -1,
618  /* c4 */ -1, 3, 5,
619  /* c5 */ -1,
620  };
621 
622  childArray.push_back( childArray_5 );
623  childArraySize.push_back( sizeof( childArray_5 ) / sizeof( int ) );
624 
638  int childArray_6[ ] =
639  {
640  /* c0 */ -1,
641  /* c1 */ -1,
642  /* c2 */ -1,
643  /* c3 */ -1, 4,
644  /* c4 */ -1, 0, 1, 2, 5, 6,
645  /* c5 */ -1,
646  /* c6 */ -1,
647  };
648 
649  childArray.push_back( childArray_6 );
650  childArraySize.push_back( sizeof( childArray_6 ) / sizeof( int ) );
651 
656  int childArray_7[ ] =
657  {
658  /* c0 */ -1,
659  };
660 
661  childArray.push_back( childArray_7 );
662  childArraySize.push_back( sizeof( childArray_7 ) / sizeof( int ) );
663 
668  int childArray_8[ ] =
669  {
670  /* c0 */ -1,
671  /* c1 */ -1, 0, 2,
672  /* c2 */ -1,
673  };
674 
675  childArray.push_back( childArray_8 );
676  childArraySize.push_back( sizeof( childArray_8 ) / sizeof( int ) );
677 
682  int childArray_9[ ] =
683  {
684  /* c0 */ -1, 1,
685  /* c1 */ -1, 2,
686  /* c2 */ -1,
687  };
688 
689  childArray.push_back( childArray_9 );
690  childArraySize.push_back( sizeof( childArray_9 ) / sizeof( int ) );
691 
693  // Run tests
695  HinesMatrix H;
696  vector< TreeNodeStruct > tree;
697  double dt = 1.0;
698 
699  /*
700  * This is the full reference matrix which will be compared to its sparse
701  * implementation.
702  */
703  vector< vector< double > > matrix;
704 
705  double epsilon = 1e-17;
706  unsigned int i;
707  unsigned int j;
708  unsigned int nCompt;
709  int* array;
710  unsigned int arraySize;
711  for ( unsigned int cell = 0; cell < childArray.size(); cell++ )
712  {
713  array = childArray[ cell ];
714  arraySize = childArraySize[ cell ];
715  nCompt = count( array, array + arraySize, -1 );
716 
717  // Prepare cell
718  tree.clear();
719  tree.resize( nCompt );
720  for ( i = 0; i < nCompt; ++i )
721  {
722  tree[ i ].Ra = 15.0 + 3.0 * i;
723  tree[ i ].Rm = 45.0 + 15.0 * i;
724  tree[ i ].Cm = 500.0 + 200.0 * i * i;
725  }
726 
727  int count = -1;
728  for ( unsigned int a = 0; a < arraySize; a++ )
729  if ( array[ a ] == -1 )
730  count++;
731  else
732  tree[ count ].children.push_back( array[ a ] );
733 
734  // Prepare local matrix
735  makeFullMatrix( tree, dt, matrix );
736 
737  // Prepare sparse matrix
738  H.setup( tree, dt );
739 
740  // Compare matrices
741  for ( i = 0; i < nCompt; ++i )
742  for ( j = 0; j < nCompt; ++j )
743  {
744  ostringstream error;
745  error << "Testing Hines' Matrix: Cell# "
746  << cell + 1 << ", entry (" << i << ", " << j << ")";
747  ASSERT(
748  fabs( matrix[ i ][ j ] - H.getA( i, j ) ) < epsilon,
749  error.str()
750  );
751  }
752  }
753  cout << "." << flush;
754 
755 }
756 
757 #endif // DO_UNIT_TESTS
double getVMid(unsigned int row) const
#define ASSERT(unused, message)
Definition: HinesMatrix.h:22
void makeFullMatrix(const vector< TreeNodeStruct > &tree, double dt, vector< vector< double > > &matrix)
static SrcFinfo0 * group()
Definition: Group.cpp:17
map< unsigned int, unsigned int > groupNumber_
Definition: HinesMatrix.h:124
vector< double > VMid_
middle of a time step.
Definition: HinesMatrix.h:83
void makeMatrix()
void setup(const vector< TreeNodeStruct > &tree, double dt)
Definition: HinesMatrix.cpp:25
bool groupCompare(const vector< unsigned int > &A, const vector< unsigned int > &B)
Definition: HinesMatrix.cpp:73
double getB(unsigned int row) const
unsigned int nCompt_
Definition: HinesMatrix.h:70
vector< vdIterator > backOperand_
Definition: HinesMatrix.h:86
int stage_
reached. Used in getA.
Definition: HinesMatrix.h:87
vector< vdIterator > operand_
Definition: HinesMatrix.h:85
void clear()
Definition: HinesMatrix.cpp:53
vector< double > HJCopy_
Definition: HinesMatrix.h:82
const vector< TreeNodeStruct > * tree_
setup.
Definition: HinesMatrix.h:112
vector< double > Ga_
Definition: HinesMatrix.h:114
vector< double > HS_
Definition: HinesMatrix.h:74
vector< vector< unsigned int > > coupled_
Definition: HinesMatrix.h:115
map< unsigned int, vdIterator > operandBase_
Definition: HinesMatrix.h:121
ostream & operator<<(ostream &s, const HinesMatrix &m)
void makeJunctions()
Definition: HinesMatrix.cpp:84
void makeOperands()
elimination easier.
unsigned int getSize() const
vector< JunctionStruct > junction_
Definition: HinesMatrix.h:73
vector< double > HJ_
Definition: HinesMatrix.h:79
vector< double >::iterator vdIterator
Definition: HinesMatrix.h:68
double getA(unsigned int row, unsigned int col) const
double dt_
Definition: HinesMatrix.h:71