PartMC  2.6.1
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 
51 static int condense_vf(realtype t, N_Vector y, N_Vector ydot, void *user_data);
52 
53 static int condense_check_flag(void *flagvalue, char *funcname, int opt);
54 
55 /*******************************************************/
56 // solver block
57 
58 static int condense_solver_Solve(double t, N_Vector ycur, N_Vector fcur,
59  N_Vector r, N_Vector b, double gamma, double delta, int lr, void *user_data);
60 
61 /*******************************************************/
62 
63 void condense_vf_f(int neq, realtype t, double *y_f, double *ydot_f);
64 void condense_jac_solve_f(int neq, double t, double *ycur_f, double *fcur_f, double *b_f, double gamma);
65 
66 /** \brief Call the ODE solver.
67  *
68  * \param neq The number of equations.
69  * \param x_f A pointer to a vector of \c neq variables, giving the
70  * initial state vector on entry and the final state vector on exit.
71  * \param abstol_f A pointer to a vector of \c neq variables, giving
72  * the absolute error tolerance for the corresponding state vector
73  * component.
74  * \param reltol_f The scalar relative tolerance.
75  * \param t_initial_f The initial time (s).
76  * \param t_final_f The final time (s).
77  * \return A result code (0 is success).
78  */
79 int condense_solver(int neq, double *x_f, double *abstol_f, double reltol_f,
80  double t_initial_f, double t_final_f)
81 {
82  realtype reltol, t_initial, t_final, t;
83  N_Vector y, abstol;
84  void *cvode_mem;
85  int flag, i;
86  realtype *y_data, *abstol_data;
87 
88  y = abstol = NULL;
89  cvode_mem = NULL;
90 
91  y = N_VNew_Serial(neq);
92  if (condense_check_flag((void *)y, "N_VNew_Serial", 0))
94 
95  abstol = N_VNew_Serial(neq);
96  if (condense_check_flag((void *)abstol, "N_VNew_Serial", 0))
98 
99  y_data = NV_DATA_S(y);
100  abstol_data = NV_DATA_S(abstol);
101  for (i = 0; i < neq; i++) {
102  y_data[i] = x_f[i];
103  abstol_data[i] = abstol_f[i];
104  }
105 
106  reltol = reltol_f;
107  t_initial = t_initial_f;
108  t_final = t_final_f;
109 
110  cvode_mem = CVodeCreate(CV_BDF);
111  if (condense_check_flag((void *)cvode_mem, "CVodeCreate", 0))
113 
114  flag = CVodeInit(cvode_mem, condense_vf, t_initial, y);
115  if (condense_check_flag(&flag, "CVodeInit", 1))
117 
118  flag = CVodeSVtolerances(cvode_mem, reltol, abstol);
119  if (condense_check_flag(&flag, "CVodeSVtolerances", 1))
121 
122  flag = CVodeSetMaxNumSteps(cvode_mem, 100000);
123  if (condense_check_flag(&flag, "CVodeSetMaxNumSteps", 1))
125 
126 
127  SUNLinearSolver LS = SUNLinSol_SPGMR(y, PREC_LEFT, 0);
128  if (condense_check_flag((void *)LS, "SUNLinSol_SPGMR", 0))
130  flag = CVodeSetLinearSolver(cvode_mem, LS, NULL);
131  if (condense_check_flag(&flag, "CVodeSetLinearSolver", 1))
133  flag = CVodeSetPreconditioner(cvode_mem, NULL, condense_solver_Solve);
134  if (condense_check_flag(&flag, "CVodeSetPreconditioner", 1))
136 
137  t = t_initial;
138  flag = CVode(cvode_mem, t_final, y, &t, CV_NORMAL);
139  if (condense_check_flag(&flag, "CVode", 1))
141 
142  for (i = 0; i < neq; i++) {
143  x_f[i] = y_data[i];
144  }
145 
146  N_VDestroy_Serial(y);
147  N_VDestroy_Serial(abstol);
148  CVodeFree(&cvode_mem);
150 }
151 
152 /** \brief The ODE vector field to integrate.
153  *
154  * \param t The current time (s).
155  * \param y The state vector.
156  * \param ydot The rate of change of the state vector.
157  * \param user_data A pointer to user-provided data.
158  * \return A result code (0 is success).
159  */
160 #pragma GCC diagnostic push
161 #pragma GCC diagnostic ignored "-Wunused-parameter"
162 static int condense_vf(realtype t, N_Vector y, N_Vector ydot, void *user_data)
163 {
164  realtype *y_data, *ydot_data;
165  int i, neq;
166  double *y_f, *ydot_f;
167 
168  neq = NV_LENGTH_S(y);
169  y_data = NV_DATA_S(y);
170  ydot_data = NV_DATA_S(ydot);
171 
172  y_f = malloc(neq * sizeof(double));
173  ydot_f = malloc(neq * sizeof(double));
174 
175  for (i = 0; i < neq; i++) {
176  y_f[i] = y_data[i];
177  }
178  condense_vf_f(neq, t, y_f, ydot_f);
179  for (i = 0; i < neq; i++) {
180  ydot_data[i] = ydot_f[i];
181  }
182 
183  free(y_f);
184  free(ydot_f);
185  return(0);
186 }
187 #pragma GCC diagnostic pop
188 
189 /** \brief Check the return value from a SUNDIALS call.
190  *
191  * - <code>opt == 0</code> means SUNDIALS function allocates memory
192  * so check if \c flagvalue is not a \c NULL pointer.
193 
194  * - <code>opt == 1</code> means SUNDIALS function returns a flag so
195  * check if the \c int pointed to by \c flagvalue has
196  * <code>flag >= 0</code>.
197 
198  * - <code>opt == 2</code> means function allocates memory so check
199  * if \c flagvalue is not a \c NULL pointer.
200  *
201  * \param flagvalue A pointer to check (either for \c NULL, or as an
202  * \c int pointer giving the flag value).
203  * \param funcname A string giving the function name returning this
204  * result code.
205  * \param opt A flag indicating the type of check to perform.
206  * \return A result code (0 is success).
207  */
208 static int condense_check_flag(void *flagvalue, char *funcname, int opt)
209 {
210  int *errflag;
211 
212  /* Check if SUNDIALS function returned NULL pointer - no memory allocated */
213  if (opt == 0 && flagvalue == NULL) {
214  fprintf(stderr,
215  "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n",
216  funcname);
217  return(1); }
218 
219  /* Check if flag < 0 */
220  else if (opt == 1) {
221  errflag = (int *) flagvalue;
222  if (*errflag < 0) {
223  fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed with flag = %d\n\n",
224  funcname, *errflag);
225  return(1); }}
226 
227  /* Check if function returned NULL pointer - no memory allocated */
228  else if (opt == 2 && flagvalue == NULL) {
229  fprintf(stderr, "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n",
230  funcname);
231  return(1); }
232 
233  return(0);
234 }
235 
236 /** \brief Linear solver routine for use by the ODE solver.
237  *
238  * Should solve the system \f$(I - \gamma J) x = b\f$, where \f$J\f$
239  * is the current vector field Jacobian, \f$\gamma\f$ is a given
240  * scalar, and \f$b\f$ is a given vector.
241  *
242  */
243 #pragma GCC diagnostic push
244 #pragma GCC diagnostic ignored "-Wunused-parameter"
245 static int condense_solver_Solve(double t, N_Vector ycur, N_Vector fcur,
246  N_Vector b, N_Vector z, double gamma, double delta, int lr, void *user_data)
247 {
248  realtype *b_data, *ycur_data, *fcur_data, *z_data;
249  int i, neq;
250  double *b_f, *ycur_f, *fcur_f;
251 
252  neq = NV_LENGTH_S(b);
253  b_data = NV_DATA_S(b);
254  z_data = NV_DATA_S(z);
255  ycur_data = NV_DATA_S(ycur);
256  fcur_data = NV_DATA_S(fcur);
257 
258  b_f = malloc(neq * sizeof(double));
259  ycur_f = malloc(neq * sizeof(double));
260  fcur_f = malloc(neq * sizeof(double));
261 
262  for (i = 0; i < neq; i++) {
263  b_f[i] = b_data[i];
264  ycur_f[i] = ycur_data[i];
265  fcur_f[i] = fcur_data[i];
266  }
267  condense_jac_solve_f(neq, t, ycur_f, fcur_f, b_f, gamma);
268  for (i = 0; i < neq; i++) {
269  z_data[i] = b_f[i];
270  }
271 
272  free(b_f);
273  free(ycur_f);
274  free(fcur_f);
275  return(0);
276 }
277 #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:162
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:79
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
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:208
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:245
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