MOOSE - Multiscale Object Oriented Simulation Environment
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
NonlinearSystem.h
Go to the documentation of this file.
1 /*
2  * =====================================================================================
3  *
4  * Description: Compute root of a multi-dimensional system using boost
5  * libraries.
6  *
7  * Version: 1.0
8  * Created: 04/13/2016 11:31:37 AM
9  * Revision: none
10  * Compiler: gcc
11  *
12  * Author: Dilawar Singh (), dilawars@ncbs.res.in
13  * Organization: NCBS Bangalore
14  *
15  * =====================================================================================
16  */
17 
18 #ifndef NonlinearSystem_INC
19 #define NonlinearSystem_INC
20 
21 #include <iostream>
22 #include <sstream>
23 #include <functional>
24 #include <cerrno>
25 #include <iomanip>
26 #include <limits>
27 #include <algorithm>
28 
29 // Boost ublas library of matrix algebra.
30 #include <boost/numeric/ublas/matrix.hpp>
31 #include <boost/numeric/ublas/lu.hpp>
32 #include <boost/numeric/ublas/vector.hpp>
33 #include <boost/numeric/ublas/io.hpp>
34 
35 
36 #include "VoxelPools.h"
37 
38 using namespace std;
39 using namespace boost::numeric;
40 
41 typedef double value_type;
42 typedef ublas::vector<value_type> vector_type;
43 typedef ublas::matrix<value_type> matrix_type;
44 
45 
46 class ReacInfo
47 {
48  public:
49  int rank;
50  int num_reacs;
51  size_t num_mols;
52  int nIter;
54  double* T;
56  vector< double > nVec;
57  ublas::matrix< value_type > Nr;
58  ublas::matrix< value_type > gamma;
59 };
60 
61 
62 /* Matrix inversion routine.
63  Uses lu_factorize and lu_substitute in uBLAS to invert a matrix */
64  template<class T>
65 bool inverse(const ublas::matrix<T>& input, ublas::matrix<T>& inverse)
66 {
67  using namespace boost::numeric::ublas;
68  typedef permutation_matrix<std::size_t> pmatrix;
69  // create a working copy of the input
70  matrix<T> A(input);
71  // create a permutation matrix for the LU-factorization
72  pmatrix pm(A.size1());
73 
74  // perform LU-factorization
75  int res = lu_factorize(A,pm);
76  if( res != 0 ) return false;
77 
78  // create identity matrix of "inverse"
79  inverse.assign(ublas::identity_matrix<T>(A.size1()));
80 
81  // backsubstitute to get the inverse
82  lu_substitute(A, pm, inverse);
83 
84  return true;
85 }
86 
87 // A sysmte of non-linear equations. Store the values in result.
89 {
90 
91 public:
92 
93  NonlinearSystem( size_t systemSize ) : size_( systemSize )
94  {
95  f_.resize( size_, 0);
96  slopes_.resize( size_, 0);
97  x_.resize( size_, 0 );
98 
99  J_.resize( size_, size_, 0);
100  invJ_.resize( size_, size_, 0);
101 
102  x2.resize( size_, 0);
103  x1.resize( size_, 0);
104 
105  ri.nVec.resize( size_ );
106  dx_ = sqrt( numeric_limits<double>::epsilon() );
107  }
108 
110  {
111  vector_type result( size_ );
112  system(x, result);
113  return result;
114  }
115 
116  int apply( )
117  {
118  return system(x_, f_);
119  }
120 
121  int compute_jacobians( const vector_type& x, bool compute_inverse = true )
122  {
123  for( size_t i = 0; i < size_; i++)
124  for( size_t j = 0; j < size_; j++)
125  {
126  vector_type temp = x;
127  temp[j] += dx_;
128  J_(i, j) = (compute_at(temp)[i] - compute_at(x)[i]) / dx_;
129  }
130 
131  // is_jacobian_valid_ = true;
132  // Keep the inverted J_ ready
133  //if(is_jacobian_valid_ and compute_inverse )
134  if( compute_inverse )
135  inverse( J_, invJ_ );
136 
137  return 0;
138  }
139 
140  template<typename T>
141  void initialize( const T& x )
142  {
144  init.resize(size_, 0);
145 
146  for( size_t i = 0; i < size_; i++)
147  init[i] = x[i];
148 
149  x_ = init;
150  apply();
151 
152  compute_jacobians( init );
153  }
154 
155  string to_string( )
156  {
157  stringstream ss;
158 
159  ss << "=======================================================";
160  ss << endl << setw(25) << "State of system: " ;
161  ss << " Argument: " << x_ << " Value : " << f_;
162  ss << endl << setw(25) << "Jacobian: " << J_;
163  ss << endl << setw(25) << "Inverse Jacobian: " << invJ_;
164  ss << endl;
165  return ss.str();
166  }
167 
168  int system( const vector_type& x, vector_type& f )
169  {
170  int num_consv = ri.num_mols - ri.rank;
171  for ( size_t i = 0; i < ri.num_mols; ++i )
172  {
173  double temp = x[i] * x[i] ;
174 
175 #if 0
176  // if overflow
177  if ( std::isnan( temp ) or std::isinf( temp ) )
178  {
179  cerr << "Failed: ";
180  for( auto v : ri.nVec ) cerr << v << ", ";
181  cerr << endl;
182  return -1;
183  }
184 #endif
185  ri.nVec[i] = temp;
186  }
187 
188  vector< double > vels;
189  ri.pool->updateReacVelocities( &ri.nVec[0], vels );
190 
191  assert( vels.size() == static_cast< unsigned int >( ri.num_reacs ) );
192 
193  // y = Nr . v
194  // Note that Nr is row-echelon: diagonal and above.
195  for ( int i = 0; i < ri.rank; ++i )
196  {
197  double temp = 0;
198  for ( int j = i; j < ri.num_reacs; ++j )
199  temp += ri.Nr(i, j ) * vels[j];
200  f[i] = temp ;
201  }
202 
203  // dT = gamma.S - T
204  for ( int i = 0; i < num_consv; ++i )
205  {
206  double dT = - ri.T[i];
207  for ( size_t j = 0; j < ri.num_mols; ++j )
208  dT += ri.gamma(i, j) * x[j] * x[j];
209 
210  f[ i + ri.rank] = dT ;
211  }
212  return 0;
213  }
214 
215 
225  bool find_roots_gnewton( double tolerance = 1e-7 , size_t max_iter = 50)
226  {
227  //tolerance = sqrt( numeric_limits<double>::epsilon() );
228  double norm2OfDiff = 1.0;
229  size_t iter = 0;
230  int status = apply();
231 
232  while( ublas::norm_2(f_) >= tolerance )
233  {
234  iter += 1;
235  compute_jacobians( x_, true );
236  vector_type correction = ublas::prod( invJ_, f_ );
237  x_ -= correction;
238 
239  // If could not compute the value of system successfully.
240  status = apply();
241  if( 0 != status )
242  return false;
243 
244  if( iter >= max_iter )
245  break;
246 
247  }
248 
249  ri.nIter = iter;
250 
251  if( iter >= max_iter )
252  return false;
253 
254  return true;
255  }
256 
264  value_type slope( unsigned int which_dimen )
265  {
266  vector_type x = x_;
267  x[which_dimen] += dx_;
268  // x1 and x2 holds the f_ of system at x_ and x (which is x +
269  // some step)
270  system( x_, x1 );
271  system( x, x2 );
272  return ublas::norm_2( (x2 - x1)/dx_ );
273  }
274 
275 
280  {
281  // Get the jacobian at current point. Notice that in this method, we
282  // don't have to compute inverse of jacobian
283 
284  vector_type direction( size_ );
285 
286  // Now take the largest step possible such that the value of system at
287  // (x_ - step ) is lower than the value of system as x_.
288  vector_type nextState( size_ );
289 
290  apply();
291 
292  unsigned int i = 0;
293 
294  double factor = 1e-2;
295  while( true )
296  {
297  i += 1;
298  compute_jacobians( x_, false );
299  // Make a move in either side of direction. In whichever direction
300  // the function decreases.
301  direction = ublas::prod( J_, f_ );
302  nextState = x_ - factor * direction;
303  if( ublas::norm_2( compute_at( nextState ) ) >= ublas::norm_2(compute_at(x_)))
304  factor = factor / 2.0;
305  else
306  {
307  cerr << "Correction term applied ";
308  x_ = nextState;
309  apply();
310  break;
311  }
312 
313  if ( i > 20 )
314  break;
315  }
316  }
317 
318 public:
319  const size_t size_;
320 
321  double dx_;
322 
328 
331 
332  // These vector keeps the temporary state computation.
334 
336 };
337 
338 #endif /* ----- #ifndef NonlinearSystem_INC ----- */
bool inverse(const ublas::matrix< T > &input, ublas::matrix< T > &inverse)
Id init(int argc, char **argv, bool &doUnitTests, bool &doRegressionTests, unsigned int &benchmark)
Definition: main.cpp:150
int system(const vector_type &x, vector_type &f)
vector_type compute_at(const vector_type &x)
double value_type
NonlinearSystem(size_t systemSize)
ublas::vector< value_type > vector_type
int compute_jacobians(const vector_type &x, bool compute_inverse=true)
size_t num_mols
const size_t size_
vector_type slopes_
VoxelPools * pool
ublas::matrix< value_type > gamma
vector< double > nVec
value_type slope(unsigned int which_dimen)
Compute the slope of function in given dimension.
ublas::matrix< value_type > matrix_type
ublas::matrix< value_type > Nr
double convergenceCriterion
matrix_type invJ_
double * T
void initialize(const T &x)
bool find_roots_gnewton(double tolerance=1e-7, size_t max_iter=50)
Find roots using Newton-Raphson method.
void correction_step()
Makes the first guess. After this call the Newton method.