PartMC  2.9.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  SUNLinSolFree(LS);
178 #if SUNDIALS_VERSION_MAJOR >= 6
179  SUNContext_Free(&sunctx);
180 #endif
182 }
183 
184 /** \brief The ODE vector field to integrate.
185  *
186  * \param t The current time (s).
187  * \param y The state vector.
188  * \param ydot The rate of change of the state vector.
189  * \param user_data A pointer to user-provided data.
190  * \return A result code (0 is success).
191  */
192 #pragma GCC diagnostic push
193 #pragma GCC diagnostic ignored "-Wunused-parameter"
194 static int condense_vf(realtype t, N_Vector y, N_Vector ydot, void *user_data)
195 {
196  realtype *y_data, *ydot_data;
197  int i, neq;
198  double *y_f, *ydot_f;
199 
200  neq = NV_LENGTH_S(y);
201  y_data = NV_DATA_S(y);
202  ydot_data = NV_DATA_S(ydot);
203 
204  y_f = malloc(neq * sizeof(double));
205  ydot_f = malloc(neq * sizeof(double));
206 
207  for (i = 0; i < neq; i++) {
208  y_f[i] = y_data[i];
209  }
210  condense_vf_f(neq, t, y_f, ydot_f);
211  for (i = 0; i < neq; i++) {
212  ydot_data[i] = ydot_f[i];
213  }
214 
215  free(y_f);
216  free(ydot_f);
217  return(0);
218 }
219 #pragma GCC diagnostic pop
220 
221 /** \brief Check the return value from a SUNDIALS call.
222  *
223  * - <code>opt == 0</code> means SUNDIALS function allocates memory
224  * so check if \c flagvalue is not a \c NULL pointer.
225 
226  * - <code>opt == 1</code> means SUNDIALS function returns a flag so
227  * check if the \c int pointed to by \c flagvalue has
228  * <code>flag >= 0</code>.
229 
230  * - <code>opt == 2</code> means function allocates memory so check
231  * if \c flagvalue is not a \c NULL pointer.
232  *
233  * \param flagvalue A pointer to check (either for \c NULL, or as an
234  * \c int pointer giving the flag value).
235  * \param funcname A string giving the function name returning this
236  * result code.
237  * \param opt A flag indicating the type of check to perform.
238  * \return A result code (0 is success).
239  */
240 static int condense_check_flag(void *flagvalue, char *funcname, int opt)
241 {
242  int *errflag;
243 
244  /* Check if SUNDIALS function returned NULL pointer - no memory allocated */
245  if (opt == 0 && flagvalue == NULL) {
246  fprintf(stderr,
247  "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n",
248  funcname);
249  return(1); }
250 
251  /* Check if flag < 0 */
252  else if (opt == 1) {
253  errflag = (int *) flagvalue;
254  if (*errflag < 0) {
255  fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed with flag = %d\n\n",
256  funcname, *errflag);
257  return(1); }}
258 
259  /* Check if function returned NULL pointer - no memory allocated */
260  else if (opt == 2 && flagvalue == NULL) {
261  fprintf(stderr, "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n",
262  funcname);
263  return(1); }
264 
265  return(0);
266 }
267 
268 /** \brief Linear solver routine for use by the ODE solver.
269  *
270  * Should solve the system \f$(I - \gamma J) x = b\f$, where \f$J\f$
271  * is the current vector field Jacobian, \f$\gamma\f$ is a given
272  * scalar, and \f$b\f$ is a given vector.
273  *
274  */
275 #pragma GCC diagnostic push
276 #pragma GCC diagnostic ignored "-Wunused-parameter"
277 static int condense_solver_Solve(double t, N_Vector ycur, N_Vector fcur,
278  N_Vector b, N_Vector z, double gamma,
279  double delta, int lr, void *user_data)
280 {
281  realtype *b_data, *ycur_data, *fcur_data, *z_data;
282  int i, neq;
283  double *b_f, *ycur_f, *fcur_f;
284 
285  neq = NV_LENGTH_S(b);
286  b_data = NV_DATA_S(b);
287  z_data = NV_DATA_S(z);
288  ycur_data = NV_DATA_S(ycur);
289  fcur_data = NV_DATA_S(fcur);
290 
291  b_f = malloc(neq * sizeof(double));
292  ycur_f = malloc(neq * sizeof(double));
293  fcur_f = malloc(neq * sizeof(double));
294 
295  for (i = 0; i < neq; i++) {
296  b_f[i] = b_data[i];
297  ycur_f[i] = ycur_data[i];
298  fcur_f[i] = fcur_data[i];
299  }
300  condense_jac_solve_f(neq, t, ycur_f, fcur_f, b_f, gamma);
301  for (i = 0; i < neq; i++) {
302  z_data[i] = b_f[i];
303  }
304 
305  free(b_f);
306  free(ycur_f);
307  free(fcur_f);
308  return(0);
309 }
310 #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:194
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:240
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:277
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