diff --git a/.github/workflows/scientific-report.yml b/.github/workflows/scientific-report.yml index f006bc37..ab7a1920 100644 --- a/.github/workflows/scientific-report.yml +++ b/.github/workflows/scientific-report.yml @@ -67,12 +67,12 @@ jobs: - name: Run selected ERI Criterion benchmarks id: eri continue-on-error: true - run: cargo bench --features bench-support --bench eri_timings + run: cargo bench --features bench --bench eri_timings - name: Run selected MP2 Criterion benchmarks id: mp2 continue-on-error: true - run: cargo bench --features bench-support --bench mp2_timings + run: cargo bench --features bench --bench mp2_timings - name: Generate performance report id: performance diff --git a/Cargo.toml b/Cargo.toml index 000dbf59..44d0bf86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,14 @@ members = ["crates/rustiq-core"] default-members = [".", "crates/rustiq-core"] resolver = "2" +[workspace.package] +authors = ["Mickaël Véril"] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/mveril/RustiQ" +homepage = "https://mveril.github.io/RustiQ/" +keywords = ["quantum-chemistry", "electronic-structure", "hartree-fock", "mp2", "chemistry"] + [workspace.lints.rust] unsafe_code = "deny" @@ -10,6 +18,7 @@ unsafe_code = "deny" # Individual lint settings and local, documented exceptions override these groups. all = { level = "deny", priority = -1 } pedantic = { level = "deny", priority = -1 } +cargo = { level = "deny", priority = -1 } unwrap_used = "deny" panic = "deny" panic_in_result_fn = "deny" @@ -56,14 +65,17 @@ tokio = { version = "1", default-features = false } [package] name = "rustiq-chem" -description = "The modern rust quantum chemistry software" -authors = ["Mickaël Véril"] +description = "A modern command-line application for quantum chemistry, written in Rust" +authors.workspace = true version = "0.1.0-alpha.1.dev" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/mveril/RustiQ" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +documentation = "https://mveril.github.io/RustiQ/" +readme = "README.md" +keywords.workspace = true +categories = ["science::computational-chemistry::electronic-structure", "command-line-utilities"] [[bin]] name = "rustiq" @@ -72,7 +84,7 @@ path = "src/main.rs" [features] default = ["online", "manpages", "completions"] online = ["rustiq-core/online", "dep:indicatif", "dep:tokio"] -bench-support = ["rustiq-core/bench-support"] +bench = ["rustiq-core/bench"] manpages = ["dep:clap_mangen"] completions = ["dep:clap_complete", "dep:clap_complete_nushell", "dep:sysinfo"] @@ -123,22 +135,22 @@ temp-env.workspace = true [[bench]] name = "mp2_timings" harness = false -required-features = ["bench-support"] +required-features = ["bench"] [[bench]] name = "eri_timings" harness = false -required-features = ["bench-support"] +required-features = ["bench"] [[bench]] name = "eri_storage_allocation" harness = false -required-features = ["bench-support"] +required-features = ["bench"] [[bench]] name = "eri_storage_memory" harness = false -required-features = ["bench-support"] +required-features = ["bench"] [build-dependencies] bat = { workspace = true, features = ["build-assets"] } diff --git a/THIRD-PARTY-NOTICES.sha256 b/THIRD-PARTY-NOTICES.sha256 index 77e3187e..4dd906b4 100644 --- a/THIRD-PARTY-NOTICES.sha256 +++ b/THIRD-PARTY-NOTICES.sha256 @@ -1 +1 @@ -1019e261ed817234736bc7b4dd0aacb41ffcbbc1c89e66bd974592b651dec752 THIRD-PARTY-NOTICES.txt +fd10896a1c4a5a5be0dfdcabb49197b9fb5dc80a93d9e0e8a917ff746ca3169e THIRD-PARTY-NOTICES.txt diff --git a/THIRD-PARTY-NOTICES.txt b/THIRD-PARTY-NOTICES.txt index c652b315..37b26622 100644 --- a/THIRD-PARTY-NOTICES.txt +++ b/THIRD-PARTY-NOTICES.txt @@ -1,5 +1,5 @@ Cargo.lock SHA-256: 7f274ca9e7f738d58ec0e46d03be9802569df0512ec91892f4ccd1edbd90ad02 -Notice inputs SHA-256: 8c276309486abfbe214c47d6d52b2f5b63cf45bb0ecdf95bb6a8610d04ff929f +Notice inputs SHA-256: f3b7a59fa3633c0574f9c0f0b1c6a0b767056d57307cba53913adf72ef06f236 Corresponding source: https://github.com/mveril/RustiQ/releases/download/v0.1.0-alpha.1.dev/rustiq-corresponding-source.tar.gz RustiQ third-party licenses diff --git a/crates/rustiq-core/Cargo.toml b/crates/rustiq-core/Cargo.toml index 9c9ccfdd..48482916 100644 --- a/crates/rustiq-core/Cargo.toml +++ b/crates/rustiq-core/Cargo.toml @@ -1,11 +1,16 @@ [package] name = "rustiq-chem-core" -description = "Reusable domain model and quantum chemistry implementation for RustiQ" -authors = ["Mickaël Véril"] +description = "A reusable Rust library for modern quantum chemistry software" +authors.workspace = true version = "0.1.0-alpha.1.dev" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/mveril/RustiQ" +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +documentation = "https://docs.rs/rustiq-chem-core" +readme = "README.md" +keywords.workspace = true +categories = ["science::computational-chemistry::electronic-structure"] # Preserve the Rust import name independently of the Cargo package name. [lib] @@ -20,7 +25,7 @@ workspace = true [features] default = [] online = ["dep:reqwest", "dep:tokio"] -bench-support = [] +bench = [] [dependencies] miette.workspace = true diff --git a/crates/rustiq-core/README.md b/crates/rustiq-core/README.md new file mode 100644 index 00000000..5cdcee41 --- /dev/null +++ b/crates/rustiq-core/README.md @@ -0,0 +1,21 @@ +# RustiQ Core + +RustiQ Core is the reusable scientific library behind [RustiQ](https://github.com/mveril/RustiQ). +It explores how Rust's strong typing, memory safety, and modularity can support +quantum chemistry software, alongside numerical validation and performance. + +The library provides molecular and basis data, scientific configuration, +calculation execution, and persistence. Runfile parsing and terminal output +belong to the RustiQ application. The Cargo package is `rustiq-chem-core`; +its Rust import name is `rustiq_core`. + +RustiQ is experimental. Independently validate results against established +quantum chemistry packages before using them for research conclusions. + +- [API documentation](https://docs.rs/rustiq-chem-core) +- [RustiQ manual](https://mveril.github.io/RustiQ/) +- [Source, examples, and scientific validation](https://github.com/mveril/RustiQ) + +RustiQ-authored source is licensed under MIT OR Apache-2.0. See the +[distribution terms](https://github.com/mveril/RustiQ/blob/main/DISTRIBUTION.md) +for third-party licensing information. diff --git a/crates/rustiq-core/src/basis/basis_store.rs b/crates/rustiq-core/src/basis/basis_store.rs index d13722f4..6439fa78 100644 --- a/crates/rustiq-core/src/basis/basis_store.rs +++ b/crates/rustiq-core/src/basis/basis_store.rs @@ -154,7 +154,7 @@ impl BasisStore { } /// Copies a basis file from another store into this store. - #[cfg(any(test, feature = "bench-support"))] + #[cfg(any(test, feature = "bench"))] #[allow( dead_code, reason = "Retained scientific helpers and representations support tests, benchmarks, or future internal use" @@ -174,7 +174,7 @@ impl BasisStore { } /// Returns the package-local fixture basis store used by tests and benches. - #[cfg(any(test, feature = "bench-support"))] + #[cfg(any(test, feature = "bench"))] #[allow( dead_code, reason = "Retained scientific helpers and representations support tests, benchmarks, or future internal use" diff --git a/crates/rustiq-core/src/eri.rs b/crates/rustiq-core/src/eri.rs index 06531264..464b47c1 100644 --- a/crates/rustiq-core/src/eri.rs +++ b/crates/rustiq-core/src/eri.rs @@ -7,9 +7,9 @@ #[cfg(test)] use mendeleev::Element; use std::f64::consts::PI; -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] use std::time::{Duration, Instant}; mod compact; pub use compact::CompactEri; @@ -27,16 +27,16 @@ use thiserror::Error; const ERI_SELF_INTEGRAL_NEGATIVE_TOLERANCE: f64 = 1e-12; const COULOMB_CACHE_SMALLVEC_CAPACITY: usize = 128; -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] const CACHE_SIZE_BUCKET_LIMITS: [usize; 8] = [16, 32, 64, 128, 256, 512, 1024, usize::MAX]; -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] static COULOMB_CACHE_SIZE_COUNT: AtomicU64 = AtomicU64::new(0); -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] static COULOMB_CACHE_SIZE_SUM: AtomicU64 = AtomicU64::new(0); -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] static COULOMB_CACHE_SIZE_MAX: AtomicUsize = AtomicUsize::new(0); -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] static COULOMB_CACHE_SIZE_BUCKETS: [AtomicU64; 8] = [ AtomicU64::new(0), AtomicU64::new(0), @@ -220,7 +220,7 @@ pub(crate) fn electron_repulsion_ints_with_threshold( )) } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] #[allow( dead_code, reason = "Retained scientific helpers and representations support tests, benchmarks, or future internal use" @@ -238,7 +238,7 @@ pub struct EriTimingBreakdown { pub coulomb_cache_sizes: CacheSizeStats, } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] #[allow( dead_code, reason = "Retained scientific helpers and representations support tests, benchmarks, or future internal use" @@ -252,7 +252,7 @@ pub struct CacheSizeStats { pub buckets: [(usize, u64); 8], } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] #[allow( dead_code, reason = "Retained scientific helpers and representations support tests, benchmarks, or future internal use" @@ -281,7 +281,7 @@ impl CacheSizeStats { } } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] #[allow( dead_code, reason = "Retained scientific helpers and representations support tests, benchmarks, or future internal use" @@ -292,7 +292,7 @@ pub fn electron_repulsion_ints_timed( electron_repulsion_ints_timed_with_observer(basis, |_, _| {}) } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] pub fn electron_repulsion_ints_timed_with_observer( basis: &Basis, mut observer: impl FnMut(&'static str, Duration), @@ -498,7 +498,7 @@ fn compute_eri_pair_primitive(primitive_ab: &PairPrimitive, primitive_cd: &PairP 2.0 * PI.powf(2.5) / (p * q * (p + q).sqrt()) * eri } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] fn reset_coulomb_cache_size_stats() { COULOMB_CACHE_SIZE_COUNT.store(0, Ordering::Relaxed); COULOMB_CACHE_SIZE_SUM.store(0, Ordering::Relaxed); @@ -508,7 +508,7 @@ fn reset_coulomb_cache_size_stats() { } } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] fn record_coulomb_cache_size(len: usize) { COULOMB_CACHE_SIZE_COUNT.fetch_add(1, Ordering::Relaxed); COULOMB_CACHE_SIZE_SUM.fetch_add(len as u64, Ordering::Relaxed); @@ -521,7 +521,7 @@ fn record_coulomb_cache_size(len: usize) { COULOMB_CACHE_SIZE_BUCKETS[bucket_index].fetch_add(1, Ordering::Relaxed); } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] fn coulomb_cache_size_stats() -> CacheSizeStats { let mut buckets = [(0, 0); 8]; for (index, limit) in CACHE_SIZE_BUCKET_LIMITS.iter().copied().enumerate() { @@ -648,7 +648,7 @@ impl CoulombAuxiliaryCache { * (u_max as usize + 1) * (v_max as usize + 1) * (n_max as usize + 1); - #[cfg(feature = "bench-support")] + #[cfg(feature = "bench")] record_coulomb_cache_size(len); let mut values = SmallVec::new(); values.resize(len, f64::NAN); diff --git a/crates/rustiq-core/src/lib.rs b/crates/rustiq-core/src/lib.rs index a0e54583..f75b79be 100644 --- a/crates/rustiq-core/src/lib.rs +++ b/crates/rustiq-core/src/lib.rs @@ -89,7 +89,7 @@ pub(crate) mod resources; #[deny(clippy::doc_markdown)] pub(crate) mod test_utils; -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] #[deny(clippy::doc_markdown)] pub mod bench_support { pub use crate::basis::BasisStore; diff --git a/crates/rustiq-core/src/mp2.rs b/crates/rustiq-core/src/mp2.rs index 96cc95bd..4c5bfec5 100644 --- a/crates/rustiq-core/src/mp2.rs +++ b/crates/rustiq-core/src/mp2.rs @@ -19,11 +19,11 @@ //! rejected to prevent numerically singular perturbative contributions. mod blocked; -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] use crate::eri::index::{EriIndex, PairIndex}; pub use blocked::{Mp2MemoryPlan, Mp2Sector}; use nalgebra::{DMatrix, DVector}; -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] use rayon::prelude::*; use thiserror::Error; @@ -263,7 +263,7 @@ pub(crate) fn correlation_energy_with_memory( ) } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn build_ovov_integrals(input: &Mp2Input<'_>) -> DMatrix { let mo_coefficients = input.mo_coefficients; let basis_functions = mo_coefficients.nrows(); @@ -366,7 +366,7 @@ pub(crate) fn uhf_correlation_energy_with_memory( Ok(result) } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn build_ao_pair_matrix( two_electron_integrals: &CompactEri, basis_functions: usize, @@ -391,7 +391,7 @@ fn build_ao_pair_matrix( matrix } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn build_orbital_pair_transform( mo_coefficients: &DMatrix, occupied_start: usize, @@ -446,7 +446,7 @@ fn validate_spin_input(input: &Mp2SpinInput<'_>) -> Result<(), Mp2Error> { Ok(()) } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn same_spin_correlation_energy( ovov_integrals: &DMatrix, orbital_energies: &DVector, @@ -496,7 +496,7 @@ fn same_spin_correlation_energy( Ok(correlation_energy) } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn opposite_spin_correlation_energy( ovov_integrals: &DMatrix, alpha_orbital_energies: &DVector, @@ -553,7 +553,7 @@ pub(crate) struct Mp2SpinInput<'a> { pub(crate) frozen_orbitals: usize, } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn pair_transform_coefficient( mo_coefficients: &DMatrix, mu: usize, @@ -569,12 +569,12 @@ fn pair_transform_coefficient( } } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn orbital_pair_index(left: usize, right: usize, right_count: usize) -> usize { left * right_count + right } -#[cfg(any(test, feature = "bench-support"))] +#[cfg(any(test, feature = "bench"))] fn basis_function_pair_count(basis_functions: usize) -> usize { basis_functions * (basis_functions + 1) / 2 } @@ -614,7 +614,7 @@ fn mo_two_electron_integral( value } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] #[non_exhaustive] pub struct Mp2BenchResult { pub dense_elapsed: std::time::Duration, @@ -624,7 +624,7 @@ pub struct Mp2BenchResult { pub memory: Mp2MemoryPlan, } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] pub struct Mp2BenchInput { coefficients: DMatrix, energies: DVector, @@ -632,7 +632,7 @@ pub struct Mp2BenchInput { occupied_orbitals: usize, } -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] impl Mp2BenchInput { /// Creates deterministic synthetic input for comparing MP2 implementations. #[must_use] @@ -735,7 +735,7 @@ impl Mp2BenchInput { } /// Synthetic, deterministic inputs; timing excludes input/ERI construction and SCF. -#[cfg(feature = "bench-support")] +#[cfg(feature = "bench")] /// /// # Errors /// diff --git a/docs/src/development/contributing.md b/docs/src/development/contributing.md index 83976b37..f5ce85e9 100644 --- a/docs/src/development/contributing.md +++ b/docs/src/development/contributing.md @@ -360,13 +360,13 @@ Run `cargo test -p rustiq-chem-core math_utils::boys` and the affected [PySCF comparisons](https://github.com/mveril/RustiQ/blob/main/tools/reference/README.md) after changing this path. The `eri_timings` and `mp2_timings` benchmarks use Criterion and require -`bench-support`: +`bench`: ```sh -cargo bench --bench mp2_timings --features bench-support -cargo bench --bench eri_timings --features bench-support -cargo bench --bench eri_storage_allocation --features bench-support -cargo bench --bench eri_storage_memory --features bench-support +cargo bench --bench mp2_timings --features bench +cargo bench --bench eri_timings --features bench +cargo bench --bench eri_storage_allocation --features bench +cargo bench --bench eri_storage_memory --features bench ``` The MP2 benchmark compares the dense AO-to-MO transformation and correlation @@ -431,8 +431,8 @@ baseline on the reference revision with the same workload, then compare from the revision under test: ```sh -cargo bench --bench mp2_timings --features bench-support -- --save-baseline before -cargo bench --bench mp2_timings --features bench-support -- --baseline before +cargo bench --bench mp2_timings --features bench -- --save-baseline before +cargo bench --bench mp2_timings --features bench -- --baseline before ``` See the [MP2 benchmark source](https://github.com/mveril/RustiQ/blob/main/benches/mp2_timings.rs) @@ -461,8 +461,8 @@ platform metadata so results can be compared across revisions. Generate Criterion measurements and their report with: ```sh -cargo bench --features bench-support --bench eri_timings -cargo bench --features bench-support --bench mp2_timings +cargo bench --features bench --bench eri_timings +cargo bench --features bench --bench mp2_timings uv run --locked python tools/benchmarks/report.py ``` diff --git a/src/cli/build_info/metadata.rs b/src/cli/build_info/metadata.rs index cc7afffa..135d7aa7 100644 --- a/src/cli/build_info/metadata.rs +++ b/src/cli/build_info/metadata.rs @@ -169,8 +169,8 @@ mod tests { #[test] fn cargo_feature_list_hides_the_default_marker_and_is_sorted() { assert_eq!( - cargo_feature_values("online,default,bench_support,online"), - ["bench-support", "online"] + cargo_feature_values("online,default,bench,online"), + ["bench", "online"] ); assert_eq!(cargo_feature_values("default"), Vec::::new()); } diff --git a/tests/version_cli.rs b/tests/version_cli.rs index 5f9ec90c..c5c6f1b1 100644 --- a/tests/version_cli.rs +++ b/tests/version_cli.rs @@ -128,15 +128,12 @@ fn verbose_version_accepts_both_orders_and_short_flags() { let feature_line = lines .iter() .find(|line| line.starts_with(" Cargo features")); - let expected_any_feature = cfg!(feature = "online") || cfg!(feature = "bench-support"); + let expected_any_feature = cfg!(feature = "online") || cfg!(feature = "bench"); assert_eq!(feature_line.is_some(), expected_any_feature); if let Some(feature_line) = feature_line { assert!(!feature_line.contains("default")); assert_eq!(feature_line.contains("online"), cfg!(feature = "online")); - assert_eq!( - feature_line.contains("bench-support"), - cfg!(feature = "bench-support") - ); + assert_eq!(feature_line.contains("bench"), cfg!(feature = "bench")); } if let Some(debug_line) = lines .iter()