Program Listing for File cvode.hpp#

↰ Return to documentation for file (source/pele/cvode.hpp)

#ifndef _PELE_CVODE_OPT_H__
#define _PELE_CVODE_OPT_H__

#include "array.hpp"
#include "base_potential.hpp"
#include "preprocessor_directives.hpp"

// #define EIGEN_USE_MKL_ALL

#include <Eigen/Dense>
#include <cstddef>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <sundials/sundials_context.h>
#include <sundials/sundials_types.h>

// pele's CVODE code passes double buffers straight to N_Vectors
static_assert(sizeof(sunrealtype) == sizeof(double),
              "SUNDIALS must be built with double precision (SUNDIALS_PRECISION=double)");

#if SUNDIALS_VERSION_MAJOR < 7
#define SUN_COMM_NULL NULL  // sundials 6 takes a void* comm
#endif
#include <vector>

#include "array.hpp"
#include "base_potential.hpp"
#include "cvode/cvode_proj.h"

// #define EIGEN_USE_MKL_ALL
// Eigen linear algebra library

#include "eigen_interface.hpp"

// line search methods
#include <cvode/cvode.h> /* access to CVODE                 */
#include <fstream>
#include <iostream>
#include <nvector/nvector_serial.h>    /* access to serial N_Vector       */
#include <sunlinsol/sunlinsol_dense.h> /* access to dense SUNLinearSolver */
#include <sunlinsol/sunlinsol_spgmr.h> /* access to SPGMR SUNLinearSolver */
#include <sunmatrix/sunmatrix_dense.h> /* access to dense SUNMatrix       */

#include "backtracking.hpp"
#include "bracketing.hpp"
#include "eigen_interface.hpp"
#include "linesearch.hpp"
#include "more_thuente.hpp"
#include "nwpele.hpp"
#include "optimizer.hpp"
#include "sundials/sundials_linearsolver.h"
#include "sundials/sundials_matrix.h"
#include "sundials/sundials_nvector.h"

extern "C" {
#include "xsum.h"
}

namespace pele {

enum HessianType {
  DENSE = 0,
  ITERATIVE = 1,
};
typedef struct UserData_ {
  double rtol; /* integration tolerances */
  double atol;
  size_t nfev;                // number of gradient(function) evaluations
  size_t nhev;                // number of st (jacobian) evaluations
  double stored_energy = 0;   // stored energy
  Array<double> stored_grad;  // stored gradient. need to pass this on to
  SUNMatrix stored_J;         // stored dense hessian
  std::shared_ptr<pele::BasePotential> pot_;
} * UserData;

inline void reset_user_data(UserData udata) {
  udata->nfev = 0;
  udata->nhev = 0;
  udata->stored_energy = 0;
  udata->stored_grad.assign(0);
}

class CVODEBDFOptimizer : public ODEBasedOptimizer {
 private:
  UserData_ udata;
  void *cvode_mem; /* CVODE memory         */
  size_t N_size;
  SUNMatrix A;
  SUNLinearSolver LS;
  double tN;
  // return code for everything else, throws exception
  int ret;
  // Single step return code for sundials 0 if sucessful
  // used for stopping if there is a failure
  int single_step_ret_code;
  SUNContext sunctx;  // SUNDIALS context

  // save construction parameters
  double rtol_;
  double atol_;
  HessianType hessian_type_;
#if PRINT_TO_FILE == 1
  std::ofstream trajectory_file;
  std::ofstream time_file;
  std::ofstream hessian_eigvals_file;
  std::ofstream gradient_file;
  std::ofstream step_file;
  std::ofstream newton_step_file;
  std::ofstream lbfgs_m_1_step_file;
  Array<double> g_old;
#endif
  N_Vector x0_N;
  Array<double> xold;
  bool use_newton_stop_criterion_;
  Eigen::MatrixXd hessian;
  void add_translation_offset_2d(Eigen::MatrixXd &hessian, double offset);
  void setup_cvode();
  void free_cvode_objects();

