From 9c83f923ace6d1fd338c7c83daa605ccd65643ee Mon Sep 17 00:00:00 2001 From: sn248 Date: Sun, 10 Nov 2024 16:34:21 -0500 Subject: [PATCH] code refactoring Just just code cleaning after fixing abstolerance --- DESCRIPTION | 2 +- R/RcppExports.R | 2 +- docs/.nojekyll | 1 + docs/404.html | 127 ++-- docs/LICENSE-text.html | 121 +--- docs/articles/index.html | 123 +--- docs/articles/my-vignette.html | 656 ++++++++++++++++++ .../figure-html/unnamed-chunk-4-1.png | Bin 85662 -> 45396 bytes .../header-attrs-2.1/header-attrs.js | 12 - docs/authors.html | 167 ++--- docs/index.html | 189 +++++ docs/pkgdown.css | 87 ++- docs/pkgdown.js | 4 +- docs/pkgdown.yml | 7 +- docs/reference/cvode.html | 324 ++++----- docs/reference/cvodes.html | 344 ++++----- docs/reference/cvsolve.html | 274 +++----- docs/reference/ida.html | 213 ++---- docs/reference/index.html | 162 +---- docs/sitemap.xml | 14 + inst/include/cvodes/cvodes.h | 3 +- inst/include/nvector/nvector_parallel.h | 6 +- inst/include/nvector/nvector_serial.h | 6 +- .../sundials/sundials_adaptcontroller.h | 4 + inst/include/sundials/sundials_config.in | 3 +- .../sundials/sundials_hip_policies.hpp | 6 +- inst/include/sunlinsol/sunlinsol_ginkgo.hpp | 8 + .../sunlinsol/sunlinsol_kokkosdense.hpp | 4 +- .../sunmatrix/sunmatrix_kokkosdense.hpp | 6 +- man/cvodes.Rd | 2 +- src/Makevars | 4 +- src/cvode.cpp | 293 ++++---- src/cvodes.cpp | 248 +++---- src/cvsolve.cpp | 16 +- src/ida.cpp | 183 +++-- src/sundials/cvode/cvode.c | 30 +- src/sundials/cvode/cvode_bandpre.c | 9 +- src/sundials/cvode/cvode_bbdpre.c | 16 +- src/sundials/cvode/cvode_diag.c | 27 +- src/sundials/cvode/cvode_fused_stubs.c | 10 +- src/sundials/cvode/cvode_impl.h | 31 +- src/sundials/cvode/cvode_io.c | 4 + src/sundials/cvode/cvode_ls.c | 5 +- src/sundials/cvode/cvode_nls.c | 6 - src/sundials/cvodes/cvodea.c | 2 +- src/sundials/cvodes/cvodes.c | 33 +- src/sundials/cvodes/cvodes_bandpre.c | 9 +- src/sundials/cvodes/cvodes_bbdpre.c | 16 +- src/sundials/cvodes/cvodes_diag.c | 13 +- src/sundials/cvodes/cvodes_impl.h | 6 +- src/sundials/cvodes/cvodes_io.c | 2 + src/sundials/cvodes/cvodes_ls.c | 13 +- src/sundials/cvodes/cvodes_nls_stg1.c | 3 +- src/sundials/ida/ida.c | 13 +- src/sundials/ida/ida_bbdpre.c | 13 +- src/sundials/ida/ida_ic.c | 2 - src/sundials/ida/ida_impl.h | 4 +- src/sundials/ida/ida_ls.c | 5 +- src/sundials/ida/ida_nls.c | 6 +- src/sundials/idas/idas.c | 26 +- src/sundials/idas/idas_bbdpre.c | 13 +- src/sundials/idas/idas_ic.c | 7 - src/sundials/idas/idas_impl.h | 4 +- src/sundials/idas/idas_ls.c | 5 +- src/sundials/idas/idas_nls.c | 6 +- src/sundials/idas/idas_nls_sim.c | 7 +- src/sundials/idas/idas_nls_stg.c | 7 +- src/sundials/kinsol/kinsol.c | 29 +- src/sundials/kinsol/kinsol_bbdpre.c | 12 +- src/sundials/kinsol/kinsol_impl.h | 4 +- src/sundials/kinsol/kinsol_ls.c | 8 +- src/sundials/nvector/serial/nvector_serial.c | 15 +- .../sundials/priv/sundials_context_impl.h | 45 ++ .../sundials/priv/sundials_errors_impl.h | 557 +++++++++++++++ .../sundials/priv/sundials_mpi_errors_impl.h | 137 ++++ .../sundials/sundials_adaptcontroller.c | 199 ++++++ .../sundials/sundials_adiak_metadata.h | 5 + src/sundials/sundials/sundials_band.c | 63 +- src/sundials/sundials/sundials_context.c | 11 +- src/sundials/sundials/sundials_cuda.h | 79 +++ .../sundials/sundials_cuda_kernels.cuh | 448 ++++++++++++ src/sundials/sundials/sundials_cusolver.h | 66 ++ src/sundials/sundials/sundials_cusparse.h | 65 ++ src/sundials/sundials/sundials_dense.c | 102 +-- src/sundials/sundials/sundials_direct.c | 59 -- src/sundials/sundials/sundials_errors.c | 18 +- src/sundials/sundials/sundials_futils.c | 1 + src/sundials/sundials/sundials_hashmap.c | 342 +++++++++ src/sundials/sundials/sundials_hashmap_impl.h | 57 ++ .../sundials/sundials_iterative_impl.h | 5 + .../sundials/sundials_lapack_defs.h.in | 117 ++++ src/sundials/sundials/sundials_logger.c | 22 +- src/sundials/sundials/sundials_logger_impl.h | 2 +- src/sundials/sundials/sundials_macros.h | 39 ++ src/sundials/sundials/sundials_nvector.c | 16 +- src/sundials/sundials/sundials_profiler.c | 32 +- src/sundials/sundials/sundials_utils.h | 2 +- src/sundials/sunlinsol/band/sunlinsol_band.c | 12 +- .../sunlinsol/dense/sunlinsol_dense.c | 13 +- src/sundials/sunmatrix/band/sunmatrix_band.c | 11 +- .../sunmatrix/dense/sunmatrix_dense.c | 11 +- .../fixedpoint/sunnonlinsol_fixedpoint.c | 11 +- .../sunnonlinsol/newton/sunnonlinsol_newton.c | 65 +- 103 files changed, 4509 insertions(+), 2145 deletions(-) create mode 100644 docs/.nojekyll create mode 100644 docs/articles/my-vignette.html delete mode 100644 docs/articles/my-vignette_files/header-attrs-2.1/header-attrs.js create mode 100644 docs/index.html create mode 100644 docs/sitemap.xml create mode 100644 src/sundials/sundials/priv/sundials_context_impl.h create mode 100644 src/sundials/sundials/priv/sundials_errors_impl.h create mode 100644 src/sundials/sundials/priv/sundials_mpi_errors_impl.h create mode 100644 src/sundials/sundials/sundials_adaptcontroller.c create mode 100644 src/sundials/sundials/sundials_cuda.h create mode 100644 src/sundials/sundials/sundials_cuda_kernels.cuh create mode 100644 src/sundials/sundials/sundials_cusolver.h create mode 100644 src/sundials/sundials/sundials_cusparse.h create mode 100644 src/sundials/sundials/sundials_hashmap.c create mode 100644 src/sundials/sundials/sundials_hashmap_impl.h create mode 100644 src/sundials/sundials/sundials_lapack_defs.h.in create mode 100644 src/sundials/sundials/sundials_macros.h diff --git a/DESCRIPTION b/DESCRIPTION index 10dc00d..4ae6dac 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -17,7 +17,7 @@ Imports: Rcpp (>= 1.0.12) LinkingTo: Rcpp, RcppArmadillo -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 Suggests: knitr, rmarkdown, diff --git a/R/RcppExports.R b/R/RcppExports.R index 1743783..ff6e6a0 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -25,7 +25,7 @@ cvode <- function(time_vector, IC, input_function, Parameters, reltolerance = 0. #'@param Parameters Parameters input to ODEs #'@param reltolerance Relative Tolerance (a scalar, default value = 1e-04) #'@param abstolerance Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04) -#'@param SensType Sensitivity Type - allowed values are Staggered (default)", "STG" (for Staggered) or "SIM" (for Simultaneous) +#'@param SensType Sensitivity Type - allowed values are "STG" (for Staggered, default) or "SIM" (for Simultaneous) #'@param ErrCon Error Control - allowed values are TRUE or FALSE (default) #'@returns A data frame. First column is the time-vector, the next y * p columns are sensitivities of y1 w.r.t all parameters, then y2 w.r.t all parameters etc. y is the state vector, p is the parameter vector #'@example /inst/examples/cvs_Roberts_dns.r diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/.nojekyll @@ -0,0 +1 @@ + diff --git a/docs/404.html b/docs/404.html index 8338231..58ac873 100644 --- a/docs/404.html +++ b/docs/404.html @@ -1,66 +1,27 @@ - - - - + + + + - Page not found (404) • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - -
-
- + +
+ + + - - -
+
+
-
- +
+ - - - + + diff --git a/docs/LICENSE-text.html b/docs/LICENSE-text.html index d07fd7d..8efe383 100644 --- a/docs/LICENSE-text.html +++ b/docs/LICENSE-text.html @@ -1,66 +1,12 @@ - - - - - - - -License • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -License • sundialr + - - - -
-
- - -
-
+
+
-
- +
+ + - - - + diff --git a/docs/articles/index.html b/docs/articles/index.html index 3b1f75b..42c9198 100644 --- a/docs/articles/index.html +++ b/docs/articles/index.html @@ -1,66 +1,12 @@ - - - - - - - -Articles • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Articles • sundialr + - - - -
-
- - -
- -
- +
+ + - - - + diff --git a/docs/articles/my-vignette.html b/docs/articles/my-vignette.html new file mode 100644 index 0000000..79d0ab5 --- /dev/null +++ b/docs/articles/my-vignette.html @@ -0,0 +1,656 @@ + + + + + + + +sundialr - An Interface to 'SUNDIALS' Ordinary Differential Equation (ODE) Solvers • sundialr + + + + + + + + + + + +
+
+ + + + +
+
+ + + + +
+

Introduction +

+

Ordinary Differential Equations (ODEs) describe the rate of change of +dependent variables with respect to a single independent variable and +are used in many fields to model behavior of the system. There are many +good C libraries available to solve (i.e., integrate +systems of ODEs) and SUNDIALS +available from the Lawrence Livermore National Laboratory is a one of +the most popular and well-respected C library for solving +non-stiff and stiff systems of ODEs.

+

Currently, this package provides an interface to the +CVODE and CVODES function (serial version) in +the library which is used to solve ODEs (or Initial Value Problems) and +calculate sensitivities.

+

The four exported functions from the package are:

+
    +
  • cvode - An interface to the CVODE +function in SUNDIALS to solve a system of ODEs.

  • +
  • cvodes - An interface to the CVODES +function in SUNDIALS to calculate forward sensitivites with +respect to parameters of the ODE system.

  • +
  • ida - An interface to the IDA function +in SUNDIALS to solve a system of Differential-Algebraic +Equations (DAEs).

  • +
  • cvsolve - A convenient interface to solve a system +of ODEs with discontinuities in solution.

  • +
+

In future, we plan to provide interface for the other solvers (i.e., +IDA/IDAS and ARCODE in the library also. Right +now, this package serves as a test case for providing an interface to +the SUNDIALS library for R users.

+

One of the advantage of using this package is that all the source +code of the SUNDIALS library is bundled with the package +itself, so it does not require the SUNDIALS library to be +installed on the machine separately (which is sometimes non trivial on a +Windows machine).

+
+
+

System of ODEs +

+

As described in the link above, the problem is from chemical +kinetics, and consists of the following three rate equations:

+

dy1dt=.04×y1+104×y2×y3dy2dt=.04×y1104×y2×y33×107×y22dy3dt=3×107×y22 +\begin{aligned} +\frac{dy_1}{dt} &= -.04 \times y_1 + 10^4 \times y_2 \times y_3 \\ +\frac{dy_2}{dt} &= .04 \times y_1 - 10^4 \times y_2 \times y_3 - 3 \times 10^7 \times y_2^2 \\ +\frac{dy_3}{dt} &= 3 \times 10^7 \times y_2^2 +\end{aligned} +

+

with time interval from +t=0.0t = 0.0 +to +t=4×1010t = 4 \times 10^{10} +and initial conditions: +y1=1.0,y2=y3=0 y_1 = 1.0 , ~y_2 = y_3 = 0

+

The problem is stiff.

+

The original example , While integrating the system, also uses the +rootfinding feature to find the points at which

+

y1=1×104 y_1 = 1 \times 10^{-4} +or at which +y3=0.01 y_3 = 0.01 +but currently root-finding is not supported in this version. As in the +original example, this package also solves the problem with the +BDF method, Newton iteration with the SUNDENSE +dense linear solver, however, without a user-supplied Jacobian routine +(unlike the original example). The future versions may include an +ability to provide Jacobian calculated analytically or via automatic +differentiation. CVODE uses a scalar relative tolerance and +a vector absolute tolerance (which can be provided as an input). Output +is printed in decades from +t=0.4t = 0.4 +to +t=4×1010t = 4 \times 10^{10} +in this example.

+
+
+

Writing the Differential Equations +

+
+

Using R +

+

Differential equations can be written as an R function +or as an Rcpp function. Differential equations function +must be written as

+
+function(t, y, p){
+  # code to write differential equations
+  # using parameter vector (p) and state/entity vector (y)
+  # should return `ydot`, the vector representing
+  # rate of change of entities in `y`
+  # length of `ydot` must be equal to `y1
+}
+

where t represents time, y is the vector +describing the values of states/entities of the ODE system at time +t and p is the vector of parameters used to +define the ODEs. The output of this function is a vector of rate of +change of entities of y.

+

The key aspect to keep in mind is that the signature of the function +must be function(t,y,p). As an example, we try to solve the +cv_Roberts_dns.c problem described above, the original code +can be found here. +An example of an R function is as follows:

+
+ODE_R <- function(t, y, p){
+
+   ## initialize the derivative vector
+   ydot <- vector(mode = "numeric", length = length(y))
+   
+   ## p (parameter vector input) is  [-0.04 1e04 3e07]
+   
+   ydot[1] = p[1]*y[1] + p[2]*y[2]*y[3]
+   ydot[2] = -p[1]*y[1] - p[2]*y[2]*y[3] - p[3]*y[2]*y[2]
+   ydot[3] = p[3]*y[2]*y[2]
+
+   ydot      ## return ydot
+}
+

where p is a parameter vector with the values +[-0.04 1e04 3e07].

+
+
+

Using Rcpp +

+

Also, since this package using Rcpp to bundle the C +code, we can use the notation used in Rcpp to describe the +system of ODEs. The cv_Roberts_dns problem describe above +can be described in an Rcpp function as follows (indices in +C++ start from 0, functions need to declare +their return type, here NumericVector and every expression +ends in a semicolon, ;) :

+
#include <Rcpp.h>
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+NumericVector ODE_Rcpp (double t, NumericVector y){
+
+  // Initialize ydot filled with zeros
+  NumericVector ydot(y.length());
+
+  // p (parameter vector) is [-0.04 1e04 3e07]
+  ydot[0] = p[0] * y[0] + p[1] * y[1] * y[2];
+  ydot[1] = -p[0]*y[0] - p[1]*y[1]*y[2] - p[2]*y[1]*y[1]
+  ydot[2] = p[2] * y[1] * y[1];
+
+  return ydot;
+
+}
+

The above is a re-write of the cvRoberts_dns.c example +in the documentation of CVODE.

+
+
+

Putting everything together +

+

The entire R file to create right hand side of ODE +function (which calculates rates of change) is as follows (also found here):

+
+# ODEs described by an R function
+ODE_R <- function(t, y, p){
+
+   ## initialize the derivative vector
+   ydot <- vector(mode = "numeric", length = length(y))
+   
+   ## p (parameter vector) is  [-0.04 1e04 3e07]
+   
+   ydot[1] = p[1]*y[1] + p[2]*y[2]*y[3]
+   ydot[2] = -p[1]*y[1] - p[2]*y[2]*y[3] - p[3]*y[2]*y[2]
+   ydot[3] = p[3]*y[2]*y[2]
+
+   ydot      ## return ydot
+}
+
+# ODEs can also be described using Rcpp
+Rcpp::sourceCpp(code = '
+
+#include <Rcpp.h>
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+NumericVector ODE_Rcpp (double t, NumericVector y){
+
+  // Initialize ydot filled with zeros
+  NumericVector ydot(y.length());
+
+  // p (parameter vector) is [-0.04 1e04 3e07]
+  ydot[0] = p[0] * y[0] + p[1] * y[1] * y[2];
+  ydot[1] = -p[0]*y[0] - p[1]*y[1]*y[2] - p[2]*y[1]*y[1]
+  ydot[2] = p[2] * y[1] * y[1];
+
+  return ydot;
+
+}')
+
+# Generate time vector, IC and call cvode to solve the equations
+# R code to genrate time vector, IC and solve the equations
+time_vec <- c(0.0, 0.4, 4.0, 40.0, 4E2, 4E3, 4E4, 4E5, 4E6, 4E7, 4E8, 4E9, 4E10)
+IC <- c(1,0,0)
+params <- c(0.04, 10000, 30000000)
+reltol <- 1e-04
+abstol <- c(1e-8,1e-14,1e-6)
+
+## Solving the ODEs using cvode function
+df1 <- cvode(time_vec, IC, ODE_R , params, reltol, abstol)           ## using R
+df2 <- cvode(time_vec, IC, ODE_Rcpp , params, reltol, abstol)        ## using Rcpp
+
+## Check that both solutions are identical
+# identical(df1, df2)
+

The final output is the df1 matrix in which first column +is time, second, third and fourth column are the values of +y1, y2 and y3 respectively.

+
> df1
+       [,1]         [,2]         [,3]       [,4]
+ [1,] 0e+00 1.000000e+00 0.000000e+00 0.00000000
+ [2,] 4e-01 9.851641e-01 3.386242e-05 0.01480205
+ [3,] 4e+00 9.055097e-01 2.240338e-05 0.09446793
+ [4,] 4e+01 7.158016e-01 9.185043e-06 0.28418924
+ [5,] 4e+02 4.505209e-01 3.222826e-06 0.54947590
+ [6,] 4e+03 1.832217e-01 8.943516e-07 0.81677741
+ [7,] 4e+04 3.898091e-02 1.621669e-07 0.96101893
+ [8,] 4e+05 4.936971e-03 1.984450e-08 0.99506301
+ [9,] 4e+06 5.170103e-04 2.069098e-09 0.99948299
+[10,] 4e+07 5.204927e-05 2.082078e-10 0.99994795
+[11,] 4e+08 5.184946e-06 2.073989e-11 0.99999482
+[12,] 4e+09 5.246212e-07 2.098486e-12 0.99999948
+[13,] 4e+10 6.043000e-08 2.417200e-13 0.99999994
+
+
+
+

Solving a system of Differential-Algebraic Equations +

+

An interface to the IDA solver is also provided to solve +a system of Differential-Algebraic equations. A system of +differential-algebraic equations is a system of equations containing +both differential and algebraic equations and can be written as +F(ẋ(t),x(t),t)=0 +F(\dot{x}(t), x(t), t) = 0 + The equations for such a system are +written in terms of residuals and require both the value of +x0x_0 +and +ẋ0\dot{x}_0 +as the initial conditions. Writing the previously solved equations as a +system of DAEs, we have, +ẏ1=p1y1+p2y2y3ẏ2=p1y1p2y2y3p3y221=y1+y2+y3 +\begin{aligned} +\dot{y}_1 &= -p_1y_1 + p_2y_2y_3 \\ +\dot{y}_2 &= p_1y_1 - p_2y_2y_3 - p_3y_2^2 \\ +1 &= y_1 + y_2 + y_3 +\end{aligned} + The above system of DAEs can be written +in terms of residuals as

+

res1=p1y1+p2y2y3ẏ1res2=p1y1p2y2y3p3y22ẏ2res3=y1+y2+y31 +\begin{aligned} +res_1 &= -p_1y_1 + p_2y_2y_3 - \dot{y}_1 \\ +res_2 &= p_1y_1 - p_2y_2y_3 - p_3y_2^2 - \dot{y}_2\\ +res_3 &= y_1 + y_2 + y_3 - 1 +\end{aligned} + Here is the complete code for solving +this system of DAEs,

+
+DAE_R <- function(t, y, ydot, p){
+
+  # vector containing the residuals
+  res = vector(mode = "numeric", length = length(y))
+
+  # R indices start from 1
+  res[1] <- -0.04 * y[1] + 10000 * y[2] * y[3] - ydot[1]
+  res[2] <- -res[1] - 30000000 * y[2] * y[2] - ydot[2]
+  res[3] <- y[1] + y[2] + y[3] - 1.0
+
+  res
+}
+
+# R code to genrate time vector, IC and solve the equations
+time_vec <- c(0.0, 0.4, 4.0, 40.0, 4E2, 4E3, 4E4, 4E5, 4E6, 4E7, 4E8, 4E9, 4E10)
+IC <- c(1,0,0)                  # Initial value of y
+IRes <- c(-0.4, 0.4, 0)         # Initial value of ydot
+params <- c(0.04, 10000, 30000000)
+reltol <- 1e-04
+abstol <- c(1e-8,1e-14,1e-6)
+
+## Solving the DAEs using the ida function
+df1 <- sundialr::ida(time_vec, IC, IRes, DAE_R , params, reltol, abstol) 
+
+
+

Solving an ODE system with discontinuties in solution +

+

The cvsolve function defined in sundialr +package provides a convenience interface to solve ODEs with one or more +discontinuities in solution. An example of such a system of ODEs would +be pharmacokinetics of a drug with repeated bolus administration. Let’s +look at a simple example of multiple doses of a drug with a first-order +degradation administered intravenously. The ODE system for the drug is +dCdt=kel*C +\frac{dC}{dt} = -k_{el} * C + where +CC +is the concentration of the drug and +kelk_{el} +is the elimination rate of the drug. The +RR +code for such a system is

+
+ODErepeated_R <- function(t, y, p){
+
+  # vector containing the right hand side gradients
+  ydot = vector(mode = "numeric", length = length(y))
+
+  # R indices start from 1
+  ydot[1] = -p[1]*y[1]
+
+  ydot
+
+}
+

We also need to define when the multiple doses are given and the +state to which they are to be applied (here to be applied to the only +state in the model, +CC). +This is provided via the +EventsEvents +dataframe (here, +TDOSETDOSE +or the dosing dataframe).

+
+TDOSE <- data.frame(ID = 1, TIMES = c(0, 10, 20, 30, 40, 50), VAL = 100)
+TDOSE
+#>   ID TIMES VAL
+#> 1  1     0 100
+#> 2  1    10 100
+#> 3  1    20 100
+#> 4  1    30 100
+#> 5  1    40 100
+#> 6  1    50 100
+

TDOSE is a data frame with the index of the state to +which discontinuity is to be applied (represented by ID), +the times at which the discontinuity is to be applied (represented by +TIMES) at the value to be added to the +value of the state at that time-point. A typical example would be +addition of the dose amount to the value of the concentration at the +specified times, e.g., the TDOSE data frame says that a +value of 100 is to be added to the concentration of the 1st +state (the only state in this system) at the times specified by the +TIMES column. In summary,

+
    +
  • ID represents the index of the state with +discontinuity

  • +
  • TIMES represents the times at which discontinutiy is +applied

  • +
  • VAL represents the value added to +the value of the state at specified times.

  • +
+

The complete code for simulating such a system is

+
+# Example of solving a set of ODEs with multiple discontinuities using cvsolve
+# A simple One dimensional equation, y = -0.1 * y
+# ODEs described by an R function
+ODE_R <- function(t, y, p){
+
+  # vector containing the right hand side gradients
+  ydot = vector(mode = "numeric", length = length(y))
+
+  # R indices start from 1
+  ydot[1] = -p[1]*y[1]
+
+  ydot
+
+}
+
+# R code to generate time vector, IC and solve the equations
+TSAMP <- seq(from = 0, to = 100, by = 0.1)      # sampling time points
+IC <- c(1)
+params <- c(0.1)
+
+# A dataset describing the dosing at times at which additions to y[1] are to be done
+# Names of the columns don't matter, but they MUST be in the order of state index,
+# times and Values at discontinuity.
+TDOSE <- data.frame(ID = 1, TIMES = c(0, 10, 20, 30, 40, 50), VAL = 100)
+
+df1 <- sundialr::cvsolve(TSAMP, c(1), ODE_R, params)         # solving without any discontinuity
+df2 <- sundialr::cvsolve(TSAMP, c(1), ODE_R, params, TDOSE)  # solving with discontinuity
+
+## Plot the solution with discontinuities
+## first column is time, second column is the state
+time <- df2[,1]
+y1 <- df2[,2]
+plot(time, y1, type = "l", lty = 1, main = "An ODE system with discontinuties", frame.plot = F)   
+

+

Note that in the example above, TSAMP is the sampling +time at which the solution is desired. Also, even though an Initial +Value of +y1y_1 +of 1 is provided by the IC parameter, it is overwritten by +the value of 100 provided in the TDOSE data frame. In +general, the values in the initial conditions are overwritten by values +in the Events input.

+
+
+

System of ODEs for Parameter Sensitivities +

+

Sensitivity with respect to the parameters of the ODE system can be +calculated using CVODES function. This package implements +Forward Sensitivity Analysis from CVODES function (see the +example cvRoberts_FSA_dns.c from the link here). +Briefly, given the ODE system as described below

+

dy1dt=p1y1+p2y2y3dy2dt=p1y1p2y2y3p3y22dy3dt=p3y22 +\begin{aligned} +\frac{dy_1}{dt} &= -p_1y_1 + p_2y_2y_3 \\ +\frac{dy_2}{dt} &= p_1y_1 - p_2y_2y_3 - p_3y_2^2 \\ +\frac{dy_3}{dt} &= p_3y_2^2 +\end{aligned} + with the same initial conditions as +above (i.e., +y1=0,y2=y3=0y_1 = 0, y_2 = y_3 = 0) +and +p1=0.04,p2=104,p3=3×107p_1 = 0.04, \quad p_2 = 10^4, \quad p_3 = 3\times10^7. +The system of Sensitivity equations (taken from +cvs_guide.pdf) that is solved can be given by +dsdt=[p1p2y3p2y2p1p2y32p3y2p2y202p3y20]si+fpi,si(t0)=[000],i=1,2,3 +\begin{aligned} +\frac{ds}{dt} = +\left[\begin{array} +{ccc} +-p_1 & p_2y_3 & p_2y_2 \\ +p_1 & -p_2y_3-2p_3y_2 & -p_2y_2 \\ +0 & 2p_3y_2 & 0 +\end{array}\right]s_i + \frac{\partial f}{\partial p_i}, +\quad s_i(t_0) = +\left[\begin{array} {c} 0 \\ 0 \\ 0 \end{array}\right], \quad i = 1, 2, 3 +\end{aligned} + where +fp1=[y1y10],fp2=[y2y3y2y30],fp3=[0y22y22] +\frac{\partial f}{\partial p_1} = \left[\begin{array} {c} -y_1 \\ y_1 \\ 0 \end{array}\right], +\quad \frac{\partial f}{\partial p_2} = \left[\begin{array} {c} y_2y_3 \\ -y_2y_3 \\ 0 \end{array}\right], +\quad +\frac{\partial f}{\partial p_3} = \left[\begin{array} {c} 0 \\ -y_2^2 \\ y_2^2 \end{array}\right] + In the original CVODES +interface from SUNDIALS, the sensitivity equations can +either be provided by the user or can be calculated using numerical +interpolation by the solver. Here, I have only included the numerical +interpolation version and currently the user cannot specify the +sensitivity equations. However, in the future versions I will provide an +ability to specify user-defined Jacobian as well as user-defined +sensitivity equations.

+

Also, currently, forward sensitivities are calculated with respect to +all parameters of the system. I plan to provide in future, an ability to +specify specific particular parameters for which sensitivity is desired. +Currently, SIMULATENOUS and STAGGERED methods +of sensitivity calculations from the SUNDIALS library are +supported in this package.

+
+
+

Calculation of Sensitivities using CVODES +

+

Once, the system of ODEs has been defined using the instructions +provided above, sensitivities can be easily calculated using the +cvodes function using the function call below (the entire +code can be found at this link)

+
+df1 <- cvodes(time_vec, IC, ODE_R , params, reltol, abstol,"STG",F)  ## using R
+df2 <- cvodes(time_vec, IC, ODE_Rcpp , params, reltol, abstol,"STG",F)  ## using Rcpp
+

The additional arguments in cvodes specify the +senstivity calculation method to be used (STG for +STAGGERED or SIM for +SIMULATENOUS) and flag for error control (either +T or F).

+

The output of cvodes is a matrix with number of rows +equal to the length of the time vector (time_vec) and the +number of columns being equal to length of (y +×\timesp + 1). The first columns is for time. Currently, the +sensitivity of every enitity is calculated with respect to every +parameter in model. For example, for the current model with +3 entities (ODEs) and 3 parameters, a total of +9 sensitivities are calculated at each output time, +i.e. y1 w.r.t p1, p2, +p3, y2 w.r.t. p1, +p2, p3 and so on. The first 3 +(length(y)) columns give sensitivity w.r.t the first +parameter, the next 3 (length(y)) columns give sensitivity +w.r.t the second parameter and so on.

+

In the Sensitivity Matrix output for the systems of equations +described above, the first column gives output time, the next +3 columns provide sensitivity of y1, +y2 and y3 w.r.t first parameter (say +p1), the next three columns provide sensitivity of +y1, y2 and y3 w.r.t. the second +parameter (p2) and so on. The output Sensitivity Matrix is +given below. The sensitivity values match with the values provided in +the CVODES documentation.

+
> df1
+       [,1]          [,2]          [,3]         [,4]         [,5]          [,6]          [,7]          [,8]          [,9]        [,10]
+ [1,] 0e+00  0.000000e+00  0.000000e+00 0.000000e+00 0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00 0.000000e+00
+ [2,] 4e-01 -3.561085e-01  3.902252e-04 3.557183e-01 9.483149e-08 -2.132509e-10 -9.461823e-08 -1.573297e-11 -5.289692e-13 1.626194e-11
+ [3,] 4e+00 -1.876130e+00  1.792229e-04 1.875951e+00 2.961233e-06 -5.830758e-10 -2.960650e-06 -4.932970e-10 -2.762408e-13 4.935732e-10
+ [4,] 4e+01 -4.247395e+00  4.592812e-05 4.247349e+00 1.372964e-05 -2.357270e-10 -1.372941e-05 -2.288274e-09 -1.138015e-13 2.288387e-09
+ [5,] 4e+02 -5.958192e+00  3.545986e-06 5.958189e+00 2.273754e-05 -2.260807e-11 -2.273752e-05 -3.789554e-09 -4.994795e-14 3.789604e-09
+ [6,] 4e+03 -4.750132e+00 -5.991971e-06 4.750138e+00 1.880937e-05  2.312156e-11 -1.880939e-05 -3.134824e-09 -1.875976e-14 3.134843e-09
+ [7,] 4e+04 -1.574902e+00 -2.761679e-06 1.574905e+00 6.288404e-06  1.100645e-11 -6.288415e-06 -1.047876e-09 -4.536508e-15 1.047881e-09
+ [8,] 4e+05 -2.363168e-01 -4.584043e-07 2.363173e-01 9.450741e-07  1.832930e-12 -9.450760e-07 -1.574929e-10 -6.362045e-16 1.574935e-10
+ [9,] 4e+06 -2.566355e-02 -5.105587e-08 2.566361e-02 1.026491e-07  2.042044e-13 -1.026493e-07 -1.711080e-11 -6.851356e-17 1.711087e-11
+[10,] 4e+07 -2.597859e-03 -5.190342e-09 2.597864e-03 1.039134e-08  2.076100e-14 -1.039136e-08 -1.732552e-12 -6.930923e-18 1.732559e-12
+[11,] 4e+08 -2.601996e-04 -5.199259e-10 2.602002e-04 1.040802e-09  2.079717e-15 -1.040804e-09 -1.737821e-13 -6.951356e-19 1.737828e-13
+[12,] 4e+09 -2.648142e-05 -5.616896e-11 2.648147e-05 1.059193e-10  2.246502e-16 -1.059195e-10 -1.804535e-14 -7.218146e-20 1.804542e-14
+[13,] 4e+10 -2.899376e-06 -7.759920e-12 2.899383e-06 1.159764e-11  3.104024e-17 -1.159768e-11 -1.727574e-15 -6.910296e-21 1.727581e-15
+

In future, I intend to provide options to select specific entities +and parameters with respect to which sensitivities are to be computed as +the sensitivity matrix can get very large for medium to large +models.

+
+
+

Summary +

+
    +
  • The package sundialr provides a way to interface +with the famous SUNDIALS C library (provided by Lawerence +Livermore National Security) to solver initial value problems.

  • +
  • The package allows the system of differential equations to be +written in R or using Rcpp. Function +cvode is used to solve initial value problems with a single +initialization, but problems with multiple discontinuities in the +solution can be solved using the cvsolve +interface.

  • +
  • For sensitivities, currently, calculation of forward +sensitivities for all entities with respect to all the parameters in the +model is implemented in the cvodes function. An ability to +select specific entities and parameters for which sensitivities is to be +calculated will be added soon.

  • +
  • To solve a system of differential algebraic systems, the +ida function is provided which is an interface to the +IDA function in SUNDIALS.

  • +
+

As a note, since this package is under active development, the +interfaces of both CVODE and CVODES (i.e., the +function signatures) may change in the future versions. Please keep this +mind if you intend to use sundialr in your applications. In +near future, interface for other solvers from the C library +such asIDAS and ARKODE may also be added.

+
+
+ + + +
+ + + +
+ +
+

+

Site built with pkgdown 2.1.1.

+
+ +
+
+ + + + + + + + diff --git a/docs/articles/my-vignette_files/figure-html/unnamed-chunk-4-1.png b/docs/articles/my-vignette_files/figure-html/unnamed-chunk-4-1.png index 88dc8b0474e60bbf4974840d70739fade491c737..630e5cc01d2bd85207c50ddca35567a4f1cc3d4a 100644 GIT binary patch literal 45396 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 literal 85662 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[s|P`Rl9-sKoXr=p^&&x0RtdhYvA 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 diff --git a/docs/articles/my-vignette_files/header-attrs-2.1/header-attrs.js b/docs/articles/my-vignette_files/header-attrs-2.1/header-attrs.js deleted file mode 100644 index dd57d92..0000000 --- a/docs/articles/my-vignette_files/header-attrs-2.1/header-attrs.js +++ /dev/null @@ -1,12 +0,0 @@ -// Pandoc 2.9 adds attributes on both header and div. We remove the former (to -// be compatible with the behavior of Pandoc < 2.8). -document.addEventListener('DOMContentLoaded', function(e) { - var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); - var i, h, a; - for (i = 0; i < hs.length; i++) { - h = hs[i]; - if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 - a = h.attributes; - while (a.length > 0) h.removeAttribute(a[0].name); - } -}); diff --git a/docs/authors.html b/docs/authors.html index 5b85200..4383a4a 100644 --- a/docs/authors.html +++ b/docs/authors.html @@ -1,66 +1,12 @@ - - - - - - - -Authors • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Authors and Citation • sundialr - - + - - -

+ + - - - + diff --git a/docs/reference/ida.html b/docs/reference/ida.html index 1dde5ce..0f8dc7f 100644 --- a/docs/reference/ida.html +++ b/docs/reference/ida.html @@ -1,67 +1,12 @@ - - - - - - - -ida — ida • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ida — ida • sundialr - - + - -
-
- - -
-
+
@@ -129,75 +59,76 @@

ida

IDA solver to solve stiff DAEs

-
ida(
-  time_vector,
-  IC,
-  IRes,
-  input_function,
-  Parameters,
-  reltolerance = 1e-04,
-  abstolerance = 1e-04
-)
- -

Arguments

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
time_vector

time vector

IC

Initial Value of y

IRes

Inital Value of ydot

input_function

Right Hand Side function of DAEs

Parameters

Parameters input to ODEs

reltolerance

Relative Tolerance (a scalar, default value = 1e-04)

abstolerance

Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)

+
+
ida(
+  time_vector,
+  IC,
+  IRes,
+  input_function,
+  Parameters,
+  reltolerance = 1e-04,
+  abstolerance = 1e-04
+)
+
+ +
+

Arguments

+ + +
time_vector
+

time vector

+ + +
IC
+

Initial Value of y

+ + +
IRes
+

Inital Value of ydot

+ +
input_function
+

Right Hand Side function of DAEs

+ + +
Parameters
+

Parameters input to ODEs

+ + +
reltolerance
+

Relative Tolerance (a scalar, default value = 1e-04)

+ + +
abstolerance
+

Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)

+ +
+
+

Value

+

A data frame. First column is the time-vector, the other columns are values of y in order they are provided.

+
+
-
- +
-
-
-
+
- @@ -143,22 +92,20 @@

Authors

-
- +
+ + - - - + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..53d00ff --- /dev/null +++ b/docs/index.html @@ -0,0 +1,189 @@ + + + + + + + +An Interface to SUNDIALS Ordinary Differential Equation (ODE) Solvers • sundialr + + + + + + + + + + + + +
+
+ + + + +
+
+
+ + + + +
+

sundialr +

+

sundialr is a wrapper around a few of the solvers in the SUNDIALS ODE solving C library produced by the Lawrence Livermore National Laboratory and Southern Methodist University. More information about SUNDIALS can be found here. SUNDIALS is one of the most popular and well-respected ODE solving libraries available and sundialr provides a way to interface some of the SUNDIALS solvers in R.

+

Currently sundialr provides an interface to the serial versions of cvode (for solving ODES), cvodes (for solving ODE with sensitivity equations) and ida (for solving differential-algebraic equations) using the Linear Solver (dense version).

+

A convenience function cvsolve is provided which allows solving a system of equations with multiple discontinutities in solution. An application of such a system of equations would be to simulate the effect of multiple bolus doses of a drug in clinical pharmacokinetics. See the vignette for more details.

+
+
+

What’s new? +

+
+

Release 0.1.4.1 +

+
    +
  • Fixed the linking bug due to multiple defined symbols. No other change.
  • +
+
+
+

Release 0.1.4 +

+
    +
  • This version has version 5.2.0 of SUNDIALS (released in March 2020) at the back end.
  • +
  • A new function cvsolve is added. It allows solving ODEs with multiple discontinuities in the solution. See a complete use case in the vignette.
  • +
  • A pkgdown create site of the package is added.
  • +
  • A hex sticker for the package is released!
  • +
+
+
+

Release 0.1.3 +

+
    +
  • This version has version 4.0.1 of SUNDIALS (released in Dec 2018) at the back end.
  • +
  • An interface to CVODES is added. It calculates forward sensitivities w.r.t all parameters of the ODE system.
  • +
  • Parameters can now be defined as an input parameter to the ODE function. This will allow performing parameter optimization via numerical optimizers.
  • +
+
+
+
+
+ + +
+ + +
+ +
+

+

Site built with pkgdown 2.1.1.

+
+ +
+
+ + + + + + + + diff --git a/docs/pkgdown.css b/docs/pkgdown.css index c01e592..80ea5b8 100644 --- a/docs/pkgdown.css +++ b/docs/pkgdown.css @@ -56,8 +56,10 @@ img.icon { float: right; } -img { +/* Ensure in-page images don't run outside their container */ +.contents img { max-width: 100%; + height: auto; } /* Fix bug in bootstrap (only seen in firefox) */ @@ -78,11 +80,10 @@ dd { /* Section anchors ---------------------------------*/ a.anchor { - margin-left: -30px; - display:inline-block; - width: 30px; - height: 30px; - visibility: hidden; + display: none; + margin-left: 5px; + width: 20px; + height: 20px; background-image: url(./link.svg); background-repeat: no-repeat; @@ -90,17 +91,15 @@ a.anchor { background-position: center center; } -.hasAnchor:hover a.anchor { - visibility: visible; -} - -@media (max-width: 767px) { - .hasAnchor:hover a.anchor { - visibility: hidden; - } +h1:hover .anchor, +h2:hover .anchor, +h3:hover .anchor, +h4:hover .anchor, +h5:hover .anchor, +h6:hover .anchor { + display: inline-block; } - /* Fixes for fixed navbar --------------------------*/ .contents h1, .contents h2, .contents h3, .contents h4 { @@ -244,14 +243,14 @@ nav[data-toggle='toc'] .nav .nav > .active:focus > a { .ref-index th {font-weight: normal;} -.ref-index td {vertical-align: top;} +.ref-index td {vertical-align: top; min-width: 100px} .ref-index .icon {width: 40px;} .ref-index .alias {width: 40%;} .ref-index-icons .alias {width: calc(40% - 40px);} .ref-index .title {width: 60%;} .ref-arguments th {text-align: right; padding-right: 10px;} -.ref-arguments th, .ref-arguments td {vertical-align: top;} +.ref-arguments th, .ref-arguments td {vertical-align: top; min-width: 100px} .ref-arguments .name {width: 20%;} .ref-arguments .desc {width: 80%;} @@ -264,31 +263,26 @@ table { /* Syntax highlighting ---------------------------------------------------- */ -pre { - word-wrap: normal; - word-break: normal; - border: 1px solid #eee; -} - -pre, code { +pre, code, pre code { background-color: #f8f8f8; color: #333; } +pre, pre code { + white-space: pre-wrap; + word-break: break-all; + overflow-wrap: break-word; +} -pre code { - overflow: auto; - word-wrap: normal; - white-space: pre; +pre { + border: 1px solid #eee; } -pre .img { +pre .img, pre .r-plt { margin: 5px 0; } -pre .img img { +pre .img img, pre .r-plt img { background-color: #fff; - display: block; - height: auto; } code a, pre a { @@ -305,9 +299,8 @@ a.sourceLine:hover { .kw {color: #264D66;} /* keyword */ .co {color: #888888;} /* comment */ -.message { color: black; font-weight: bolder;} -.error { color: orange; font-weight: bolder;} -.warning { color: #6A0366; font-weight: bolder;} +.error {font-weight: bolder;} +.warning {font-weight: bolder;} /* Clipboard --------------------------*/ @@ -365,3 +358,27 @@ mark { content: ""; } } + +/* Section anchors --------------------------------- + Added in pandoc 2.11: https://github.com/jgm/pandoc-templates/commit/9904bf71 +*/ + +div.csl-bib-body { } +div.csl-entry { + clear: both; +} +.hanging-indent div.csl-entry { + margin-left:2em; + text-indent:-2em; +} +div.csl-left-margin { + min-width:2em; + float:left; +} +div.csl-right-inline { + margin-left:2em; + padding-left:1em; +} +div.csl-indent { + margin-left: 2em; +} diff --git a/docs/pkgdown.js b/docs/pkgdown.js index 7e7048f..6f0eee4 100644 --- a/docs/pkgdown.js +++ b/docs/pkgdown.js @@ -80,7 +80,7 @@ $(document).ready(function() { var copyButton = ""; - $(".examples, div.sourceCode").addClass("hasCopyButton"); + $("div.sourceCode").addClass("hasCopyButton"); // Insert copy buttons: $(copyButton).prependTo(".hasCopyButton"); @@ -91,7 +91,7 @@ // Initialize clipboard: var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { text: function(trigger) { - return trigger.parentNode.textContent; + return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); } }); diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 17645ac..6e32b98 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -1,7 +1,6 @@ -pandoc: 2.9.2.1 -pkgdown: 1.5.1 +pandoc: '3.2' +pkgdown: 2.1.1 pkgdown_sha: ~ articles: my-vignette: my-vignette.html -last_built: 2020-05-31T18:50Z - +last_built: 2024-11-10T21:17Z diff --git a/docs/reference/cvode.html b/docs/reference/cvode.html index 565609d..99110cd 100644 --- a/docs/reference/cvode.html +++ b/docs/reference/cvode.html @@ -1,67 +1,12 @@ - - - - - - - -cvode — cvode • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -cvode — cvode • sundialr - - + - -
-
- - -
-
+
@@ -129,127 +59,131 @@

cvode

CVODE solver to solve stiff ODEs

-
cvode(
-  time_vector,
-  IC,
-  input_function,
-  Parameters,
-  reltolerance = 1e-04,
-  abstolerance = 1e-04
-)
- -

Arguments

- - - - - - - - - - - - - - - - - - - - - - - - - - -
time_vector

time vector

IC

Initial Conditions

input_function

Right Hand Side function of ODEs

Parameters

Parameters input to ODEs

reltolerance

Relative Tolerance (a scalar, default value = 1e-04)

abstolerance

Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)

- - -

Examples

-
# Example of solving a set of ODEs with cvode function -# ODEs described by an R function -ODE_R <- function(t, y, p){ - - # vector containing the right hand side gradients - ydot = vector(mode = "numeric", length = length(y)) - - # R indices start from 1 - ydot[1] = -p[1]*y[1] + p[2]*y[2]*y[3] - ydot[2] = p[1]*y[1] - p[2]*y[2]*y[3] - p[3]*y[2]*y[2] - ydot[3] = p[3]*y[2]*y[2] - - # ydot[1] = -0.04 * y[1] + 10000 * y[2] * y[3] - # ydot[3] = 30000000 * y[2] * y[2] - # ydot[2] = -ydot[1] - ydot[3] - - ydot - -} - -# ODEs can also be described using Rcpp -Rcpp::sourceCpp(code = ' - - #include <Rcpp.h> - using namespace Rcpp; - - // ODE functions defined using Rcpp - // [[Rcpp::export]] - NumericVector ODE_Rcpp (double t, NumericVector y, NumericVector p){ - - // Initialize ydot filled with zeros - NumericVector ydot(y.length()); - - ydot[0] = -p[0]*y[0] + p[1]*y[1]*y[2]; - ydot[1] = p[0]*y[0] - p[1]*y[1]*y[2] - p[2]*y[1]*y[1]; - ydot[2] = p[2]*y[1]*y[1]; - - return ydot; - - }') - - - -# R code to genrate time vector, IC and solve the equations -time_vec <- c(0.0, 0.4, 4.0, 40.0, 4E2, 4E3, 4E4, 4E5, 4E6, 4E7, 4E8, 4E9, 4E10) -IC <- c(1,0,0) -params <- c(0.04, 10000, 30000000) -reltol <- 1e-04 -abstol <- c(1e-8,1e-14,1e-6) - -## Solving the ODEs using cvode function -df1 <- cvode(time_vec, IC, ODE_R , params, reltol, abstol) ## using R -df2 <- cvode(time_vec, IC, ODE_Rcpp , params, reltol, abstol) ## using Rcpp - -## Check that both solutions are identical -# identical(df1, df2)
+
+
cvode(
+  time_vector,
+  IC,
+  input_function,
+  Parameters,
+  reltolerance = 1e-04,
+  abstolerance = 1e-04
+)
+
+ +
+

Arguments

+ + +
time_vector
+

time vector

+ + +
IC
+

Initial Conditions

+ + +
input_function
+

Right Hand Side function of ODEs

+ + +
Parameters
+

Parameters input to ODEs

+ + +
reltolerance
+

Relative Tolerance (a scalar, default value = 1e-04)

+ + +
abstolerance
+

Absolute Tolerance (a scalar or vector with length equal to ydot (dy/dx), default = 1e-04)

+ +
+
+

Value

+

A data frame. First column is the time-vector, the other columns are values of y in order they are provided.

+
+ +
+

Examples

+
# Example of solving a set of ODEs with cvode function
+# ODEs described by an R function
+ODE_R <- function(t, y, p){
+
+  # vector containing the right hand side gradients
+  ydot = vector(mode = "numeric", length = length(y))
+
+  # R indices start from 1
+  ydot[1] = -p[1]*y[1] + p[2]*y[2]*y[3]
+  ydot[2] = p[1]*y[1] - p[2]*y[2]*y[3] - p[3]*y[2]*y[2]
+  ydot[3] = p[3]*y[2]*y[2]
+
+  # ydot[1] = -0.04 * y[1] + 10000 * y[2] * y[3]
+  # ydot[3] = 30000000 * y[2] * y[2]
+  # ydot[2] = -ydot[1] - ydot[3]
+
+  ydot
+
+}
+
+# ODEs can also be described using Rcpp
+Rcpp::sourceCpp(code = '
+
+                #include <Rcpp.h>
+                using namespace Rcpp;
+
+                // ODE functions defined using Rcpp
+                // [[Rcpp::export]]
+                NumericVector ODE_Rcpp (double t, NumericVector y, NumericVector p){
+
+                // Initialize ydot filled with zeros
+                NumericVector ydot(y.length());
+
+                ydot[0] = -p[0]*y[0] + p[1]*y[1]*y[2];
+                ydot[1] = p[0]*y[0] - p[1]*y[1]*y[2] - p[2]*y[1]*y[1];
+                ydot[2] = p[2]*y[1]*y[1];
+
+                return ydot;
+
+                }')
+
+
+
+# R code to genrate time vector, IC and solve the equations
+time_vec <- c(0.0, 0.4, 4.0, 40.0, 4E2, 4E3, 4E4, 4E5, 4E6, 4E7, 4E8, 4E9, 4E10)
+IC <- c(1,0,0)
+params <- c(0.04, 10000, 30000000)
+reltol <- 1e-04
+abstol <- c(1e-8,1e-14,1e-6)
+
+## Solving the ODEs using cvode function
+df1 <- cvode(time_vec, IC, ODE_R , params, reltol, abstol)           ## using R
+df2 <- cvode(time_vec, IC, ODE_Rcpp , params, reltol, abstol)        ## using Rcpp
+
+## Check that both solutions are identical
+# identical(df1, df2)
+
+
+
-
- +
+ + - - - + diff --git a/docs/reference/cvodes.html b/docs/reference/cvodes.html index c476440..78ffc32 100644 --- a/docs/reference/cvodes.html +++ b/docs/reference/cvodes.html @@ -1,67 +1,12 @@ - - - - - - - -cvodes — cvodes • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -cvodes — cvodes • sundialr - + - - -
-
- - -
-
+
@@ -129,137 +59,141 @@

cvodes

CVODES solver to solve ODEs and calculate sensitivities

-
cvodes(
-  time_vector,
-  IC,
-  input_function,
-  Parameters,
-  reltolerance = 1e-04,
-  abstolerance = 1e-04,
-  SensType = "STG",
-  ErrCon = "F"
-)
- -

Arguments

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
time_vector

time vector

IC

Initial Conditions

input_function

Right Hand Side function of ODEs

Parameters

Parameters input to ODEs

reltolerance

Relative Tolerance (a scalar, default value = 1e-04)

abstolerance

Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)

SensType

Sensitivity Type - allowed values are Staggered (default)", "STG" (for Staggered) or "SIM" (for Simultaneous)

ErrCon

Error Control - allowed values are TRUE or FALSE (default)

- - -

Examples

-
# Example of solving a set sensitivity equations for ODEs with cvodes function -# ODEs described by an R function -ODE_R <- function(t, y, p){ - - # vector containing the right hand side gradients - ydot = vector(mode = "numeric", length = length(y)) - - # R indices start from 1 - ydot[1] = -p[1]*y[1] + p[2]*y[2]*y[3] - ydot[2] = p[1]*y[1] - p[2]*y[2]*y[3] - p[3]*y[2]*y[2] - ydot[3] = p[3]*y[2]*y[2] - - # ydot[1] = -0.04 * y[1] + 10000 * y[2] * y[3] - # ydot[3] = 30000000 * y[2] * y[2] - # ydot[2] = -ydot[1] - ydot[3] - - ydot - -} - -# ODEs can also be described using Rcpp -Rcpp::sourceCpp(code = ' - - #include <Rcpp.h> - using namespace Rcpp; - - // ODE functions defined using Rcpp - // [[Rcpp::export]] - NumericVector ODE_Rcpp (double t, NumericVector y, NumericVector p){ - - // Initialize ydot filled with zeros - NumericVector ydot(y.length()); - - ydot[0] = -p[0]*y[0] + p[1]*y[1]*y[2]; - ydot[1] = p[0]*y[0] - p[1]*y[1]*y[2] - p[2]*y[1]*y[1]; - ydot[2] = p[2]*y[1]*y[1]; - - return ydot; - - }') - - - -# R code to genrate time vector, IC and solve the equations -time_vec <- c(0.0, 0.4, 4.0, 40.0, 4E2, 4E3, 4E4, 4E5, 4E6, 4E7, 4E8, 4E9, 4E10) -IC <- c(1,0,0) -params <- c(0.04, 10000, 30000000) -reltol <- 1e-04 -abstol <- c(1e-8,1e-14,1e-6) - -## Solving the ODEs and Sensitivities using cvodes function -df1 <- cvodes(time_vec, IC, ODE_R , params, reltol, abstol,"STG",FALSE) ## using R -df2 <- cvodes(time_vec, IC, ODE_Rcpp , params, reltol, abstol,"STG",FALSE) ## using Rcpp - -## Check that both solutions are identical -# identical(df1, df2)
+
+
cvodes(
+  time_vector,
+  IC,
+  input_function,
+  Parameters,
+  reltolerance = 1e-04,
+  abstolerance = 1e-04,
+  SensType = "STG",
+  ErrCon = "F"
+)
+
+ +
+

Arguments

+ + +
time_vector
+

time vector

+ + +
IC
+

Initial Conditions

+ + +
input_function
+

Right Hand Side function of ODEs

+ + +
Parameters
+

Parameters input to ODEs

+ + +
reltolerance
+

Relative Tolerance (a scalar, default value = 1e-04)

+ + +
abstolerance
+

Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)

+ + +
SensType
+

Sensitivity Type - allowed values are "STG" (for Staggered, default) or "SIM" (for Simultaneous)

+ + +
ErrCon
+

Error Control - allowed values are TRUE or FALSE (default)

+ +
+
+

Value

+

A data frame. First column is the time-vector, the next y * p columns are sensitivities of y1 w.r.t all parameters, then y2 w.r.t all parameters etc. y is the state vector, p is the parameter vector

+
+ +
+

Examples

+
# Example of solving a set sensitivity equations for ODEs with cvodes function
+# ODEs described by an R function
+ODE_R <- function(t, y, p){
+
+  # vector containing the right hand side gradients
+  ydot = vector(mode = "numeric", length = length(y))
+
+  # R indices start from 1
+  ydot[1] = -p[1]*y[1] + p[2]*y[2]*y[3]
+  ydot[2] = p[1]*y[1] - p[2]*y[2]*y[3] - p[3]*y[2]*y[2]
+  ydot[3] = p[3]*y[2]*y[2]
+
+  # ydot[1] = -0.04 * y[1] + 10000 * y[2] * y[3]
+  # ydot[3] = 30000000 * y[2] * y[2]
+  # ydot[2] = -ydot[1] - ydot[3]
+
+  ydot
+
+}
+
+# ODEs can also be described using Rcpp
+Rcpp::sourceCpp(code = '
+
+                #include <Rcpp.h>
+                using namespace Rcpp;
+
+                // ODE functions defined using Rcpp
+                // [[Rcpp::export]]
+                NumericVector ODE_Rcpp (double t, NumericVector y, NumericVector p){
+
+                // Initialize ydot filled with zeros
+                NumericVector ydot(y.length());
+
+                ydot[0] = -p[0]*y[0] + p[1]*y[1]*y[2];
+                ydot[1] = p[0]*y[0] - p[1]*y[1]*y[2] - p[2]*y[1]*y[1];
+                ydot[2] = p[2]*y[1]*y[1];
+
+                return ydot;
+
+                }')
+
+
+
+# R code to genrate time vector, IC and solve the equations
+time_vec <- c(0.0, 0.4, 4.0, 40.0, 4E2, 4E3, 4E4, 4E5, 4E6, 4E7, 4E8, 4E9, 4E10)
+IC <- c(1,0,0)
+params <- c(0.04, 10000, 30000000)
+reltol <- 1e-04
+abstol <- c(1e-8,1e-14,1e-6)
+
+## Solving the ODEs and Sensitivities using cvodes function
+df1 <- cvodes(time_vec, IC, ODE_R , params, reltol, abstol,"STG",FALSE)           ## using R
+df2 <- cvodes(time_vec, IC, ODE_Rcpp , params, reltol, abstol,"STG",FALSE)        ## using Rcpp
+
+## Check that both solutions are identical
+# identical(df1, df2)
+
+
+
-
- +
+ + - - - + diff --git a/docs/reference/cvsolve.html b/docs/reference/cvsolve.html index 25ad7e7..c9b9cc1 100644 --- a/docs/reference/cvsolve.html +++ b/docs/reference/cvsolve.html @@ -1,67 +1,12 @@ - - - - - - - -cvsolve — cvsolve • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -cvsolve — cvsolve • sundialr + - - - -
-
- - -
-
+
@@ -129,102 +59,106 @@

cvsolve

CVSOLVE solver to solve stiff ODEs with discontinuties

-
cvsolve(
-  time_vector,
-  IC,
-  input_function,
-  Parameters,
-  Events = NULL,
-  reltolerance = 1e-04,
-  abstolerance = 1e-04
-)
- -

Arguments

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
time_vector

time vector

IC

Initial Conditions

input_function

Right Hand Side function of ODEs

Parameters

Parameters input to ODEs

Events

Discontinuities in the solution (a DataFrame, default value is NULL)

reltolerance

Relative Tolerance (a scalar, default value = 1e-04)

abstolerance

Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)

- - -

Examples

-
# Example of solving a set of ODEs with multiple discontinuities using cvsolve -# A simple One dimensional equation, y = -0.1 * y -# ODEs described by an R function -ODE_R <- function(t, y, p){ - - # vector containing the right hand side gradients - ydot = vector(mode = "numeric", length = length(y)) - - # R indices start from 1 - ydot[1] = -p[1]*y[1] - - ydot - -} - -# R code to generate time vector, IC and solve the equations -TSAMP <- seq(from = 0, to = 100, by = 0.1) # sampling time points -IC <- c(1) -params <- c(0.1) - -# A dataset describing the dosing at times at which additions to y[1] are to be done -# Names of the columns don't matter, but they MUST be in the order of state index, -# times and Values at discontinuity. -TDOSE <- data.frame(ID = 1, TIMES = c(0, 10, 20, 30, 40, 50), VAL = 100) -df1 <- cvsolve(TSAMP, c(1), ODE_R, params) # solving without any discontinuity -df2 <- cvsolve(TSAMP, c(1), ODE_R, params, TDOSE) # solving with discontinuity
+
+
cvsolve(
+  time_vector,
+  IC,
+  input_function,
+  Parameters,
+  Events = NULL,
+  reltolerance = 1e-04,
+  abstolerance = 1e-04
+)
+
+ +
+

Arguments

+ + +
time_vector
+

time vector

+ + +
IC
+

Initial Conditions

+ + +
input_function
+

Right Hand Side function of ODEs

+ + +
Parameters
+

Parameters input to ODEs

+ + +
Events
+

Discontinuities in the solution (a DataFrame, default value is NULL)

+ + +
reltolerance
+

Relative Tolerance (a scalar, default value = 1e-04)

+ + +
abstolerance
+

Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)

+ +
+
+

Value

+

A data frame. First column is the time-vector, the other columns are values of y in order they are provided.

+
+ +
+

Examples

+
# Example of solving a set of ODEs with multiple discontinuities using cvsolve
+# A simple One dimensional equation, y = -0.1 * y
+# ODEs described by an R function
+ODE_R <- function(t, y, p){
+
+  # vector containing the right hand side gradients
+  ydot = vector(mode = "numeric", length = length(y))
+
+  # R indices start from 1
+  ydot[1] = -p[1]*y[1]
+
+  ydot
+
+}
+
+# R code to generate time vector, IC and solve the equations
+TSAMP <- seq(from = 0, to = 100, by = 0.1)      # sampling time points
+IC <- c(1)
+params <- c(0.1)
+
+# A dataset describing the dosing at times at which additions to y[1] are to be done
+# Names of the columns don't matter, but they MUST be in the order of state index,
+# times and Values at discontinuity.
+TDOSE <- data.frame(ID = 1, TIMES = c(0, 10, 20, 30, 40, 50), VAL = 100)
+df1 <- cvsolve(TSAMP, c(1), ODE_R, params)         # solving without any discontinuity
+df2 <- cvsolve(TSAMP, c(1), ODE_R, params, TDOSE)  # solving with discontinuity
+
+
+
-
- +
+ + - - - + diff --git a/docs/reference/index.html b/docs/reference/index.html index 9ac5094..62701e0 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -1,66 +1,12 @@ - - - - - - - -Function reference • sundialr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Package index • sundialr + - - - -
-
- - -
-
+
- - - - - - - - - - -
-

All functions

+ - - - - - - - - - - - - - - - - - - - - -
+

All functions

+

cvode()

cvode

+

cvodes()

cvodes

+

cvsolve()

cvsolve

+

ida()

ida

- +
+
-
- +
+ + - - - + diff --git a/docs/sitemap.xml b/docs/sitemap.xml new file mode 100644 index 0000000..2c72a3f --- /dev/null +++ b/docs/sitemap.xml @@ -0,0 +1,14 @@ + +/404.html +/LICENSE-text.html +/articles/index.html +/articles/my-vignette.html +/authors.html +/index.html +/reference/cvode.html +/reference/cvodes.html +/reference/cvsolve.html +/reference/ida.html +/reference/index.html + + diff --git a/inst/include/cvodes/cvodes.h b/inst/include/cvodes/cvodes.h index 813ded3..8af98fc 100644 --- a/inst/include/cvodes/cvodes.h +++ b/inst/include/cvodes/cvodes.h @@ -579,7 +579,8 @@ SUNDIALS_EXPORT int CVodeGetAdjCheckPointsInfo(void* cvode_mem, CVadjCheckPointRec* ckpnt); /* CVLS interface function that depends on CVRhsFn */ -int CVodeSetJacTimesRhsFnB(void* cvode_mem, int which, CVRhsFn jtimesRhsFn); +SUNDIALS_EXPORT int CVodeSetJacTimesRhsFnB(void* cvode_mem, int which, + CVRhsFn jtimesRhsFn); /* Undocumented Optional Output Functions For Backward Problems */ diff --git a/inst/include/nvector/nvector_parallel.h b/inst/include/nvector/nvector_parallel.h index e05e4c7..385ca52 100644 --- a/inst/include/nvector/nvector_parallel.h +++ b/inst/include/nvector/nvector_parallel.h @@ -117,10 +117,8 @@ void N_VPrint_Parallel(N_Vector v); SUNDIALS_EXPORT void N_VPrintFile_Parallel(N_Vector v, FILE* outfile); -static inline N_Vector_ID N_VGetVectorID_Parallel(N_Vector v) -{ - return SUNDIALS_NVEC_PARALLEL; -} +SUNDIALS_EXPORT +N_Vector_ID N_VGetVectorID_Parallel(N_Vector v); SUNDIALS_EXPORT N_Vector N_VCloneEmpty_Parallel(N_Vector w); diff --git a/inst/include/nvector/nvector_serial.h b/inst/include/nvector/nvector_serial.h index bcbc995..988e090 100644 --- a/inst/include/nvector/nvector_serial.h +++ b/inst/include/nvector/nvector_serial.h @@ -102,10 +102,8 @@ void N_VPrint_Serial(N_Vector v); SUNDIALS_EXPORT void N_VPrintFile_Serial(N_Vector v, FILE* outfile); -static inline N_Vector_ID N_VGetVectorID_Serial(N_Vector v) -{ - return SUNDIALS_NVEC_SERIAL; -} +SUNDIALS_EXPORT +N_Vector_ID N_VGetVectorID_Serial(N_Vector v); SUNDIALS_EXPORT N_Vector N_VCloneEmpty_Serial(N_Vector w); diff --git a/inst/include/sundials/sundials_adaptcontroller.h b/inst/include/sundials/sundials_adaptcontroller.h index 9cde03b..b27d7c7 100644 --- a/inst/include/sundials/sundials_adaptcontroller.h +++ b/inst/include/sundials/sundials_adaptcontroller.h @@ -91,6 +91,10 @@ struct _generic_SUNAdaptController SUNDIALS_EXPORT SUNAdaptController SUNAdaptController_NewEmpty(SUNContext sunctx); +/* Function to free a generic SUNAdaptController (assumes content is already empty) */ +SUNDIALS_EXPORT +void SUNAdaptController_DestroyEmpty(SUNAdaptController C); + /* Function to report the type of a SUNAdaptController object. */ SUNDIALS_EXPORT SUNAdaptController_Type SUNAdaptController_GetType(SUNAdaptController C); diff --git a/inst/include/sundials/sundials_config.in b/inst/include/sundials/sundials_config.in index b56ae8f..def1a87 100644 --- a/inst/include/sundials/sundials_config.in +++ b/inst/include/sundials/sundials_config.in @@ -60,6 +60,7 @@ #cmakedefine SUNDIALS_C_COMPILER_HAS_ATTRIBUTE_ASSUME #cmakedefine SUNDIALS_C_COMPILER_HAS_BUILTIN_ASSUME #cmakedefine SUNDIALS_C_COMPILER_HAS_ASSUME +#cmakedefine SUNDIALS_C_COMPILER_HAS_ATTRIBUTE_UNUSED /* Define precision of SUNDIALS data type 'sunrealtype' * Depending on the precision level, one of the following @@ -220,7 +221,7 @@ #cmakedefine SUNDIALS_GINKGO_BACKENDS_HIP #cmakedefine SUNDIALS_GINKGO_BACKENDS_OMP #cmakedefine SUNDIALS_GINKGO_BACKENDS_REF -#cmakedefine SUNDIALS_GINKGO_BACKENDS_DPCPP +#cmakedefine SUNDIALS_GINKGO_BACKENDS_SYCL /* MAGMA backends */ #cmakedefine SUNDIALS_MAGMA_BACKENDS_CUDA diff --git a/inst/include/sundials/sundials_hip_policies.hpp b/inst/include/sundials/sundials_hip_policies.hpp index d5592ae..f7950f8 100644 --- a/inst/include/sundials/sundials_hip_policies.hpp +++ b/inst/include/sundials/sundials_hip_policies.hpp @@ -27,10 +27,12 @@ namespace sundials { namespace hip { -#if defined(__HIP_PLATFORM_HCC__) +#if defined(__HIP_PLATFORM_HCC__) || defined(__HIP_PLATFORM_AMD__) constexpr const sunindextype WARP_SIZE = 64; -#elif defined(__HIP_PLATFORM_NVCC__) +#elif defined(__HIP_PLATFORM_NVCC__) || defined(__HIP_PLATFORM_NVDIA__) constexpr const sunindextype WARP_SIZE = 32; +#else +#error "Unknown HIP_PLATFORM, report to github.com/LLNL/sundials/issues" #endif constexpr const sunindextype MAX_BLOCK_SIZE = 1024; constexpr const sunindextype MAX_WARPS = MAX_BLOCK_SIZE / WARP_SIZE; diff --git a/inst/include/sunlinsol/sunlinsol_ginkgo.hpp b/inst/include/sunlinsol/sunlinsol_ginkgo.hpp index d801cb1..2817d42 100644 --- a/inst/include/sunlinsol/sunlinsol_ginkgo.hpp +++ b/inst/include/sunlinsol/sunlinsol_ginkgo.hpp @@ -332,11 +332,19 @@ class LinearSolver : public ConvertibleTo } iter_count_ = static_cast(logger->get_num_iterations()); +#if (GKO_VERSION_MAJOR == 1) && (GKO_VERSION_MINOR < 6) GkoExec()->get_master()->copy_from(gko::lend(GkoExec()), 1, gko::as( logger->get_residual_norm()) ->get_const_values(), &res_norm_); +#else + GkoExec()->get_master()->copy_from(GkoExec(), 1, + gko::as( + logger->get_residual_norm()) + ->get_const_values(), + &res_norm_); +#endif return result; } diff --git a/inst/include/sunlinsol/sunlinsol_kokkosdense.hpp b/inst/include/sunlinsol/sunlinsol_kokkosdense.hpp index ba109bb..2fbda95 100644 --- a/inst/include/sunlinsol/sunlinsol_kokkosdense.hpp +++ b/inst/include/sunlinsol/sunlinsol_kokkosdense.hpp @@ -40,12 +40,12 @@ class DenseLinearSolver; namespace impl { -SUNLinearSolver_Type SUNLinSolGetType_KokkosDense(SUNLinearSolver S) +static SUNLinearSolver_Type SUNLinSolGetType_KokkosDense(SUNLinearSolver S) { return SUNLINEARSOLVER_DIRECT; } -SUNLinearSolver_ID SUNLinSolGetID_KokkosDense(SUNLinearSolver S) +static SUNLinearSolver_ID SUNLinSolGetID_KokkosDense(SUNLinearSolver S) { return SUNLINEARSOLVER_KOKKOSDENSE; } diff --git a/inst/include/sunmatrix/sunmatrix_kokkosdense.hpp b/inst/include/sunmatrix/sunmatrix_kokkosdense.hpp index b2b82ad..15bf696 100644 --- a/inst/include/sunmatrix/sunmatrix_kokkosdense.hpp +++ b/inst/include/sunmatrix/sunmatrix_kokkosdense.hpp @@ -46,7 +46,7 @@ inline MatrixType* GetDenseMat(SUNMatrix A) namespace impl { -SUNMatrix_ID SUNMatGetID_KokkosDense(SUNMatrix A) +static SUNMatrix_ID SUNMatGetID_KokkosDense(SUNMatrix A) { return SUNMATRIX_KOKKOSDENSE; } @@ -210,7 +210,11 @@ SUNErrCode SUNMatMatvec_KokkosDense(SUNMatrix A, N_Vector x, N_Vector y) Kokkos::subview(y_data, Kokkos::pair(idx * rows, (idx + 1) * rows)); +#if KOKKOSKERNELS_VERSION_MAJOR > 3 + KokkosBlas::TeamVectorGemv< +#else KokkosBatched::TeamVectorGemv< +#endif member_type, KokkosBatched::Trans::NoTranspose, KokkosBatched::Algo::Gemv::Unblocked>::invoke(team_member, SUN_RCONST(1.0), diff --git a/man/cvodes.Rd b/man/cvodes.Rd index ed9be88..16ec095 100644 --- a/man/cvodes.Rd +++ b/man/cvodes.Rd @@ -28,7 +28,7 @@ cvodes( \item{abstolerance}{Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04)} -\item{SensType}{Sensitivity Type - allowed values are Staggered (default)", "STG" (for Staggered) or "SIM" (for Simultaneous)} +\item{SensType}{Sensitivity Type - allowed values are "STG" (for Staggered, default) or "SIM" (for Simultaneous)} \item{ErrCon}{Error Control - allowed values are TRUE or FALSE (default)} } diff --git a/src/Makevars b/src/Makevars index 080e23b..34a4a8b 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,6 +1,6 @@ CXX=clang++ -PKG_CPPFLAGS= -I../inst/include/ -I./inst/ -DHAVE_CONFIG_H -DARMA_USE_CXX11 -I./sundials/sundials -#CXX_STD = CXX11 +PKG_CPPFLAGS= -I../inst/include/ -I./inst/ -DHAVE_CONFIG_H -DARMA_USE_CXX17 -I./sundials/sundials +CXX_STD = CXX17 PKG_LIBS= $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -L../inst/ -lsundials_all LIBS=-L./ -L../inst/ diff --git a/src/cvode.cpp b/src/cvode.cpp index 3b9d752..e5cfd37 100644 --- a/src/cvode.cpp +++ b/src/cvode.cpp @@ -57,154 +57,149 @@ using namespace Rcpp; //'@returns A data frame. First column is the time-vector, the other columns are values of y in order they are provided. //'@example /inst/examples/cv_Roberts_dns.r // [[Rcpp::export]] -NumericMatrix cvode(NumericVector time_vector, NumericVector IC, SEXP input_function, - NumericVector Parameters, - double reltolerance = 0.0001, NumericVector abstolerance = 0.0001){ - - int flag; - - int time_vec_len = time_vector.length(); - double time; - int NOUT = time_vec_len; - sunrealtype T0 = SUN_RCONST(time_vector[0]); //RCONST(0.0); // Initial Time - - // Initial Conditions - int y_len = IC.length(); - - // Relative tolerance - sunrealtype reltol = reltolerance; - - // Absolute tolerance - int abstol_len = abstolerance.length(); - - // absolute tolerance is either length == 1 or equal to length of IC - // If abstol is not equal to 1 and abstol is not equal to IC, then stop - if(abstol_len != 1 && abstol_len != y_len){ - stop("Absolute tolerance must be a scalar or a vector of same length as IC \n"); - } - - // Set the vector absolute tolerance ----------------------------------------- - // abstol must be same length as IC - SUNContext sunctx; - SUNContext_Create(SUN_COMM_NULL, &sunctx); - N_Vector abstol = N_VNew_Serial(y_len, sunctx); - sunrealtype *abstol_ptr = N_VGetArrayPointer(abstol); - if(abstol_len == 1){ - // if a scalar is provided - use it to make a vector with same values - for (int i = 0; i >(Parameters), - reltolerance, abstolerance}; - - // setting the user_data in rhs function - flag = CVodeSetUserData(cvode_mem, (void*)&my_rhs_function); - if (check_retval(&flag, "CVodeSetUserData", 1)) { stop("Stopping cvodes, something went wrong in setting user data!"); } - - /* Allocate space for CVODES */ - flag = CVodeInit(cvode_mem, rhs_function_sens, T0, y0); - if (check_retval(&flag, "CVodeInit", 1)) { stop("Stopping cvodes, something went wrong in allocating space for CVODES!"); } - - /* Use private function to compute error weights */ - flag = CVodeWFtolerances(cvode_mem, ewt); - if (check_retval(&flag, "CVodeSetEwtFn", 1)) { stop("Stopping cvodes, something went wrong in computing error weights!"); } - - /* Create dense SUNMatrix */ - sunindextype y_len_M = y_len; - SUNMatrix SM = SUNDenseMatrix(y_len_M, y_len_M, sunctx); - if (check_retval((void *)SM, "SUNDenseMatrix", 0)) { stop("Stopping cvodes, something went wrong in setting SUNDenseMatrix!"); } - - /* Create dense SUNLinearSolver */ - SUNLinearSolver LS = SUNLinSol_Dense(y0, SM, sunctx); - if (check_retval((void *)LS, "SUNLinSol_Dense", 0)) { stop("Stopping cvodes, something went wrong in setting Linear Solver!"); } - - /* Attach the matrix and linear solver */ - flag = CVodeSetLinearSolver(cvode_mem, LS, SM); - if (check_retval(&flag, "CVodeSetLinearSolver", 1)) { stop("Stopping cvodes, something went wrong in attaching SUNDenseMatrix and Linear Solver!"); } - - if (check_retval((void *)yS, "N_VCloneVectorArray", 0)) { stop("Stopping cvodes, something went wrong in setting Sensitivity Array!"); } - for (int is=0;isparams[0]); - flag = CVodeSetSensParams(cvode_mem, (my_rhs_function.params).begin(), Parameters.begin(), NULL); // double *y = x.begin() - if (check_retval(&flag, "CVodeSetSensParams", 1)) { stop("Stopping cvodes, something went wrong in setting Sensitivity Parameters!"); } - - // First row for initial conditions, First column is for time - int y_len_1 = y_len + 1; - NumericMatrix soln(Dimension(time_vec_len,y_len_1)); - - // fill the first row of soln matrix with Initial Conditions - soln(0,0) = time_vector[0]; // get the first time value - for(int i = 0; i >(Parameters), + reltolerance, abstolerance}; + + // setting the user_data in rhs function + flag = CVodeSetUserData(cvode_mem, (void*)&my_rhs_function); + if (check_retval(&flag, "CVodeSetUserData", 1)) { stop("Stopping cvodes, something went wrong in setting user data!"); } + + /* Allocate space for CVODES */ + flag = CVodeInit(cvode_mem, rhs_function_sens, T0, y0); + if (check_retval(&flag, "CVodeInit", 1)) { stop("Stopping cvodes, something went wrong in allocating space for CVODES!"); } + + /* Use private function to compute error weights */ + flag = CVodeWFtolerances(cvode_mem, ewt); + if (check_retval(&flag, "CVodeSetEwtFn", 1)) { stop("Stopping cvodes, something went wrong in computing error weights!"); } + + /* Create dense SUNMatrix */ + sunindextype y_len_M = y_len; + SUNMatrix SM = SUNDenseMatrix(y_len_M, y_len_M, sunctx); + if (check_retval((void *)SM, "SUNDenseMatrix", 0)) { stop("Stopping cvodes, something went wrong in setting SUNDenseMatrix!"); } + + /* Create dense SUNLinearSolver */ + SUNLinearSolver LS = SUNLinSol_Dense(y0, SM, sunctx); + if (check_retval((void *)LS, "SUNLinSol_Dense", 0)) { stop("Stopping cvodes, something went wrong in setting Linear Solver!"); } + + /* Attach the matrix and linear solver */ + flag = CVodeSetLinearSolver(cvode_mem, LS, SM); + if (check_retval(&flag, "CVodeSetLinearSolver", 1)) { stop("Stopping cvodes, something went wrong in attaching SUNDenseMatrix and Linear Solver!"); } + + if (check_retval((void *)yS, "N_VCloneVectorArray", 0)) { stop("Stopping cvodes, something went wrong in setting Sensitivity Array!"); } + for (int is=0;isparams[0]); + flag = CVodeSetSensParams(cvode_mem, (my_rhs_function.params).begin(), Parameters.begin(), NULL); // double *y = x.begin() + if (check_retval(&flag, "CVodeSetSensParams", 1)) { stop("Stopping cvodes, something went wrong in setting Sensitivity Parameters!"); } + + // First row for initial conditions, First column is for time + int y_len_1 = y_len + 1; + NumericMatrix soln(Dimension(time_vec_len,y_len_1)); + + // fill the first row of soln matrix with Initial Conditions + soln(0,0) = time_vector[0]; // get the first time value + for(int i = 0; i y_len) { + stop("The state intex in first column of Events dataframe cannot be higher than number of states"); + } + + // Check that the highest event time is less than or equal to the last time point + Rcpp::NumericVector EventsDF_time_col = Events_DF[1]; + if(Rcpp::max(EventsDF_time_col) > Rcpp::max(time_vector)) { + stop("Event cannot happen after the last time point. See the third column of the Events dataframe."); + } + + // Sort the Event DataFrame to combine discontinuities and sampling data points TCOMB = sorted_times(Events_DF, time_vector, NSTATES); // get the sorted Combined time matrix // Decrease State index by 1 to convert R state index to internal cpp index, all sampling times get state index of -1 for(int i = 0; i < TCOMB.nrow(); i++){ diff --git a/src/ida.cpp b/src/ida.cpp index fb8db36..f929c4a 100644 --- a/src/ida.cpp +++ b/src/ida.cpp @@ -48,7 +48,6 @@ struct res_func{ NumericVector params; }; - // function called by IDAInit if user inputs R function int res_function(sunrealtype t, N_Vector yy, N_Vector yp, N_Vector rr, void* user_data){ @@ -56,7 +55,7 @@ int res_function(sunrealtype t, N_Vector yy, N_Vector yp, N_Vector rr, void* use int yy_len = NV_LENGTH_S(yy); // NumericVector analog of yy - NumericVector yy1(yy_len); // filled with zeros + NumericVector yy1(yy_len); // filled with zeros sunrealtype *yy_ptr = N_VGetArrayPointer(yy); for (int i = 0; i < yy_len; i++){ yy1[i] = yy_ptr[i]; // Ith(y,i+1); @@ -67,12 +66,12 @@ int res_function(sunrealtype t, N_Vector yy, N_Vector yp, N_Vector rr, void* use NumericVector yp1(yp_len); // filled with zeros sunrealtype *yp_ptr = N_VGetArrayPointer(yp); for (int i = 0; i < yp_len; i++){ - yp1[i] = yp_ptr[i]; // Ith(y,i+1); + yp1[i] = yp_ptr[i]; // Ith(y,i+1); } // NumericVector analog of rr int rr_len = NV_LENGTH_S(rr); - NumericVector rr1(rr_len); // filled with zeros + NumericVector rr1(rr_len); // filled with zeros if(!user_data){ stop("Something went wrong, stopping!"); @@ -91,11 +90,11 @@ int res_function(sunrealtype t, N_Vector yy, N_Vector yp, N_Vector rr, void* use rr1 = res_fun(t, yy1, yp1, p_values); } else{ - stop("Something went wrong, stopping!"); + stop("Something went wrong in evaluating the residual function, stopping!"); } } else { - stop("Something went wrong, stopping!"); + stop("Something went wrong in accessing the residual function, stopping!"); } // convert NumericVector rr1 to N_Vector rr @@ -122,19 +121,18 @@ int res_function(sunrealtype t, N_Vector yy, N_Vector yp, N_Vector rr, void* use //'@param abstolerance Absolute Tolerance (a scalar or vector with length equal to ydot, default = 1e-04) //'@returns A data frame. First column is the time-vector, the other columns are values of y in order they are provided. // [[Rcpp::export]] -NumericMatrix ida(NumericVector time_vector, NumericVector IC, NumericVector IRes, SEXP input_function, +NumericMatrix ida(NumericVector time_vector, NumericVector IC, + NumericVector IRes, SEXP input_function, NumericVector Parameters, - double reltolerance = 0.0001, NumericVector abstolerance = 0.0001){ - + double reltolerance = 0.0001, + NumericVector abstolerance = 0.0001){ int time_vec_len = time_vector.length(); int y_len = IC.length(); SUNContext sunctx; SUNContext_Create(SUN_COMM_NULL, &sunctx); - if(y_len != IRes.length()){ - stop("IC and IRes should be of same length"); - } + if(y_len != IRes.length()){ stop("IC (Initial Conditions) and IRes (Residuals) should be of same length"); } int abstol_len = abstolerance.length(); // absolute tolerance is either length == 1 or equal to length of IC @@ -166,6 +164,7 @@ NumericMatrix ida(NumericVector time_vector, NumericVector IC, NumericVector IRe abstol_ptr[i] = abstolerance[i]; } } + //---------------------------------------------------------------------------- // // Set the initial values of y ----------------------------------------------- N_Vector yy0 = N_VNew_Serial(y_len, sunctx); // declared as yy0 to be consistent with example C code @@ -188,124 +187,110 @@ NumericMatrix ida(NumericVector time_vector, NumericVector IC, NumericVector IRe /* Call IDACreate and IDAInit to initialize IDA memory */ ida_mem = IDACreate(sunctx); - if(check_retval((void *)ida_mem, "IDACreate", 0)) { - stop("Stopping IDA, something went wrong in allocating memory!"); - } + if(check_retval((void *)ida_mem, "IDACreate", 0)) { stop("Stopping IDA, something went wrong in allocating memory!"); } // -- assign user input to the struct based on SEXP type of input_function - if(!input_function){ - stop("Something is wrong with the input function, stopping!"); - } - - switch(TYPEOF(input_function)){ + if(!input_function){ stop("Something is wrong with the input function, stopping!"); } - case CLOSXP:{ + if(TYPEOF(input_function) != CLOSXP) { stop("Incorrect input function type - input function can be an R or Rcpp function"); } - struct res_func my_res_function = {input_function, Parameters}; + struct res_func my_res_function = {input_function, Parameters}; - // setting the user data in the rhs residual function - flag = IDASetUserData(ida_mem, (void*)&my_res_function); - if (check_retval(&flag, "CVodeSetUserData", 1)) { stop("Stopping IDA, something went wrong in setting user data!"); } + // setting the user data in the rhs residual function + flag = IDASetUserData(ida_mem, (void*)&my_res_function); + if (check_retval(&flag, "CVodeSetUserData", 1)) { stop("Stopping IDA, something went wrong in setting user data!"); } - flag = IDAInit(ida_mem, res_function, T0, yy0, yp0); - if(check_retval(&flag, "IDAInit", 1)) { stop("Stopping, something went wrong in initializing IDA!"); }; + flag = IDAInit(ida_mem, res_function, T0, yy0, yp0); + if(check_retval(&flag, "IDAInit", 1)) { stop("Stopping, something went wrong in initializing IDA!"); }; - /* Call IDASVtolerances to set tolerances */ - flag = IDASVtolerances(ida_mem, reltol, abstol); - if(check_retval(&flag, "IDASVtolerances", 1)) { stop("Stopping, something went wrong in setting tolerances!"); }; + /* Call IDASVtolerances to set tolerances */ + flag = IDASVtolerances(ida_mem, reltol, abstol); + if(check_retval(&flag, "IDASVtolerances", 1)) { stop("Stopping, something went wrong in setting tolerances!"); }; - /* Call IDARootInit to specify the root function grob with 2 components */ - // retval = IDARootInit(mem, 2, grob); - // if (check_retval(&retval, "IDARootInit", 1)) return(1); + /* Call IDARootInit to specify the root function grob with 2 components */ + // retval = IDARootInit(mem, 2, grob); + // if (check_retval(&retval, "IDARootInit", 1)) return(1); - /* Create dense SUNMatrix for use in linear solves */ - sunindextype y_len_M = y_len; - SUNMatrix SM = SUNDenseMatrix(y_len_M, y_len_M, sunctx); - if(check_retval((void *)SM, "SUNDenseMatrix", 0)) { stop("Stopping IDA, something went wrong in setting the dense matrix!"); } + /* Create dense SUNMatrix for use in linear solves */ + sunindextype y_len_M = y_len; + SUNMatrix SM = SUNDenseMatrix(y_len_M, y_len_M, sunctx); + if(check_retval((void *)SM, "SUNDenseMatrix", 0)) { stop("Stopping IDA, something went wrong in setting the dense matrix!"); } - // Create dense SUNLinearSolver object for use by IDA - SUNLinearSolver LS = SUNLinSol_Dense(yy0, SM, sunctx); - if(check_retval((void *)LS, "SUNLinSol_Dense", 0)) { stop("Stopping IDA, something went wrong in setting the linear solver!"); } + // Create dense SUNLinearSolver object for use by IDA + SUNLinearSolver LS = SUNLinSol_Dense(yy0, SM, sunctx); + if(check_retval((void *)LS, "SUNLinSol_Dense", 0)) { stop("Stopping IDA, something went wrong in setting the linear solver!"); } - /* Attach the matrix and linear solver */ - flag = IDASetLinearSolver(ida_mem, LS, SM); - if(check_retval(&flag, "IDASetLinearSolver", 1)) return(1); + /* Attach the matrix and linear solver */ + flag = IDASetLinearSolver(ida_mem, LS, SM); + if(check_retval(&flag, "IDASetLinearSolver", 1)) { stop("Stopping IDA, something went wrong in setting the linear solver!"); } - // /* Set the user-supplied Jacobian routine */ - // retval = IDASetJacFn(mem, jacrob); - // if(check_retval(&retval, "IDASetJacFn", 1)) return(1); + // /* Set the user-supplied Jacobian routine */ + // retval = IDASetJacFn(mem, jacrob); + // if(check_retval(&retval, "IDASetJacFn", 1)) return(1); - /* Create Newton SUNNonlinearSolver object. IDA uses a - * Newton SUNNonlinearSolver by default, so it is unecessary - * to create it and attach it. */ - SUNNonlinearSolver NLS = SUNNonlinSol_Newton(yy0, sunctx); - if(check_retval((void *)NLS, "SUNNonlinSol_Newton", 0)) return(1); + /* Create Newton SUNNonlinearSolver object. IDA uses a + * Newton SUNNonlinearSolver by default, so it is unecessary + * to create it and attach it. */ + SUNNonlinearSolver NLS = SUNNonlinSol_Newton(yy0, sunctx); + if(check_retval((void *)NLS, "SUNNonlinSol_Newton", 0)) { stop("Stopping IDA, something went wrong in creating the Non-linear Solver in IDA!"); } - /* Attach the nonlinear solver */ - flag = IDASetNonlinearSolver(ida_mem, NLS); - if(check_retval(&flag, "IDASetNonlinearSolver", 1)) return(1); + /* Attach the nonlinear solver */ + flag = IDASetNonlinearSolver(ida_mem, NLS); + if(check_retval(&flag, "IDASetNonlinearSolver", 1)) { stop("Stopping IDA, something went wrong in attaching the Non-linear Solver in IDA!"); } - /* In loop, call IDASolve, print results, and test for error. - Break out of loop when NOUT preset output times have been reached. */ + /* In loop, call IDASolve, print results, and test for error. + Break out of loop when NOUT preset output times have been reached. */ - sunrealtype tout; // For output times + sunrealtype tout; // For output times - int y_len_1 = y_len + 1; // remove later - NumericMatrix soln(Dimension(time_vec_len,y_len_1)); // remove later + int y_len_1 = y_len + 1; // remove later + NumericMatrix soln(Dimension(time_vec_len,y_len_1)); // remove later - // nothing to do with Events - single initialization of the ODE system - // First row for initial conditions, First column is for time - // int y_len_1 = y_len + 1; - // NumericMatrix soln(Dimension(time_vec_len,y_len_1)); + // nothing to do with Events - single initialization of the ODE system + // First row for initial conditions, First column is for time + // int y_len_1 = y_len + 1; + // NumericMatrix soln(Dimension(time_vec_len,y_len_1)); - // fill the first row of soln matrix with Initial Conditions - soln(0,0) = time_vector[0]; // get the first time value - for(int i = 0; icv_zn[0], CV_LOGGER->debug_fp); #endif } @@ -4824,15 +4809,20 @@ static int cvEwtSetSV(CVodeMem cv_mem, N_Vector ycur, N_Vector weight) void cvProcessError(CVodeMem cv_mem, int error_code, int line, const char* func, const char* file, const char* msgfmt, ...) { - /* Initialize the argument pointer variable + /* We initialize the argument pointer variable before each vsnprintf call to avoid undefined behavior (msgfmt is the last required argument to cvProcessError) */ va_list ap; - va_start(ap, msgfmt); /* Compose the message */ + va_start(ap, msgfmt); size_t msglen = vsnprintf(NULL, 0, msgfmt, ap) + 1; - char* msg = (char*)malloc(msglen); + va_end(ap); + + char* msg = (char*)malloc(msglen); + + va_start(ap, msgfmt); vsnprintf(msg, msglen, msgfmt, ap); + va_end(ap); do { if (cv_mem == NULL) @@ -4860,8 +4850,6 @@ void cvProcessError(CVodeMem cv_mem, int error_code, int line, const char* func, } while (0); - /* Finalize argument processing */ - va_end(ap); free(msg); return; diff --git a/src/sundials/cvode/cvode_bandpre.c b/src/sundials/cvode/cvode_bandpre.c index f421257..b19c32e 100644 --- a/src/sundials/cvode/cvode_bandpre.c +++ b/src/sundials/cvode/cvode_bandpre.c @@ -435,9 +435,12 @@ static int CVBandPrecSetup(sunrealtype t, N_Vector y, N_Vector fy, The value returned by the CVBandPrecSolve function is always 0, indicating success. -----------------------------------------------------------------*/ -static int CVBandPrecSolve(sunrealtype t, N_Vector y, N_Vector fy, N_Vector r, - N_Vector z, sunrealtype gamma, sunrealtype delta, - int lr, void* bp_data) +static int CVBandPrecSolve(SUNDIALS_MAYBE_UNUSED sunrealtype t, + SUNDIALS_MAYBE_UNUSED N_Vector y, + SUNDIALS_MAYBE_UNUSED N_Vector fy, N_Vector r, + N_Vector z, SUNDIALS_MAYBE_UNUSED sunrealtype gamma, + SUNDIALS_MAYBE_UNUSED sunrealtype delta, + SUNDIALS_MAYBE_UNUSED int lr, void* bp_data) { CVBandPrecData pdata; int retval; diff --git a/src/sundials/cvode/cvode_bbdpre.c b/src/sundials/cvode/cvode_bbdpre.c index 6c8a284..4cdefc1 100644 --- a/src/sundials/cvode/cvode_bbdpre.c +++ b/src/sundials/cvode/cvode_bbdpre.c @@ -467,9 +467,10 @@ int CVBBDPrecGetNumGfnEvals(void* cvode_mem, long int* ngevalsBBDP) 0 if successful, 1 for a recoverable error (step will be retried). -----------------------------------------------------------------*/ -static int CVBBDPrecSetup(sunrealtype t, N_Vector y, N_Vector fy, - sunbooleantype jok, sunbooleantype* jcurPtr, - sunrealtype gamma, void* bbd_data) +static int CVBBDPrecSetup(sunrealtype t, N_Vector y, + SUNDIALS_MAYBE_UNUSED N_Vector fy, sunbooleantype jok, + sunbooleantype* jcurPtr, sunrealtype gamma, + void* bbd_data) { CVBBDPrecData pdata; CVodeMem cv_mem; @@ -556,9 +557,12 @@ static int CVBBDPrecSetup(sunrealtype t, N_Vector y, N_Vector fy, The value returned by the CVBBDPrecSolve function is always 0, indicating success. -----------------------------------------------------------------*/ -static int CVBBDPrecSolve(sunrealtype t, N_Vector y, N_Vector fy, N_Vector r, - N_Vector z, sunrealtype gamma, sunrealtype delta, - int lr, void* bbd_data) +static int CVBBDPrecSolve(SUNDIALS_MAYBE_UNUSED sunrealtype t, + SUNDIALS_MAYBE_UNUSED N_Vector y, + SUNDIALS_MAYBE_UNUSED N_Vector fy, N_Vector r, + N_Vector z, SUNDIALS_MAYBE_UNUSED sunrealtype gamma, + SUNDIALS_MAYBE_UNUSED sunrealtype delta, + SUNDIALS_MAYBE_UNUSED int lr, void* bbd_data) { int retval; CVBBDPrecData pdata; diff --git a/src/sundials/cvode/cvode_diag.c b/src/sundials/cvode/cvode_diag.c index e24592c..25af67f 100644 --- a/src/sundials/cvode/cvode_diag.c +++ b/src/sundials/cvode/cvode_diag.c @@ -23,20 +23,6 @@ #include "cvode_diag_impl.h" #include "cvode_impl.h" -#ifdef SUNDIALS_BUILD_PACKAGE_FUSED_KERNELS -extern int cvDiagSetup_formY(const sunrealtype h, const sunrealtype r, - const N_Vector fpred, const N_Vector zn1, - const N_Vector ypred, N_Vector ftemp, N_Vector y); - -extern int cvDiagSetup_buildM(const sunrealtype fract, const sunrealtype uround, - const sunrealtype h, const N_Vector ftemp, - const N_Vector fpred, const N_Vector ewt, - N_Vector bit, N_Vector bitcomp, N_Vector y, - N_Vector M); - -int cvDiagSolve_updateM(const sunrealtype r, N_Vector M); -#endif - /* Other Constants */ #define FRACT SUN_RCONST(0.1) @@ -334,9 +320,10 @@ static int CVDiagInit(CVodeMem cv_mem) * ----------------------------------------------------------------- */ -static int CVDiagSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, - N_Vector fpred, sunbooleantype* jcurPtr, N_Vector vtemp1, - N_Vector vtemp2, N_Vector vtemp3) +static int CVDiagSetup(CVodeMem cv_mem, SUNDIALS_MAYBE_UNUSED int convfail, + N_Vector ypred, N_Vector fpred, sunbooleantype* jcurPtr, + N_Vector vtemp1, N_Vector vtemp2, + SUNDIALS_MAYBE_UNUSED N_Vector vtemp3) { sunrealtype r; N_Vector ftemp, y; @@ -426,8 +413,10 @@ static int CVDiagSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, * ----------------------------------------------------------------- */ -static int CVDiagSolve(CVodeMem cv_mem, N_Vector b, N_Vector weight, - N_Vector ycur, N_Vector fcur) +static int CVDiagSolve(CVodeMem cv_mem, N_Vector b, + SUNDIALS_MAYBE_UNUSED N_Vector weight, + SUNDIALS_MAYBE_UNUSED N_Vector ycur, + SUNDIALS_MAYBE_UNUSED N_Vector fcur) { sunbooleantype invOK; sunrealtype r; diff --git a/src/sundials/cvode/cvode_fused_stubs.c b/src/sundials/cvode/cvode_fused_stubs.c index cd7aa6a..7bc41c5 100644 --- a/src/sundials/cvode/cvode_fused_stubs.c +++ b/src/sundials/cvode/cvode_fused_stubs.c @@ -18,6 +18,7 @@ #include "cvode_diag_impl.h" #include "cvode_impl.h" +#include "sundials_macros.h" #define ZERO SUN_RCONST(0.0) #define PT1 SUN_RCONST(0.1) @@ -121,10 +122,11 @@ int cvDiagSetup_formY(const sunrealtype h, const sunrealtype r, * ----------------------------------------------------------------- */ -int cvDiagSetup_buildM(const sunrealtype fract, const sunrealtype uround, - const sunrealtype h, const N_Vector ftemp, - const N_Vector fpred, const N_Vector ewt, N_Vector bit, - N_Vector bitcomp, N_Vector y, N_Vector M) +int cvDiagSetup_buildM(SUNDIALS_MAYBE_UNUSED const sunrealtype fract, + const sunrealtype uround, const sunrealtype h, + const N_Vector ftemp, const N_Vector fpred, + const N_Vector ewt, N_Vector bit, N_Vector bitcomp, + N_Vector y, N_Vector M) { N_VLinearSum(ONE, M, -ONE, fpred, M); N_VLinearSum(FRACT, ftemp, -h, M, M); diff --git a/src/sundials/cvode/cvode_impl.h b/src/sundials/cvode/cvode_impl.h index d7d9d08..e2f3a36 100644 --- a/src/sundials/cvode/cvode_impl.h +++ b/src/sundials/cvode/cvode_impl.h @@ -22,10 +22,11 @@ #include #include #include +#include #include "cvode_proj_impl.h" -#include "sundials/sundials_math.h" #include "sundials_logger_impl.h" +#include "sundials_macros.h" #ifdef __cplusplus /* wrapper to enable C++ usage */ extern "C" { @@ -634,6 +635,34 @@ void cvRestore(CVodeMem cv_mem, sunrealtype saved_t); void cvRescale(CVodeMem cv_mem); +#ifdef SUNDIALS_BUILD_PACKAGE_FUSED_KERNELS +int cvEwtSetSS_fused(const sunbooleantype atolmin0, const sunrealtype reltol, + const sunrealtype Sabstol, const N_Vector ycur, + N_Vector tempv, N_Vector weight); + +int cvEwtSetSV_fused(const sunbooleantype atolmin0, const sunrealtype reltol, + const N_Vector Vabstol, const N_Vector ycur, + N_Vector tempv, N_Vector weight); + +int cvCheckConstraints_fused(const N_Vector c, const N_Vector ewt, + const N_Vector y, const N_Vector mm, N_Vector tempv); + +int cvNlsResid_fused(const sunrealtype rl1, const sunrealtype ngamma, + const N_Vector zn1, const N_Vector ycor, + const N_Vector ftemp, N_Vector res); + +int cvDiagSetup_formY(const sunrealtype h, const sunrealtype r, + const N_Vector fpred, const N_Vector zn1, + const N_Vector ypred, N_Vector ftemp, N_Vector y); + +int cvDiagSetup_buildM(const sunrealtype fract, const sunrealtype uround, + const sunrealtype h, const N_Vector ftemp, + const N_Vector fpred, const N_Vector ewt, N_Vector bit, + N_Vector bitcomp, N_Vector y, N_Vector M); + +int cvDiagSolve_updateM(const sunrealtype r, N_Vector M); +#endif + /* * ================================================================= * E R R O R M E S S A G E S diff --git a/src/sundials/cvode/cvode_io.c b/src/sundials/cvode/cvode_io.c index 1a545a0..1f23b85 100644 --- a/src/sundials/cvode/cvode_io.c +++ b/src/sundials/cvode/cvode_io.c @@ -104,6 +104,8 @@ int CVodeSetMonitorFn(void* cvode_mem, CVMonitorFn fn) cv_mem->cv_monitorfun = fn; return (CV_SUCCESS); #else + /* silence warnings when monitoring is disabled */ + ((void)fn); cvProcessError(cv_mem, CV_ILL_INPUT, __LINE__, __func__, __FILE__, "SUNDIALS was not built with monitoring enabled."); return (CV_ILL_INPUT); @@ -1023,6 +1025,8 @@ int CVodeSetUseIntegratorFusedKernels(void* cvode_mem, sunbooleantype onoff) cv_mem->cv_usefused = onoff; return (CV_SUCCESS); #else + /* silence warnings when fused kernels are disabled */ + ((void)onoff); cvProcessError(cv_mem, CV_ILL_INPUT, __LINE__, __func__, __FILE__, "CVODE was not built with fused integrator kernels enabled"); return (CV_ILL_INPUT); diff --git a/src/sundials/cvode/cvode_ls.c b/src/sundials/cvode/cvode_ls.c index a51ef16..50c60c6 100644 --- a/src/sundials/cvode/cvode_ls.c +++ b/src/sundials/cvode/cvode_ls.c @@ -977,7 +977,8 @@ int cvLsPSolve(void* cvode_mem, N_Vector r, N_Vector z, sunrealtype tol, int lr) approximation routines. ---------------------------------------------------------------*/ int cvLsDQJac(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix Jac, - void* cvode_mem, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + void* cvode_mem, N_Vector tmp1, N_Vector tmp2, + SUNDIALS_MAYBE_UNUSED N_Vector tmp3) { CVodeMem cv_mem; int retval; @@ -1213,7 +1214,7 @@ int cvLsBandDQJac(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix Jac, } /* Evaluate f with incremented y */ - retval = cv_mem->cv_f(cv_mem->cv_tn, ytemp, ftemp, cv_mem->cv_user_data); + retval = cv_mem->cv_f(t, ytemp, ftemp, cv_mem->cv_user_data); cvls_mem->nfeDQ++; if (retval != 0) { break; } diff --git a/src/sundials/cvode/cvode_nls.c b/src/sundials/cvode/cvode_nls.c index ade248a..7821bd9 100644 --- a/src/sundials/cvode/cvode_nls.c +++ b/src/sundials/cvode/cvode_nls.c @@ -40,12 +40,6 @@ static int cvNlsLSolve(N_Vector delta, void* cvode_mem); static int cvNlsConvTest(SUNNonlinearSolver NLS, N_Vector ycor, N_Vector del, sunrealtype tol, N_Vector ewt, void* cvode_mem); -#ifdef SUNDIALS_BUILD_PACKAGE_FUSED_KERNELS -int cvNlsResid_fused(const sunrealtype rl1, const sunrealtype ngamma, - const N_Vector zn1, const N_Vector ycor, - const N_Vector ftemp, N_Vector res); -#endif - /* ----------------------------------------------------------------------------- * Exported functions * ---------------------------------------------------------------------------*/ diff --git a/src/sundials/cvodes/cvodea.c b/src/sundials/cvodes/cvodea.c index fd4ae06..2df97cf 100644 --- a/src/sundials/cvodes/cvodea.c +++ b/src/sundials/cvodes/cvodea.c @@ -1793,7 +1793,7 @@ static CVckpntMem CVAckpntInit(CVodeMem cv_mem) ck_mem->ck_t0 = cv_mem->cv_tn; ck_mem->ck_nst = 0; ck_mem->ck_q = 1; - ck_mem->ck_h = 0.0; + ck_mem->ck_h = ZERO; /* Do we need to carry quadratures */ ck_mem->ck_quadr = cv_mem->cv_quadr && cv_mem->cv_errconQ; diff --git a/src/sundials/cvodes/cvodes.c b/src/sundials/cvodes/cvodes.c index d1e994f..55399d7 100644 --- a/src/sundials/cvodes/cvodes.c +++ b/src/sundials/cvodes/cvodes.c @@ -2057,7 +2057,7 @@ int CVodeSensReInit(void* cvode_mem, int ism, N_Vector* yS0) * CVodeSensSVtolerances specifies scalar relative tolerance and a vector * absolute tolerance for each sensitivity vector (a potentially different * absolute tolerance for each vector component). - * CVodeEEtolerances specifies that tolerances for sensitivity variables + * CVodeSensEEtolerances specifies that tolerances for sensitivity variables * should be estimated from those provided for the state variables. */ @@ -6531,7 +6531,7 @@ static void cvPredict(CVodeMem cv_mem) #ifdef SUNDIALS_LOGGING_EXTRA_DEBUG SUNLogger_QueueMsg(CV_LOGGER, SUN_LOGLEVEL_DEBUG, "CVODES::cvPredict", - "forward", "predictor =", ""); + "forward", "zn_0(:) =", ""); N_VPrintFile(cv_mem->cv_zn[0], CV_LOGGER->debug_fp); #endif @@ -6548,7 +6548,7 @@ static void cvPredict(CVodeMem cv_mem) #ifdef SUNDIALS_LOGGING_EXTRA_DEBUG SUNLogger_QueueMsg(CV_LOGGER, SUN_LOGLEVEL_DEBUG, "CVODES::cvPredict", - "quad", "predictor =", ""); + "quad", "znQ_0(:) =", ""); N_VPrintFile(cv_mem->cv_znQ[0], CV_LOGGER->debug_fp); #endif } @@ -6566,7 +6566,7 @@ static void cvPredict(CVodeMem cv_mem) for (i = 0; i < cv_mem->cv_Ns; i++) { SUNLogger_QueueMsg(CV_LOGGER, SUN_LOGLEVEL_DEBUG, "CVODES::cvPredict", - "sensi", " i = %d, predictor_i = ", i); + "sensi", " i = %d, znS_i(:) = ", i); N_VPrintFile(cv_mem->cv_znS[0][i], CV_LOGGER->debug_fp); } #endif @@ -6587,7 +6587,7 @@ static void cvPredict(CVodeMem cv_mem) for (i = 0; i < cv_mem->cv_Ns; i++) { SUNLogger_QueueMsg(CV_LOGGER, SUN_LOGLEVEL_DEBUG, "CVODES::cvPredict", - "quad-sensi", " i = %d, predictor_i = ", i); + "quad-sensi", " i = %d, znQS_i(:) = ", i); N_VPrintFile(cv_mem->cv_znQS[0][i], CV_LOGGER->debug_fp); } #endif @@ -9402,8 +9402,8 @@ static int cvQuadSensEwtSetSV(CVodeMem cv_mem, N_Vector* yQScur, * Updates the norm old_nrm to account for all quadratures. */ -static sunrealtype cvQuadUpdateNorm(CVodeMem cv_mem, sunrealtype old_nrm, - N_Vector xQ, N_Vector wQ) +static sunrealtype cvQuadUpdateNorm(SUNDIALS_MAYBE_UNUSED CVodeMem cv_mem, + sunrealtype old_nrm, N_Vector xQ, N_Vector wQ) { sunrealtype qnrm; @@ -9620,9 +9620,9 @@ int cvSensRhsInternalDQ(int Ns, sunrealtype t, N_Vector y, N_Vector ydot, * non-zero return value from f(). */ -int cvSensRhs1InternalDQ(int Ns, sunrealtype t, N_Vector y, N_Vector ydot, - int is, N_Vector yS, N_Vector ySdot, void* cvode_mem, - N_Vector ytemp, N_Vector ftemp) +int cvSensRhs1InternalDQ(SUNDIALS_MAYBE_UNUSED int Ns, sunrealtype t, N_Vector y, + N_Vector ydot, int is, N_Vector yS, N_Vector ySdot, + void* cvode_mem, N_Vector ytemp, N_Vector ftemp) { CVodeMem cv_mem; int retval, method; @@ -9908,15 +9908,20 @@ static int cvQuadSensRhs1InternalDQ(CVodeMem cv_mem, int is, sunrealtype t, void cvProcessError(CVodeMem cv_mem, int error_code, int line, const char* func, const char* file, const char* msgfmt, ...) { - /* Initialize the argument pointer variable + /* We initialize the argument pointer variable before each vsnprintf call to avoid undefined behavior (msgfmt is the last required argument to cvProcessError) */ va_list ap; - va_start(ap, msgfmt); /* Compose the message */ + va_start(ap, msgfmt); size_t msglen = vsnprintf(NULL, 0, msgfmt, ap) + 1; - char* msg = (char*)malloc(msglen); + va_end(ap); + + char* msg = (char*)malloc(msglen); + + va_start(ap, msgfmt); vsnprintf(msg, msglen, msgfmt, ap); + va_end(ap); do { if (cv_mem == NULL) @@ -9944,8 +9949,6 @@ void cvProcessError(CVodeMem cv_mem, int error_code, int line, const char* func, } while (0); - /* Finalize argument processing */ - va_end(ap); free(msg); return; diff --git a/src/sundials/cvodes/cvodes_bandpre.c b/src/sundials/cvodes/cvodes_bandpre.c index 98a9308..55dbe1a 100644 --- a/src/sundials/cvodes/cvodes_bandpre.c +++ b/src/sundials/cvodes/cvodes_bandpre.c @@ -438,9 +438,12 @@ static int cvBandPrecSetup(sunrealtype t, N_Vector y, N_Vector fy, The value returned by the cvBandPrecSolve function is always 0, indicating success. -----------------------------------------------------------------*/ -static int cvBandPrecSolve(sunrealtype t, N_Vector y, N_Vector fy, N_Vector r, - N_Vector z, sunrealtype gamma, sunrealtype delta, - int lr, void* bp_data) +static int cvBandPrecSolve(SUNDIALS_MAYBE_UNUSED sunrealtype t, + SUNDIALS_MAYBE_UNUSED N_Vector y, + SUNDIALS_MAYBE_UNUSED N_Vector fy, N_Vector r, + N_Vector z, SUNDIALS_MAYBE_UNUSED sunrealtype gamma, + SUNDIALS_MAYBE_UNUSED sunrealtype delta, + SUNDIALS_MAYBE_UNUSED int lr, void* bp_data) { CVBandPrecData pdata; int retval; diff --git a/src/sundials/cvodes/cvodes_bbdpre.c b/src/sundials/cvodes/cvodes_bbdpre.c index af9cba7..12eb1b7 100644 --- a/src/sundials/cvodes/cvodes_bbdpre.c +++ b/src/sundials/cvodes/cvodes_bbdpre.c @@ -479,9 +479,10 @@ int CVBBDPrecGetNumGfnEvals(void* cvode_mem, long int* ngevalsBBDP) 0 if successful, 1 for a recoverable error (step will be retried). -----------------------------------------------------------------*/ -static int cvBBDPrecSetup(sunrealtype t, N_Vector y, N_Vector fy, - sunbooleantype jok, sunbooleantype* jcurPtr, - sunrealtype gamma, void* bbd_data) +static int cvBBDPrecSetup(sunrealtype t, N_Vector y, + SUNDIALS_MAYBE_UNUSED N_Vector fy, sunbooleantype jok, + sunbooleantype* jcurPtr, sunrealtype gamma, + void* bbd_data) { CVBBDPrecData pdata; CVodeMem cv_mem; @@ -568,9 +569,12 @@ static int cvBBDPrecSetup(sunrealtype t, N_Vector y, N_Vector fy, The value returned by the cvBBDPrecSolve function is always 0, indicating success. -----------------------------------------------------------------*/ -static int cvBBDPrecSolve(sunrealtype t, N_Vector y, N_Vector fy, N_Vector r, - N_Vector z, sunrealtype gamma, sunrealtype delta, - int lr, void* bbd_data) +static int cvBBDPrecSolve(SUNDIALS_MAYBE_UNUSED sunrealtype t, + SUNDIALS_MAYBE_UNUSED N_Vector y, + SUNDIALS_MAYBE_UNUSED N_Vector fy, N_Vector r, + N_Vector z, SUNDIALS_MAYBE_UNUSED sunrealtype gamma, + SUNDIALS_MAYBE_UNUSED sunrealtype delta, + SUNDIALS_MAYBE_UNUSED int lr, void* bbd_data) { int retval; CVBBDPrecData pdata; diff --git a/src/sundials/cvodes/cvodes_diag.c b/src/sundials/cvodes/cvodes_diag.c index b1db687..857a81b 100644 --- a/src/sundials/cvodes/cvodes_diag.c +++ b/src/sundials/cvodes/cvodes_diag.c @@ -326,9 +326,10 @@ static int CVDiagInit(CVodeMem cv_mem) * ----------------------------------------------------------------- */ -static int CVDiagSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, - N_Vector fpred, sunbooleantype* jcurPtr, N_Vector vtemp1, - N_Vector vtemp2, N_Vector vtemp3) +static int CVDiagSetup(CVodeMem cv_mem, SUNDIALS_MAYBE_UNUSED int convfail, + N_Vector ypred, N_Vector fpred, sunbooleantype* jcurPtr, + N_Vector vtemp1, N_Vector vtemp2, + SUNDIALS_MAYBE_UNUSED N_Vector vtemp3) { sunrealtype r; N_Vector ftemp, y; @@ -400,8 +401,10 @@ static int CVDiagSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, * ----------------------------------------------------------------- */ -static int CVDiagSolve(CVodeMem cv_mem, N_Vector b, N_Vector weight, - N_Vector ycur, N_Vector fcur) +static int CVDiagSolve(CVodeMem cv_mem, N_Vector b, + SUNDIALS_MAYBE_UNUSED N_Vector weight, + SUNDIALS_MAYBE_UNUSED N_Vector ycur, + SUNDIALS_MAYBE_UNUSED N_Vector fcur) { sunbooleantype invOK; sunrealtype r; diff --git a/src/sundials/cvodes/cvodes_impl.h b/src/sundials/cvodes/cvodes_impl.h index e98b88b..c22d166 100644 --- a/src/sundials/cvodes/cvodes_impl.h +++ b/src/sundials/cvodes/cvodes_impl.h @@ -18,12 +18,14 @@ #define _CVODES_IMPL_H #include + +#include #include +#include -#include "cvodes/cvodes.h" #include "cvodes_proj_impl.h" -#include "sundials/sundials_math.h" #include "sundials_logger_impl.h" +#include "sundials_macros.h" #ifdef __cplusplus /* wrapper to enable C++ usage */ extern "C" { diff --git a/src/sundials/cvodes/cvodes_io.c b/src/sundials/cvodes/cvodes_io.c index 4114259..e4599bd 100644 --- a/src/sundials/cvodes/cvodes_io.c +++ b/src/sundials/cvodes/cvodes_io.c @@ -104,6 +104,8 @@ int CVodeSetMonitorFn(void* cvode_mem, CVMonitorFn fn) cv_mem->cv_monitorfun = fn; return (CV_SUCCESS); #else + /* silence warnings when monitoring is disabled */ + ((void)fn); cvProcessError(cv_mem, CV_ILL_INPUT, __LINE__, __func__, __FILE__, "SUNDIALS was not built with monitoring enabled."); return (CV_ILL_INPUT); diff --git a/src/sundials/cvodes/cvodes_ls.c b/src/sundials/cvodes/cvodes_ls.c index 6c7362d..539a69a 100644 --- a/src/sundials/cvodes/cvodes_ls.c +++ b/src/sundials/cvodes/cvodes_ls.c @@ -1055,7 +1055,8 @@ int cvLsPSolve(void* cvode_mem, N_Vector r, N_Vector z, sunrealtype tol, int lr) approximation routines. ---------------------------------------------------------------*/ int cvLsDQJac(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix Jac, - void* cvode_mem, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + void* cvode_mem, N_Vector tmp1, N_Vector tmp2, + SUNDIALS_MAYBE_UNUSED N_Vector tmp3) { CVodeMem cv_mem; int retval; @@ -1291,7 +1292,7 @@ int cvLsBandDQJac(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix Jac, } /* Evaluate f with incremented y */ - retval = cv_mem->cv_f(cv_mem->cv_tn, ytemp, ftemp, cv_mem->cv_user_data); + retval = cv_mem->cv_f(t, ytemp, ftemp, cv_mem->cv_user_data); cvls_mem->nfeDQ++; if (retval != 0) { break; } @@ -2884,7 +2885,7 @@ int cvLs_AccessLMemBCur(void* cvode_mem, const char* fname, CVodeMem* cv_mem, /* access CVodeMem structure */ if (cvode_mem == NULL) { - cvProcessError(NULL, CVLS_MEM_NULL, __LINE__, __func__, __FILE__, + cvProcessError(NULL, CVLS_MEM_NULL, __LINE__, fname, __FILE__, MSG_LS_CVMEM_NULL); return (CVLS_MEM_NULL); } @@ -2893,7 +2894,7 @@ int cvLs_AccessLMemBCur(void* cvode_mem, const char* fname, CVodeMem* cv_mem, /* access CVadjMem structure */ if ((*cv_mem)->cv_adjMallocDone == SUNFALSE) { - cvProcessError(*cv_mem, CVLS_NO_ADJ, __LINE__, __func__, __FILE__, + cvProcessError(*cv_mem, CVLS_NO_ADJ, __LINE__, fname, __FILE__, MSG_LS_NO_ADJ); return (CVLS_NO_ADJ); } @@ -2902,7 +2903,7 @@ int cvLs_AccessLMemBCur(void* cvode_mem, const char* fname, CVodeMem* cv_mem, /* get current backward problem */ if ((*ca_mem)->ca_bckpbCrt == NULL) { - cvProcessError(*cv_mem, CVLS_LMEMB_NULL, __LINE__, __func__, __FILE__, + cvProcessError(*cv_mem, CVLS_LMEMB_NULL, __LINE__, fname, __FILE__, MSG_LS_LMEMB_NULL); return (CVLS_LMEMB_NULL); } @@ -2911,7 +2912,7 @@ int cvLs_AccessLMemBCur(void* cvode_mem, const char* fname, CVodeMem* cv_mem, /* access CVLsMemB structure */ if ((*cvB_mem)->cv_lmem == NULL) { - cvProcessError(*cv_mem, CVLS_LMEMB_NULL, __LINE__, __func__, __FILE__, + cvProcessError(*cv_mem, CVLS_LMEMB_NULL, __LINE__, fname, __FILE__, MSG_LS_LMEMB_NULL); return (CVLS_LMEMB_NULL); } diff --git a/src/sundials/cvodes/cvodes_nls_stg1.c b/src/sundials/cvodes/cvodes_nls_stg1.c index a1b4fdc..c292572 100644 --- a/src/sundials/cvodes/cvodes_nls_stg1.c +++ b/src/sundials/cvodes/cvodes_nls_stg1.c @@ -258,7 +258,8 @@ static int cvNlsLSolveSensStg1(N_Vector delta, void* cvode_mem) return (CV_SUCCESS); } -static int cvNlsConvTestSensStg1(SUNNonlinearSolver NLS, N_Vector ycor, +static int cvNlsConvTestSensStg1(SUNNonlinearSolver NLS, + SUNDIALS_MAYBE_UNUSED N_Vector ycor, N_Vector delta, sunrealtype tol, N_Vector ewt, void* cvode_mem) { diff --git a/src/sundials/ida/ida.c b/src/sundials/ida/ida.c index 5236754..ea06a7a 100644 --- a/src/sundials/ida/ida.c +++ b/src/sundials/ida/ida.c @@ -3992,15 +3992,20 @@ static int IDARootfind(IDAMem IDA_mem) void IDAProcessError(IDAMem IDA_mem, int error_code, int line, const char* func, const char* file, const char* msgfmt, ...) { - /* Initialize the argument pointer variable + /* We initialize the argument pointer variable before each vsnprintf call to avoid undefined behavior (msgfmt is the last required argument to IDAProcessError) */ va_list ap; - va_start(ap, msgfmt); /* Compose the message */ + va_start(ap, msgfmt); size_t msglen = vsnprintf(NULL, 0, msgfmt, ap) + 1; - char* msg = (char*)malloc(msglen); + va_end(ap); + + char* msg = (char*)malloc(msglen); + + va_start(ap, msgfmt); vsnprintf(msg, msglen, msgfmt, ap); + va_end(ap); do { if (IDA_mem == NULL) @@ -4028,8 +4033,6 @@ void IDAProcessError(IDAMem IDA_mem, int error_code, int line, const char* func, } while (0); - /* Finalize argument processing */ - va_end(ap); free(msg); return; diff --git a/src/sundials/ida/ida_bbdpre.c b/src/sundials/ida/ida_bbdpre.c index 2352ff3..8bce33e 100644 --- a/src/sundials/ida/ida_bbdpre.c +++ b/src/sundials/ida/ida_bbdpre.c @@ -455,7 +455,8 @@ int IDABBDPrecGetNumGfnEvals(void* ida_mem, long int* ngevalsBBDP) < 0 for a nonrecoverable error (step fails). ----------------------------------------------------------------*/ static int IDABBDPrecSetup(sunrealtype tt, N_Vector yy, N_Vector yp, - N_Vector rr, sunrealtype c_j, void* bbd_data) + SUNDIALS_MAYBE_UNUSED N_Vector rr, sunrealtype c_j, + void* bbd_data) { IBBDPrecData pdata; IDAMem IDA_mem; @@ -502,9 +503,13 @@ static int IDABBDPrecSetup(sunrealtype tt, N_Vector yy, N_Vector yp, IDABBDPrecSolve returns the value returned from the linear solver object. ---------------------------------------------------------------*/ -static int IDABBDPrecSolve(sunrealtype tt, N_Vector yy, N_Vector yp, - N_Vector rr, N_Vector rvec, N_Vector zvec, - sunrealtype c_j, sunrealtype delta, void* bbd_data) +static int IDABBDPrecSolve(SUNDIALS_MAYBE_UNUSED sunrealtype tt, + SUNDIALS_MAYBE_UNUSED N_Vector yy, + SUNDIALS_MAYBE_UNUSED N_Vector yp, + SUNDIALS_MAYBE_UNUSED N_Vector rr, N_Vector rvec, + N_Vector zvec, SUNDIALS_MAYBE_UNUSED sunrealtype c_j, + SUNDIALS_MAYBE_UNUSED sunrealtype delta, + void* bbd_data) { IBBDPrecData pdata; int retval; diff --git a/src/sundials/ida/ida_ic.c b/src/sundials/ida/ida_ic.c index f1a0dac..2d44eab 100644 --- a/src/sundials/ida/ida_ic.c +++ b/src/sundials/ida/ida_ic.c @@ -58,8 +58,6 @@ */ extern int IDAInitialSetup(IDAMem IDA_mem); -extern sunrealtype IDAWrmsNorm(IDAMem IDA_mem, N_Vector x, N_Vector w, - sunbooleantype mask); static int IDAnlsIC(IDAMem IDA_mem); static int IDANewtonIC(IDAMem IDA_mem); diff --git a/src/sundials/ida/ida_impl.h b/src/sundials/ida/ida_impl.h index b68debb..f61a793 100644 --- a/src/sundials/ida/ida_impl.h +++ b/src/sundials/ida/ida_impl.h @@ -20,10 +20,12 @@ #define _IDA_IMPL_H #include + +#include #include -#include "ida/ida.h" #include "sundials_logger_impl.h" +#include "sundials_macros.h" #ifdef __cplusplus /* wrapper to enable C++ usage */ extern "C" { diff --git a/src/sundials/ida/ida_ls.c b/src/sundials/ida/ida_ls.c index c0a0c73..a8e2707 100644 --- a/src/sundials/ida/ida_ls.c +++ b/src/sundials/ida/ida_ls.c @@ -860,7 +860,8 @@ int idaLsPSetup(void* ida_mem) is the only case in which the user's psolve routine is allowed to be NULL. ---------------------------------------------------------------*/ -int idaLsPSolve(void* ida_mem, N_Vector r, N_Vector z, sunrealtype tol, int lr) +int idaLsPSolve(void* ida_mem, N_Vector r, N_Vector z, sunrealtype tol, + SUNDIALS_MAYBE_UNUSED int lr) { IDAMem IDA_mem; IDALsMem idals_mem; @@ -1138,7 +1139,7 @@ int idaLsBandDQJac(sunrealtype tt, sunrealtype c_j, N_Vector yy, N_Vector yp, /* Increment yj and ypj. */ ytemp_data[j] += inc; - yptemp_data[j] += IDA_mem->ida_cj * inc; + yptemp_data[j] += c_j * inc; } /* Call res routine with incremented arguments. */ diff --git a/src/sundials/ida/ida_nls.c b/src/sundials/ida/ida_nls.c index 8873023..160b817 100644 --- a/src/sundials/ida/ida_nls.c +++ b/src/sundials/ida/ida_nls.c @@ -233,7 +233,8 @@ int idaNlsInit(IDAMem IDA_mem) return (IDA_SUCCESS); } -static int idaNlsLSetup(sunbooleantype jbad, sunbooleantype* jcur, void* ida_mem) +static int idaNlsLSetup(SUNDIALS_MAYBE_UNUSED sunbooleantype jbad, + sunbooleantype* jcur, void* ida_mem) { IDAMem IDA_mem; int retval; @@ -318,7 +319,8 @@ static int idaNlsResidual(N_Vector ycor, N_Vector res, void* ida_mem) return (IDA_SUCCESS); } -static int idaNlsConvTest(SUNNonlinearSolver NLS, N_Vector ycor, N_Vector del, +static int idaNlsConvTest(SUNNonlinearSolver NLS, + SUNDIALS_MAYBE_UNUSED N_Vector ycor, N_Vector del, sunrealtype tol, N_Vector ewt, void* ida_mem) { IDAMem IDA_mem; diff --git a/src/sundials/idas/idas.c b/src/sundials/idas/idas.c index d42aeba..3174660 100644 --- a/src/sundials/idas/idas.c +++ b/src/sundials/idas/idas.c @@ -7836,8 +7836,9 @@ static sunrealtype IDAQuadSensWrmsNorm(IDAMem IDA_mem, N_Vector* xQS, * Updates the norm old_nrm to account for all quadratures. */ -static sunrealtype IDAQuadWrmsNormUpdate(IDAMem IDA_mem, sunrealtype old_nrm, - N_Vector xQ, N_Vector wQ) +static sunrealtype IDAQuadWrmsNormUpdate(SUNDIALS_MAYBE_UNUSED IDAMem IDA_mem, + sunrealtype old_nrm, N_Vector xQ, + N_Vector wQ) { sunrealtype qnrm; @@ -8441,10 +8442,10 @@ int IDASensResDQ(int Ns, sunrealtype t, N_Vector yy, N_Vector yp, * (<0 if res fails unrecoverably, >0 if res has a recoverable error). */ -static int IDASensRes1DQ(int Ns, sunrealtype t, N_Vector yy, N_Vector yp, - N_Vector resval, int is, N_Vector yyS, N_Vector ypS, - N_Vector resvalS, void* user_dataS, N_Vector ytemp, - N_Vector yptemp, N_Vector restemp) +static int IDASensRes1DQ(SUNDIALS_MAYBE_UNUSED int Ns, sunrealtype t, N_Vector yy, + N_Vector yp, N_Vector resval, int is, N_Vector yyS, + N_Vector ypS, N_Vector resvalS, void* user_dataS, + N_Vector ytemp, N_Vector yptemp, N_Vector restemp) { IDAMem IDA_mem; int method; @@ -8756,15 +8757,20 @@ static int IDAQuadSensRhs1InternalDQ(IDAMem IDA_mem, int is, sunrealtype t, void IDAProcessError(IDAMem IDA_mem, int error_code, int line, const char* func, const char* file, const char* msgfmt, ...) { - /* Initialize the argument pointer variable + /* We initialize the argument pointer variable before each vsnprintf call to avoid undefined behavior (msgfmt is the last required argument to IDAProcessError) */ va_list ap; - va_start(ap, msgfmt); /* Compose the message */ + va_start(ap, msgfmt); size_t msglen = vsnprintf(NULL, 0, msgfmt, ap) + 1; - char* msg = (char*)malloc(msglen); + va_end(ap); + + char* msg = (char*)malloc(msglen); + + va_start(ap, msgfmt); vsnprintf(msg, msglen, msgfmt, ap); + va_end(ap); do { if (IDA_mem == NULL) @@ -8792,8 +8798,6 @@ void IDAProcessError(IDAMem IDA_mem, int error_code, int line, const char* func, } while (0); - /* Finalize argument processing */ - va_end(ap); free(msg); return; diff --git a/src/sundials/idas/idas_bbdpre.c b/src/sundials/idas/idas_bbdpre.c index a584499..25521a9 100644 --- a/src/sundials/idas/idas_bbdpre.c +++ b/src/sundials/idas/idas_bbdpre.c @@ -469,7 +469,8 @@ int IDABBDPrecGetNumGfnEvals(void* ida_mem, long int* ngevalsBBDP) < 0 for a nonrecoverable error (step fails). ----------------------------------------------------------------*/ static int IDABBDPrecSetup(sunrealtype tt, N_Vector yy, N_Vector yp, - N_Vector rr, sunrealtype c_j, void* bbd_data) + SUNDIALS_MAYBE_UNUSED N_Vector rr, sunrealtype c_j, + void* bbd_data) { IBBDPrecData pdata; IDAMem IDA_mem; @@ -516,9 +517,13 @@ static int IDABBDPrecSetup(sunrealtype tt, N_Vector yy, N_Vector yp, IDABBDPrecSolve returns the value returned from the linear solver object. ---------------------------------------------------------------*/ -static int IDABBDPrecSolve(sunrealtype tt, N_Vector yy, N_Vector yp, - N_Vector rr, N_Vector rvec, N_Vector zvec, - sunrealtype c_j, sunrealtype delta, void* bbd_data) +static int IDABBDPrecSolve(SUNDIALS_MAYBE_UNUSED sunrealtype tt, + SUNDIALS_MAYBE_UNUSED N_Vector yy, + SUNDIALS_MAYBE_UNUSED N_Vector yp, + SUNDIALS_MAYBE_UNUSED N_Vector rr, N_Vector rvec, + N_Vector zvec, SUNDIALS_MAYBE_UNUSED sunrealtype c_j, + SUNDIALS_MAYBE_UNUSED sunrealtype delta, + void* bbd_data) { IBBDPrecData pdata; int retval; diff --git a/src/sundials/idas/idas_ic.c b/src/sundials/idas/idas_ic.c index 783a75a..c2f4d03 100644 --- a/src/sundials/idas/idas_ic.c +++ b/src/sundials/idas/idas_ic.c @@ -58,13 +58,6 @@ */ extern int IDAInitialSetup(IDAMem IDA_mem); -extern sunrealtype IDAWrmsNorm(IDAMem IDA_mem, N_Vector x, N_Vector w, - sunbooleantype mask); -extern sunrealtype IDASensWrmsNorm(IDAMem IDA_mem, N_Vector* xS, N_Vector* wS, - sunbooleantype mask); -extern sunrealtype IDASensWrmsNormUpdate(IDAMem IDA_mem, sunrealtype old_nrm, - N_Vector* xS, N_Vector* wS, - sunbooleantype mask); extern int IDASensEwtSet(IDAMem IDA_mem, N_Vector* yScur, N_Vector* weightS); diff --git a/src/sundials/idas/idas_impl.h b/src/sundials/idas/idas_impl.h index 90abcbc..e54b593 100644 --- a/src/sundials/idas/idas_impl.h +++ b/src/sundials/idas/idas_impl.h @@ -19,10 +19,12 @@ #define _IDAS_IMPL_H #include + +#include #include -#include "idas/idas.h" #include "sundials_logger_impl.h" +#include "sundials_macros.h" #ifdef __cplusplus /* wrapper to enable C++ usage */ extern "C" { diff --git a/src/sundials/idas/idas_ls.c b/src/sundials/idas/idas_ls.c index dc0c0ba..088bcbb 100644 --- a/src/sundials/idas/idas_ls.c +++ b/src/sundials/idas/idas_ls.c @@ -901,7 +901,8 @@ int idaLsPSetup(void* ida_mem) is the only case in which the user's psolve routine is allowed to be NULL. ---------------------------------------------------------------*/ -int idaLsPSolve(void* ida_mem, N_Vector r, N_Vector z, sunrealtype tol, int lr) +int idaLsPSolve(void* ida_mem, N_Vector r, N_Vector z, sunrealtype tol, + SUNDIALS_MAYBE_UNUSED int lr) { IDAMem IDA_mem; IDALsMem idals_mem; @@ -1179,7 +1180,7 @@ int idaLsBandDQJac(sunrealtype tt, sunrealtype c_j, N_Vector yy, N_Vector yp, /* Increment yj and ypj. */ ytemp_data[j] += inc; - yptemp_data[j] += IDA_mem->ida_cj * inc; + yptemp_data[j] += c_j * inc; } /* Call res routine with incremented arguments. */ diff --git a/src/sundials/idas/idas_nls.c b/src/sundials/idas/idas_nls.c index 034f2c3..17ba1e6 100644 --- a/src/sundials/idas/idas_nls.c +++ b/src/sundials/idas/idas_nls.c @@ -233,7 +233,8 @@ int idaNlsInit(IDAMem IDA_mem) return (IDA_SUCCESS); } -static int idaNlsLSetup(sunbooleantype jbad, sunbooleantype* jcur, void* ida_mem) +static int idaNlsLSetup(SUNDIALS_MAYBE_UNUSED sunbooleantype jbad, + sunbooleantype* jcur, void* ida_mem) { IDAMem IDA_mem; int retval; @@ -321,7 +322,8 @@ static int idaNlsResidual(N_Vector ycor, N_Vector res, void* ida_mem) return (IDA_SUCCESS); } -static int idaNlsConvTest(SUNNonlinearSolver NLS, N_Vector ycor, N_Vector del, +static int idaNlsConvTest(SUNNonlinearSolver NLS, + SUNDIALS_MAYBE_UNUSED N_Vector ycor, N_Vector del, sunrealtype tol, N_Vector ewt, void* ida_mem) { IDAMem IDA_mem; diff --git a/src/sundials/idas/idas_nls_sim.c b/src/sundials/idas/idas_nls_sim.c index d23a6c2..d527189 100644 --- a/src/sundials/idas/idas_nls_sim.c +++ b/src/sundials/idas/idas_nls_sim.c @@ -277,8 +277,8 @@ int idaNlsInitSensSim(IDAMem IDA_mem) return (IDA_SUCCESS); } -static int idaNlsLSetupSensSim(sunbooleantype jbad, sunbooleantype* jcur, - void* ida_mem) +static int idaNlsLSetupSensSim(SUNDIALS_MAYBE_UNUSED sunbooleantype jbad, + sunbooleantype* jcur, void* ida_mem) { IDAMem IDA_mem; int retval; @@ -415,7 +415,8 @@ static int idaNlsResidualSensSim(N_Vector ycorSim, N_Vector resSim, void* ida_me return (IDA_SUCCESS); } -static int idaNlsConvTestSensSim(SUNNonlinearSolver NLS, N_Vector ycor, +static int idaNlsConvTestSensSim(SUNNonlinearSolver NLS, + SUNDIALS_MAYBE_UNUSED N_Vector ycor, N_Vector del, sunrealtype tol, N_Vector ewt, void* ida_mem) { diff --git a/src/sundials/idas/idas_nls_stg.c b/src/sundials/idas/idas_nls_stg.c index 6311cf6..3e1590c 100644 --- a/src/sundials/idas/idas_nls_stg.c +++ b/src/sundials/idas/idas_nls_stg.c @@ -232,8 +232,8 @@ int idaNlsInitSensStg(IDAMem IDA_mem) return (IDA_SUCCESS); } -static int idaNlsLSetupSensStg(sunbooleantype jbad, sunbooleantype* jcur, - void* ida_mem) +static int idaNlsLSetupSensStg(SUNDIALS_MAYBE_UNUSED sunbooleantype jbad, + sunbooleantype* jcur, void* ida_mem) { IDAMem IDA_mem; int retval; @@ -326,7 +326,8 @@ static int idaNlsResidualSensStg(N_Vector ycorStg, N_Vector resStg, void* ida_me return (IDA_SUCCESS); } -static int idaNlsConvTestSensStg(SUNNonlinearSolver NLS, N_Vector ycor, +static int idaNlsConvTestSensStg(SUNNonlinearSolver NLS, + SUNDIALS_MAYBE_UNUSED N_Vector ycor, N_Vector del, sunrealtype tol, N_Vector ewt, void* ida_mem) { diff --git a/src/sundials/kinsol/kinsol.c b/src/sundials/kinsol/kinsol.c index 205ffbc..acfe65f 100644 --- a/src/sundials/kinsol/kinsol.c +++ b/src/sundials/kinsol/kinsol.c @@ -2491,8 +2491,10 @@ static sunrealtype KINScSNorm(KINMem kin_mem, N_Vector v, N_Vector u) * passes it to the info handler function. */ -void KINPrintInfo(KINMem kin_mem, int info_code, const char* module, - const char* fname, const char* msgfmt, ...) +void KINPrintInfo(SUNDIALS_MAYBE_UNUSED KINMem kin_mem, int info_code, + SUNDIALS_MAYBE_UNUSED const char* module, + SUNDIALS_MAYBE_UNUSED const char* fname, const char* msgfmt, + ...) { va_list ap; char msg[256], msg1[40]; @@ -2540,7 +2542,7 @@ void KINPrintInfo(KINMem kin_mem, int info_code, const char* module, { /* Compose the message */ - vsprintf(msg, msgfmt, ap); + vsnprintf(msg, sizeof msg, msgfmt, ap); } #if SUNDIALS_LOGGING_LEVEL >= SUNDIALS_LOGGING_INFO @@ -2563,15 +2565,20 @@ void KINPrintInfo(KINMem kin_mem, int info_code, const char* module, void KINProcessError(KINMem kin_mem, int error_code, int line, const char* func, const char* file, const char* msgfmt, ...) { - /* Initialize the argument pointer variable + /* We initialize the argument pointer variable before each vsnprintf call to avoid undefined behavior (msgfmt is the last required argument to KINProcessError) */ va_list ap; - va_start(ap, msgfmt); /* Compose the message */ + va_start(ap, msgfmt); size_t msglen = vsnprintf(NULL, 0, msgfmt, ap) + 1; - char* msg = (char*)malloc(msglen); + va_end(ap); + + char* msg = (char*)malloc(msglen); + + va_start(ap, msgfmt); vsnprintf(msg, msglen, msgfmt, ap); + va_end(ap); do { if (kin_mem == NULL) @@ -2599,8 +2606,6 @@ void KINProcessError(KINMem kin_mem, int error_code, int line, const char* func, } while (0); - /* Finalize argument processing */ - va_end(ap); free(msg); return; @@ -2837,7 +2842,7 @@ static int KINFP(KINMem kin_mem) #ifdef SUNDIALS_LOGGING_EXTRA_DEBUG SUNLogger_QueueMsg(KIN_LOGGER, SUN_LOGLEVEL_DEBUG, "KINSOL::KINFP", "begin", - "%s", "u_0:"); + "%s", "u_0(:) ="); N_VPrintFile(kin_mem->kin_uu, KIN_LOGGER->debug_fp); #endif @@ -2857,7 +2862,7 @@ static int KINFP(KINMem kin_mem) #ifdef SUNDIALS_LOGGING_EXTRA_DEBUG SUNLogger_QueueMsg(KIN_LOGGER, SUN_LOGLEVEL_DEBUG, "KINSOL::KINFP", "while-loop-before-compute-new", - "G_%ld:", kin_mem->kin_nni - 1); + "G_%ld(:) =", kin_mem->kin_nni - 1); N_VPrintFile(kin_mem->kin_fval, KIN_LOGGER->debug_fp); #endif @@ -2912,8 +2917,8 @@ static int KINFP(KINMem kin_mem) #ifdef SUNDIALS_LOGGING_EXTRA_DEBUG SUNLogger_QueueMsg(KIN_LOGGER, SUN_LOGLEVEL_DEBUG, "KINSOL::KINFP", - "while-loop-after-compute-new", "u_%ld:\n", - kin_mem->kin_nni); + "while-loop-after-compute-new", + "u_%ld(:) =", kin_mem->kin_nni); N_VPrintFile(kin_mem->kin_unew, KIN_LOGGER->debug_fp); #endif diff --git a/src/sundials/kinsol/kinsol_bbdpre.c b/src/sundials/kinsol/kinsol_bbdpre.c index 3cd8b78..7025f38 100644 --- a/src/sundials/kinsol/kinsol_bbdpre.c +++ b/src/sundials/kinsol/kinsol_bbdpre.c @@ -399,8 +399,9 @@ int KINBBDPrecGetNumGfnEvals(void* kinmem, long int* ngevalsBBDP) 0 if successful, > 0 for a recoverable error - step will be retried. ------------------------------------------------------------------*/ -static int KINBBDPrecSetup(N_Vector uu, N_Vector uscale, N_Vector fval, - N_Vector fscale, void* bbd_data) +static int KINBBDPrecSetup(N_Vector uu, N_Vector uscale, + SUNDIALS_MAYBE_UNUSED N_Vector fval, + SUNDIALS_MAYBE_UNUSED N_Vector fscale, void* bbd_data) { KBBDPrecData pdata; KINMem kin_mem; @@ -463,8 +464,11 @@ static int KINBBDPrecSetup(N_Vector uu, N_Vector uscale, N_Vector fval, flag returned from the lienar solver object. ------------------------------------------------------------------*/ -static int KINBBDPrecSolve(N_Vector uu, N_Vector uscale, N_Vector fval, - N_Vector fscale, N_Vector vv, void* bbd_data) +static int KINBBDPrecSolve(SUNDIALS_MAYBE_UNUSED N_Vector uu, + SUNDIALS_MAYBE_UNUSED N_Vector uscale, + SUNDIALS_MAYBE_UNUSED N_Vector fval, + SUNDIALS_MAYBE_UNUSED N_Vector fscale, N_Vector vv, + void* bbd_data) { KBBDPrecData pdata; sunrealtype* vd; diff --git a/src/sundials/kinsol/kinsol_impl.h b/src/sundials/kinsol/kinsol_impl.h index 7122c07..117bf00 100644 --- a/src/sundials/kinsol/kinsol_impl.h +++ b/src/sundials/kinsol/kinsol_impl.h @@ -20,12 +20,14 @@ #ifndef _KINSOL_IMPL_H #define _KINSOL_IMPL_H -#include #include + +#include #include #include "sundials_iterative_impl.h" #include "sundials_logger_impl.h" +#include "sundials_macros.h" #ifdef __cplusplus /* wrapper to enable C++ usage */ extern "C" { diff --git a/src/sundials/kinsol/kinsol_ls.c b/src/sundials/kinsol/kinsol_ls.c index 684995a..37c706e 100644 --- a/src/sundials/kinsol/kinsol_ls.c +++ b/src/sundials/kinsol/kinsol_ls.c @@ -678,7 +678,9 @@ int kinLsPSetup(void* kinmem) in the case in which preconditioning is not done. This is the only case in which the user's psolve routine is allowed to be NULL. ------------------------------------------------------------------*/ -int kinLsPSolve(void* kinmem, N_Vector r, N_Vector z, sunrealtype tol, int lr) +int kinLsPSolve(void* kinmem, N_Vector r, N_Vector z, + SUNDIALS_MAYBE_UNUSED sunrealtype tol, + SUNDIALS_MAYBE_UNUSED int lr) { KINMem kin_mem; KINLsMem kinls_mem; @@ -941,8 +943,8 @@ int kinLsBandDQJac(N_Vector u, N_Vector fu, SUNMatrix Jac, KINMem kin_mem, a recovery may still be possible even if the system function fails (recoverably). ------------------------------------------------------------------*/ -int kinLsDQJtimes(N_Vector v, N_Vector Jv, N_Vector u, sunbooleantype* new_u, - void* kinmem) +int kinLsDQJtimes(N_Vector v, N_Vector Jv, N_Vector u, + SUNDIALS_MAYBE_UNUSED sunbooleantype* new_u, void* kinmem) { sunrealtype sigma, sigma_inv, sutsv, sq1norm, sign, vtv; KINMem kin_mem; diff --git a/src/sundials/nvector/serial/nvector_serial.c b/src/sundials/nvector/serial/nvector_serial.c index a4a1bff..255aca3 100644 --- a/src/sundials/nvector/serial/nvector_serial.c +++ b/src/sundials/nvector/serial/nvector_serial.c @@ -16,14 +16,16 @@ * of the NVECTOR package. * -----------------------------------------------------------------*/ -#include #include #include + +#include #include #include #include +#include -#include "sundials/sundials_errors.h" +#include "sundials_macros.h" #define ZERO SUN_RCONST(0.0) #define HALF SUN_RCONST(0.5) @@ -217,6 +219,15 @@ N_Vector N_VMake_Serial(sunindextype length, sunrealtype* v_data, return (v); } +/* ---------------------------------------------------------------- + * Returns vector type ID. Used to identify vector implementation + * from abstract N_Vector interface. + */ +N_Vector_ID N_VGetVectorID_Serial(SUNDIALS_MAYBE_UNUSED N_Vector v) +{ + return SUNDIALS_NVEC_SERIAL; +} + /* ---------------------------------------------------------------------------- * Function to return number of vector elements */ diff --git a/src/sundials/sundials/priv/sundials_context_impl.h b/src/sundials/sundials/priv/sundials_context_impl.h new file mode 100644 index 0000000..1281333 --- /dev/null +++ b/src/sundials/sundials/priv/sundials_context_impl.h @@ -0,0 +1,45 @@ +/* ----------------------------------------------------------------- + * Programmer(s): Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * !!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This is a 'private' header file and should not be used in user + * code. It is subject to change without warning. + * !!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ----------------------------------------------------------------- + * SUNDIALS context class implementation. + * ----------------------------------------------------------------*/ + +#ifndef _SUNDIALS_CONTEXT_IMPL_H +#define _SUNDIALS_CONTEXT_IMPL_H + +#include + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +extern "C" { +#endif + +struct SUNContext_ +{ + SUNProfiler profiler; + sunbooleantype own_profiler; + SUNLogger logger; + sunbooleantype own_logger; + SUNErrCode last_err; + SUNErrHandler err_handler; + SUNComm comm; +}; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/sundials/sundials/priv/sundials_errors_impl.h b/src/sundials/sundials/priv/sundials_errors_impl.h new file mode 100644 index 0000000..115b5ee --- /dev/null +++ b/src/sundials/sundials/priv/sundials_errors_impl.h @@ -0,0 +1,557 @@ +/* ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * !!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This is a 'private' header file and should not be used in user + * code. It is subject to change without warning. + * !!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ----------------------------------------------------------------- + * Contains all error checking macros and private error handling API. + * -----------------------------------------------------------------*/ + +#ifndef _SUNDIALS_ERRORS_IMPL_H +#define _SUNDIALS_ERRORS_IMPL_H + +#include + +#include "sundials/sundials_config.h" +#include "sundials/sundials_context.h" +#include "sundials/sundials_export.h" +#include "sundials/sundials_logger.h" +#include "sundials/sundials_types.h" + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +extern "C" { +#endif + +/* ---------------------------------------------------------------------------- + * Macros used in error handling + * ---------------------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + * SUNDIALS __builtin_expect related macros. + * These macros provide hints to the compiler that the condition + * is typically false (or true) which may allow the compiler to + * optimize. + * -----------------------------------------------------------------*/ + +/* Hint to the compiler that the branch is unlikely to be taken */ +#ifdef SUNDIALS_C_COMPILER_HAS_BUILTIN_EXPECT +#define SUNHintFalse(cond) __builtin_expect((cond), 0) +#else +#define SUNHintFalse(cond) (cond) +#endif + +/* Hint to the compiler that the branch is likely to be taken */ +#ifdef SUNDIALS_C_COMPILER_HAS_BUILTIN_EXPECT +#define SUNHintTrue(cond) __builtin_expect((cond), 1) +#else +#define SUNHintTrue(cond) (cond) +#endif + +/* ------------------------------------------------------------------ + * SUNAssume + * + * This macro tells the compiler that the condition should be assumed + * to be true. The consequence is that what happens if the assumption + * is violated is undefined. If there is not compiler support for + * assumptions, then we dont do anything as there is no reliable + * way to avoid the condition being executed in all cases (such as + * the condition being an opaque function call, which we have a lot of). + * -----------------------------------------------------------------*/ + +#if __cplusplus >= 202302L +#define SUNAssume(...) [[assume(__VA_ARGS__)]] +#elif defined(SUNDIALS_C_COMPILER_HAS_ATTRIBUTE_ASSUME) +#define SUNAssume(...) __attribute__((assume(__VA_ARGS__))) +#elif defined(SUNDIALS_C_COMPILER_HAS_BUILTIN_ASSUME) +#define SUNAssume(...) __builtin_assume(__VA_ARGS__) +#elif defined(SUNDIALS_C_COMPILER_HAS_ASSUME) +#define SUNAssume(...) __assume(__VA_ARGS__) +#else +#define SUNAssume(...) +#endif + +/* ---------------------------------------------------------------------------- + * SUNErrHandler_ definition. + * ---------------------------------------------------------------------------*/ + +struct SUNErrHandler_ +{ + SUNErrHandler previous; /* next error handler to call (singly linked-list) */ + SUNErrHandlerFn call; + void* data; +}; + +/* + This function creates a new SUNErrHandler object which is a node in a + singly linked-lis of error handlers. + + :param eh_fn: An error handler callback function + :param eh_data: A pointer that will be passed back to the error handler + callback function + :param eh_out: The new SUNErrHandler object + + :return: A SUNErrCode indicating success or failure +*/ +SUNDIALS_EXPORT +SUNErrCode SUNErrHandler_Create(SUNErrHandlerFn eh_fn, void* eh_data, + SUNErrHandler* eh_out); + +/* + This function destroys and frees the memory for the given SUNErrHandler + object. + + :param eh: A pointer to a SUNErrHandler object + + :return: void +*/ +SUNDIALS_EXPORT +void SUNErrHandler_Destroy(SUNErrHandler* eh); + +/* + This function will print an error out to stderr. It is used as a fallback + when the SUNContext is NULL or corrupt. It should not be used otherwise. + + :param line: the line number of the error + :param func: the function in which the error occurred + :param file: the file in which the error occurred + :param msg: a message associated with the error + :param args: the arguments to be provided to the format message + + :return: void +*/ +SUNDIALS_EXPORT +void SUNGlobalFallbackErrHandler(int line, const char* func, const char* file, + const char* msgfmt, SUNErrCode code, ...); + +/* + This function calls the error handlers registered with the SUNContext + with the provided message. + + :param line: the line number of the error + :param func: the function in which the error occurred + :param file: the file in which the error occurred + :param msgfmt: a message associated with the error with formatting + :param code: the SUNErrCode for the error + :param sunctx: a valid SUNContext object + + :return: void +*/ +static inline void SUNHandleErrWithMsg(int line, const char* func, + const char* file, const char* msg, + SUNErrCode code, SUNContext sunctx) +{ + if (!sunctx) { SUNGlobalFallbackErrHandler(line, func, file, msg, code); } + + sunctx->last_err = code; + SUNErrHandler eh = sunctx->err_handler; + while (eh != NULL) + { + eh->call(line, func, file, msg, code, eh->data, sunctx); + eh = eh->previous; + } +} + +/* + This function calls the error handlers registered with the SUNContext + with the provided format message. + + :param line: the line number of the error + :param func: the function in which the error occurred + :param file: the file in which the error occurred + :param msgfmt: a message associated with the error with formatting + :param code: the SUNErrCode for the error + :param sunctx: a valid SUNContext object + :param args: the arguments to be provided to the format message + + :return: void +*/ +static inline void SUNHandleErrWithFmtMsg(int line, const char* func, + const char* file, const char* msgfmt, + SUNErrCode code, SUNContext sunctx, ...) +{ + size_t msglen; + char* msg; + va_list values; + va_start(values, sunctx); + msglen = (size_t)vsnprintf(NULL, (size_t)0, msgfmt, values); /* determine size + of buffer + needed */ + msg = (char*)malloc(msglen + 1); + vsnprintf(msg, msglen + 1, msgfmt, values); + SUNHandleErrWithMsg(line, func, file, msg, code, sunctx); + va_end(values); + free(msg); +} + +/* + The SUNCTX_ macro expands to the name of the local SUNContext object + defined by SUNFunctionBegin. SUNCTX_ should be used to reference the + SUNContext inside of SUNDIALS functions. + */ +#define SUNCTX_ sunctx_local_scope_ + +/* + The SUNFunctionBegin macro is used to declare the local SUNContext object to + be used a function. It should be used at the start of every SUNDIALS + functions. + + :param sunctx: the SUNContext to set the local SUNContext variable to + */ +#define SUNFunctionBegin(sunctx) \ + SUNContext SUNCTX_ = sunctx; \ + (void)SUNCTX_ + +/* ---------------------------------------------------------------------------- + * SUNCheck* family of error checking macros + * + * We define several different versions of SUNCheck* macros to cover various + * programming scenarios. + * + * SUNCheckCall* macros are used to check SUNDIALS function calls that + * return a SUNErrCode. + * + * SUNCheckLastErr* macros are used to check SUNDIALS function calls that + * do not return a SUNErrCode. + * ---------------------------------------------------------------------------*/ + +/* + SUNCheck evaluates the given expression and calls the error handler if it is + false. It should be used to check expressions that do not imply stringent + assumptions. If the expression should be strictly assumed as true, then use + SUNAssert instead. + + Use SUNAssert macros to check for conditions that do not make sense e.g., + to check if malloc returned NULL. + + :param expr: an expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheck(expr, code) \ + do { \ + if (SUNHintFalse(!(expr))) \ + { \ + SUNHandleErrWithFmtMsg(__LINE__, __func__, __FILE__, "expected %s", \ + code, SUNCTX_, #expr); \ + return code; \ + } \ + } \ + while (0) +#else +#define SUNCheck(expr, code) +#endif + +/* + SUNCheckNoRet is the same as SUNCheck but *does not return from the caller*. + Use SUNAssert macros to check for conditions that do not make sense e.g., + to check if malloc returned NULL. + + :param expr: an expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ + +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckNoRet(expr, code) \ + do { \ + if (SUNHintFalse(!(expr))) \ + { \ + SUNHandleErrWithFmtMsg(__LINE__, __func__, __FILE__, "expected %s", \ + code, SUNCTX_, #expr); \ + } \ + } \ + while (0) +#else +#define SUNCheckNoRet(expr, code) +#endif + +/* + SUNCheckNull is the same as SUNCheck but *returns NULL from the caller*. + Use SUNAssert macros to check for conditions that do not make sense e.g., + to check if malloc returned NULL. + + :param expr: an expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ + +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckNull(expr, code) \ + do { \ + if (SUNHintFalse(!(expr))) \ + { \ + SUNHandleErrWithFmtMsg(__LINE__, __func__, __FILE__, "expected %s", \ + code, SUNCTX_, #expr); \ + return NULL; \ + } \ + } \ + while (0) +#else +#define SUNCheckNull(expr, code) +#endif + +/* + SUNCheckNull is the same as SUNCheck but *returns void from the caller*. + Use SUNAssert macros to check for conditions that do not make sense e.g., + to check if malloc returned NULL. + + :param expr: an expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ + +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckVoid(expr, code) \ + do { \ + if (SUNHintFalse(!(expr))) \ + { \ + SUNHandleErrWithFmtMsg(__LINE__, __func__, __FILE__, "expected %s", \ + code, SUNCTX_, #expr); \ + return; \ + } \ + } \ + while (0) +#else +#define SUNCheckVoid(expr, code) +#endif + +/* + SUNCheckCallMsg performs the SUNDIALS function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then return the error code**. + + :param call: the function call + :param msg: an error message +*/ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckCallMsg(call, msg) \ + do { \ + SUNErrCode sun_chk_call_err_code_ = call; \ + if (SUNHintFalse(sun_chk_call_err_code_ < 0)) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, \ + sun_chk_call_err_code_, SUNCTX_); \ + return sun_chk_call_err_code_; \ + } \ + } \ + while (0) +#else +#define SUNCheckCallMsg(call, msg) (void)call +#endif + +/* + SUNCheckCallNoRetMsg performs the SUNDIALS function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, and call the error handler. **It does not return**. + + :param call: the function call + :param msg: an error message +*/ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckCallNoRetMsg(call, msg) \ + do { \ + SUNErrCode sun_chk_call_err_code_ = call; \ + if (SUNHintFalse(sun_chk_call_err_code_ < 0)) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, \ + sun_chk_call_err_code_, SUNCTX_); \ + } \ + } \ + while (0) +#else +#define SUNCheckCallNoRetMsg(call, msg) (void)call +#endif + +/* + SUNCheckCallNullMsg performs the SUNDIALS function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then returns NULL**. + + :param call: the function call + :param msg: an error message +*/ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckCallNullMsg(call, msg) \ + do { \ + SUNErrCode sun_chk_call_err_code_ = call; \ + if (SUNHintFalse(sun_chk_call_err_code_ < 0)) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, \ + sun_chk_call_err_code_, SUNCTX_); \ + return NULL; \ + } \ + } \ + while (0) +#else +#define SUNCheckCallNullMsg(call, msg) (void)call +#endif + +/* + SUNCheckCallNullMsg performs the SUNDIALS function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then returns void**. + + :param call: the function call + :param msg: an error message +*/ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckCallVoidMsg(call, msg) \ + do { \ + SUNErrCode sun_chk_call_err_code_ = call; \ + if (SUNHintFalse(sun_chk_call_err_code_ < 0)) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, \ + sun_chk_call_err_code_, SUNCTX_); \ + return; \ + } \ + } \ + while (0) +#else +#define SUNCheckCallVoidMsg(call, msg) (void)call +#endif + +/* These versions of SUNCheckCall do not take a custom message so a + default message associated with the error code will be used. */ +#define SUNCheckCall(call) SUNCheckCallMsg(call, NULL) +#define SUNCheckCallNoRet(call) SUNCheckCallNoRetMsg(call, NULL) +#define SUNCheckCallNull(call) SUNCheckCallNullMsg(call, NULL) +#define SUNCheckCallVoid(call) SUNCheckCallVoidMsg(call, NULL) + +/* SUNCheckLastErrMsg checks the last_err value in the SUNContext. + If an error occured, then it will log the error, set the last_err + value, and call the error handler, **and then returns the code**. */ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckLastErrMsg(msg) \ + do { \ + SUNCheckCallMsg(SUNContext_PeekLastError(SUNCTX_), msg); \ + } \ + while (0) + +/* + SUNCheckLastErrNoRetMsg performs the SUNDIALS function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler. **It does not return.** + + :param msg: an error message +*/ +#define SUNCheckLastErrNoRetMsg(msg) \ + do { \ + SUNCheckCallNoRetMsg(SUNContext_PeekLastError(SUNCTX_), msg); \ + } \ + while (0) + +/* + SUNCheckLastErrNullMsg performs the SUNDIALS function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then returns NULL**. + + :param msg: an error message +*/ +#define SUNCheckLastErrNullMsg(msg) \ + do { \ + SUNCheckCallNullMsg(SUNContext_PeekLastError(SUNCTX_), msg); \ + } \ + while (0) + +/* + SUNCheckLastErrVoidMsg performs the SUNDIALS function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then returns void**. + + :param msg: an error message +*/ +#define SUNCheckLastErrVoidMsg(msg) \ + do { \ + SUNCheckCallVoidMsg(SUNContext_PeekLastError(SUNCTX_), msg); \ + } \ + while (0) +#else +#define SUNCheckLastErrNoRetMsg(msg) +#define SUNCheckLastErrMsg(msg) +#define SUNCheckLastErrVoidMsg(msg) +#define SUNCheckLastErrNullMsg(msg) +#endif + +/* These versions of SUNCheckLastErr do not take a custom message so the + default message associated with the error code will be used. */ +#define SUNCheckLastErr() SUNCheckLastErrMsg(NULL) +#define SUNCheckLastErrNoRet() SUNCheckLastErrNoRetMsg(NULL) +#define SUNCheckLastErrVoid() SUNCheckLastErrVoidMsg(NULL) +#define SUNCheckLastErrNull() SUNCheckLastErrNullMsg(NULL) + +/* + SUNAssert checks if an expression is true. It expands to SUNCheck in debug + builds othewrise we try to expand it to an assumption, if the compiler + supports assumptions, so that the compiler can make optimizations based on the + assumption. + + :param expr: a expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ + +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNAssert(expr, code) SUNCheck(expr, code) +#else +#define SUNAssert(expr, code) +#endif + +/* + SUNAssertNoRet is the same as SUNAssert but it does not return from the + caller. + + :param expr: a expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ + +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNAssertNoRet(expr, code) SUNCheckNoRet(expr, code) +#else +#define SUNAssertNoRet(expr, code) +#endif + +/* + SUNAssertNull is the same as SUNAssert but it *returns NULL from the caller*. + + :param expr: a expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ + +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNAssertNull(expr, code) SUNCheckNull(expr, code) +#else +#define SUNAssertNull(expr, code) +#endif + +/* + SUNAssertVoid is the same as SUNAssert but it *returns void from the caller*. + + :param expr: a expression to evaluate as true or false + :param code: the error code to pass to the error handler if the expression is + false +*/ + +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNAssertVoid(expr, code) SUNCheckVoid(expr, code) +#else +#define SUNAssertVoid(expr, code) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SUNDIALS_ERRORS_IMPL_H */ diff --git a/src/sundials/sundials/priv/sundials_mpi_errors_impl.h b/src/sundials/sundials/priv/sundials_mpi_errors_impl.h new file mode 100644 index 0000000..003cccf --- /dev/null +++ b/src/sundials/sundials/priv/sundials_mpi_errors_impl.h @@ -0,0 +1,137 @@ +/* ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * !!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This is a 'private' header file and should not be used in user + * code. It is subject to change without warning. + * !!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ----------------------------------------------------------------- + * Contains error checking macros and prototypes for MPI calls. + * -----------------------------------------------------------------*/ + +#ifndef _SUNDIALS_MPI_ERRORS_IMPL_H +#define _SUNDIALS_MPI_ERRORS_IMPL_H + +#include +#include +#include + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +extern "C" { +#endif + +/* + SUNCheckMPICallMsg performs the MPI function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then return SUN_ERR_MPI_FAIL**. + + :param call: the MPI function call + :param msg: an error message + */ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckMPICallMsg(call, msg) \ + do { \ + int sun_chk_mpi_call_err_code_ = call; \ + if (sun_chk_mpi_call_err_code_ != MPI_SUCCESS) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, SUN_ERR_MPI_FAIL, \ + SUNCTX_); \ + return SUN_ERR_MPI_FAIL; \ + } \ + } \ + while (0) +#else +#define SUNCheckMPICallMsg(call, msg) (void)call +#endif + +/* + SUNCheckMPICallNullMsg performs the MPI function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then return NULL**. + + :param call: the MPI function call + :param msg: an error message + */ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckMPICallNullMsg(call, msg) \ + do { \ + int sun_chk_mpi_call_err_code_ = call; \ + if (sun_chk_mpi_call_err_code_ != MPI_SUCCESS) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, SUN_ERR_MPI_FAIL, \ + SUNCTX_); \ + return NULL; \ + } \ + } \ + while (0) +#else +#define SUNCheckMPICallNullMsg(call, msg) (void)call +#endif + +/* + SUNCheckMPICallVoidMsg performs the MPI function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, call the error handler, **and then return void**. + + :param call: the MPI function call + :param msg: an error message + */ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckMPICallVoidMsg(call, msg) \ + do { \ + int sun_chk_mpi_call_err_code_ = call; \ + if (sun_chk_mpi_call_err_code_ != MPI_SUCCESS) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, SUN_ERR_MPI_FAIL, \ + SUNCTX_); \ + return; \ + } \ + } \ + while (0) +#else +#define SUNCheckMPICallVoidMsg(call, msg) (void)call +#endif + +/* + SUNCheckMPICallNoRetMsg performs the MPI function call, and checks the + returned error code. If an error occured, then it will log the error, set the + last_err value, and call the error handler. **It does not return**. + + :param call: the MPI function call + :param msg: an error message + */ +#if defined(SUNDIALS_ENABLE_ERROR_CHECKS) +#define SUNCheckMPICallNoRetMsg(call, msg) \ + do { \ + int sun_chk_mpi_call_err_code_ = call; \ + if (sun_chk_mpi_call_err_code_ != MPI_SUCCESS) \ + { \ + SUNHandleErrWithMsg(__LINE__, __func__, __FILE__, msg, SUN_ERR_MPI_FAIL, \ + SUNCTX_); \ + } \ + } \ + while (0) +#else +#define SUNCheckMPICallNoRetMsg(call, msg) (void)call +#endif + +/* These versions of SUNCheckMPICall do not take a custom message so a + default message associated with the error code will be used. */ +#define SUNCheckMPICall(call) SUNCheckMPICallMsg(call, NULL) +#define SUNCheckMPICallNoRet(call) SUNCheckMPICallNoRetMsg(call, NULL) +#define SUNCheckMPICallNull(call) SUNCheckMPICallNullMsg(call, NULL) +#define SUNCheckMPICallVoid(call) SUNCheckMPICallVoidMsg(call, NULL) + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +} +#endif + +#endif /* _SUNDIALS_MPI_ERRORS_IMPL_H */ diff --git a/src/sundials/sundials/sundials_adaptcontroller.c b/src/sundials/sundials/sundials_adaptcontroller.c new file mode 100644 index 0000000..39a5648 --- /dev/null +++ b/src/sundials/sundials/sundials_adaptcontroller.c @@ -0,0 +1,199 @@ +/* ----------------------------------------------------------------- + * Programmer(s): Daniel R. Reynolds @ SMU + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * This is the implementation file for a generic SUNAdaptController + * package. It contains the implementation of the SUNAdaptController + * operations listed in sundials_adaptcontroller.h + * -----------------------------------------------------------------*/ + +#include +#include + +#include "sundials/sundials_errors.h" + +/* ----------------------------------------------------------------- + * Create a new empty SUNAdaptController object + * ----------------------------------------------------------------- */ + +SUNAdaptController SUNAdaptController_NewEmpty(SUNContext sunctx) +{ + SUNAdaptController C; + SUNAdaptController_Ops ops; + + /* a context is required */ + if (sunctx == NULL) { return (NULL); } + + SUNFunctionBegin(sunctx); + + /* create controller object */ + C = NULL; + C = (SUNAdaptController)malloc(sizeof *C); + SUNAssertNull(C, SUN_ERR_MALLOC_FAIL); + + /* create matrix ops structure */ + ops = NULL; + ops = (SUNAdaptController_Ops)malloc(sizeof *ops); + SUNAssertNull(ops, SUN_ERR_MALLOC_FAIL); + + /* initialize operations to NULL */ + ops->gettype = NULL; + ops->destroy = NULL; + ops->reset = NULL; + ops->estimatestep = NULL; + ops->setdefaults = NULL; + ops->write = NULL; + ops->seterrorbias = NULL; + ops->updateh = NULL; + ops->space = NULL; + + /* attach ops and initialize content to NULL */ + C->ops = ops; + C->content = NULL; + C->sunctx = sunctx; + + return (C); +} + +/* ----------------------------------------------------------------- + * Free a generic SUNAdaptController (assumes content is already empty) + * ----------------------------------------------------------------- */ + +void SUNAdaptController_DestroyEmpty(SUNAdaptController C) +{ + if (C == NULL) { return; } + + /* free non-NULL ops structure */ + if (C->ops) { free(C->ops); } + C->ops = NULL; + + /* free overall SUNAdaptController object and return */ + free(C); + return; +} + +/* ----------------------------------------------------------------- + * Required functions in the 'ops' structure for non-NULL controller + * ----------------------------------------------------------------- */ + +SUNAdaptController_Type SUNAdaptController_GetType(SUNAdaptController C) +{ + if (C == NULL) { return SUN_ADAPTCONTROLLER_NONE; } + if (C->ops->gettype) { return C->ops->gettype(C); } + return SUN_ADAPTCONTROLLER_NONE; +} + +/* ----------------------------------------------------------------- + * Optional functions in the 'ops' structure + * ----------------------------------------------------------------- */ + +SUNErrCode SUNAdaptController_Destroy(SUNAdaptController C) +{ + if (C == NULL) { return (SUN_SUCCESS); } + + /* if the destroy operation exists use it */ + if (C->ops) + { + if (C->ops->destroy) { return (C->ops->destroy(C)); } + } + + /* if we reach this point, either ops == NULL or destroy == NULL, + try to cleanup by freeing the content, ops, and matrix */ + if (C->content) + { + free(C->content); + C->content = NULL; + } + if (C->ops) + { + free(C->ops); + C->ops = NULL; + } + free(C); + C = NULL; + + return (SUN_SUCCESS); +} + +SUNErrCode SUNAdaptController_EstimateStep(SUNAdaptController C, sunrealtype h, + int p, sunrealtype dsm, + sunrealtype* hnew) +{ + SUNErrCode ier = SUN_SUCCESS; + if (C == NULL) { return SUN_ERR_ARG_CORRUPT; } + SUNFunctionBegin(C->sunctx); + SUNAssert(hnew, SUN_ERR_ARG_CORRUPT); + *hnew = h; /* initialize output with identity */ + if (C->ops->estimatestep) { ier = C->ops->estimatestep(C, h, p, dsm, hnew); } + return (ier); +} + +SUNErrCode SUNAdaptController_Reset(SUNAdaptController C) +{ + SUNErrCode ier = SUN_SUCCESS; + if (C == NULL) { return SUN_ERR_ARG_CORRUPT; } + SUNFunctionBegin(C->sunctx); + if (C->ops->reset) { ier = C->ops->reset(C); } + return (ier); +} + +SUNErrCode SUNAdaptController_SetDefaults(SUNAdaptController C) +{ + SUNErrCode ier = SUN_SUCCESS; + if (C == NULL) { return SUN_ERR_ARG_CORRUPT; } + SUNFunctionBegin(C->sunctx); + if (C->ops->setdefaults) { ier = C->ops->setdefaults(C); } + return (ier); +} + +SUNErrCode SUNAdaptController_Write(SUNAdaptController C, FILE* fptr) +{ + SUNErrCode ier = SUN_SUCCESS; + if (C == NULL) { return SUN_ERR_ARG_CORRUPT; } + SUNFunctionBegin(C->sunctx); + SUNAssert(fptr, SUN_ERR_ARG_CORRUPT); + if (C->ops->write) { ier = C->ops->write(C, fptr); } + return (ier); +} + +SUNErrCode SUNAdaptController_SetErrorBias(SUNAdaptController C, sunrealtype bias) +{ + SUNErrCode ier = SUN_SUCCESS; + if (C == NULL) { return SUN_ERR_ARG_CORRUPT; } + SUNFunctionBegin(C->sunctx); + if (C->ops->seterrorbias) { ier = C->ops->seterrorbias(C, bias); } + return (ier); +} + +SUNErrCode SUNAdaptController_UpdateH(SUNAdaptController C, sunrealtype h, + sunrealtype dsm) +{ + SUNErrCode ier = SUN_SUCCESS; + if (C == NULL) { return SUN_ERR_ARG_CORRUPT; } + SUNFunctionBegin(C->sunctx); + if (C->ops->updateh) { ier = C->ops->updateh(C, h, dsm); } + return (ier); +} + +SUNErrCode SUNAdaptController_Space(SUNAdaptController C, long int* lenrw, + long int* leniw) +{ + SUNErrCode ier = SUN_SUCCESS; + if (C == NULL) { return SUN_ERR_ARG_CORRUPT; } + SUNFunctionBegin(C->sunctx); + SUNAssert(lenrw, SUN_ERR_ARG_CORRUPT); + SUNAssert(leniw, SUN_ERR_ARG_CORRUPT); + *lenrw = 0; /* initialize outputs with identity */ + *leniw = 0; + if (C->ops->space) { ier = C->ops->space(C, lenrw, leniw); } + return (ier); +} diff --git a/src/sundials/sundials/sundials_adiak_metadata.h b/src/sundials/sundials/sundials_adiak_metadata.h index 8013dbd..4e7e908 100644 --- a/src/sundials/sundials/sundials_adiak_metadata.h +++ b/src/sundials/sundials/sundials_adiak_metadata.h @@ -12,6 +12,9 @@ * SUNDIALS Copyright End * ----------------------------------------------------------------*/ +#ifndef _SUNDIALS_ADIAK_METADATA_H +#define _SUNDIALS_ADIAK_METADATA_H + #ifdef SUNDIALS_ADIAK_ENABLED #include @@ -142,3 +145,5 @@ static void sunAdiakCollectMetadata() #endif } #endif + +#endif diff --git a/src/sundials/sundials/sundials_band.c b/src/sundials/sundials/sundials_band.c index e8051d8..d5fbcbb 100644 --- a/src/sundials/sundials/sundials_band.c +++ b/src/sundials/sundials/sundials_band.c @@ -38,64 +38,33 @@ sunindextype SUNDlsMat_BandGBTRF(SUNDlsMat A, sunindextype* p) return (SUNDlsMat_bandGBTRF(A->cols, A->M, A->mu, A->ml, A->s_mu, p)); } -sunindextype BandGBTRF(SUNDlsMat A, sunindextype* p) -{ - return (SUNDlsMat_bandGBTRF(A->cols, A->M, A->mu, A->ml, A->s_mu, p)); -} - void SUNDlsMat_BandGBTRS(SUNDlsMat A, sunindextype* p, sunrealtype* b) { SUNDlsMat_bandGBTRS(A->cols, A->M, A->s_mu, A->ml, p, b); } -void BandGBTRS(SUNDlsMat A, sunindextype* p, sunrealtype* b) -{ - SUNDlsMat_bandGBTRS(A->cols, A->M, A->s_mu, A->ml, p, b); -} - void SUNDlsMat_BandCopy(SUNDlsMat A, SUNDlsMat B, sunindextype copymu, sunindextype copyml) { SUNDlsMat_bandCopy(A->cols, B->cols, A->M, A->s_mu, B->s_mu, copymu, copyml); } -void BandCopy(SUNDlsMat A, SUNDlsMat B, sunindextype copymu, sunindextype copyml) -{ - SUNDlsMat_bandCopy(A->cols, B->cols, A->M, A->s_mu, B->s_mu, copymu, copyml); -} - void SUNDlsMat_BandScale(sunrealtype c, SUNDlsMat A) { SUNDlsMat_bandScale(c, A->cols, A->M, A->mu, A->ml, A->s_mu); } -void BandScale(sunrealtype c, SUNDlsMat A) -{ - SUNDlsMat_bandScale(c, A->cols, A->M, A->mu, A->ml, A->s_mu); -} - void SUNDlsMat_BandMatvec(SUNDlsMat A, sunrealtype* x, sunrealtype* y) { SUNDlsMat_bandMatvec(A->cols, x, y, A->M, A->mu, A->ml, A->s_mu); } -void BandMatvec(SUNDlsMat A, sunrealtype* x, sunrealtype* y) -{ - SUNDlsMat_bandMatvec(A->cols, x, y, A->M, A->mu, A->ml, A->s_mu); -} - /* * ----------------------------------------------------- * Functions working on sunrealtype** * ----------------------------------------------------- */ -sunindextype bandGBTRF(sunrealtype** a, sunindextype n, sunindextype mu, - sunindextype ml, sunindextype smu, sunindextype* p) -{ - return (SUNDlsMat_bandGBTRF(a, n, mu, ml, smu, p)); -} - sunindextype SUNDlsMat_bandGBTRF(sunrealtype** a, sunindextype n, sunindextype mu, sunindextype ml, sunindextype smu, sunindextype* p) @@ -212,12 +181,6 @@ sunindextype SUNDlsMat_bandGBTRF(sunrealtype** a, sunindextype n, return (0); } -void bandGBTRS(sunrealtype** a, sunindextype n, sunindextype smu, - sunindextype ml, sunindextype* p, sunrealtype* b) -{ - SUNDlsMat_bandGBTRS(a, n, smu, ml, p, b); -} - void SUNDlsMat_bandGBTRS(sunrealtype** a, sunindextype n, sunindextype smu, sunindextype ml, sunindextype* p, sunrealtype* b) { @@ -252,13 +215,6 @@ void SUNDlsMat_bandGBTRS(sunrealtype** a, sunindextype n, sunindextype smu, } } -void bandCopy(sunrealtype** a, sunrealtype** b, sunindextype n, - sunindextype a_smu, sunindextype b_smu, sunindextype copymu, - sunindextype copyml) -{ - SUNDlsMat_bandCopy(a, b, n, a_smu, b_smu, copymu, copyml); -} - void SUNDlsMat_bandCopy(sunrealtype** a, sunrealtype** b, sunindextype n, sunindextype a_smu, sunindextype b_smu, sunindextype copymu, sunindextype copyml) @@ -276,12 +232,6 @@ void SUNDlsMat_bandCopy(sunrealtype** a, sunrealtype** b, sunindextype n, } } -void bandScale(sunrealtype c, sunrealtype** a, sunindextype n, sunindextype mu, - sunindextype ml, sunindextype smu) -{ - SUNDlsMat_bandScale(c, a, n, mu, ml, smu); -} - void SUNDlsMat_bandScale(sunrealtype c, sunrealtype** a, sunindextype n, sunindextype mu, sunindextype ml, sunindextype smu) { @@ -297,11 +247,6 @@ void SUNDlsMat_bandScale(sunrealtype c, sunrealtype** a, sunindextype n, } } -void bandAddIdentity(sunrealtype** a, sunindextype n, sunindextype smu) -{ - SUNDlsMat_bandAddIdentity(a, n, smu); -} - void SUNDlsMat_bandAddIdentity(sunrealtype** a, sunindextype n, sunindextype smu) { sunindextype j; @@ -309,12 +254,6 @@ void SUNDlsMat_bandAddIdentity(sunrealtype** a, sunindextype n, sunindextype smu for (j = 0; j < n; j++) { a[j][smu] += ONE; } } -void bandMatvec(sunrealtype** a, sunrealtype* x, sunrealtype* y, sunindextype n, - sunindextype mu, sunindextype ml, sunindextype smu) -{ - SUNDlsMat_bandMatvec(a, x, y, n, mu, ml, smu); -} - void SUNDlsMat_bandMatvec(sunrealtype** a, sunrealtype* x, sunrealtype* y, sunindextype n, sunindextype mu, sunindextype ml, sunindextype smu) @@ -322,7 +261,7 @@ void SUNDlsMat_bandMatvec(sunrealtype** a, sunrealtype* x, sunrealtype* y, sunindextype i, j, is, ie; sunrealtype* col_j; - for (i = 0; i < n; i++) { y[i] = 0.0; } + for (i = 0; i < n; i++) { y[i] = ZERO; } for (j = 0; j < n; j++) { diff --git a/src/sundials/sundials/sundials_context.c b/src/sundials/sundials/sundials_context.c index f031dea..86b0a49 100644 --- a/src/sundials/sundials/sundials_context.c +++ b/src/sundials/sundials/sundials_context.c @@ -18,15 +18,17 @@ #include #include #include + #include #include #include +#include #include #include +#include -#include "sundials/sundials_errors.h" -#include "sundials/sundials_types.h" #include "sundials_adiak_metadata.h" +#include "sundials_macros.h" SUNErrCode SUNContext_Create(SUNComm comm, SUNContext* sunctx_out) { @@ -209,6 +211,9 @@ SUNErrCode SUNContext_SetProfiler(SUNContext sunctx, SUNProfiler profiler) /* set profiler */ sunctx->profiler = profiler; sunctx->own_profiler = SUNFALSE; +#else + /* silence warnings when profiling is disabled */ + ((void)profiler); #endif return SUN_SUCCESS; @@ -285,7 +290,7 @@ SUNErrCode SUNContext_Free(SUNContext* sunctx) SUNLogger_Destroy(&(*sunctx)->logger); } - SUNErrHandler_Destroy(&(*sunctx)->err_handler); + SUNContext_ClearErrHandlers(*sunctx); free(*sunctx); *sunctx = NULL; diff --git a/src/sundials/sundials/sundials_cuda.h b/src/sundials/sundials/sundials_cuda.h new file mode 100644 index 0000000..d0202ad --- /dev/null +++ b/src/sundials/sundials/sundials_cuda.h @@ -0,0 +1,79 @@ +/* + * ----------------------------------------------------------------- + * Programmer(s): Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * This header files defines internal utility functions and macros + * for working with CUDA. + * ----------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#ifndef _SUNDIALS_CUDA_H +#define _SUNDIALS_CUDA_H + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +extern "C" { +#endif + +/* --------------------------------------------------------------------------- + * Utility macros + * ---------------------------------------------------------------------------*/ + +#define SUNDIALS_CUDA_VERIFY(cuerr) \ + SUNDIALS_CUDA_Assert(cuerr, __FILE__, __LINE__) + +#define SUNDIALS_KERNEL_NAME(...) __VA_ARGS__ +#ifndef SUNDIALS_DEBUG_CUDA_LASTERROR +#define SUNDIALS_LAUNCH_KERNEL(kernel, gridDim, blockDim, shMem, stream, ...) \ + { \ + kernel<<>>(__VA_ARGS__); \ + } +#else +#define SUNDIALS_LAUNCH_KERNEL(kernel, gridDim, blockDim, shMem, stream, ...) \ + { \ + kernel<<>>(__VA_ARGS__); \ + cudaDeviceSynchronize(); \ + SUNDIALS_CUDA_VERIFY(cudaGetLastError()); \ + } +#endif + +/* --------------------------------------------------------------------------- + * Utility functions + * ---------------------------------------------------------------------------*/ + +inline sunbooleantype SUNDIALS_CUDA_Assert(cudaError_t cuerr, const char* file, + int line) +{ + if (cuerr != cudaSuccess) + { +#ifdef SUNDIALS_DEBUG + fprintf(stderr, "ERROR in CUDA runtime operation: %s %s:%d\n", + cudaGetErrorString(cuerr), file, line); +#ifdef SUNDIALS_DEBUG_ASSERT + assert(false); +#endif +#endif + return SUNFALSE; /* Assert failed */ + } + return SUNTRUE; /* Assert OK */ +} + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +} +#endif + +#endif /* _SUNDIALS_CUDA_H */ diff --git a/src/sundials/sundials/sundials_cuda_kernels.cuh b/src/sundials/sundials/sundials_cuda_kernels.cuh new file mode 100644 index 0000000..529c344 --- /dev/null +++ b/src/sundials/sundials/sundials_cuda_kernels.cuh @@ -0,0 +1,448 @@ +/* + * ----------------------------------------------------------------- + * Programmer(s): Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + */ + +#ifndef _SUNDIALS_CUDA_KERNELS_CUH +#define _SUNDIALS_CUDA_KERNELS_CUH + +#define SUNDIALS_HOST_DEVICE __host__ __device__ +#define SUNDIALS_DEVICE_INLINE __forceinline__ +#include "sundials_reductions.hpp" + +#define GRID_STRIDE_XLOOP(type, iter, max) \ + for (type iter = blockDim.x * blockIdx.x + threadIdx.x; iter < max; \ + iter += blockDim.x * gridDim.x) + +#include "sundials_cuda.h" + +namespace sundials { +namespace cuda { +namespace impl { + +template +__forceinline__ __device__ T shfl_xor_sync(T var, int laneMask); + +template +__forceinline__ __device__ T shfl_sync(T var, int srcLane); + +template +__forceinline__ __device__ T shfl_down_sync(T var, int srcLane); + +template<> +__forceinline__ __device__ float shfl_xor_sync(float var, int laneMask) +{ + return ::__shfl_xor_sync(0xFFFFFFFF, var, laneMask); +} + +template<> +__forceinline__ __device__ double shfl_xor_sync(double var, int laneMask) +{ + return ::__shfl_xor_sync(0xFFFFFFFF, var, laneMask); +} + +template<> +__forceinline__ __device__ float shfl_sync(float var, int srcLane) +{ + return ::__shfl_sync(0xFFFFFFFF, var, srcLane); +} + +template<> +__forceinline__ __device__ double shfl_sync(double var, int srcLane) +{ + return ::__shfl_sync(0xFFFFFFFF, var, srcLane); +} + +template<> +__forceinline__ __device__ float shfl_down_sync(float val, int srcLane) +{ + return ::__shfl_down_sync(0xFFFFFFFF, val, srcLane); +} + +template<> +__forceinline__ __device__ double shfl_down_sync(double val, int srcLane) +{ + return ::__shfl_down_sync(0xFFFFFFFF, val, srcLane); +} + +/* The atomic functions below are implemented using the atomic compare and swap + function atomicCAS which performs an atomic version of + (*address == assumed) ? (assumed + val) : *address. Since *address could change + between when the value is loaded and the atomicCAS call the operation is repeated + until *address does not change between the read and the compare and swap operation. */ + +__forceinline__ __device__ double atomicAdd(double* address, double val) +{ +#if __CUDA_ARCH__ < 600 + unsigned long long int* address_as_ull = (unsigned long long int*)address; + unsigned long long int old = *address_as_ull, assumed; + + do { + assumed = old; + old = atomicCAS(address_as_ull, assumed, + __double_as_longlong(val + __longlong_as_double(assumed))); + // Note: uses integer comparison to avoid hang in case of NaN (since NaN != NaN) + } + while (assumed != old); + + return __longlong_as_double(old); +#else + return ::atomicAdd(address, val); +#endif +} + +__forceinline__ __device__ float atomicAdd(float* address, float val) +{ +#if __CUDA_ARCH__ < 600 + unsigned int* address_as_ull = (unsigned int*)address; + unsigned int old = *address_as_ull, assumed; + + do { + assumed = old; + old = atomicCAS(address_as_ull, assumed, + __float_as_int(val + __int_as_float(assumed))); + // Note: uses integer comparison to avoid hang in case of NaN (since NaN != NaN) + } + while (assumed != old); + + return __int_as_float(old); +#else + return ::atomicAdd(address, val); +#endif +} + +/* + * Compute the maximum of 2 double-precision floating point values using an atomic operation + * "address" is the address of the reference value which might get updated with the maximum + * "value" is the value that is compared to the reference in order to determine the maximum + */ +__forceinline__ __device__ void atomicMax(double* const address, + const double value) +{ + if (*address >= value) { return; } + + unsigned long long* const address_as_i = (unsigned long long*)address; + unsigned long long old = *address_as_i, assumed; + + do { + assumed = old; + if (__longlong_as_double(assumed) >= value) { break; } + old = atomicCAS(address_as_i, assumed, __double_as_longlong(value)); + } + while (assumed != old); +} + +/* + * Compute the maximum of 2 single-precision floating point values using an atomic operation + * "address" is the address of the reference value which might get updated with the maximum + * "value" is the value that is compared to the reference in order to determine the maximum + */ +__forceinline__ __device__ void atomicMax(float* const address, const float value) +{ + if (*address >= value) { return; } + + unsigned int* const address_as_i = (unsigned int*)address; + unsigned int old = *address_as_i, assumed; + + do { + assumed = old; + if (__int_as_float(assumed) >= value) { break; } + old = atomicCAS(address_as_i, assumed, __float_as_int(value)); + } + while (assumed != old); +} + +/* + * Compute the minimum of 2 double-precision floating point values using an atomic operation + * "address" is the address of the reference value which might get updated with the minimum + * "value" is the value that is compared to the reference in order to determine the minimum + */ +__forceinline__ __device__ void atomicMin(double* const address, + const double value) +{ + if (*address <= value) { return; } + + unsigned long long* const address_as_i = (unsigned long long*)address; + unsigned long long old = *address_as_i, assumed; + + do { + assumed = old; + if (__longlong_as_double(assumed) <= value) { break; } + old = atomicCAS(address_as_i, assumed, __double_as_longlong(value)); + } + while (assumed != old); +} + +/* + * Compute the minimum of 2 single-precision floating point values using an atomic operation + * "address" is the address of the reference value which might get updated with the minimum + * "value" is the value that is compared to the reference in order to determine the minimum + */ +__forceinline__ __device__ void atomicMin(float* const address, const float value) +{ + if (*address <= value) { return; } + + unsigned int* const address_as_i = (unsigned int*)address; + unsigned int old = *address_as_i, assumed; + + do { + assumed = old; + if (__int_as_float(assumed) <= value) { break; } + old = atomicCAS(address_as_i, assumed, __float_as_int(value)); + } + while (assumed != old); +} + +// +// Atomic specializations of sundials::reduction operators +// + +template +struct atomic; + +template +struct atomic> +{ + __device__ __forceinline__ void operator()(T* out, const T val) + { + atomicAdd(out, val); + } +}; + +template +struct atomic> +{ + __device__ __forceinline__ void operator()(T* out, const T val) + { + atomicMax(out, val); + } +}; + +template +struct atomic> +{ + __device__ __forceinline__ void operator()(T* out, const T val) + { + atomicMin(out, val); + } +}; + +/* + * Perform a reduce on the warp to get the operation result. + */ +template +__inline__ __device__ T warpReduceShflDown(T val) +{ + for (int offset = warpSize / 2; offset > 0; offset /= 2) + { + T rhs = shfl_down_sync(val, offset); + val = BinaryReductionOp{}(val, rhs); + } + return val; +} + +/* + * Reduce value across the thread block. + */ +template +__inline__ __device__ T blockReduceShflDown(T val, T identity) +{ + // Shared memory for the partial sums + static __shared__ T shared[MAX_WARPS]; + + int numThreads = blockDim.x * blockDim.y * blockDim.z; + + int threadId = threadIdx.x + blockDim.x * threadIdx.y + + (blockDim.x * blockDim.y) * threadIdx.z; + + int warpId = threadId / WARP_SIZE; + int warpLane = threadId % WARP_SIZE; + + // Each warp performs partial reduction + val = warpReduceShflDown(val); + + // Write reduced value from each warp to shared memory + if (warpLane == 0) shared[warpId] = val; + + // Wait for all partial reductions to complete + __syncthreads(); + + // Read per warp values from shared memory only if that warp existed + val = (threadId < numThreads / warpSize) ? shared[warpLane] : identity; + + // Final reduce within first warp + if (warpId == 0) val = warpReduceShflDown(val); + + return val; +} + +/* + * Warp reduce + block reduce using shfl instead of shfl_down. + */ +template +__inline__ __device__ T blockReduceShfl(T val, T identity) +{ + int numThreads = blockDim.x * blockDim.y * blockDim.z; + + int threadId = threadIdx.x + blockDim.x * threadIdx.y + + (blockDim.x * blockDim.y) * threadIdx.z; + + int warpId = threadId / WARP_SIZE; + int warpLane = threadId % WARP_SIZE; + + T temp = val; + + // Reduce each warp + if (numThreads % WARP_SIZE == 0) + { + for (int i = 1; i < WARP_SIZE; i *= 2) + { + T rhs = shfl_xor_sync(temp, i); + temp = BinaryReductionOp{}(temp, rhs); + } + } + else + { + for (int i = 1; i < WARP_SIZE; i *= 2) + { + int srcLane = threadId ^ i; + T rhs = shfl_sync(temp, srcLane); + // Only add from threads that exist to avoid double counting + if (srcLane < numThreads) temp = BinaryReductionOp{}(temp, rhs); + } + } + + // Reduce per warp values + if (numThreads > WARP_SIZE) + { + static_assert(MAX_WARPS <= WARP_SIZE, + "max warps must be <= warp size for this algorithm to work"); + + __shared__ T shared[MAX_WARPS]; + + // Write per warp values to shared memory + if (warpLane == 0) shared[warpId] = temp; + + __syncthreads(); + + if (warpId == 0) + { + // Read per warp values only if the warp existed + temp = (warpLane * WARP_SIZE < numThreads) ? shared[warpLane] : identity; + + // Final reduction + for (int i = 1; i < MAX_WARPS; i *= 2) + { + T rhs = shfl_xor_sync(temp, i); + temp = BinaryReductionOp{}(temp, rhs); + } + } + + __syncthreads(); + } + + return temp; +} + +/* + * Reduce values into thread 0 of the last running thread block. + * Output value is device_mem[0]. + */ +template +__device__ __forceinline__ void gridReduce(T val, T identity, T* device_mem, + unsigned int* device_count) +{ + int numBlocks = gridDim.x * gridDim.y * gridDim.z; + int numThreads = blockDim.x * blockDim.y * blockDim.z; + unsigned int wrap_around = numBlocks - 1; + + int blockId = blockIdx.x + gridDim.x * blockIdx.y + + (gridDim.x * gridDim.y) * blockIdx.z; + + int threadId = threadIdx.x + blockDim.x * threadIdx.y + + (blockDim.x * blockDim.y) * threadIdx.z; + + // Each block reduces a subset of the input + T temp = blockReduceShfl(val, identity); + + __shared__ bool isLastBlockDone; + if (threadId == 0) + { + // One thread per block stores the partial reductions to global memory + device_mem[blockId] = temp; + + // Ensure write visible to all threads + __threadfence(); + + // Increment counter, (wraps back to zero if old count == wrap_around) + unsigned int old_count = atomicInc(device_count, wrap_around); + isLastBlockDone = (old_count == wrap_around) ? 1 : 0; + } + + // Synchronize to ensure that each thread reads the + // correct value of isLastBlockDone. + __syncthreads(); + + // The last block reduces values in device_mem + if (isLastBlockDone) + { + // Reduce thread_i in each block into temp + temp = identity; + for (int i = threadId; i < numBlocks; i += numThreads) + temp = BinaryReductionOp{}(temp, device_mem[i]); + + // Compute the final block partial reductions + temp = blockReduceShfl(temp, identity); + + // One thread returns the final value + if (threadId == 0) device_mem[0] = temp; + } +} + +template +__device__ __forceinline__ void gridReduceAtomic(T val, T identity, T* device_mem) +{ + int threadId = threadIdx.x + blockDim.x * threadIdx.y + + (blockDim.x * blockDim.y) * threadIdx.z; + val = blockReduceShflDown(val, identity); + // Final reduction of all block values into the output device_mem + if (threadId == 0) atomic{}(device_mem, val); +} + +template +struct GridReducerLDS +{ + __device__ __forceinline__ void operator()(T val, T identity, T* device_mem, + unsigned int* device_count) + { + return gridReduce(val, identity, device_mem, + device_count); + } +}; + +template +struct GridReducerAtomic +{ + __device__ __forceinline__ void operator()(T val, T identity, T* device_mem, + unsigned int* device_count) + { + return gridReduceAtomic(val, identity, device_mem); + } +}; + +} // namespace impl +} // namespace cuda +} // namespace sundials + +#endif // _SUNDIALS_CUDA_KERNELS_CUH \ No newline at end of file diff --git a/src/sundials/sundials/sundials_cusolver.h b/src/sundials/sundials/sundials_cusolver.h new file mode 100644 index 0000000..87c9644 --- /dev/null +++ b/src/sundials/sundials/sundials_cusolver.h @@ -0,0 +1,66 @@ +/* + * ----------------------------------------------------------------- + * Programmer(s): Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * This header files defines internal utility functions and macros + * for working with CUDA. + * ----------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include + +#ifndef _SUNDIALS_CUSOLVER_H +#define _SUNDIALS_CUSOLVER_H + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +extern "C" { +#endif + +/* --------------------------------------------------------------------------- + * Utility macros + * ---------------------------------------------------------------------------*/ + +#define SUNDIALS_CUSOLVER_VERIFY(cuerr) \ + SUNDIALS_CUSOLVER_Assert(cuerr, __FILE__, __LINE__) + +/* --------------------------------------------------------------------------- + * Utility functions + * ---------------------------------------------------------------------------*/ + +inline sunbooleantype SUNDIALS_CUSOLVER_Assert(cusolverStatus_t status, + const char* file, int line) +{ + if (status != CUSOLVER_STATUS_SUCCESS) + { +#ifdef SUNDIALS_DEBUG + fprintf(stderr, "ERROR in cuSOLVER runtime operation: cusolverStatus_t = %d %s:%d\n", + status, file, line); +#ifdef SUNDIALS_DEBUG_ASSERT + assert(false); +#endif +#endif + return SUNFALSE; /* Assert failed */ + } + return SUNTRUE; /* Assert OK */ +} + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +} +#endif + +#endif /* _SUNDIALS_CUSOLVER_H */ diff --git a/src/sundials/sundials/sundials_cusparse.h b/src/sundials/sundials/sundials_cusparse.h new file mode 100644 index 0000000..c05f3e8 --- /dev/null +++ b/src/sundials/sundials/sundials_cusparse.h @@ -0,0 +1,65 @@ +/* + * ----------------------------------------------------------------- + * Programmer(s): Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * This header files defines internal utility functions and macros + * for working with CUDA. + * ----------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include + +#ifndef _SUNDIALS_CUSPARSE_H +#define _SUNDIALS_CUSPARSE_H + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +extern "C" { +#endif + +/* --------------------------------------------------------------------------- + * Utility macros + * ---------------------------------------------------------------------------*/ + +#define SUNDIALS_CUSPARSE_VERIFY(cuerr) \ + SUNDIALS_CUSPARSE_Assert(cuerr, __FILE__, __LINE__) + +/* --------------------------------------------------------------------------- + * Utility functions + * ---------------------------------------------------------------------------*/ + +inline sunbooleantype SUNDIALS_CUSPARSE_Assert(cusparseStatus_t status, + const char* file, int line) +{ + if (status != CUSPARSE_STATUS_SUCCESS) + { +#ifdef SUNDIALS_DEBUG + fprintf(stderr, "ERROR in cuSPARSE runtime operation: cusparseStatus_t = %d %s:%d\n", + status, file, line); +#ifdef SUNDIALS_DEBUG_ASSERT + assert(false); +#endif +#endif + return SUNFALSE; /* Assert failed */ + } + return SUNTRUE; /* Assert OK */ +} + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +} +#endif + +#endif /* _SUNDIALS_CUSPARSE_H */ diff --git a/src/sundials/sundials/sundials_dense.c b/src/sundials/sundials/sundials_dense.c index f022212..ce420fc 100644 --- a/src/sundials/sundials/sundials_dense.c +++ b/src/sundials/sundials/sundials_dense.c @@ -38,99 +38,47 @@ sunindextype SUNDlsMat_DenseGETRF(SUNDlsMat A, sunindextype* p) return (SUNDlsMat_denseGETRF(A->cols, A->M, A->N, p)); } -sunindextype DenseGETRF(SUNDlsMat A, sunindextype* p) -{ - return (SUNDlsMat_denseGETRF(A->cols, A->M, A->N, p)); -} - void SUNDlsMat_DenseGETRS(SUNDlsMat A, sunindextype* p, sunrealtype* b) { SUNDlsMat_denseGETRS(A->cols, A->N, p, b); } -void DenseGETRS(SUNDlsMat A, sunindextype* p, sunrealtype* b) -{ - SUNDlsMat_denseGETRS(A->cols, A->N, p, b); -} - sunindextype SUNDlsMat_DensePOTRF(SUNDlsMat A) { return (SUNDlsMat_densePOTRF(A->cols, A->M)); } -sunindextype DensePOTRF(SUNDlsMat A) -{ - return (SUNDlsMat_densePOTRF(A->cols, A->M)); -} - void SUNDlsMat_DensePOTRS(SUNDlsMat A, sunrealtype* b) { SUNDlsMat_densePOTRS(A->cols, A->M, b); } -void DensePOTRS(SUNDlsMat A, sunrealtype* b) -{ - SUNDlsMat_densePOTRS(A->cols, A->M, b); -} - int SUNDlsMat_DenseGEQRF(SUNDlsMat A, sunrealtype* beta, sunrealtype* wrk) { return (SUNDlsMat_denseGEQRF(A->cols, A->M, A->N, beta, wrk)); } -int DenseGEQRF(SUNDlsMat A, sunrealtype* beta, sunrealtype* wrk) -{ - return (SUNDlsMat_denseGEQRF(A->cols, A->M, A->N, beta, wrk)); -} - int SUNDlsMat_DenseORMQR(SUNDlsMat A, sunrealtype* beta, sunrealtype* vn, sunrealtype* vm, sunrealtype* wrk) { return (SUNDlsMat_denseORMQR(A->cols, A->M, A->N, beta, vn, vm, wrk)); } -int DenseORMQR(SUNDlsMat A, sunrealtype* beta, sunrealtype* vn, sunrealtype* vm, - sunrealtype* wrk) -{ - return (SUNDlsMat_denseORMQR(A->cols, A->M, A->N, beta, vn, vm, wrk)); -} - void SUNDlsMat_DenseCopy(SUNDlsMat A, SUNDlsMat B) { SUNDlsMat_denseCopy(A->cols, B->cols, A->M, A->N); } -void DenseCopy(SUNDlsMat A, SUNDlsMat B) -{ - SUNDlsMat_denseCopy(A->cols, B->cols, A->M, A->N); -} - void SUNDlsMat_DenseScale(sunrealtype c, SUNDlsMat A) { SUNDlsMat_denseScale(c, A->cols, A->M, A->N); } -void DenseScale(sunrealtype c, SUNDlsMat A) -{ - SUNDlsMat_denseScale(c, A->cols, A->M, A->N); -} - void SUNDlsMat_DenseMatvec(SUNDlsMat A, sunrealtype* x, sunrealtype* y) { SUNDlsMat_denseMatvec(A->cols, x, y, A->M, A->N); } -void DenseMatvec(SUNDlsMat A, sunrealtype* x, sunrealtype* y) -{ - SUNDlsMat_denseMatvec(A->cols, x, y, A->M, A->N); -} - -sunindextype denseGETRF(sunrealtype** a, sunindextype m, sunindextype n, - sunindextype* p) -{ - return (SUNDlsMat_denseGETRF(a, m, n, p)); -} - sunindextype SUNDlsMat_denseGETRF(sunrealtype** a, sunindextype m, sunindextype n, sunindextype* p) { @@ -199,11 +147,6 @@ sunindextype SUNDlsMat_denseGETRF(sunrealtype** a, sunindextype m, return (0); } -void denseGETRS(sunrealtype** a, sunindextype n, sunindextype* p, sunrealtype* b) -{ - SUNDlsMat_denseGETRS(a, n, p, b); -} - void SUNDlsMat_denseGETRS(sunrealtype** a, sunindextype n, sunindextype* p, sunrealtype* b) { @@ -246,11 +189,6 @@ void SUNDlsMat_denseGETRS(sunrealtype** a, sunindextype n, sunindextype* p, * the lower triangle of C. */ -sunindextype densePOTRF(sunrealtype** a, sunindextype m) -{ - return (SUNDlsMat_densePOTRF(a, m)); -} - sunindextype SUNDlsMat_densePOTRF(sunrealtype** a, sunindextype m) { sunrealtype *a_col_j, *a_col_k; @@ -289,11 +227,6 @@ sunindextype SUNDlsMat_densePOTRF(sunrealtype** a, sunindextype m) * */ -void densePOTRS(sunrealtype** a, sunindextype m, sunrealtype* b) -{ - SUNDlsMat_densePOTRS(a, m, b); -} - void SUNDlsMat_densePOTRS(sunrealtype** a, sunindextype m, sunrealtype* b) { sunrealtype *col_j, *col_i; @@ -334,12 +267,6 @@ void SUNDlsMat_densePOTRS(sunrealtype** a, sunindextype m, sunrealtype* b) * */ -int denseGEQRF(sunrealtype** a, sunindextype m, sunindextype n, - sunrealtype* beta, sunrealtype* v) -{ - return (SUNDlsMat_denseGEQRF(a, m, n, beta, v)); -} - int SUNDlsMat_denseGEQRF(sunrealtype** a, sunindextype m, sunindextype n, sunrealtype* beta, sunrealtype* v) { @@ -404,12 +331,6 @@ int SUNDlsMat_denseGEQRF(sunrealtype** a, sunindextype m, sunindextype n, * v (of length m) must be provided as workspace. */ -int denseORMQR(sunrealtype** a, sunindextype m, sunindextype n, sunrealtype* beta, - sunrealtype* vn, sunrealtype* vm, sunrealtype* v) -{ - return (SUNDlsMat_denseORMQR(a, m, n, beta, vn, vm, v)); -} - int SUNDlsMat_denseORMQR(sunrealtype** a, sunindextype m, sunindextype n, sunrealtype* beta, sunrealtype* vn, sunrealtype* vm, sunrealtype* v) @@ -441,11 +362,6 @@ int SUNDlsMat_denseORMQR(sunrealtype** a, sunindextype m, sunindextype n, return (0); } -void denseCopy(sunrealtype** a, sunrealtype** b, sunindextype m, sunindextype n) -{ - SUNDlsMat_denseCopy(a, b, m, n); -} - void SUNDlsMat_denseCopy(sunrealtype** a, sunrealtype** b, sunindextype m, sunindextype n) { @@ -460,11 +376,6 @@ void SUNDlsMat_denseCopy(sunrealtype** a, sunrealtype** b, sunindextype m, } } -void denseScale(sunrealtype c, sunrealtype** a, sunindextype m, sunindextype n) -{ - SUNDlsMat_denseScale(c, a, m, n); -} - void SUNDlsMat_denseScale(sunrealtype c, sunrealtype** a, sunindextype m, sunindextype n) { @@ -478,11 +389,6 @@ void SUNDlsMat_denseScale(sunrealtype c, sunrealtype** a, sunindextype m, } } -void denseAddIdentity(sunrealtype** a, sunindextype n) -{ - SUNDlsMat_denseAddIdentity(a, n); -} - void SUNDlsMat_denseAddIdentity(sunrealtype** a, sunindextype n) { sunindextype i; @@ -490,19 +396,13 @@ void SUNDlsMat_denseAddIdentity(sunrealtype** a, sunindextype n) for (i = 0; i < n; i++) { a[i][i] += ONE; } } -void denseMatvec(sunrealtype** a, sunrealtype* x, sunrealtype* y, - sunindextype m, sunindextype n) -{ - SUNDlsMat_denseMatvec(a, x, y, m, n); -} - void SUNDlsMat_denseMatvec(sunrealtype** a, sunrealtype* x, sunrealtype* y, sunindextype m, sunindextype n) { sunindextype i, j; sunrealtype* col_j; - for (i = 0; i < m; i++) { y[i] = 0.0; } + for (i = 0; i < m; i++) { y[i] = ZERO; } for (j = 0; j < n; j++) { diff --git a/src/sundials/sundials/sundials_direct.c b/src/sundials/sundials/sundials_direct.c index 2bf135b..02ff31d 100644 --- a/src/sundials/sundials/sundials_direct.c +++ b/src/sundials/sundials/sundials_direct.c @@ -23,11 +23,6 @@ #define ZERO SUN_RCONST(0.0) #define ONE SUN_RCONST(1.0) -SUNDlsMat NewDenseMat(sunindextype M, sunindextype N) -{ - return (SUNDlsMat_NewDenseMat(M, N)); -} - SUNDlsMat SUNDlsMat_NewDenseMat(sunindextype M, sunindextype N) { SUNDlsMat A; @@ -68,11 +63,6 @@ SUNDlsMat SUNDlsMat_NewDenseMat(sunindextype M, sunindextype N) return (A); } -sunrealtype** newDenseMat(sunindextype m, sunindextype n) -{ - return (SUNDlsMat_newDenseMat(m, n)); -} - sunrealtype** SUNDlsMat_newDenseMat(sunindextype m, sunindextype n) { sunindextype j; @@ -98,12 +88,6 @@ sunrealtype** SUNDlsMat_newDenseMat(sunindextype m, sunindextype n) return (a); } -SUNDlsMat NewBandMat(sunindextype N, sunindextype mu, sunindextype ml, - sunindextype smu) -{ - return (SUNDlsMat_NewBandMat(N, mu, ml, smu)); -} - SUNDlsMat SUNDlsMat_NewBandMat(sunindextype N, sunindextype mu, sunindextype ml, sunindextype smu) { @@ -151,11 +135,6 @@ SUNDlsMat SUNDlsMat_NewBandMat(sunindextype N, sunindextype mu, sunindextype ml, return (A); } -sunrealtype** newBandMat(sunindextype n, sunindextype smu, sunindextype ml) -{ - return (SUNDlsMat_newBandMat(n, smu, ml)); -} - sunrealtype** SUNDlsMat_newBandMat(sunindextype n, sunindextype smu, sunindextype ml) { @@ -183,8 +162,6 @@ sunrealtype** SUNDlsMat_newBandMat(sunindextype n, sunindextype smu, return (a); } -void DestroyMat(SUNDlsMat A) { SUNDlsMat_DestroyMat(A); } - void SUNDlsMat_DestroyMat(SUNDlsMat A) { free(A->data); @@ -194,8 +171,6 @@ void SUNDlsMat_DestroyMat(SUNDlsMat A) A = NULL; } -void destroyMat(sunrealtype** a) { SUNDlsMat_destroyMat(a); } - void SUNDlsMat_destroyMat(sunrealtype** a) { free(a[0]); @@ -204,8 +179,6 @@ void SUNDlsMat_destroyMat(sunrealtype** a) a = NULL; } -int* NewIntArray(int N) { return (SUNDlsMat_NewIntArray(N)); } - int* SUNDlsMat_NewIntArray(int N) { int* vec; @@ -218,8 +191,6 @@ int* SUNDlsMat_NewIntArray(int N) return (vec); } -int* newIntArray(int N) { return (SUNDlsMat_newIntArray(N)); } - int* SUNDlsMat_newIntArray(int n) { int* v; @@ -232,11 +203,6 @@ int* SUNDlsMat_newIntArray(int n) return (v); } -sunindextype* NewIndexArray(sunindextype N) -{ - return (SUNDlsMat_NewIndexArray(N)); -} - sunindextype* SUNDlsMat_NewIndexArray(sunindextype N) { sunindextype* vec; @@ -249,11 +215,6 @@ sunindextype* SUNDlsMat_NewIndexArray(sunindextype N) return (vec); } -sunindextype* newIndexArray(sunindextype n) -{ - return (SUNDlsMat_newIndexArray(n)); -} - sunindextype* SUNDlsMat_newIndexArray(sunindextype n) { sunindextype* v; @@ -266,11 +227,6 @@ sunindextype* SUNDlsMat_newIndexArray(sunindextype n) return (v); } -sunrealtype* NewRealArray(sunindextype N) -{ - return (SUNDlsMat_NewRealArray(N)); -} - sunrealtype* SUNDlsMat_NewRealArray(sunindextype N) { sunrealtype* vec; @@ -283,11 +239,6 @@ sunrealtype* SUNDlsMat_NewRealArray(sunindextype N) return (vec); } -sunrealtype* newRealArray(sunindextype N) -{ - return (SUNDlsMat_newRealArray(N)); -} - sunrealtype* SUNDlsMat_newRealArray(sunindextype m) { sunrealtype* v; @@ -300,24 +251,18 @@ sunrealtype* SUNDlsMat_newRealArray(sunindextype m) return (v); } -void DestroyArray(void* p) { SUNDlsMat_DestroyArray(p); } - void SUNDlsMat_DestroyArray(void* V) { free(V); V = NULL; } -void destroyArray(void* p) { SUNDlsMat_destroyArray(p); } - void SUNDlsMat_destroyArray(void* v) { free(v); v = NULL; } -void AddIdentity(SUNDlsMat A) { SUNDlsMat_AddIdentity(A); } - void SUNDlsMat_AddIdentity(SUNDlsMat A) { sunindextype i; @@ -334,8 +279,6 @@ void SUNDlsMat_AddIdentity(SUNDlsMat A) } } -void SetToZero(SUNDlsMat A) { SUNDlsMat_SetToZero(A); } - void SUNDlsMat_SetToZero(SUNDlsMat A) { sunindextype i, j, colSize; @@ -366,8 +309,6 @@ void SUNDlsMat_SetToZero(SUNDlsMat A) } } -void PrintMat(SUNDlsMat A, FILE* outfile) { SUNDlsMat_PrintMat(A, outfile); } - void SUNDlsMat_PrintMat(SUNDlsMat A, FILE* outfile) { sunindextype i, j, start, finish; diff --git a/src/sundials/sundials/sundials_errors.c b/src/sundials/sundials/sundials_errors.c index 667a3a4..acc3a0e 100644 --- a/src/sundials/sundials/sundials_errors.c +++ b/src/sundials/sundials/sundials_errors.c @@ -10,17 +10,18 @@ * SUNDIALS Copyright End * -----------------------------------------------------------------*/ -#include "sundials/sundials_errors.h" - #include #include #include + #include #include +#include +#include +#include -#include "sundials/sundials_logger.h" -#include "sundials/sundials_types.h" #include "sundials_logger_impl.h" +#include "sundials_macros.h" #include "sundials_utils.h" SUNErrCode SUNErrHandler_Create(SUNErrHandlerFn eh_fn, void* eh_data, @@ -62,7 +63,8 @@ const char* SUNGetErrMsg(SUNErrCode code) void SUNLogErrHandlerFn(int line, const char* func, const char* file, const char* msg, SUNErrCode err_code, - void* err_user_data, SUNContext sunctx) + SUNDIALS_MAYBE_UNUSED void* err_user_data, + SUNContext sunctx) { char* file_and_line = sunCombineFileAndLine(line, file); if (msg == NULL) { msg = SUNGetErrMsg(err_code); } @@ -72,8 +74,10 @@ void SUNLogErrHandlerFn(int line, const char* func, const char* file, } void SUNAbortErrHandlerFn(int line, const char* func, const char* file, - const char* msg, SUNErrCode err_code, - void* err_user_data, SUNContext sunctx) + SUNDIALS_MAYBE_UNUSED const char* msg, + SUNDIALS_MAYBE_UNUSED SUNErrCode err_code, + SUNDIALS_MAYBE_UNUSED void* err_user_data, + SUNContext sunctx) { char* file_and_line = sunCombineFileAndLine(line, file); SUNLogger_QueueMsg(sunctx->logger, SUN_LOGLEVEL_ERROR, file_and_line, func, diff --git a/src/sundials/sundials/sundials_futils.c b/src/sundials/sundials/sundials_futils.c index e494fe7..3a0d0f2 100644 --- a/src/sundials/sundials/sundials_futils.c +++ b/src/sundials/sundials/sundials_futils.c @@ -17,6 +17,7 @@ #include #include #include +#include /* Create a file pointer with the given file name and mode. */ SUNErrCode SUNDIALSFileOpen(const char* filename, const char* mode, FILE** fp_out) diff --git a/src/sundials/sundials/sundials_hashmap.c b/src/sundials/sundials/sundials_hashmap.c new file mode 100644 index 0000000..acf1c12 --- /dev/null +++ b/src/sundials/sundials/sundials_hashmap.c @@ -0,0 +1,342 @@ +/* ----------------------------------------------------------------- + * Programmer: Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * A simple hashmap implementation for char* keys and + * void* values. Uses linear probing to resolve collisions. + * The values can be anything, but will be freed by + * the hash map upon its destruction. + * -----------------------------------------------------------------*/ + +#include +#include +#include + +#include +#include + +#include "sundials_hashmap_impl.h" +#include "sundials_macros.h" + +static const uint64_t HASH_PRIME = 14695981039346656037U; +static const uint64_t HASH_OFFSET_BASIS = 1099511628211U; + +/* + For a nice discussion on popular hashing algorithms see: + https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633 + + This is a 64-bit implementation of the 'a' modification of the + Fowler-Noll-Vo hash (i.e., FNV1-a). + */ +static uint64_t fnv1a_hash(const char* str) +{ + uint64_t hash = HASH_OFFSET_BASIS; + char c; + while ((c = *str++)) { hash = (hash ^ c) * HASH_PRIME; } + return hash; +} + +/* + This function creates a new SUNHashMap object allocated to hold + up to 'max_size' entries. + + **Arguments:** + * ``max_size`` -- the max number of entries in the hashmap + * ``map`` -- on input, a SUNHasMap pointer, on output the SUNHashMap will be + allocated + + **Returns:** + * A SUNErrCode indicating success or a failure + */ +SUNErrCode SUNHashMap_New(int max_size, SUNHashMap* map) +{ + int i; + + if (max_size <= 0) { return SUN_ERR_ARG_OUTOFRANGE; } + + *map = NULL; + *map = (SUNHashMap)malloc(sizeof(**map)); + + if (!map) { return SUN_ERR_MALLOC_FAIL; } + + (*map)->size = 0; + (*map)->max_size = max_size; + + (*map)->buckets = NULL; + (*map)->buckets = + (SUNHashMapKeyValue*)malloc(max_size * sizeof(*((*map)->buckets))); + + if (!(*map)->buckets) + { + free(*map); + return SUN_ERR_MALLOC_FAIL; + } + + /* Initialize all buckets to NULL */ + for (i = 0; i < max_size; i++) { (*map)->buckets[i] = NULL; } + + return SUN_SUCCESS; +} + +/* + This function frees the SUNHashMap object. + + **Arguments:** + * ``map`` -- on input, a SUNHasMap pointer, on output the SUNHashMap will be + deallocated and set to ``NULL`` + * ``freevalue`` -- callback function that should free the value object + + **Returns:** + * A SUNErrCode indicating success or a failure + */ +SUNErrCode SUNHashMap_Destroy(SUNHashMap* map, void (*freevalue)(void* ptr)) +{ + int i; + + if (map == NULL || freevalue == NULL) { return SUN_SUCCESS; } + + for (i = 0; i < (*map)->max_size; i++) + { + if ((*map)->buckets[i] && (*map)->buckets[i]->value) + { + freevalue((*map)->buckets[i]->value); + } + + if ((*map)->buckets[i]) { free((*map)->buckets[i]); } + } + if ((*map)->buckets) { free((*map)->buckets); } + if (*map) { free(*map); } + *map = NULL; + + return SUN_SUCCESS; +} + +/* + This function iterates the map over the range [start, N]. N is either the + index at which ``yieldfn`` indicates the iteration should stop, or the max + entries in the map. + + **Arguments:** + * ``map`` -- the ``SUNHashMap`` object to operate on + * ``start`` -- the start of the iteration range + * ``yieldfn`` -- the callback function to call every iteration + this should return -1 to continue the iteration, or >= 0 to + stop; the first argument is the current index, the second + argument is the current key-value pair, and the final + argument is the same pointer ``ctx`` as the final argument + to SUNHashMapIterate. + * ``ctx`` -- a pointer to pass on to ``yieldfn`` + + **Returns:** + * ``max_size`` -- iterated the whole map + * ``>=0`` -- the index at which the iteration stopped + * ``<-1`` -- an error occurred + */ +int SUNHashMap_Iterate(SUNHashMap map, int start, + int (*yieldfn)(int, SUNHashMapKeyValue, const void*), + const void* ctx) +{ + int i; + + if (map == NULL || yieldfn == NULL) { return (-2); } + + for (i = start; i < map->max_size; i++) + { + int retval = yieldfn(i, map->buckets[i], ctx); + if (retval >= 0) + { + return (retval); /* yieldfn indicates the loop should break */ + } + if (retval < -1) { return (retval); /* error occurred */ } + } + + return (map->max_size); +} + +static int sunHashMapLinearProbeInsert(int idx, SUNHashMapKeyValue kv, + SUNDIALS_MAYBE_UNUSED const void* ctx) +{ + /* find the next open spot */ + if (kv == NULL) { return (idx); /* open spot found at idx */ } + return (-1); /* keep looking */ +} + +/* + This function creates a key-value pair and attempts to insert it into the map. + Will use linear probing if there is a collision. + + **Arguments:** + * ``map`` -- the ``SUNHashMap`` object to operate on + * ``key`` -- the key to store + * ``value`` -- the value associated with the key + + **Returns:** + * ``0`` -- success + * ``-1`` -- an error occurred + * ``-2`` -- the map is full + */ +int SUNHashMap_Insert(SUNHashMap map, const char* key, void* value) +{ + int idx; + int retval; + SUNHashMapKeyValue kvp; + + if (map == NULL || key == NULL || value == NULL) { return (-1); } + + /* We want the index to be in (0, map->max_size) */ + idx = (int)(fnv1a_hash(key) % map->max_size); + + /* Check if the bucket is already filled */ + if (map->buckets[idx] != NULL) + { + /* Find the next open spot */ + retval = SUNHashMap_Iterate(map, idx, sunHashMapLinearProbeInsert, NULL); + if (retval < 0) { return (-1); /* error occurred */ } + if (retval == map->max_size) { return (-2); /* no open entry */ } + + idx = retval; + } + + /* Create the key-value pair */ + kvp = (SUNHashMapKeyValue)malloc(sizeof(*kvp)); + if (kvp == NULL) { return (-1); } + + kvp->key = key; + kvp->value = value; + + /* Insert the key-value pair */ + map->buckets[idx] = kvp; + map->size++; + + return (0); +} + +static int sunHashMapLinearProbeGet(int idx, SUNHashMapKeyValue kv, + const void* key) +{ + /* target key cannot be NULL */ + if (key == NULL) { return (-2); } + + /* find the matching entry */ + if (kv == NULL) { return (-1); /* keep looking since this bucket is empty */ } + if (!strcmp(kv->key, (const char*)key)) + { + return (idx); /* found it at idx */ + } + return (-1); /* keep looking */ +} + +/* + This function gets the value for the given key. + + **Arguments:** + * ``map`` -- the ``SUNHashMap`` object to operate on + * ``key`` -- the key to look up + * ``value`` -- the value associated with the key + + **Returns:** + * ``0`` -- success + * ``-1`` -- an error occurred + * ``-2`` -- key not found + */ +int SUNHashMap_GetValue(SUNHashMap map, const char* key, void** value) +{ + int idx; + int retval; + + if (map == NULL || key == NULL || value == NULL) { return (-1); } + + /* We want the index to be in (0, map->max_size) */ + idx = (int)(fnv1a_hash(key) % map->max_size); + + /* Check if the key exists */ + if (map->buckets[idx] == NULL) { return (-2); } + + /* Check to see if this is a collision */ + if (strcmp(map->buckets[idx]->key, key)) + { + /* Keys did not match, so we have a collision and need to probe */ + retval = SUNHashMap_Iterate(map, idx + 1, sunHashMapLinearProbeGet, key); + if (retval < 0) { return (-1); /* error occurred */ } + if (retval == map->max_size) { return (-2); /* not found */ } + } + + /* Return a reference to the value only */ + *value = map->buckets[idx]->value; + + return (0); +} + +/* + This function allocates a new array the same max_size as the map, + then it sorts map into a new array of key-value pairs leaving + the map unchanged. + + **Arguments:** + * ``map`` -- the ``SUNHashMap`` object to operate on + * ``sorted`` -- pointer to the sorted array of key-value pairs, this + function will allocate the array + * ``compar`` -- comparator function that is passed to the C standard qsort + function + + **Returns:** + * A SUNErrCode indicating success or a failure + */ +SUNErrCode SUNHashMap_Sort(SUNHashMap map, SUNHashMapKeyValue** sorted, + int (*compar)(const void*, const void*)) +{ + int i; + + if (!map || !compar) { return SUN_ERR_ARG_CORRUPT; } + + *sorted = (SUNHashMapKeyValue*)malloc(map->max_size * sizeof(**sorted)); + if (!(*sorted)) { return SUN_ERR_MALLOC_FAIL; } + + /* Copy the buckets into a new array */ + for (i = 0; i < map->max_size; i++) { (*sorted)[i] = map->buckets[i]; } + + qsort(*sorted, map->max_size, sizeof(SUNHashMapKeyValue), compar); + + return SUN_SUCCESS; +} + +/* + This function allocates a new array with just they values of the map. + + **Arguments:** + * ``map`` -- the ``SUNHashMap`` object to operate on + * ``values`` -- pointer to the array of keys + * ``value_size`` -- the size of the values in bytes + + **Returns:** + * A SUNErrCode indicating success or a failure + */ +#if SUNDIALS_MPI_ENABLED +SUNErrCode SUNHashMap_Values(SUNHashMap map, void*** values, size_t value_size) +{ + int i; + int count = 0; + + if (!map) { return SUN_ERR_ARG_CORRUPT; } + + *values = (void**)malloc(map->size * value_size); + if (!values) { return SUN_ERR_MALLOC_FAIL; } + + /* Copy the values into a new array */ + for (i = 0; i < map->max_size; i++) + { + if (map->buckets[i]) { (*values)[count++] = map->buckets[i]->value; } + } + + return SUN_SUCCESS; +} +#endif diff --git a/src/sundials/sundials/sundials_hashmap_impl.h b/src/sundials/sundials/sundials_hashmap_impl.h new file mode 100644 index 0000000..235947e --- /dev/null +++ b/src/sundials/sundials/sundials_hashmap_impl.h @@ -0,0 +1,57 @@ +/* ----------------------------------------------------------------- + * Programmer: Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------- + * A simple hashmap implementation for char* keys and + * void* values. Uses linear probing to resolve collisions. + * The values can be anything, but will be freed by + * the hash map upon its destruction. + * -----------------------------------------------------------------*/ + +#ifndef _SUNDIALS_HASHMAP_IMPL_H +#define _SUNDIALS_HASHMAP_IMPL_H + +#include +#include + +typedef struct SUNHashMapKeyValue_* SUNHashMapKeyValue; + +struct SUNHashMapKeyValue_ +{ + const char* key; + void* value; +}; + +typedef struct SUNHashMap_* SUNHashMap; + +struct SUNHashMap_ +{ + int size; /* current number of entries */ + int max_size; /* max number of entries */ + SUNHashMapKeyValue* buckets; +}; + +SUNErrCode SUNHashMap_New(int max_size, SUNHashMap* map); +SUNErrCode SUNHashMap_Destroy(SUNHashMap* map, void (*freevalue)(void* ptr)); +int SUNHashMap_Iterate(SUNHashMap map, int start, + int (*yieldfn)(int, SUNHashMapKeyValue, const void*), + const void* ctx); +int SUNHashMap_Insert(SUNHashMap map, const char* key, void* value); +int SUNHashMap_GetValue(SUNHashMap map, const char* key, void** value); +SUNErrCode SUNHashMap_Sort(SUNHashMap map, SUNHashMapKeyValue** sorted, + int (*compar)(const void*, const void*)); + +#if SUNDIALS_MPI_ENABLED +SUNErrCode SUNHashMap_Values(SUNHashMap map, void*** values, size_t value_size); +#endif + +#endif diff --git a/src/sundials/sundials/sundials_iterative_impl.h b/src/sundials/sundials/sundials_iterative_impl.h index 5ebf796..ea6c17c 100644 --- a/src/sundials/sundials/sundials_iterative_impl.h +++ b/src/sundials/sundials/sundials_iterative_impl.h @@ -15,6 +15,9 @@ * different iterative solvers. * ---------------------------------------------------------------------------*/ +#ifndef _SUNDIALS_ITERATIVE_IMPL_H +#define _SUNDIALS_ITERATIVE_IMPL_H + #include /* ----------------------------------------------------------------------------- @@ -33,3 +36,5 @@ struct _SUNQRData N_Vector vtemp2; sunrealtype* temp_array; }; + +#endif diff --git a/src/sundials/sundials/sundials_lapack_defs.h.in b/src/sundials/sundials/sundials_lapack_defs.h.in new file mode 100644 index 0000000..1e6a0c0 --- /dev/null +++ b/src/sundials/sundials/sundials_lapack_defs.h.in @@ -0,0 +1,117 @@ +/* ----------------------------------------------------------------- + * Programmer: Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * -----------------------------------------------------------------*/ + +#ifndef _SUNDIALS_LAPACK_H +#define _SUNDIALS_LAPACK_H + +#include + +#ifdef __cplusplus /* wrapper to enable C++ usage */ +extern "C" { +#endif + +/* Define LAPACK name-mangling macros. Depending on the inferred scheme, one of + * the following six macros will be defined: + * + * #define SUNDIALS_LAPACK_FUNC(name,NAME) name + * #define SUNDIALS_LAPACK_FUNC(name,NAME) name ## _ + * #define SUNDIALS_LAPACK_FUNC(name,NAME) name ## __ + * #define SUNDIALS_LAPACK_FUNC(name,NAME) NAME + * #define SUNDIALS_LAPACK_FUNC(name,NAME) NAME ## _ + * #define SUNDIALS_LAPACK_FUNC(name,NAME) NAME ## __ + */ +@LAPACK_MANGLE_MACRO1@ + +/* Define LAPACK name-mangling macro for C identifiers + * which contain underscores. + */ +@LAPACK_MANGLE_MACRO2@ + +/* + * ================================================================== + * Blas and Lapack functions + * ================================================================== + */ + +#if defined(SUNDIALS_LAPACK_FUNC) + +#define dgbtrf_f77 SUNDIALS_LAPACK_FUNC(dgbtrf, DGBTRF) +#define dgbtrs_f77 SUNDIALS_LAPACK_FUNC(dgbtrs, DGBTRS) +#define dgetrf_f77 SUNDIALS_LAPACK_FUNC(dgetrf, DGETRF) +#define dgetrs_f77 SUNDIALS_LAPACK_FUNC(dgetrs, DGETRS) + +#define sgbtrf_f77 SUNDIALS_LAPACK_FUNC(sgbtrf, SGBTRF) +#define sgbtrs_f77 SUNDIALS_LAPACK_FUNC(sgbtrs, SGBTRS) +#define sgetrf_f77 SUNDIALS_LAPACK_FUNC(sgetrf, SGETRF) +#define sgetrs_f77 SUNDIALS_LAPACK_FUNC(sgetrs, SGETRS) + +#else + +#define dgbtrf_f77 dgbtrf_ +#define dgbtrs_f77 dgbtrs_ +#define dgetrf_f77 dgetrf_ +#define dgetrs_f77 dgetrs_ + +#define sgbtrf_f77 sgbtrf_ +#define sgbtrs_f77 sgbtrs_ +#define sgetrf_f77 sgetrf_ +#define sgetrs_f77 sgetrs_ + +#endif + +/* LAPACK */ + +extern void dgbtrf_f77(const sunindextype* m, const sunindextype* n, + const sunindextype* kl, const sunindextype* ku, + double* ab, sunindextype* ldab, sunindextype* ipiv, + sunindextype* info); + +extern void dgbtrs_f77(const char* trans, const sunindextype* n, + const sunindextype* kl, const sunindextype* ku, + const sunindextype* nrhs, double* ab, + const sunindextype* ldab, sunindextype* ipiv, double* b, + const sunindextype* ldb, sunindextype* info); + +extern void dgetrf_f77(const sunindextype* m, const sunindextype* n, double* a, + sunindextype* lda, sunindextype* ipiv, sunindextype* info); + +extern void dgetrs_f77(const char* trans, const sunindextype* n, + const sunindextype* nrhs, double* a, + const sunindextype* lda, sunindextype* ipiv, double* b, + const sunindextype* ldb, sunindextype* info); + +extern void sgbtrf_f77(const sunindextype* m, const sunindextype* n, + const sunindextype* kl, const sunindextype* ku, + float* ab, sunindextype* ldab, sunindextype* ipiv, + sunindextype* info); + +extern void sgbtrs_f77(const char* trans, const sunindextype* n, + const sunindextype* kl, const sunindextype* ku, + const sunindextype* nrhs, float* ab, + const sunindextype* ldab, sunindextype* ipiv, float* b, + const sunindextype* ldb, sunindextype* info); + +extern void sgetrf_f77(const sunindextype* m, const sunindextype* n, float* a, + sunindextype* lda, sunindextype* ipiv, sunindextype* info); + +extern void sgetrs_f77(const char* trans, const sunindextype* n, + const sunindextype* nrhs, float* a, + const sunindextype* lda, sunindextype* ipiv, float* b, + const sunindextype* ldb, sunindextype* info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sundials/sundials/sundials_logger.c b/src/sundials/sundials/sundials_logger.c index 2b109f6..bc4d686 100644 --- a/src/sundials/sundials/sundials_logger.c +++ b/src/sundials/sundials/sundials_logger.c @@ -16,18 +16,19 @@ #include #include #include + #include #include +#include #include - -#include "sundials/sundials_errors.h" -#include "sundials/sundials_types.h" +#include #if SUNDIALS_MPI_ENABLED #include #endif #include "sundials_logger_impl.h" +#include "sundials_macros.h" #include "sundials_utils.h" /* max number of files that can be opened */ @@ -65,6 +66,7 @@ void sunCreateLogMessage(SUNLogLevel lvl, int rank, const char* scope, free(formatted_txt); } +#if SUNDIALS_LOGGING_LEVEL > 0 static FILE* sunOpenLogFile(const char* fname, const char* mode) { FILE* fp = NULL; @@ -78,13 +80,15 @@ static FILE* sunOpenLogFile(const char* fname, const char* mode) return fp; } +#endif static void sunCloseLogFile(void* fp) { if (fp && fp != stdout && fp != stderr) { fclose((FILE*)fp); } } -static sunbooleantype sunLoggerIsOutputRank(SUNLogger logger, int* rank_ref) +static sunbooleantype sunLoggerIsOutputRank(SUNDIALS_MAYBE_UNUSED SUNLogger logger, + int* rank_ref) { sunbooleantype retval; @@ -371,6 +375,13 @@ SUNErrCode SUNLogger_QueueMsg(SUNLogger logger, SUNLogLevel lvl, va_end(args); } +#else + /* silence warnings when all logging is disabled */ + ((void)logger); + ((void)lvl); + ((void)scope); + ((void)label); + ((void)msg_txt); #endif return retval; @@ -417,6 +428,9 @@ SUNErrCode SUNLogger_Flush(SUNLogger logger, SUNLogLevel lvl) } } } +#else + /* silence warnings when all logging is disabled */ + ((void)lvl); #endif return retval; diff --git a/src/sundials/sundials/sundials_logger_impl.h b/src/sundials/sundials/sundials_logger_impl.h index 7cecc00..6fbb7ca 100644 --- a/src/sundials/sundials/sundials_logger_impl.h +++ b/src/sundials/sundials/sundials_logger_impl.h @@ -21,7 +21,7 @@ #include #include -#include "sundials_hashmap.h" +#include "sundials_hashmap_impl.h" #include "sundials_utils.h" #define SUNDIALS_LOGGING_ERROR 1 diff --git a/src/sundials/sundials/sundials_macros.h b/src/sundials/sundials/sundials_macros.h new file mode 100644 index 0000000..4bf007e --- /dev/null +++ b/src/sundials/sundials/sundials_macros.h @@ -0,0 +1,39 @@ +/* ----------------------------------------------------------------------------- + * Programmer(s): David J. Gardner @ LLNL + * ----------------------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2024, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ----------------------------------------------------------------------------- + * SUNDIALS macros + * ---------------------------------------------------------------------------*/ + +#ifndef _SUNDIALS_MACROS_H +#define _SUNDIALS_MACROS_H + +#include "sundials/sundials_config.h" + +/* ----------------------------------------------------------------------------- + * SUNDIALS_MAYBE_UNUSED + * + * This maps to an attribute that can be used to silence warnings about unused + * classes, typedefs, variables, functions, or methods when the entity cannot be + * removed. For example, functions or variables that are only used when error + * checks or profiling is enabled. + * ---------------------------------------------------------------------------*/ + +#if __cplusplus >= 201703L || __STDC_VERSION__ > 201710L +#define SUNDIALS_MAYBE_UNUSED [[maybe_unused]] +#elif defined(SUNDIALS_C_COMPILER_HAS_ATTRIBUTE_UNUSED) +#define SUNDIALS_MAYBE_UNUSED __attribute__((unused)) +#else +#define SUNDIALS_MAYBE_UNUSED +#endif + +#endif /* _SUNDIALS_MACROS_H */ diff --git a/src/sundials/sundials/sundials_nvector.c b/src/sundials/sundials/sundials_nvector.c index a6549d8..9b15332 100644 --- a/src/sundials/sundials/sundials_nvector.c +++ b/src/sundials/sundials/sundials_nvector.c @@ -284,8 +284,8 @@ N_Vector N_VClone(N_Vector w) { N_Vector result = NULL; SUNDIALS_MARK_FUNCTION_BEGIN(getSUNProfiler(w)); - result = w->ops->nvclone(w); - result->sunctx = w->sunctx; + result = w->ops->nvclone(w); + if (result) { result->sunctx = w->sunctx; } SUNDIALS_MARK_FUNCTION_END(getSUNProfiler(w)); return result; } @@ -294,8 +294,8 @@ N_Vector N_VCloneEmpty(N_Vector w) { N_Vector result; SUNDIALS_MARK_FUNCTION_BEGIN(getSUNProfiler(w)); - result = w->ops->nvcloneempty(w); - result->sunctx = w->sunctx; + result = w->ops->nvcloneempty(w); + if (result) { result->sunctx = w->sunctx; } SUNDIALS_MARK_FUNCTION_END(getSUNProfiler(w)); return result; } @@ -335,7 +335,11 @@ void N_VSpace(N_Vector v, sunindextype* lrw, sunindextype* liw) sunrealtype* N_VGetArrayPointer(N_Vector v) { - return ((sunrealtype*)v->ops->nvgetarraypointer(v)); + if (v->ops->nvgetarraypointer) + { + return (sunrealtype*)v->ops->nvgetarraypointer(v); + } + else { return NULL; } } sunrealtype* N_VGetDeviceArrayPointer(N_Vector v) @@ -349,7 +353,7 @@ sunrealtype* N_VGetDeviceArrayPointer(N_Vector v) void N_VSetArrayPointer(sunrealtype* v_data, N_Vector v) { - v->ops->nvsetarraypointer(v_data, v); + if (v->ops->nvsetarraypointer) { v->ops->nvsetarraypointer(v_data, v); } return; } diff --git a/src/sundials/sundials/sundials_profiler.c b/src/sundials/sundials/sundials_profiler.c index bfc7196..c852448 100644 --- a/src/sundials/sundials/sundials_profiler.c +++ b/src/sundials/sundials/sundials_profiler.c @@ -12,11 +12,16 @@ * SUNDIALS Copyright End * -----------------------------------------------------------------*/ +#include +#include +#include + #include #include - -#include "sundials/sundials_errors.h" -#include "sundials/sundials_types.h" +#include +#include +#include +#include #if SUNDIALS_MPI_ENABLED #include @@ -32,14 +37,9 @@ #error SUNProfiler needs POSIX or Windows timers #endif -#include -#include -#include -#include -#include - #include "sundials_debug.h" -#include "sundials_hashmap.h" +#include "sundials_hashmap_impl.h" +#include "sundials_macros.h" #define SUNDIALS_ROOT_TIMER ((const char*)"From profiler epoch") @@ -57,7 +57,7 @@ typedef struct _sunTimespec #if SUNDIALS_MPI_ENABLED static SUNErrCode sunCollectTimers(SUNProfiler p); #endif -static void sunPrintTimers(int idx, SUNHashMapKeyValue kv, FILE* fp, void* pvoid); +static void sunPrintTimer(SUNHashMapKeyValue kv, FILE* fp, void* pvoid); static int sunCompareTimes(const void* l, const void* r); static int sunclock_gettime_monotonic(sunTimespec* tp); @@ -419,7 +419,7 @@ SUNErrCode SUNProfiler_Print(SUNProfiler p, FILE* fp) /* Print all the other timers out */ for (i = 0; i < p->map->size; i++) { - if (sorted[i]) { sunPrintTimers(i, sorted[i], fp, (void*)p); } + if (sorted[i]) { sunPrintTimer(sorted[i], fp, (void*)p); } } free(sorted); } @@ -442,7 +442,7 @@ SUNErrCode SUNProfiler_Print(SUNProfiler p, FILE* fp) #if SUNDIALS_MPI_ENABLED static void sunTimerStructReduceMaxAndSum(void* a, void* b, int* len, - MPI_Datatype* dType) + SUNDIALS_MAYBE_UNUSED MPI_Datatype* dType) { sunTimerStruct* a_ts = (sunTimerStruct*)a; sunTimerStruct* b_ts = (sunTimerStruct*)b; @@ -522,7 +522,7 @@ SUNErrCode sunCollectTimers(SUNProfiler p) /* Print out the: timer name, percentage of exec time (based on the max), max across ranks, average across ranks, and the timer counter. */ -void sunPrintTimers(int idx, SUNHashMapKeyValue kv, FILE* fp, void* pvoid) +void sunPrintTimer(SUNHashMapKeyValue kv, FILE* fp, void* pvoid) { SUNProfiler p = (SUNProfiler)pvoid; sunTimerStruct* ts = (sunTimerStruct*)kv->value; @@ -542,8 +542,8 @@ int sunCompareTimes(const void* l, const void* r) double left_max; double right_max; - const SUNHashMapKeyValue left = *((SUNHashMapKeyValue*)l); - const SUNHashMapKeyValue right = *((SUNHashMapKeyValue*)r); + const SUNHashMapKeyValue left = *((const SUNHashMapKeyValue*)l); + const SUNHashMapKeyValue right = *((const SUNHashMapKeyValue*)r); if (left == NULL && right == NULL) { return 0; } if (left == NULL) { return 1; } diff --git a/src/sundials/sundials/sundials_utils.h b/src/sundials/sundials/sundials_utils.h index 6866ffa..44ce2a7 100644 --- a/src/sundials/sundials/sundials_utils.h +++ b/src/sundials/sundials/sundials_utils.h @@ -71,7 +71,7 @@ static inline int sunvasnprintf(char** str, const char* fmt, va_list args) *str = (char*)malloc(size + 1); if (NULL == *str) { return -1; } - size = vsprintf(*str, fmt, args); + size = vsnprintf(*str, size + 1, fmt, args); return size; } diff --git a/src/sundials/sunlinsol/band/sunlinsol_band.c b/src/sundials/sunlinsol/band/sunlinsol_band.c index 775466b..d72e37a 100644 --- a/src/sundials/sunlinsol/band/sunlinsol_band.c +++ b/src/sundials/sunlinsol/band/sunlinsol_band.c @@ -17,11 +17,14 @@ #include #include + #include #include #include #include +#include "sundials_macros.h" + #define ZERO SUN_RCONST(0.0) #define ONE SUN_RCONST(1.0) #define ROW(i, j, smu) (i - j + smu) @@ -46,7 +49,8 @@ * Function to create a new band linear solver */ -SUNLinearSolver SUNLinSol_Band(N_Vector y, SUNMatrix A, SUNContext sunctx) +SUNLinearSolver SUNLinSol_Band(SUNDIALS_MAYBE_UNUSED N_Vector y, SUNMatrix A, + SUNContext sunctx) { SUNFunctionBegin(sunctx); SUNLinearSolver S; @@ -107,12 +111,12 @@ SUNLinearSolver SUNLinSol_Band(N_Vector y, SUNMatrix A, SUNContext sunctx) * ----------------------------------------------------------------- */ -SUNLinearSolver_Type SUNLinSolGetType_Band(SUNLinearSolver S) +SUNLinearSolver_Type SUNLinSolGetType_Band(SUNDIALS_MAYBE_UNUSED SUNLinearSolver S) { return (SUNLINEARSOLVER_DIRECT); } -SUNLinearSolver_ID SUNLinSolGetID_Band(SUNLinearSolver S) +SUNLinearSolver_ID SUNLinSolGetID_Band(SUNDIALS_MAYBE_UNUSED SUNLinearSolver S) { return (SUNLINEARSOLVER_BAND); } @@ -156,7 +160,7 @@ int SUNLinSolSetup_Band(SUNLinearSolver S, SUNMatrix A) } int SUNLinSolSolve_Band(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_Vector b, - sunrealtype tol) + SUNDIALS_MAYBE_UNUSED sunrealtype tol) { SUNFunctionBegin(S->sunctx); sunrealtype **A_cols, *xdata; diff --git a/src/sundials/sunlinsol/dense/sunlinsol_dense.c b/src/sundials/sunlinsol/dense/sunlinsol_dense.c index 13a1fa9..41e6a1b 100644 --- a/src/sundials/sunlinsol/dense/sunlinsol_dense.c +++ b/src/sundials/sunlinsol/dense/sunlinsol_dense.c @@ -17,12 +17,14 @@ #include #include + #include +#include #include #include -#include "sundials/sundials_errors.h" #include "sundials_logger_impl.h" +#include "sundials_macros.h" #define ONE SUN_RCONST(1.0) @@ -46,7 +48,8 @@ * Function to create a new dense linear solver */ -SUNLinearSolver SUNLinSol_Dense(N_Vector y, SUNMatrix A, SUNContext sunctx) +SUNLinearSolver SUNLinSol_Dense(SUNDIALS_MAYBE_UNUSED N_Vector y, SUNMatrix A, + SUNContext sunctx) { SUNFunctionBegin(sunctx); SUNLinearSolver S; @@ -102,12 +105,12 @@ SUNLinearSolver SUNLinSol_Dense(N_Vector y, SUNMatrix A, SUNContext sunctx) * ----------------------------------------------------------------- */ -SUNLinearSolver_Type SUNLinSolGetType_Dense(SUNLinearSolver S) +SUNLinearSolver_Type SUNLinSolGetType_Dense(SUNDIALS_MAYBE_UNUSED SUNLinearSolver S) { return (SUNLINEARSOLVER_DIRECT); } -SUNLinearSolver_ID SUNLinSolGetID_Dense(SUNLinearSolver S) +SUNLinearSolver_ID SUNLinSolGetID_Dense(SUNDIALS_MAYBE_UNUSED SUNLinearSolver S) { return (SUNLINEARSOLVER_DENSE); } @@ -146,7 +149,7 @@ int SUNLinSolSetup_Dense(SUNLinearSolver S, SUNMatrix A) } int SUNLinSolSolve_Dense(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_Vector b, - sunrealtype tol) + SUNDIALS_MAYBE_UNUSED sunrealtype tol) { SUNFunctionBegin(S->sunctx); sunrealtype **A_cols, *xdata; diff --git a/src/sundials/sunmatrix/band/sunmatrix_band.c b/src/sundials/sunmatrix/band/sunmatrix_band.c index 2384504..f3d06c7 100644 --- a/src/sundials/sunmatrix/band/sunmatrix_band.c +++ b/src/sundials/sunmatrix/band/sunmatrix_band.c @@ -22,11 +22,13 @@ #include #include + #include +#include #include #include -#include "sundials/sundials_errors.h" +#include "sundials_macros.h" #define ZERO SUN_RCONST(0.0) #define ONE SUN_RCONST(1.0) @@ -230,7 +232,10 @@ sunrealtype* SUNBandMatrix_Column(SUNMatrix A, sunindextype j) * ----------------------------------------------------------------- */ -SUNMatrix_ID SUNMatGetID_Band(SUNMatrix A) { return SUNMATRIX_BAND; } +SUNMatrix_ID SUNMatGetID_Band(SUNDIALS_MAYBE_UNUSED SUNMatrix A) +{ + return SUNMATRIX_BAND; +} SUNMatrix SUNMatClone_Band(SUNMatrix A) { @@ -428,6 +433,7 @@ SUNErrCode SUNMatSpace_Band(SUNMatrix A, long int* lenrw, long int* leniw) * ----------------------------------------------------------------- */ +SUNDIALS_MAYBE_UNUSED static sunbooleantype compatibleMatrices(SUNMatrix A, SUNMatrix B) { /* both matrices must have the same number of columns @@ -437,6 +443,7 @@ static sunbooleantype compatibleMatrices(SUNMatrix A, SUNMatrix B) return SUNTRUE; } +SUNDIALS_MAYBE_UNUSED static sunbooleantype compatibleMatrixAndVectors(SUNMatrix A, N_Vector x, N_Vector y) { diff --git a/src/sundials/sunmatrix/dense/sunmatrix_dense.c b/src/sundials/sunmatrix/dense/sunmatrix_dense.c index 5067379..c1250a9 100644 --- a/src/sundials/sunmatrix/dense/sunmatrix_dense.c +++ b/src/sundials/sunmatrix/dense/sunmatrix_dense.c @@ -20,10 +20,12 @@ #include #include + #include +#include #include -#include "sundials/sundials_errors.h" +#include "sundials_macros.h" #define ZERO SUN_RCONST(0.0) #define ONE SUN_RCONST(1.0) @@ -178,7 +180,10 @@ sunrealtype* SUNDenseMatrix_Column(SUNMatrix A, sunindextype j) * ----------------------------------------------------------------- */ -SUNMatrix_ID SUNMatGetID_Dense(SUNMatrix A) { return SUNMATRIX_DENSE; } +SUNMatrix_ID SUNMatGetID_Dense(SUNDIALS_MAYBE_UNUSED SUNMatrix A) +{ + return SUNMATRIX_DENSE; +} SUNMatrix SUNMatClone_Dense(SUNMatrix A) { @@ -346,6 +351,7 @@ SUNErrCode SUNMatSpace_Dense(SUNMatrix A, long int* lenrw, long int* leniw) * ----------------------------------------------------------------- */ +SUNDIALS_MAYBE_UNUSED static sunbooleantype compatibleMatrices(SUNMatrix A, SUNMatrix B) { /* both matrices must have the same shape */ @@ -357,6 +363,7 @@ static sunbooleantype compatibleMatrices(SUNMatrix A, SUNMatrix B) return SUNTRUE; } +SUNDIALS_MAYBE_UNUSED static sunbooleantype compatibleMatrixAndVectors(SUNMatrix A, N_Vector x, N_Vector y) { diff --git a/src/sundials/sunnonlinsol/fixedpoint/sunnonlinsol_fixedpoint.c b/src/sundials/sunnonlinsol/fixedpoint/sunnonlinsol_fixedpoint.c index 1ec2289..ff1d45a 100644 --- a/src/sundials/sunnonlinsol/fixedpoint/sunnonlinsol_fixedpoint.c +++ b/src/sundials/sunnonlinsol/fixedpoint/sunnonlinsol_fixedpoint.c @@ -18,11 +18,13 @@ #include #include #include + #include #include #include #include "sundials_logger_impl.h" +#include "sundials_macros.h" /* Internal utility routines */ static SUNErrCode AndersonAccelerate(SUNNonlinearSolver NLS, N_Vector gval, @@ -129,7 +131,8 @@ SUNNonlinearSolver SUNNonlinSol_FixedPointSens(int count, N_Vector y, int m, GetType, Initialize, Setup, Solve, and Free operations ============================================================================*/ -SUNNonlinearSolver_Type SUNNonlinSolGetType_FixedPoint(SUNNonlinearSolver NLS) +SUNNonlinearSolver_Type SUNNonlinSolGetType_FixedPoint( + SUNDIALS_MAYBE_UNUSED SUNNonlinearSolver NLS) { return (SUNNONLINEARSOLVER_FIXEDPOINT); } @@ -165,9 +168,11 @@ SUNErrCode SUNNonlinSolInitialize_FixedPoint(SUNNonlinearSolver NLS) by the Sys function provided to the nonlinear solver. ---------------------------------------------------------------------------*/ -int SUNNonlinSolSolve_FixedPoint(SUNNonlinearSolver NLS, N_Vector y0, +int SUNNonlinSolSolve_FixedPoint(SUNNonlinearSolver NLS, + SUNDIALS_MAYBE_UNUSED N_Vector y0, N_Vector ycor, N_Vector w, sunrealtype tol, - sunbooleantype callSetup, void* mem) + SUNDIALS_MAYBE_UNUSED sunbooleantype callSetup, + void* mem) { SUNFunctionBegin(NLS->sunctx); /* local variables */ diff --git a/src/sundials/sunnonlinsol/newton/sunnonlinsol_newton.c b/src/sundials/sunnonlinsol/newton/sunnonlinsol_newton.c index c796171..c4c0af9 100644 --- a/src/sundials/sunnonlinsol/newton/sunnonlinsol_newton.c +++ b/src/sundials/sunnonlinsol/newton/sunnonlinsol_newton.c @@ -18,12 +18,14 @@ #include #include #include + #include #include #include #include #include "sundials_logger_impl.h" +#include "sundials_macros.h" /* Content structure accessibility macros */ #define NEWTON_CONTENT(S) ((SUNNonlinearSolverContent_Newton)(S->content)) @@ -126,7 +128,8 @@ SUNNonlinearSolver SUNNonlinSol_NewtonSens(int count, N_Vector y, GetType, Initialize, Setup, Solve, and Free operations ============================================================================*/ -SUNNonlinearSolver_Type SUNNonlinSolGetType_Newton(SUNNonlinearSolver NLS) +SUNNonlinearSolver_Type SUNNonlinSolGetType_Newton( + SUNDIALS_MAYBE_UNUSED SUNNonlinearSolver NLS) { return (SUNNONLINEARSOLVER_ROOTFIND); } @@ -170,7 +173,8 @@ SUNErrCode SUNNonlinSolInitialize_Newton(SUNNonlinearSolver NLS) Note return values beginning with * are package specific values returned by the Sys, LSetup, and LSolve functions provided to the nonlinear solver. ----------------------------------------------------------------------------*/ -int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS, N_Vector y0, N_Vector ycor, +int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS, + SUNDIALS_MAYBE_UNUSED N_Vector y0, N_Vector ycor, N_Vector w, sunrealtype tol, sunbooleantype callLSetup, void* mem) { @@ -203,6 +207,20 @@ int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS, N_Vector y0, N_Vector ycor, Preform Newton iteraion */ for (;;) { + /* initialize current iteration counter for this solve attempt */ + NEWTON_CONTENT(NLS)->curiter = 0; + +#if SUNDIALS_LOGGING_LEVEL >= SUNDIALS_LOGGING_INFO + SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, __func__, + "begin-attempt", "iter = %ld, nni = %ld", + (long int)NEWTON_CONTENT(NLS)->curiter, + NEWTON_CONTENT(NLS)->niters); + SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, __func__, + "start-iterate", "iter = %ld, nni = %ld", + (long int)NEWTON_CONTENT(NLS)->curiter, + NEWTON_CONTENT(NLS)->niters); +#endif + /* compute the nonlinear residual, store in delta */ retval = NEWTON_CONTENT(NLS)->Sys(ycor, delta, mem); if (retval != SUN_SUCCESS) { break; } @@ -215,16 +233,6 @@ int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS, N_Vector y0, N_Vector ycor, if (retval != SUN_SUCCESS) { break; } } - /* initialize current iteration counter for this solve attempt */ - NEWTON_CONTENT(NLS)->curiter = 0; - -#if SUNDIALS_LOGGING_LEVEL >= SUNDIALS_LOGGING_INFO - SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, - "SUNNonlinSolSolve_Newton", "begin-iteration", - "iter = %ld, nni = %ld", (long int)0, - NEWTON_CONTENT(NLS)->niters); -#endif - /* looping point for Newton iteration. Break out on any error. */ for (;;) { @@ -248,16 +256,24 @@ int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS, N_Vector y0, N_Vector ycor, NEWTON_CONTENT(NLS)->ctest_data); #if SUNDIALS_LOGGING_LEVEL >= SUNDIALS_LOGGING_INFO - SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, - "SUNNonlinSolSolve_Newton", "end-of-iterate", - "iter = %ld, nni = %ld, wrmsnorm = %.16g", + SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, __func__, + "end-iterate", "iter = %ld, nni = %ld, wrmsnorm = %.16g", NEWTON_CONTENT(NLS)->curiter, - NEWTON_CONTENT(NLS)->niters, N_VWrmsNorm(delta, w)); + NEWTON_CONTENT(NLS)->niters - 1, N_VWrmsNorm(delta, w)); #endif + /* Update here so begin/end logging iterations match */ + NEWTON_CONTENT(NLS)->curiter++; + /* if successful update Jacobian status and return */ if (retval == SUN_SUCCESS) { +#if SUNDIALS_LOGGING_LEVEL >= SUNDIALS_LOGGING_INFO + SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, __func__, + "end-attempt", "success, iter = %ld, nni = %ld", + (long int)NEWTON_CONTENT(NLS)->curiter, + NEWTON_CONTENT(NLS)->niters); +#endif NEWTON_CONTENT(NLS)->jcur = SUNFALSE; return SUN_SUCCESS; } @@ -265,14 +281,20 @@ int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS, N_Vector y0, N_Vector ycor, /* check if the iteration should continue; otherwise exit Newton loop */ if (retval != SUN_NLS_CONTINUE) { break; } - /* not yet converged. Increment curiter and test for max allowed. */ - NEWTON_CONTENT(NLS)->curiter++; + /* not yet converged, test for max allowed iterations. */ if (NEWTON_CONTENT(NLS)->curiter >= NEWTON_CONTENT(NLS)->maxiters) { retval = SUN_NLS_CONV_RECVR; break; } +#if SUNDIALS_LOGGING_LEVEL >= SUNDIALS_LOGGING_INFO + SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, __func__, + "start-iterate", "iter = %ld, nni = %ld", + (long int)NEWTON_CONTENT(NLS)->curiter, + NEWTON_CONTENT(NLS)->niters); +#endif + /* compute the nonlinear residual, store in delta */ retval = NEWTON_CONTENT(NLS)->Sys(ycor, delta, mem); if (retval != SUN_SUCCESS) { break; } @@ -281,6 +303,13 @@ int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS, N_Vector y0, N_Vector ycor, /* all errors go here */ +#if SUNDIALS_LOGGING_LEVEL >= SUNDIALS_LOGGING_INFO + SUNLogger_QueueMsg(NLS->sunctx->logger, SUN_LOGLEVEL_INFO, __func__, + "end-attempt", "failure, iter = %ld, nni = %ld", + (long int)NEWTON_CONTENT(NLS)->curiter, + NEWTON_CONTENT(NLS)->niters); +#endif + /* If there is a recoverable convergence failure and the Jacobian-related data appears not to be current, increment the convergence failure count, reset the initial correction to zero, and loop again with a call to