PartMC  2.8.0
condense_solver.c
Go to the documentation of this file.
1 /* Copyright (C) 2009-2012 Matthew West
2  * Licensed under the GNU General Public License version 2 or (at your
3  * option) any later version. See the file COPYING for details.
4  */
5 
6 /** \file
7  * \brief Interface to SUNDIALS ODE solver library for condensation.
8  */
9 
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <cvode/cvode.h>
13 #include <nvector/nvector_serial.h>
14 #include <sundials/sundials_types.h>
15 #include <sunlinsol/sunlinsol_spgmr.h>
16 
17 /** \brief Result code indicating successful completion.
18  */
19 #define PMC_CONDENSE_SOLVER_SUCCESS 0
20 /** \brief Result code indicating failure to allocate \c y vector.
21  */
22 #define PMC_CONDENSE_SOLVER_INIT_Y 1
23 /** \brief Result code indicating failure to allocate \c abstol vector.
24  */
25 #define PMC_CONDENSE_SOLVER_INIT_ABSTOL 2
26 /** \brief Result code indicating failure to create the solver.
27  */
28 #define PMC_CONDENSE_SOLVER_INIT_CVODE_MEM 3
29 /** \brief Result code indicating failure to initialize the solver.
30  */
31 #define PMC_CONDENSE_SOLVER_INIT_CVODE 4
32 /** \brief Result code indicating failure to set tolerances.
33  */
34 #define PMC_CONDENSE_SOLVER_SVTOL 5
35 /** \brief Result code indicating failure to set maximum steps.
36  */
37 #define PMC_CONDENSE_SOLVER_SET_MAX_STEPS 6
38 /** \brief Result code indicating failure of the solver.
39  */
40 #define PMC_CONDENSE_SOLVER_FAIL 7
41 /** \brief Result code indicating failure in creation of the linear solver.
42  */
43 #define PMC_CONDENSE_SOLVER_LINSOL_CTOR 8
44 /** \brief Result code indicating failure in setting the linear solver.
45  */
46 #define PMC_CONDENSE_SOLVER_LINSOL_SET 9
47 /** \brief Result code indicating failure in setting the preconditioner.
48  */
49 #define PMC_CONDENSE_SOLVER_LINSOL_PREC 10
50 /** \brief Result code indicating failure to allocate the SUNDIALS Context.
51  */
52 #define PMC_CONDENSE_SOLVER_INIT_SUNDIALS 11
53 
54 static int condense_vf(realtype t, N_Vector y, N_Vector ydot, void *user_data);
55 
56 static int condense_check_flag(void *flagvalue, char *funcname, int opt);
57 
58 /*******************************************************/
59 // solver block
60 
61 static int condense_solver_Solve(double t, N_Vector ycur, N_Vector fcur,
62  N_Vector r, N_Vector b, double gamma,
63  double delta, int lr, void *user_data);
64 
65 /*******************************************************/
66 
67 void condense_vf_f(int neq, realtype t, double *y_f, double *ydot_f);
68 void condense_jac_solve_f(int neq, double t, double *ycur_f, double *fcur_f,
69  double *b_f, double gamma);
70 
71 /** \brief Call the ODE solver.
72  *
73  * \param neq The number of equations.
74  * \param x_f A pointer to a vector of \c neq variables, giving the
75  * initial state vector on entry and the final state vector on exit.
76  * \param abstol_f A pointer to a vector of \c neq variables, giving
77  * the absolute error tolerance for the corresponding state vector
78  * component.
79  * \param reltol_f The scalar relative tolerance.
80  * \param t_initial_f The initial time (s).
81  * \param t_final_f The final time (s).
82  * \return A result code (0 is success).
83  */
84 int condense_solver(int neq, double *x_f, double *abstol_f, double reltol_f,
85  double t_initial_f, double t_final_f)
86 {
87  realtype reltol, t_initial, t_final, t;
88  N_Vector y, abstol;
89  void *cvode_mem;
90  int flag, i;
91  realtype *y_data, *abstol_data;
92 
93  y = abstol = NULL;
94  cvode_mem = NULL;
95 
96 #if SUNDIALS_VERSION_MAJOR >= 6
97  SUNContext sunctx = NULL;
98  flag = SUNContext_Create(NULL, &sunctx);
99  if (condense_check_flag(&flag, "SUNContext_Create", 1))
101 #endif
102 
103 #if SUNDIALS_VERSION_MAJOR >= 6
104  y = N_VNew_Serial(neq, sunctx);
105 #else
106  y = N_VNew_Serial(neq);
107 #endif
108  if (condense_check_flag((void *)y, "N_VNew_Serial", 0))
110 
111 #if SUNDIALS_VERSION_MAJOR >= 6
112  abstol = N_VNew_Serial(neq, sunctx);
113 #else
114  abstol = N_VNew_Serial(neq);
115 #endif
116  if (condense_check_flag((void *)abstol, "N_VNew_Serial", 0))
118 
119  y_data = NV_DATA_S(y);
120  abstol_data = NV_DATA_S(abstol);
121  for (i = 0; i < neq; i++) {
122  y_data[i] = x_f[i];
123  abstol_data[i] = abstol_f[i];
124  }
125 
126  reltol = reltol_f;
127  t_initial = t_initial_f;
128  t_final = t_final_f;
129 
130 #if SUNDIALS_VERSION_MAJOR >= 6
131  cvode_mem = CVodeCreate(CV_BDF, sunctx);
132 #else
133  cvode_mem = CVodeCreate(CV_BDF);
134 #endif
135  if (condense_check_flag((void *)cvode_mem, "CVodeCreate", 0))
137 
138  flag = CVodeInit(cvode_mem, condense_vf, t_initial, y);
139  if (condense_check_flag(&flag, "CVodeInit", 1))
141 
142  flag = CVodeSVtolerances(cvode_mem, reltol, abstol);
143  if (condense_check_flag(&flag, "CVodeSVtolerances", 1))
145 
146  flag = CVodeSetMaxNumSteps(cvode_mem, 100000);
147  if (condense_check_flag(&flag, "CVodeSetMaxNumSteps", 1))
149 
150 
151 #if SUNDIALS_VERSION_MAJOR >= 6
152  SUNLinearSolver LS = SUNLinSol_SPGMR(y, PREC_LEFT, 0, sunctx);
153 #else
154  SUNLinearSolver LS = SUNLinSol_SPGMR(y, PREC_LEFT, 0);
155 #endif
156  if (condense_check_flag((void *)LS, "SUNLinSol_SPGMR", 0))
158  flag = CVodeSetLinearSolver(cvode_mem, LS, NULL);
159  if (condense_check_flag(&flag, "CVodeSetLinearSolver", 1))
161  flag = CVodeSetPreconditioner(cvode_mem, NULL, condense_solver_Solve);
162  if (condense_check_flag(&flag, "CVodeSetPreconditioner", 1))
164 
165  t = t_initial;
166  flag = CVode(cvode_mem, t_final, y, &t, CV_NORMAL);
167  if (condense_check_flag(&flag, "CVode", 1))
169 
170  for (i = 0; i < neq; i++) {
171  x_f[i] = y_data[i];
172  }
173 
174  N_VDestroy_Serial(y);
175  N_VDestroy_Serial(abstol);
176  CVodeFree(&cvode_mem);
177 #if SUNDIALS_VERSION_MAJOR >= 6
178  SUNContext_Free(&sunctx);
179 #endif
181 }
182 
183 /** \brief The ODE vector field to integrate.
184  *
185  * \param t The current time (s).
186  * \param y The state vector.
187  * \param ydot The rate of change of the state vector.
188  * \param user_data A pointer to user-provided data.
189  * \return A result code (0 is success).
190  */
191 #pragma GCC diagnostic push
192 #pragma GCC diagnostic ignored "-Wunused-parameter"
193 static int condense_vf(realtype t, N_Vector y, N_Vector ydot, void *user_data)
194 {
195  realtype *y_data, *ydot_data;
196  int i, neq;
197  double *y_f, *ydot_f;
198 
199  neq = NV_LENGTH_S(y);
200  y_data = NV_DATA_S(y);
201  ydot_data = NV_DATA_S(ydot);
202 
203  y_f = malloc(neq * sizeof(double));
204  ydot_f = malloc(neq * sizeof(double));
205 
206  for (i = 0; i < neq; i++) {
207  y_f[i] = y_data[i];
208  }
209  condense_vf_f(neq, t, y_f, ydot_f);
210  for (i = 0; i < neq; i++) {
211  ydot_data[i] = ydot_f[i];
212  }
213 
214  free(y_f);
215  free(ydot_f);
216  return(0);
217 }
218 #pragma GCC diagnostic pop
219 
220 /** \brief Check the return value from a SUNDIALS call.
221  *
222  * - <code>opt == 0</code> means SUNDIALS function allocates memory
223  * so check if \c flagvalue is not a \c NULL pointer.
224 
225  * - <code>opt == 1</code> means SUNDIALS function returns a flag so
226  * check if the \c int pointed to by \c flagvalue has
227  * <code>flag >= 0</code>.
228 
229  * - <code>opt == 2</code> means function allocates memory so check
230  * if \c flagvalue is not a \c NULL pointer.
231  *
232  * \param flagvalue A pointer to check (either for \c NULL, or as an
233  * \c int pointer giving the flag value).
234  * \param funcname A string giving the function name returning this
235  * result code.
236  * \param opt A flag indicating the type of check to perform.
237  * \return A result code (0 is success).
238  */
239 static int condense_check_flag(void *flagvalue, char *funcname, int opt)
240 {
241  int *errflag;
242 
243  /* Check if SUNDIALS function returned NULL pointer - no memory allocated */
244  if (opt == 0 && flagvalue == NULL) {
245  fprintf(stderr,
246  "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n",
247  funcname);
248  return(1); }
249 
250  /* Check if flag < 0 */
251  else if (opt == 1) {
252  errflag = (int *) flagvalue;
253  if (*errflag < 0) {
254  fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed with flag = %d\n\n",
255  funcname, *errflag);
256  return(1); }}
257 
258  /* Check if function returned NULL pointer - no memory allocated */
259  else if (opt == 2 && flagvalue == NULL) {
260  fprintf(stderr, "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n",
261  funcname);
262  return(1); }
263 
264  return(0);
265 }
266 
267 /** \brief Linear solver routine for use by the ODE solver.
268  *
269  * Should solve the system \f$(I - \gamma J) x = b\f$, where \f$J\f$
270  * is the current vector field Jacobian, \f$\gamma\f$ is a given
271  * scalar, and \f$b\f$ is a given vector.
272  *
273  */
274 #pragma GCC diagnostic push
275 #pragma GCC diagnostic ignored "-Wunused-parameter"
276 static int condense_solver_Solve(double t, N_Vector ycur, N_Vector fcur,
277  N_Vector b, N_Vector z, double gamma,
278  double delta, int lr, void *user_data)
279 {
280  realtype *b_data, *ycur_data, *fcur_data, *z_data;
281  int i, neq;
282  double *b_f, *ycur_f, *fcur_f;
283 
284  neq = NV_LENGTH_S(b);
285  b_data = NV_DATA_S(b);
286  z_data = NV_DATA_S(z);
287  ycur_data = NV_DATA_S(ycur);
288  fcur_data = NV_DATA_S(fcur);
289 
290  b_f = malloc(neq * sizeof(double));
291  ycur_f = malloc(neq * sizeof(double));
292  fcur_f = malloc(neq * sizeof(double));
293 
294  for (i = 0; i < neq; i++) {
295  b_f[i] = b_data[i];
296  ycur_f[i] = ycur_data[i];
297  fcur_f[i] = fcur_data[i];
298  }
299  condense_jac_solve_f(neq, t, ycur_f, fcur_f, b_f, gamma);
300  for (i = 0; i < neq; i++) {
301  z_data[i] = b_f[i];
302  }
303 
304  free(b_f);
305  free(ycur_f);
306  free(fcur_f);
307  return(0);
308 }
309 #pragma GCC diagnostic pop
PMC_CONDENSE_SOLVER_INIT_Y
#define PMC_CONDENSE_SOLVER_INIT_Y
Result code indicating failure to allocate y vector.
Definition: condense_solver.c:22
condense_jac_solve_f
void condense_jac_solve_f(int neq, double t, double *ycur_f, double *fcur_f, double *b_f, double gamma)
PMC_CONDENSE_SOLVER_LINSOL_PREC
#define PMC_CONDENSE_SOLVER_LINSOL_PREC
Result code indicating failure in setting the preconditioner.
Definition: condense_solver.c:49
PMC_CONDENSE_SOLVER_SET_MAX_STEPS
#define PMC_CONDENSE_SOLVER_SET_MAX_STEPS
Result code indicating failure to set maximum steps.
Definition: condense_solver.c:37
condense_vf
static int condense_vf(realtype t, N_Vector y, N_Vector ydot, void *user_data)
The ODE vector field to integrate.
Definition: condense_solver.c:193
PMC_CONDENSE_SOLVER_SVTOL
#define PMC_CONDENSE_SOLVER_SVTOL
Result code indicating failure to set tolerances.
Definition: condense_solver.c:34
PMC_CONDENSE_SOLVER_SUCCESS
#define PMC_CONDENSE_SOLVER_SUCCESS
Result code indicating successful completion.
Definition: condense_solver.c:19
PMC_CONDENSE_SOLVER_LINSOL_CTOR
#define PMC_CONDENSE_SOLVER_LINSOL_CTOR
Result code indicating failure in creation of the linear solver.
Definition: condense_solver.c:43
condense_solver
int condense_solver(int neq, double *x_f, double *abstol_f, double reltol_f, double t_initial_f, double t_final_f)
Call the ODE solver.
Definition: condense_solver.c:84
PMC_CONDENSE_SOLVER_LINSOL_SET
#define PMC_CONDENSE_SOLVER_LINSOL_SET
Result code indicating failure in setting the linear solver.
Definition: condense_solver.c:46
PMC_CONDENSE_SOLVER_INIT_CVODE_MEM
#define PMC_CONDENSE_SOLVER_INIT_CVODE_MEM
Result code indicating failure to create the solver.
Definition: condense_solver.c:28
PMC_CONDENSE_SOLVER_INIT_CVODE
#define PMC_CONDENSE_SOLVER_INIT_CVODE
Result code indicating failure to initialize the solver.
Definition: condense_solver.c:31
PMC_CONDENSE_SOLVER_INIT_SUNDIALS
#define PMC_CONDENSE_SOLVER_INIT_SUNDIALS
Result code indicating failure to allocate the SUNDIALS Context.
Definition: condense_solver.c:52
condense_check_flag
static int condense_check_flag(void *flagvalue, char *funcname, int opt)
Check the return value from a SUNDIALS call.
Definition: condense_solver.c:239
condense_solver_Solve
static int condense_solver_Solve(double t, N_Vector ycur, N_Vector fcur, N_Vector r, N_Vector b, double gamma, double delta, int lr, void *user_data)
Linear solver routine for use by the ODE solver.
Definition: condense_solver.c:276
PMC_CONDENSE_SOLVER_INIT_ABSTOL
#define PMC_CONDENSE_SOLVER_INIT_ABSTOL
Result code indicating failure to allocate abstol vector.
Definition: condense_solver.c:25
condense_vf_f
void condense_vf_f(int neq, realtype t, double *y_f, double *ydot_f)
PMC_CONDENSE_SOLVER_FAIL
#define PMC_CONDENSE_SOLVER_FAIL
Result code indicating failure of the solver.
Definition: condense_solver.c:40