  // Tolerance of the newton step
  double newton_tol_;
  // offset factor that controls how much of the
  // average eigenvalue is used to offset the hessian
  double offset_factor_;

 public:
  void one_iteration();
  // int f(sunrealtype t, N_Vector y, N_Vector ydot, void *user_data);
  // static int Jac(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix J,
  //                void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector
  //                tmp3);
  CVODEBDFOptimizer(std::shared_ptr<pele::BasePotential> potential,
                    const pele::Array<double> x0, double tol = 1e-5,
                    double rtol = 1e-5, double atol = 1e-5,
                    HessianType hessian_type = DENSE,
                    bool use_newton_stop_criterion = false,
                    bool save_trajectory = false,
                    int iterations_before_save = 1, double newton_tol = 1e-5,
                    double offset_factor = 0.1);

  ~CVODEBDFOptimizer();

  // Copy Constructor (raises a not implemented exception)
  CVODEBDFOptimizer(const CVODEBDFOptimizer &) : ODEBasedOptimizer() {
    throw std::runtime_error("CVODEBDFOptimizer is not implemented");
  };

  //
  CVODEBDFOptimizer &operator=(const CVODEBDFOptimizer &other) {
    this->potential_ = other.potential_;
    this->x_ = other.x_;
    this->tol_ = other.tol_;
    this->rtol_ = other.rtol_;
    this->atol_ = other.atol_;
    this->hessian_type_ = other.hessian_type_;
    this->use_newton_stop_criterion_ = other.use_newton_stop_criterion_;

    // free cvode memory and setup again
    this->free_cvode_objects();
    this->setup_cvode();
    return *this;
  }

  inline int get_nhev() const { return udata.nhev; }

  void reset(Array<double> &x0) {
    x_.assign(x0);
    udata.nhev = 0;
    udata.nfev = 0;
    nfev_ = 0;
    nhev_ = 0;
    succeeded_ = false;
    iter_number_ = 0;
    udata.stored_energy = 0;
    func_initialized_ = false;
    this->udata.stored_grad = Array<double>(x0.size());
    this->free_cvode_objects();
    this->setup_cvode();
  }
  bool stop_criterion_satisfied();

 protected:
  double H02;
};

inline N_Vector N_Vector_eq_pele(pele::Array<double> x, SUNContext sunctx) {
  N_Vector y;
  y = N_VNew_Serial(x.size(), sunctx);
  for (size_t i = 0; i < x.size(); ++i) {
    NV_Ith_S(y, i) = x[i];
  }
  // NV_DATA_S(y) = x.copy().data();
  return y;
}

inline pele::Array<double> pele_eq_N_Vector(N_Vector x) {
  Array<double> y = Array<double>(N_VGetLength(x));
  for (size_t i = 0; i < y.size(); ++i) {
    y[i] = NV_Ith_S(x, i);
  }
  return pele::Array<double>(NV_DATA_S(x), N_VGetLength(x)).copy();
}

int f(sunrealtype t, N_Vector y, N_Vector ydot, void *user_data);
int Jac(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix J, void *user_data,
        N_Vector tmp1, N_Vector tmp2, N_Vector tmp3);
static int Jac2(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix J,
                void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3);
static int f2(sunrealtype t, N_Vector y, N_Vector ydot, void *user_data);

inline int check_sundials_retval(void *return_value, const char *funcname,
                                 int opt) {
  int *retval;

  /* Check if SUNDIALS function returned NULL pointer - no memory allocated */

  if (opt == 0 && return_value == NULL) {
    fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n",
            funcname);
    return (1);
  }
  /* Check if retval < 0 */

  else if (opt == 1) {
    retval = (int *)return_value;
    if (*retval < 0) {
      fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed with retval = %d\n\n",
              funcname, *retval);
      return (1);
    }
  }

  /* Check if function returned NULL pointer - no memory allocated */

  else if (opt == 2 && return_value == NULL) {
    fprintf(stderr, "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n",
            funcname);
    return (1);
  }

  return (0);
}

}  // namespace pele

#endif