Skip to main content

cuest_cudarc/
lib.rs

1//! Safe Rust wrapper for [cuEST](https://developer.nvidia.com/cuest) (CUDA
2//! Electronic Structure Toolkit) built on [cudarc].
3//!
4//! This crate wraps the raw [`cuest_ffi`] bindings into a safe API: cuEST
5//! handles become owned Rust objects, user-allocated workspaces are managed
6//! internally, every return code is checked, and device buffers are tied to
7//! the cudarc stream the cuEST handle was created on.
8//!
9//! This crate is not an official NVIDIA project. cuEST itself is proprietary
10//! NVIDIA software and is not distributed with this crate.
11//!
12//! # Design notes
13//!
14//! - A [`CuestContext`] wraps a user-supplied `Arc<CudaStream>`. The stream is
15//!   bound once at `cuestCreate` (cuEST has no per-call streams), so every
16//!   device buffer passed to a compute call must live on that stream; this is
17//!   checked at runtime and reported as [`CuestError::Rust`] otherwise.
18//! - Objects keep their dependencies alive (an [`Arc`](std::sync::Arc)-based
19//!   keep-alive, not lifetime parameters), so cuEST's destruction order is
20//!   enforced by normal `Drop` order.
21//! - Device buffers for inputs are taken as `&impl DevicePtr<f64>` (any
22//!   [`CudaSlice`](cudarc::driver::CudaSlice),
23//!   [`CudaView`](cudarc::driver::CudaView) or
24//!   [`CudaViewMut`](cudarc::driver::CudaViewMut)); outputs are either
25//!   auto-allocated or written into an `&mut impl DevicePtrMut<f64>` (`*_into`
26//!   methods). No synchronization is performed inside compute methods — the
27//!   work is stream-ordered, as with cudarc itself.
28//! - All calls are error-checked; a failing cuEST status becomes
29//!   [`CuestError::Status`]. Buffer sizes the cuEST C API cannot validate (the
30//!   caller contract) are validated here instead.
31//! - The `unsafe` blocks are cuEST FFI calls (justified by owned handles, the
32//!   cuEST call contracts, and the validations above) and one uninitialized
33//!   scratch allocation (cuEST workspace buffers, matching the C samples'
34//!   `cudaMalloc`).
35//!
36//! # Example
37//!
38//! ```no_run
39//! use cuest_cudarc::prelude::*;
40//! use cuest_cudarc::cudarc::driver::CudaContext;
41//!
42//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
43//! let stream = CudaContext::new(0)?.default_stream();
44//! let context = CuestContext::new(&stream)?;
45//!
46//! // H2 at 1.4 Bohr (0.7408 Angstrom), STO-3G, on one atom each.
47//! let xyz = Xyz::parse(
48//!     "2
49//! H2
50//! H 0.0 0.0 0.0
51//! H 0.0 0.0 0.7408
52//! ",
53//! )?;
54//!
55//! let spec = CuestShellSpec::new(0, true, vec![3.42525091, 0.62391373, 0.16885540], vec![
56//!     0.15432897, 0.53532814, 0.44463454,
57//! ])?
58//! .normalized(1.0)?;
59//! let shells: Vec<CuestAOShell> =
60//!     (0..xyz.len()).map(|_| CuestAOShell::new(&context, &spec)).collect::<Result<_, _>>()?;
61//! let basis = CuestAOBasis::new(&context, &[&shells[0..1], &shells[1..2]])?;
62//!
63//! let pair_list = CuestAOPairList::from_xyz(&basis, &xyz, 1.0e-14)?;
64//! let plan = CuestOeIntPlan::new(&basis, &pair_list)?;
65//!
66//! let s = plan.overlap()?; // CudaSlice<f64>, nao x nao, on `stream`
67//! let t = plan.kinetic()?;
68//! # Ok(())
69//! # }
70//! ```
71//!
72//! # Loading modes
73//!
74//! The loading mode is selected through this crate's features (matching
75//! `cuest-ffi`):
76//!
77//! - **Dynamic loading** (default, `dynamic_loading` feature): the cuEST shared
78//!   library is loaded at runtime; set `CUEST_DYLOAD` or see
79//!   [`cuest_ffi::ffi_dynamic`] for the search order.
80//! - **Static linking** (`--no-default-features`): links `libcuest` at build
81//!   time (`CUEST_LIB_DIR` overrides the search path).
82//!
83//! # Thread safety
84//!
85//! cuEST documents no thread-safety guarantees beyond the usage model of one
86//! handle per stream. The types in this crate are therefore `Send` (they can
87//! be moved between threads) but **not** `Sync`: a given handle (and any
88//! object derived from it) must be used from one thread at a time. The
89//! underlying CUDA stream is the real serialization point.
90//!
91//! # Escape hatch
92//!
93//! The raw bindings are re-exported as [`cuest_ffi`] for API surface this
94//! crate does not wrap yet; every object also exposes its raw handle
95//! (`raw_handle()`).
96
97#![doc = include_str!("../readme.md")]
98#![cfg_attr(docsrs, feature(doc_cfg))]
99#![allow(non_snake_case)]
100// Deliberate: the shared per-object state (`Arc<...Inner>`) holds a raw cuEST
101// handle and is therefore neither `Send` nor `Sync`; the public wrapper types
102// add a documented `unsafe impl Send` instead (see "Thread safety" above).
103#![allow(clippy::arc_with_non_send_sync)]
104
105pub mod basis;
106pub mod context;
107pub mod error;
108pub mod oeint;
109pub mod pair_list;
110pub mod query;
111pub mod shell;
112pub mod xyz;
113
114pub(crate) mod parameters;
115pub(crate) mod workspace;
116
117/// The cudarc version this crate was built against.
118pub use cudarc;
119/// The raw cuEST FFI bindings this crate builds on (escape hatch).
120pub use cuest_ffi;
121
122pub use crate::basis::CuestAOBasis;
123pub use crate::context::{CuestContext, CuestHandleOptions, CuestMathMode};
124pub use crate::error::CuestError;
125pub use crate::oeint::{
126    CuestFfloatUsageMode, CuestJitUsageMode, CuestOeIntPlan, CuestPotentialComputeOptions,
127};
128pub use crate::pair_list::CuestAOPairList;
129pub use crate::shell::{CuestAOShell, CuestShellSpec};
130pub use crate::xyz::Xyz;
131
132/// Common imports.
133pub mod prelude {
134    pub use crate::basis::CuestAOBasis;
135    pub use crate::context::{CuestContext, CuestHandleOptions, CuestMathMode};
136    pub use crate::error::CuestError;
137    pub use crate::oeint::{
138        CuestFfloatUsageMode, CuestJitUsageMode, CuestOeIntPlan, CuestPotentialComputeOptions,
139    };
140    pub use crate::pair_list::CuestAOPairList;
141    pub use crate::shell::{CuestAOShell, CuestShellSpec};
142    pub use crate::xyz::Xyz;
143}