use crate::check::check_is_success;
use crate::context::Context;
use crate::nvector::NVector;
use crate::sunlinsol::LinearSolver;
use crate::sunmatrix::SparseMatrix;
use anyhow::Result;
use std::ffi::c_int;
use std::os::raw::{c_long, c_void};
use std::pin::Pin;
use std::slice::{from_raw_parts, from_raw_parts_mut};
use sundials_sys::{
sunrealtype, IDACreate, IDAEwtFn, IDAFree, IDAGetNumSteps, IDAInit, IDASStolerances,
IDASVtolerances, IDASetInitStep, IDASetLinearSolver, IDASetMaxConvFails, IDASetMaxNonlinIters,
IDASetMaxNumSteps, IDASetMaxOrd, IDASetNonlinConvCoef, IDASetNonlinearSolver, IDASetStopTime,
IDASetUserData, IDAWFtolerances, N_VGetArrayPointer_Serial, N_VGetLength, N_Vector,
SUNNonlinearSolver,
};
pub type ResFn<U> = fn(
tt: f64,
yy: &[sunrealtype],
yp: &[sunrealtype],
rr: &[sunrealtype],
user_data: &Option<U>,
) -> i32;
struct UserDataWrapper<U> {
actual_user_data: Option<U>,
res_fn: ResFn<U>,
}
fn empty_res_fn<U>(
_tt: f64,
_yy: &[sunrealtype],
_yp: &[sunrealtype],
_rr: &[sunrealtype],
_user_data: &Option<U>,
) -> i32 {
sundials_sys::KIN_SUCCESS
}
extern "C" fn res_fn_wrapper<U>(
tt: sunrealtype,
yy: N_Vector,
yp: N_Vector,
rr: N_Vector,
user_data: *mut c_void,
) -> c_int {
let yy = unsafe {
let pointer = N_VGetArrayPointer_Serial(yy);
let length = N_VGetLength(yy);
from_raw_parts(pointer, length as usize)
};
let yp = unsafe {
let pointer = N_VGetArrayPointer_Serial(yp);
let length = N_VGetLength(yp);
from_raw_parts(pointer, length as usize)
};
let rr = unsafe {
let pointer = N_VGetArrayPointer_Serial(rr);
let length = N_VGetLength(rr);
from_raw_parts_mut(pointer, length as usize)
};
let wrapper = unsafe { &*(user_data as *const UserDataWrapper<U>) };
(wrapper.res_fn)(tt, yy, yp, rr, &wrapper.actual_user_data)
}
pub struct IDA<U> {
ida_mem: *mut c_void,
wrapped_user_data: Pin<Box<UserDataWrapper<U>>>,
}
impl<U> IDA<U> {
pub fn new(context: &Context) -> Self {
Self {
ida_mem: unsafe { IDACreate(context.sunctx) },
wrapped_user_data: Box::pin(UserDataWrapper {
actual_user_data: None,
res_fn: empty_res_fn,
}),
}
}
pub fn init(
&mut self,
res_fn: Option<ResFn<U>>,
t0: f64,
yy0: &NVector,
yp0: &NVector,
user_data: Option<U>,
) -> Result<()> {
let retval = unsafe {
IDAInit(
self.ida_mem,
Some(res_fn_wrapper::<U>),
t0,
yy0.n_vector,
yp0.n_vector,
)
};
check_is_success(retval, "IDAInit")?;
self.wrapped_user_data = Box::pin(UserDataWrapper {
actual_user_data: user_data,
res_fn: match res_fn {
Some(res_fn) => res_fn,
None => empty_res_fn,
},
});
let retval = unsafe {
IDASetUserData(
self.ida_mem,
self.wrapped_user_data.as_ref().get_ref() as *const _ as *mut c_void,
)
};
check_is_success(retval, "IDASetUserData")?;
Ok(())
}
pub fn set_max_ord(&mut self, maxord: usize) -> Result<()> {
let retval = unsafe { IDASetMaxOrd(self.ida_mem, maxord as c_int) };
check_is_success(retval, "IDASetMaxOrd")
}
pub fn set_max_num_steps(&mut self, mxsteps: usize) -> Result<()> {
let retval = unsafe { IDASetMaxNumSteps(self.ida_mem, mxsteps as c_long) };
check_is_success(retval, "IDASetMaxNumSteps")
}
pub fn set_init_step(&mut self, hin: impl Into<sunrealtype>) -> Result<()> {
let retval = unsafe { IDASetInitStep(self.ida_mem, hin.into()) };
check_is_success(retval, "IDASetInitStep")
}
pub fn set_stop_time(&mut self, tstop: impl Into<sunrealtype>) -> Result<()> {
let retval = unsafe { IDASetStopTime(self.ida_mem, tstop.into()) };
check_is_success(retval, "IDASetStopTime")
}
pub fn ss_tolerances(
&mut self,
reltol: impl Into<sunrealtype>,
abstol: impl Into<sunrealtype>,
) -> Result<()> {
let retval = unsafe { IDASStolerances(self.ida_mem, reltol.into(), abstol.into()) };
check_is_success(retval, "IDASStolerances")
}
pub fn sv_tolerances(
&mut self,
reltol: impl Into<sunrealtype>,
abstol: &NVector,
) -> Result<()> {
let retval = unsafe { IDASVtolerances(self.ida_mem, reltol.into(), abstol.n_vector) };
check_is_success(retval, "IDASVtolerances")
}
pub fn wf_tolerances(&mut self, efun: IDAEwtFn) -> Result<()> {
let retval = unsafe { IDAWFtolerances(self.ida_mem, efun) };
check_is_success(retval, "IDAWFtolerances")
}
pub fn set_linear_solver(
&mut self,
ls: LinearSolver,
a_mat: &Option<SparseMatrix>,
) -> Result<()> {
let a = a_mat.as_ref().unwrap().sunmatrix; let retval = unsafe { IDASetLinearSolver(self.ida_mem, ls.sunlinsol, a) };
check_is_success(retval, "IDASetLinearSolver")
}
pub fn set_non_linear_solver(&mut self, nls: SUNNonlinearSolver) -> Result<()> {
let retval = unsafe { IDASetNonlinearSolver(self.ida_mem, nls) };
check_is_success(retval, "IDASetNonlinearSolver")
}
pub fn set_max_nonlin_iters(&mut self, maxcor: usize) -> Result<()> {
let retval = unsafe { IDASetMaxNonlinIters(self.ida_mem, maxcor as c_int) };
check_is_success(retval, "IDASetMaxNonlinIters")
}
pub fn set_max_conv_fails(&mut self, maxncf: usize) -> Result<()> {
let retval = unsafe { IDASetMaxConvFails(self.ida_mem, maxncf as c_int) };
check_is_success(retval, "IDASetMaxConvFails")
}
pub fn set_nonlin_conv_coef(&mut self, epcon: impl Into<sunrealtype>) -> Result<()> {
let retval = unsafe { IDASetNonlinConvCoef(self.ida_mem, epcon.into()) };
check_is_success(retval, "IDASetNonlinConvCoef")
}
pub fn num_steps(&self) -> Result<usize> {
let mut nsteps: c_long = 0;
let retval = unsafe { IDAGetNumSteps(self.ida_mem, &mut nsteps) };
check_is_success(retval, "IDAGetNumSteps")?;
Ok(nsteps as usize)
}
}
impl<U> Drop for IDA<U> {
fn drop(&mut self) {
unsafe {
IDAFree(&mut self.ida_mem);
}
}
}