From 86831c500d544a662dba4aecfebd042e81fa72de Mon Sep 17 00:00:00 2001 From: Shirong_Wang Date: Sun, 14 Jun 2026 16:32:31 +0800 Subject: [PATCH 1/2] add rest-libcint --- AGENTS.md | 1 + skills/rest-libcint-knowledge/SKILL.md | 548 +++++++++++++++++++++++++ 2 files changed, 549 insertions(+) create mode 100644 skills/rest-libcint-knowledge/SKILL.md diff --git a/AGENTS.md b/AGENTS.md index b1f1e59..db7c60f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,5 +68,6 @@ $REST_HOME/ | `develop-with-rstsr` | 任务涉及张量运算、矩阵操作、线性代数或使用 `rstsr` / `rest_tensors` crate 时必须加载。 | | `rest-doc-guide` | 任务涉及编写、修改、翻译文档(`rest_doc/` 目录)时必须加载。 | | `rest-regression-guide` | 任务涉及新增、更新或运行回归测试(`rest_regression/` 目录)时必须加载。 | +| `rest-libcint-knowledge` | 任务涉及 `rest_libcint/` crate 的代码修改、积分逻辑、内存分配、FFI 绑定、并行填充或架构理解时必须加载。 | **步骤**:识别任务 → 加载匹配的 skill(s) → 按 skill 指导工作。若任务的多个方面匹配不同 skill,逐一加载。 diff --git a/skills/rest-libcint-knowledge/SKILL.md b/skills/rest-libcint-knowledge/SKILL.md new file mode 100644 index 0000000..8d01dc4 --- /dev/null +++ b/skills/rest-libcint-knowledge/SKILL.md @@ -0,0 +1,548 @@ +--- +name: rest-libcint-knowledge +description: rest_libcint crate 的结构、架构与关键代码模式,面向 AI 开发者 +--- + +# rest_libcint 知识图谱 + +> Rust 封装 libcint C 库 — 量子化学 GTO 分子积分引擎 + +--- + +## 目录 + +1. 项目总览 +2. 目录结构 +3. 模块依赖与架构 +4. 核心类型 +5. FFI 三层架构 +6. 调用流程:从 Rust API 到 C 积分函数 +7. 关键代码模式 +8. 构建系统 +9. 测试 +10. 已知问题与注意事项 + +--- + +## 1. 项目总览 + +| 属性 | 值 | +|------|-----| +| 路径 | `$REST_HOME/rest_libcint/` | +| 版本 | 0.1.1 | +| 语言 | 纯 Rust (封装 C 库 libcint) | +| 构建系统 | Cargo + `build.rs` (编译 C ECP 源码) | +| 并行模型 | Rayon (线程池) | +| 核心依赖 | `rstsr-common`, `rayon`, `itertools`, `num`, `serde`, `derive_builder` | +| 外部链接 | libcint (C 库), ECP C 源码 (编译进 crate) | + +**职责**: 提供 Rust 风格 API 来调用 libcint 的 C 积分函数,处理并行调度、内存布局转换、对称性打包。 + +--- + +## 2. 目录结构 + +``` +rest_libcint/ +├── build.rs # 编译 src/cecp/*.c,可选链接 libcint +├── Cargo.toml # 9 个依赖, 5 个 feature flags +├── src/ +│ ├── lib.rs # 22 个 pub mod + pub use prelude::* +│ ├── prelude.rs # 公共与 crate-internal 重导出中枢 +│ ├── util.rs # 工具函数: aligned alloc, copy_f_*, get_f_index_* +│ ├── test_mol.rs # 测试用分子 + fingerprint 函数 +│ │ +│ ├── cint.rs # CInt 主结构体 + CIntOutput, CIntSymm 等 +│ ├── cint_crafter.rs # 积分构建/分派: integral_block(), max_cache_size() +│ ├── cint_prop.rs # CInt 只读属性: ao_loc(), make_loc() +│ ├── cint_initialize.rs # serde + Builder 构造 +│ ├── cint_result.rs # CIntError 错误类型 +│ ├── cint_change.rs # 数据修改: fakemol_for_charges 等 +│ ├── cint_fill_col_major.rs # 列优先并行填充 (Fortran 顺序) +│ ├── cint_fill_row_major.rs # 行优先并行填充 +│ ├── cint_fill_grids.rs # 网格 GTO 积分 +│ ├── cint_old.rs # 遗留 CINTR2CDATA API +│ ├── cint_old_crafter.rs # 遗留积分构建 +│ │ +│ ├── ffi/ +│ │ ├── cint_ffi.rs # Tier 1: bindgen 生成的原始 C 绑定 +│ │ ├── cecp_ffi.rs # Tier 1: ECP C 绑定 +│ │ ├── wrapper_traits.rs # Tier 2: Integrator trait + impl_integrator! 宏 +│ │ ├── cint_wrapper.rs # Tier 3: 100+ 积分器结构体 + 查找表 +│ │ └── cecp_wrapper.rs # Tier 3: ECP 积分器 +│ │ +│ └── gto/ +│ ├── gto_crafter.rs # GTO 网格求值构建器 +│ ├── grid_ao_drv.rs # GtoEvalAPI trait +│ ├── deriv_util.rs # FpSimd SIMD 工具 +│ ├── deriv_impl/ # 14 个导数实现文件 +│ └── prelude_dev.rs # 重导出所有 deriv 项 +│ +└── tests/ # 11 个集成测试 +``` + +--- + +## 3. 模块依赖与架构 + +### 模块层级 + +``` + lib.rs (pub use prelude::*) + │ + ┌────────────────────────┼────────────────────────┐ + │ │ │ + cint.rs ffi/mod.rs gto/mod.rs + (CInt 定义) (FFI 三层) (GTO 网格求值) + │ │ │ + ┌──────────┼──────────┐ ┌──────┼──────┐ ┌─────────┼─────────┐ + │ │ │ │ │ │ │ │ │ +cint_crafter │ cint_fill_* cint_ffi cecp_ffi gto_ deriv_impl/ deriv_util +(积分构建) │ (并行填充) (C绑定) (ECP绑定) crafter (14文件) (SIMD) + │ + cint_prop cint_result + (属性查询) (错误类型) +``` + +### 关键关系 + +| 关系 | 说明 | +|------|------| +| `cint.rs` → 6 个扩展模块 | `cint_crafter`, `cint_fill_*`, `cint_prop` 等通过 `impl CInt { ... }` 给 `CInt` 添加方法 | +| `cint_crafter` ↔ `ffi/wrapper_traits` | 通过 `Integrator` trait 分派到具体 C 函数 | +| `cint_fill_col_major.rs` 等 ↔ `util.rs` | 使用 `copy_f_*d_s*()` 从线程本地缓冲区复制到输出 | +| `gto/` ↔ `cint.rs` | `CInt` 通过 `gto_crafter.rs` 实现 GTO 网格求值方法 | +| `prelude.rs` ↔ 所有模块 | `pub(crate) use X::*` 使得常用类型在 crate 内全局可见 | + +--- + +## 4. 核心类型 + +### CInt (cint.rs:41) + +```rust +pub struct CInt { + pub atm: Vec<[c_int; 6]>, // 原子插槽 (电荷, 坐标指针, 核模型, ...) + pub bas: Vec<[c_int; 8]>, // 壳层插槽 (原子, 角动量, nprim, nctr, kappa, ...) + pub ecpbas: Vec<[c_int; 8]>, // ECP 壳层插槽 + pub env: Vec, // 浮点变量 (坐标, 指数, 系数, 截断, omega, ...) + pub cint_type: CIntType, // Spheric / Cartesian / Spinor + pub c_opt: Option>, +} +``` + +这是 libcint 的核心数据结构,映射 C 库的 AOS (Array of Structs) 内存布局。 + +### CIntType (cint.rs:144) + +```rust +pub enum CIntType { Spheric, Cartesian, Spinor } +``` + +决定输出类型: `Spheric`/`Cartesian` → `f64`,`Spinor` → `Complex`。 + +### CIntSymm (cint.rs:165) + +```rust +pub enum CIntSymm { S1, S2ij, S2kl, S4, S8 } +``` + +控制积分对称性利用(影响输出形状和打包方式): +- `S1`: 无对称性 (默认) +- `S2ij`: μ↔ν 对称 +- `S2kl`: κ↔λ 对称 (4 中心) +- `S4`: 前两 + 后两对称 (4 中心) +- `S8`: 完全 8 重对称 (4 中心) + +### CIntOutput (cint.rs:198) + +```rust +pub struct CIntOutput { + pub out: Option>, // None = 用户提供了外部缓冲区 + pub shape: Vec, // 输出形状 (列优先或行优先) +} +``` + +通过 `.into()` 转换为 `(Vec, Vec)`。 + +### CIntOptimizer (cint.rs:187) + +```rust +pub enum CIntOptimizer { + Int(*mut CINTOpt), // 通用积分优化器 + Ecp(*mut ECPOpt), // ECP 优化器 +} +``` + +实现 `Drop` 时调用相应的 C 释放函数(`CINTdel_optimizer` 或 `ECPdel_optimizer`)。 + +### CIntError (cint_result.rs) + +错误变体: `IntegratorNotFound`, `IntegratorNotAvailable`, `RuntimeError`, `InvalidValue`, `UninitializedFieldError`, `ParseError`, `Miscellaneous`。包含 backtrace。 + +--- + +## 5. FFI 三层架构 + +``` + ┌─────────────────────────┐ + │ cint_crafter.rs │ (积分构建/分派) + │ integral_block() │ + └────────────┬────────────┘ + │ 调用 trait 方法 + ▼ + ┌─────────────────────────┐ + Tier 2 ─────────── │ Integrator trait │ (wrapper_traits.rs) + │ + impl_integrator! 宏 │ + └────────────┬────────────┘ + │ 生成零大小结构体 + ▼ + ┌────────────────┐ ┌──────────────────┐ + │ cint_wrapper.rs│ │ cecp_wrapper.rs │ Tier 3 + │ 100+ 结构体 │ │ ECP 结构体 │ 薄胶水层 + │ get_cint_...() │ │ get_ecp_...() │ + └───────┬────────┘ └────────┬─────────┘ + │ │ + ▼ ▼ + ┌────────────────┐ ┌──────────────────┐ + │ cint_ffi.rs │ │ cecp_ffi.rs │ Tier 1 + │ extern "C" │ │ extern "C" │ 原始绑定 + │ (bindgen) │ │ (bindgen) │ + └───────┬────────┘ └────────┬─────────┘ + │ │ + ▼ ▼ + ┌───────────────────────────────────────┐ + │ C 库 libcint + libcecp │ + └───────────────────────────────────────┘ +``` + +### Integrator trait 核心方法 + +```rust +pub trait Integrator: Send + Sync { + unsafe fn optimizer(&self, opt: *mut *mut c_void, atm: *const c_int, ...); + unsafe fn integral_sph(&self, out: *mut f64, dims: *const c_int, shls: *const c_int, ...) -> c_int; + unsafe fn integral_cart(&self, ...) -> c_int; + unsafe fn integral_spinor(&self, out: *mut c_void, ...) -> c_int; + fn is_sph_available(&self) -> bool; + fn is_cart_available(&self) -> bool; + fn is_spinor_available(&self) -> bool; + fn n_comp(&self) -> usize; + fn n_spinor_comp(&self) -> usize; + fn n_center(&self) -> usize; + fn ng(&self) -> Vec; + fn integrator_category(&self) -> &'static str; + fn name(&self) -> &'static str; + fn kind(&self) -> CIntKind; +} +``` + +### impl_integrator! 宏示例 + +```rust +impl_integrator!( + int2e, // 结构体名称 + int2e_optimizer, // C 优化器函数 + int2e_sph, // C 球形积分函数 + int2e_cart, // C 笛卡尔积分函数 + int2e_spinor, // C 旋量积分函数 + true, // sph 可用 + true, // cart 可用 + true, // spinor 可用 + 1, // n_comp + 1, // n_spinor_comp + 4, // n_center + vec![0,0,0,0,0,1,1,1], // ng (导数阶数) + "int2e", // 类别 + "int2e", // 名称 + CIntKind::Int // 种别 +); +``` + +宏展开生成一个零大小结构体和 `Integrator` trait 实现。`integral_sph` 等方法直接调用对应的 C FFI 函数。 + +--- + +## 6. 调用流程 + +以 `cint.integrate("int2e", "s1", None)` 为例: + +``` +CInt::integrate("int2e", "s1", None) cint.rs + │ + ├─► integrate_with_args_inner() cint_crafter.rs:145 + │ + ├─► 查找积分器: get_cint_integrator("int2e") + │ → Box (int2e 结构体) + │ + ├─► 创建优化器: integrator.optimizer() + │ → 调用 C 函数 int2e_optimizer(...) + │ + ├─► 并行填充: integral_s1_inplace() cint_fill_col_major.rs + │ │ + │ ├─► 计算缓冲区大小: + │ │ max_cache_size() = 查询 libcint (null 输出指针) + │ │ max_buffer_size() = n_comp × ∏ max(cgto_i) + │ │ + │ ├─► 使用 Rayon 并行迭代 shell pairs + │ │ 每个线程拥有 (cache: Vec, buf: Vec) + │ │ + │ ├─► 对每个 shell pair: + │ │ │ + │ │ ├─►integral_block(out=buf, shls, cache) cint_crafter.rs:1195 + │ │ │ ├─ 根据 CIntType 分派: + │ │ │ │ Spheric → integrator.integral_sph(out as *mut f64, ...) + │ │ │ │ Cartesian→ integrator.integral_cart(...) + │ │ │ │ Spinor → integrator.integral_spinor(out as *mut c_void, ...) + │ │ │ └─ C 函数将积分值写入 buf + │ │ │ + │ │ └─► copy_f_3d_s1() / copy_f_4d_s1() 等 + │ │ 将 buf 中结果复制到 output 正确位置 (列优先索引) + │ │ + │ └─► Rayon 完成后,返回 Ok(()) + │ + └─► 返回 CIntOutput { out: Some(out_vec), shape } +``` + +### 关键要点 + +1. **`cache`** 是 libcint 的临时缓冲区。通过调用带有 `null_mut()` 输出指针的积分函数来查询大小(返回值为所需的 doubles 数量)。 +2. **`buf`** 是线程本地的输出缓冲区。大小由 `max_buffer_size()` 计算,保证 ≥ 任何单个 shell block 的输出大小。 +3. **`integral_block`** 将被调用时的 `out: &mut [F]` 通过 `as *mut f64`/`as *mut c_void` 传递给 C 函数 — 仅当 `F = f64` (sph/cart) 或 `F = Complex` (spinor) 时才是正确的。 +4. **副本函数** (`copy_f_*d_s*`) 使用 Fortran (列优先) 索引从 `buf` 复制到已打包的对称输出(对称性变体如 `s2ij` 使用特定的打包索引函数)。 + +--- + +## 7. 关键代码模式 + +### 7.1 线程本地缓冲区 (cint_fill_col_major.rs 等) + +```rust +let thread_init = || { + let cache = unsafe { aligned_uninitialized_vec::(cache_size) }; + let buf = unsafe { aligned_uninitialized_vec::(buffer_size) }; + (cache, buf) +}; + +iter_par.for_each_init(thread_init, |(cache, buf), indices| { + // cache: &mut Vec → &mut [f64] (自动解引用) + // buf: &mut Vec → &mut [F] + // 在迭代间作为临时空间重用 +}); +``` + +Rayon 的 `for_each_init` 为每个线程调用一次 `thread_init`,然后在所有迭代中重用该值。`(cache, buf)` 元组在 Rayon 工作线程结束时被丢弃。 + +### 7.2 共享可变输出 (所有 fill 模块) + +```rust +// out 是 &mut [F],由并行迭代器的所有线程共享 +let out = unsafe { cast_mut_slice(&*out) }; + +// cast_mut_slice 将 &[T] 转换为 &mut [T] +pub(crate) unsafe fn cast_mut_slice(slc: &[T]) -> &mut [T] { + let len = slc.len(); + let ptr = slc.as_ptr() as *mut T; + unsafe { std::slice::from_raw_parts_mut(ptr, len) } +} +``` + +**安全前提**: 每次迭代使用 `copy_f_*` 函数写入 `out` 的不重叠区域,使得共享可变性在实际上是安全的。 + +### 7.3 对齐分配 (util.rs:843) + +```rust +pub unsafe fn aligned_uninitialized_vec(size: usize) -> Vec { + const MIN_ALIGN: usize = 64; + const ALIGNMENT: usize = 64; // AVX-512 最小要求 + + if size < MIN_ALIGN { + unsafe { unaligned_uninitialized_vec(size) } // 标准 Vec 分配 + } else { + // 通过 alloc::alloc::alloc 分配 64 字节对齐的内存 + // 通过 Vec::from_raw_parts 包装 + // ⚠️ 已知 UB: 释放布局与分配布局不匹配 (见 §10) + } +} +``` + +### 7.4 列优先 (Fortran 顺序) 索引 + +```rust +pub(crate) fn get_f_index_3d(indices: &[usize; 3], shape: &[usize; 3]) -> usize { + indices[0] + shape[0] * (indices[1] + shape[1] * (indices[2])) +} +``` + +libcint 将积分写入 Fortran 顺序的缓冲区。所有 `get_f_index_*` 函数实现此布局。对称性变体(`get_f_index_3d_s2ij` 等)添加了下三角打包。 + +### 7.5 对称性打包 (S2ij 示例) + +```rust +// S2ij: 仅 μ ≥ ν 被存储和计算 +let nidx_ij = nidx_i * (nidx_i + 1) / 2; // 三角数 + +// 解包: 从扁平三角索引恢复 (i, j) +fn unravel_s2_indices(x: usize) -> [usize; 2] { + let j = (((x * 2 + 1) as f64).sqrt() - 0.5) as usize; + let i = x - j * (j + 1) / 2; + [i, j] +} +``` + +### 7.6 S8 跳过 (cint_fill_col_major.rs:583) + +```rust +iter_par.for_each_init(thread_init, |(cache, buf), ([idx_ij, idx_kl], _)| { + let [idx_i, idx_j] = unravel_s2_indices(idx_ij); + let [idx_k, idx_l] = unravel_s2_indices(idx_kl); + if idx_l < idx_j { + return; // 跳过冗余迭代 (l ≥ j 约束) + } + // ... +}); +``` + +S8 对称性执行冗余迭代但提前返回以避免重复工作。这比完全消除它们具有更好的 Rayon 并行化效果。 + +--- + +## 8. 构建系统 + +### build.rs + +```rust +fn build_ecp() { + cc::Build::new() + .file("src/cecp/f2c_dgemm.c") + .file("src/cecp/nr_ecp.c") + .file("src/cecp/nr_ecp_deriv.c") + .compile("cecp"); +} + +fn main() { + build_ecp(); + if std::env::var("CINT_DEV").unwrap_or_default() == "1" { + let cint_dir = std::env::var("CINT_DIR").unwrap(); + println!("cargo:rustc-link-search=native={}", cint_dir); + println!("cargo:rustc-link-lib=cint"); + } +} +``` + +- **始终**: 将 3 个 ECP C 文件编译为静态库 `cecp` +- **开发捷径**: `CINT_DEV=1` + `CINT_DIR=` 链接预构建的 `libcint` + +### Feature Flags + +| Feature | 效果 | +|---------|------| +| `with_f12` | 启用 F12 积分 | +| `with_4c1e` | 启用 4 分量 1 电子积分 | +| `build_from_source` | 从源码构建 libcint | +| `static` | 静态链接 | +| `qcint` | Q-Chem CINT 接口变体 | + +### 开发配置 + +```toml +[profile.dev] +opt-level = 3 # 调试构建需要优化以获得可接受的积分性能 +``` + +--- + +## 9. 测试 + +### 集成测试 (`tests/` — 11 个文件) + +| 类别 | 文件 | +|------|------| +| H2O def2-TZVP | `intor_h2o_tzvp.rs`, `intor_h2o_tzvp_row_major.rs` | +| Sb2Me4 (ECP) | `intor_sb2me4.rs` | +| 网格积分 | `intor_grids.rs` | +| 压力测试 | `stress_integral_c15_tzvp.rs` | +| GTO 测试 | `gto_sb2me4_pvtz.rs`, `gto_c10h22_qzvp.rs` | +| 验证 | `valid_integral_h2o_tzvp.rs`, `valid_ecp_sb2me4_tzvp.rs` | +| 特殊 | `special_na_ecpso.rs`, `special_change.rs` | + +### 测试分子 (src/test_mol.rs) + +三个测试分子,用于文档测试: +- `init_h2o_def2_tzvp()` — H2O, def2-TZVP 基组 +- `init_h2o_def2_jk()` — H2O, def2-universal-jkfit 辅助基组 +- `init_sb2me4_cc_pvtz()` — Sb2Me4 配合 ECP +- `init_c10h22_def2_qzvp()` — C10H22 (压力测试) + +### 运行测试 + +```bash +cd $REST_HOME/rest_libcint +cargo test +``` + +测试数值结果使用预计算参考值进行验证(通过 `FingerPrint` trait 的指纹计算)。 + +--- + +## 10. 已知问题与注意事项 + +### 10.1 `aligned_uninitialized_vec` 布局不匹配 (⚠️ 活动 Bug) + +**位置**: `src/util.rs:843-862` + +**问题**: 该函数分配内存时使用 `Layout { align: 64, size: padded }`,但通过 `Vec::from_raw_parts` 包装。当 `Vec` 被丢弃时,使用 `Layout { align: align_of::() }` (对于 f64 通常为 8) 进行释放。布局不匹配违反了 Rust 的 `GlobalAlloc` 契约。 + +**影响**: 在 Windows 上,分配器可能会对对齐分配使用特殊处理(例如,额外分配 + 指针调整,或 CRT 的 `_aligned_malloc`)。当使用不匹配的布局进行释放时,分配器可能无法恢复原始指针,导致堆损坏。在 Linux (glibc) 上,`free()` 忽略对齐信息,因此此问题无表现。 + +**修正方向**: 用一个自定义类型替换 `Vec` 的返回,该类型存储原始的分配 Layout 并实现自定义 `Drop` 以使用正确的布局。 + +### 10.2 调用的外部 C 代码需要 64 字节对齐 (AVX-512) + +代码注释说明需要 64 字节对齐 "AVX-512 最小要求"。缓冲区 (`cache` 和 `buf`) 通过 `aligned_uninitialized_vec` 分配以满足此要求。libcint 的 C 函数接收这些缓冲区作为临时空间。如果这些指针没有正确对齐,C 代码中的 AVX-512 `vmovaps` 指令将引发对齐错误 (SIGSEGV)。 + +### 10.3 `cast_mut_slice` UB 风险 + +```rust +let out = unsafe { cast_mut_slice(&*out) }; +``` + +在并行迭代中,这创建了从 `&[F]` 到 `&mut [F]` 的多个可变引用,违反了 Rust 的别名规则。在实践中是安全的,因为每次迭代写入不重叠的区域,但如果 `cgto_locs` 或 `out_shape` 计算错误,可能导致数据竞争。 + +### 10.4 set_len 在未初始化内存上执行 + +`unaligned_uninitialized_vec` 对未初始化的内存调用 `v.set_len(size)`。注释承认这是 UB,但也指出对于 `MaybeUninit` 类型不会发生。在实践中,libcint C 函数会在读取之前写入所有元素。 + +### 10.5 检查 shls_slice 后缺少 n_center 约束 + +在 `integral_s2kl_inplace` 和 `integral_s4_inplace` 中,`n_center` 被注释为必须为 4,并声称由 `check_shls_slice` 保证,但函数中没有对该假设的显式运行时断言。 + +### 10.6 S8 约束中的冗余迭代被跳过而非完全消除 + +S8 并行生成迭代所有 `(ij, kl)` 对但跳过那些 `l < j` 的对(以及其他约束)。这比消除非规范对具有更好的并行化效果,但增加了潜在的索引边界问题。 + +--- + +## 11. 扩展开发指南 + +### 添加新的积分器 + +1. 在 `src/ffi/cint_ffi.rs` 中添加 `extern "C"` 声明 (如果尚未由 bindgen 生成) +2. 在 `src/ffi/cint_wrapper.rs` 中添加 `impl_integrator!` 调用 +3. 在 `src/ffi/cint_wrapper.rs` 的 `get_cint_integrator()` 中添加查找条目 + +### 修改积分输出布局 + +1. 在相应的 `cint_fill_*.rs` 文件中修改并行填充逻辑 +2. 如果需要新的打包方案,在 `src/util.rs` 中添加新的 `get_f_index_*` + `copy_f_*` 函数 +3. 更新 `cgto_shape_*` 以计算正确的输出形状 + +### 代码约定 + +| 方面 | 约定 | +|------|------| +| 错误处理 | `CIntError` 及 `cint_error!` / `cint_raise!` 宏 | +| 并行 | 始终使用 Rayon + `for_each_init` 作为线程本地缓存 | +| 索引 | 列优先 (Fortran 顺序) 作为内部表示;行优先由单独的 `_row_major` 变体处理 | +| 不安全 | 每个不安全块都注释说明安全前置条件;所有 FFI 调用都是 `unsafe` | +| 命名 | libcint 风格: `n_center`, `cgto_i`, `shl_i`, `idx_ij` | -- Gitee From 467ef3655307932e5a08d042949ff8d11785e7f7 Mon Sep 17 00:00:00 2001 From: Shirong_Wang Date: Sun, 14 Jun 2026 22:12:31 +0800 Subject: [PATCH 2/2] update rest-libcint --- AGENTS.md | 4 +- skills/create-skill/SKILL.md | 116 ++++++ skills/rest-feedstock-guide/SKILL.md | 285 +++++++++++++ skills/rest-libcint-knowledge/SKILL.md | 528 ++++++++----------------- 4 files changed, 565 insertions(+), 368 deletions(-) create mode 100644 skills/create-skill/SKILL.md create mode 100644 skills/rest-feedstock-guide/SKILL.md diff --git a/AGENTS.md b/AGENTS.md index db7c60f..36216d3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,6 +68,8 @@ $REST_HOME/ | `develop-with-rstsr` | 任务涉及张量运算、矩阵操作、线性代数或使用 `rstsr` / `rest_tensors` crate 时必须加载。 | | `rest-doc-guide` | 任务涉及编写、修改、翻译文档(`rest_doc/` 目录)时必须加载。 | | `rest-regression-guide` | 任务涉及新增、更新或运行回归测试(`rest_regression/` 目录)时必须加载。 | -| `rest-libcint-knowledge` | 任务涉及 `rest_libcint/` crate 的代码修改、积分逻辑、内存分配、FFI 绑定、并行填充或架构理解时必须加载。 | +| `rest-libcint-knowledge` | 修改 `rest_libcint/` 或涉及大量 rest_libcint 调用时加载。 | +| `rest-feedstock-guide` | 任务涉及 Conda 打包、修改 recipe、管理依赖、调试 conda 构建或修改 `rest-feedstock/` 目录时必须加载。 | +| `create-skill` | 任务涉及创建、修改或设计 Agent Skill(`rest-dev-ai-agent/skills/` 目录)时必须加载。 | **步骤**:识别任务 → 加载匹配的 skill(s) → 按 skill 指导工作。若任务的多个方面匹配不同 skill,逐一加载。 diff --git a/skills/create-skill/SKILL.md b/skills/create-skill/SKILL.md new file mode 100644 index 0000000..c922d5f --- /dev/null +++ b/skills/create-skill/SKILL.md @@ -0,0 +1,116 @@ +--- +name: create-skill +description: 如何为 REST 项目创建新的 Agent Skill — 文件格式、存放位置、注册到 AGENTS.md 以及编写规范。 +--- + +# 创建 REST 项目 Skill + +> 本 Skill 教你如何为 REST 项目新增一个 Agent Skill。 + +## 1. 什么是 Skill + +Skill 是面向 AI agent 的领域知识文件(Markdown + YAML),agent 在遇到匹配任务时自动加载。Skill 帮助 agent 理解代码架构、开发流程和领域约定,无需在每个对话中重新解释。 + +**适合创建 Skill 的场景**: +- 某个 crate/模块的架构与代码模式需要 AI 快速理解 +- 反复出现但需要领域知识的开发任务(如添加测试、写文档、处理 FFI) +- 希望 AI 遵循特定的代码约定或工作流程 + +## 2. Skill 文件格式 + +### 必需结构 + +```markdown +--- +name: +description: <一句话描述 Skill 的作用> +--- + +# <中文标题> + +> 简短摘要 +``` + +### 完整示例 + +```markdown +--- +name: my-skill +description: 描述这个 Skill 解决什么问题,什么场景下加载。 +--- + +# 我的 Skill 标题 + +> 一行概述 + +## 1. 概述 +... +## 2. 核心内容 +... +``` + +**要求**: +- `name` 使用 kebab-case(小写 + 连字符),如 `rest-libcint-knowledge`、`create-skill` +- `description` 一句话说明用途与加载条件,会被写入系统提示的 skill 表 +- 正文使用 Markdown,支持表格、代码块、列表 + +## 3. 存放位置 + +本项目 Skill 统一放在: + +``` +rest-dev-ai-agent/skills// +``` + +每个 Skill 一个子目录,目录名与 `name` 字段一致,文件名为 `SKILL.md`。 + +## 4. 注册 Skill + +Skill 创建后,必须在 `AGENTS.md` 的 skill 表中注册,agent 才能按条件加载。 + +### 编辑 AGENTS.md + +在文件末尾的 skill 表中新增一行: + +```markdown +| `skill-name` | **触发条件描述** | +``` + +例如: + +```markdown +| `rest-libcint-knowledge` | 任务涉及 `rest_libcint/` crate 的代码修改、积分逻辑、内存分配、FFI 绑定、并行填充或架构理解时必须加载。 | +``` + +### 加载机制 + +Agent 启动时读取 `AGENTS.md` 中的 skill 表。当用户任务匹配触发条件时,agent 调用 `skill(name="skill-name")` 加载对应的 `SKILL.md`。Skill 内容被注入到当前对话上下文中作为指令。 + +**不需要在 AGENTS.md 中写 `file://` URI** — agent 会根据 skill 名称自动查找。 + +## 5. 编写规范 + +基于现有 Skill(`rest-libcint-knowledge`、`rest-regression-guide`、`rest-knowledge-graph` 等)总结: + +| 规范 | 说明 | +|------|------| +| 语言 | 中文为主(REST 项目约定)。技术术语保留英文 | +| 长度 | 控制在 200–400 行。过于冗长 agent 难以高效消费 | +| 结构 | 使用 `## N. 标题` 编号分节。每节聚焦一个主题 | +| 表格优先 | 对比信息(类型列表、文件对应关系等)优先用表格而非段落 | +| 代码块 | 精确引用源码中的签名/结构体,标注文件路径与行号 | +| 避免复杂图表 | 不用大型 ASCII 流程图。用有序列表(1. 2. 3.)或编号步骤替代 | +| 聚焦 AI 视角 | 只写 AI 开发时需要知道的信息,省略面向人类初学者的教程内容 | +| 与源码一致 | 引用行号、模块名、函数签名必须与当前源码匹配(写 Skill 前先读代码验证) | + +## 6. 检查清单 + +创建 Skill 后逐项确认: + +- [ ] `SKILL.md` 位于 `rest-dev-ai-agent/skills//SKILL.md` +- [ ] YAML frontmatter 包含 `name` 和 `description` +- [ ] `name` 使用 kebab-case,与目录名一致 +- [ ] 已在 `AGENTS.md` 中新增一行触发条件 +- [ ] 引用的源码路径和行号已核实 +- [ ] 无复杂 ASCII 图 +- [ ] 中文为主,长度控制在 200–400 行 diff --git a/skills/rest-feedstock-guide/SKILL.md b/skills/rest-feedstock-guide/SKILL.md new file mode 100644 index 0000000..6bd7657 --- /dev/null +++ b/skills/rest-feedstock-guide/SKILL.md @@ -0,0 +1,285 @@ +--- +name: rest-feedstock-guide +description: REST 程序 Conda 打包指南。涵盖 rattler-build recipe 维护、依赖管理、版本更新、跨平台构建(linux/osx/win)以及构建失败调试。 +--- + +# REST Conda 打包指南 + +## 1. 项目总览 + +`rest-feedstock` 是 REST 程序的 Conda 打包仓库。使用 **rattler-build**(通过 pixi 管理)构建三个 conda 包: + +| 包名 | Recipe 目录 | 内容 | +|------|------------|------| +| `rest` | `recipe_rattler/` | REST 主程序(二进制 + pyrest 库 + 基组) | +| `rest_regression` | `recipe_rest_reg/` | 回归测试二进制 | +| `rest-util` | `recipe_rest_util/` | Python 工具脚本 | + +构建产物输出到 `output/` 目录。 + +## 2. 目录结构 + +``` +rest-feedstock/ +├── pixi.toml # 项目配置: channels, platforms, tasks +├── recipe_rattler/ # rest 主包 recipe +│ ├── recipe.yaml # 包元数据 + 依赖 + 测试 +│ ├── build.sh # 构建脚本 (unix) +│ ├── build.bat # 构建脚本 (win) +│ ├── conda_build_config.yaml# 依赖版本 pinning +│ └── run_tests.sh # 回归测试脚本 +├── recipe_rest_reg/ # rest_regression 包 recipe +├── recipe_rest_util/ # rest-util 包 recipe +├── actions/ # CI 辅助 action +│ ├── cache-setup-conda/ # conda 缓存 +│ └── setup-rattle/ # rattler-build 安装 +├── scripts/ +│ └── analyze_changes.py # recipe 变更分析 +├── .github/workflows/ # CI 流水线 +│ ├── conda_rattler.yml # 主构建流水线 (rattler-build) +│ ├── conda.yml # 旧版 conda-build 流水线 +│ └── rest_reg.yml # 回归测试专用流水线 +├── output/ # 构建产物 (gitignored) +└── README.md +``` + +## 3. 构建系统 + +### pixi 入口 + +```toml +# pixi.toml +[project] +name = "rest-feedstock" +channels = ["conda-forge", "mokit", "restgroup"] +platforms = ["win-64", "linux-64", "osx-arm64"] + +[tasks] +build = "rattler-build build --recipe-dir recipe_rattler --output-dir output --skip-existing=all" + +[dependencies] +rattler-build = "*" +``` + +构建命令:`pixi run build`(自动激活 conda 环境并运行 rattler-build)。 + +### CI 流水线 (`conda_rattler.yml`) + +CI 构建所有平台和变体,使用 `actions/setup-rattle/` 安装 rattler-build,上传产物到 anaconda.org。 + +## 4. Recipe 结构 (`recipe_rattler/recipe.yaml`) + +Recipe 是 rattler-build 格式的 YAML 文件,非 conda-build 的 meta.yaml。 + +### 4.1 package 段 + +```yaml +package: + name: "rest" + version: "2026.1.0.1" +``` + +### 4.2 source 段 — 多 git 仓库 + +REST workspace 由 4 个独立 git 仓库组成,recipe 中分别指定: + +```yaml +source: + - git: https://gitee.com/restgroup/rest.git + rev: + target_directory: rest + - git: https://gitee.com/restgroup/rest_tensors.git + rev: + target_directory: rest_tensors + - git: https://gitee.com/restgroup/rest_libcint.git + rev: + target_directory: rest_libcint + - git: https://gitee.com/restgroup/rest_regression.git + rev: + target_directory: rest_regression +``` + +**更新版本流程**:更新各 `rev` 为最新 commit SHA → 更新 `version` → 重置 `build.number` 为 0 或递增。 + +### 4.3 build 段 + +```yaml +build: + number: 1 + files: + exclude: + - "**/__pycache__" + - "**/test-files" + script: + env: + REST_HOME: ${SRC_DIR} +``` + +- `number`: 同一版本下的构建迭代号 +- `script.env`: 环境变量注入到 build.sh + +### 4.4 requirements 段 + +三个依赖层级: + +| 层级 | 用途 | +|------|------| +| `build` | 构建时需要的工具(编译器、构建系统、Python)和链接所需的库 | +| `host` | 需要在 `$PREFIX` 中可用的库(与 build 基本对称) | +| `run` | 运行时依赖(自动从 host 继承,显式列出确保安装) | + +平台条件语法: + +```yaml +- if: osx + then: llvm-openmp # 仅在 macOS 添加 +- if: win + then: m2w64-sysroot_win-64 # 仅在 Windows 添加 +- if: not win + then: openmpi 4.* # 仅在非 Windows 添加 +``` + +### 4.5 compiler 宏 + +```yaml +- ${{ compiler('c') }} # → clang_osx-arm64 (macOS) / gcc (linux) / m2w64 (win) +- ${{ compiler('cxx') }} # → clangxx_osx-arm64 / gxx / m2w64 +- ${{ compiler('fortran') }} # → gfortran(所有平台) +- ${{ compiler('rust') }} # → rust + rust-std-$target +- ${{ stdlib('c') }} # → 系统 C 标准库 +``` + +### 4.6 tests 段 + +```yaml +tests: + - if: not win + then: + script: run_tests.sh + requirements: + run: + - restgroup::rest_regression + files: + source: + - rest_regression +``` + +## 5. 构建脚本 (`build.sh`) + +### 5.1 Workspace 组装 + +`build.sh` 首先生成临时 `Cargo.toml` 将 4 个源码目录组装为 workspace: + +```bash +cat > Cargo.toml << 'EOF' +[workspace] +resolver = "2" +members = ["rest_tensors", "rest_libcint", "rest", "rest_regression"] +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +EOF +``` + +### 5.2 平台路径 + +```bash +if [[ "$target_platform" == win-64 ]]; then + LIB_EXT="dll" + REST_EXT_DIR="${PREFIX}/Library/bin" # Windows: .dll 在 Library/bin +else + if [[ "$target_platform" == osx-* ]]; then + LIB_EXT="dylib" + else + LIB_EXT="so" + fi + REST_EXT_DIR="${PREFIX}/lib" +fi +``` + +### 5.3 MOKIT 桥接库 + +MOKIT 的 Fortran FFI 库需要在编译前复制到 `REST_EXT_DIR`: + +```bash +MOKIT_LIB="${BUILD_PREFIX}/lib/python${PY_VER}/site-packages/mokit/lib/librest2fch.${LIB_EXT}" +cp "${MOKIT_LIB}" "${REST_EXT_DIR}" +``` + +### 5.4 Cargo 构建 + +```bash +# macOS / Linux: +cargo install --path . --profile release --root ${PREFIX} + +# Windows: +cargo install --path . --profile release --target x86_64-pc-windows-gnu \ + --no-default-features --features "dftd3,dftd4" --root ${PREFIX} +``` + +### 5.5 macOS CFLAGS 处理 + +macOS 上 conda 的 `CFLAGS` 会绕过 cc crate 的默认警告标志(见 §7)。如需注入编译旗标,在 `build.sh` 中附加: + +```bash +if [[ "$target_platform" == osx-* ]]; then + export CFLAGS="${CFLAGS} -Wno-implicit-function-declaration" +fi +``` + +## 6. 依赖版本 Pinning + +`conda_build_config.yaml` 固定关键依赖版本: + +```yaml +c_stdlib_version: + - 11.0 # [osx] +fortran_compiler: + - gfortran # [win] +python: + - 3.11 +rust_compiler_version: + - "1.94" +simple-dftd3: + - 1.* +dftd4: + - 4.* +hdf5: + - 1.14.* +openmpi: + - 4.* +``` + +## 7. 已知问题与调试 + +### 7.1 CFLAGS 与 cc crate 的相互作用 + +conda 激活脚本始终设置 `CFLAGS`。cc crate (`rest_libcint/build.rs`) 检测到 `CFLAGS` 环境变量后,**跳过**默认的 `-Wall -Wextra`(假定 CFLAGS 已包含所需的警告旗标)。同时 `flag_if_supported()` 旗标在 CFLAGS 之前被处理,可能被 CFLAGS 的后续旗标覆盖。若需特定编译器旗标,直接在 `build.sh` 中修改 `CFLAGS`。 + +### 7.2 macOS gcc_osx-arm64 激活 + +`${{ compiler('fortran') }}` 引入 `gfortran_osx-arm64`,其依赖 `gcc_osx-arm64`(激活包)。gcc 激活脚本在 clang 激活前运行,设置初始 `CC` 和 `CFLAGS`。gcc 激活包版本变更可能影响最终的编译器旗标组合。调试时检查 CI 日志中 `activate-gcc_osx-arm64.sh` 和 `activate_clang_osx-arm64.sh` 的环境变量变更。 + + +## 8. 常见任务 + +### 更新版本 + +1. 在 `recipe_rattler/recipe.yaml` 更新各 source 段的 `rev` (commit SHA) +2. 更新 `package.version` +3. 重置 `build.number` 为 0(或递增若仅修复 recipe) +4. 同步更新 `recipe_rest_reg/recipe.yaml` 和 `recipe_rest_util/recipe.yaml` + +### 添加新依赖 + +1. 确认 `conda-forge` channel 中存在该包 +2. 若有版本约束,在 `conda_build_config.yaml` 添加 +3. 在 `recipe.yaml` 的 build/host/run 段添加 +4. 如需链接,在 `build.sh` 中设置 `LIBRARY_PATH` 或相关环境变量 + +### 调试构建失败 + +1. 查看 CI 日志中 `activate-*.sh` 的环境变量变更 +2. 对比不同平台/构建的 conda 包版本(`conda list` 输出) + diff --git a/skills/rest-libcint-knowledge/SKILL.md b/skills/rest-libcint-knowledge/SKILL.md index 8d01dc4..a470693 100644 --- a/skills/rest-libcint-knowledge/SKILL.md +++ b/skills/rest-libcint-knowledge/SKILL.md @@ -7,38 +7,18 @@ description: rest_libcint crate 的结构、架构与关键代码模式,面向 > Rust 封装 libcint C 库 — 量子化学 GTO 分子积分引擎 ---- - -## 目录 - -1. 项目总览 -2. 目录结构 -3. 模块依赖与架构 -4. 核心类型 -5. FFI 三层架构 -6. 调用流程:从 Rust API 到 C 积分函数 -7. 关键代码模式 -8. 构建系统 -9. 测试 -10. 已知问题与注意事项 - ---- - ## 1. 项目总览 | 属性 | 值 | |------|-----| | 路径 | `$REST_HOME/rest_libcint/` | -| 版本 | 0.1.1 | -| 语言 | 纯 Rust (封装 C 库 libcint) | -| 构建系统 | Cargo + `build.rs` (编译 C ECP 源码) | -| 并行模型 | Rayon (线程池) | -| 核心依赖 | `rstsr-common`, `rayon`, `itertools`, `num`, `serde`, `derive_builder` | +| 语言 | 纯 Rust, 封装 C 库 libcint | +| 构建 | Cargo + `build.rs` (编译 ECP C 源码) | +| 并行 | Rayon 线程池 | +| 核心依赖 | `rstsr-common`, `rayon`, `itertools`, `num`, `serde`, `derive_builder` 等 (共 9 个) | | 外部链接 | libcint (C 库), ECP C 源码 (编译进 crate) | -**职责**: 提供 Rust 风格 API 来调用 libcint 的 C 积分函数,处理并行调度、内存布局转换、对称性打包。 - ---- +**职责**: 提供 Rust 风格 API 调用 libcint C 积分函数,处理并行调度、内存布局转换、对称性打包。 ## 2. 目录结构 @@ -47,25 +27,25 @@ rest_libcint/ ├── build.rs # 编译 src/cecp/*.c,可选链接 libcint ├── Cargo.toml # 9 个依赖, 5 个 feature flags ├── src/ -│ ├── lib.rs # 22 个 pub mod + pub use prelude::* +│ ├── lib.rs # 15 个 pub mod + pub use prelude::* │ ├── prelude.rs # 公共与 crate-internal 重导出中枢 -│ ├── util.rs # 工具函数: aligned alloc, copy_f_*, get_f_index_* -│ ├── test_mol.rs # 测试用分子 + fingerprint 函数 +│ ├── util.rs # 工具: aligned alloc, copy_f_*, get_f_index_* +│ ├── test_mol.rs # 测试用分子 + fingerprint │ │ -│ ├── cint.rs # CInt 主结构体 + CIntOutput, CIntSymm 等 +│ ├── cint.rs # CInt 结构体 + CIntOutput, CIntSymm 等 │ ├── cint_crafter.rs # 积分构建/分派: integral_block(), max_cache_size() │ ├── cint_prop.rs # CInt 只读属性: ao_loc(), make_loc() │ ├── cint_initialize.rs # serde + Builder 构造 -│ ├── cint_result.rs # CIntError 错误类型 +│ ├── cint_result.rs # CIntError 错误类型 + cint_error!/cint_raise! 宏 │ ├── cint_change.rs # 数据修改: fakemol_for_charges 等 │ ├── cint_fill_col_major.rs # 列优先并行填充 (Fortran 顺序) │ ├── cint_fill_row_major.rs # 行优先并行填充 -│ ├── cint_fill_grids.rs # 网格 GTO 积分 +│ ├── cint_fill_grids.rs # 网格 GTO 积分填充 │ ├── cint_old.rs # 遗留 CINTR2CDATA API │ ├── cint_old_crafter.rs # 遗留积分构建 │ │ │ ├── ffi/ -│ │ ├── cint_ffi.rs # Tier 1: bindgen 生成的原始 C 绑定 +│ │ ├── cint_ffi.rs # Tier 1: bindgen 原始 C 绑定 │ │ ├── cecp_ffi.rs # Tier 1: ECP C 绑定 │ │ ├── wrapper_traits.rs # Tier 2: Integrator trait + impl_integrator! 宏 │ │ ├── cint_wrapper.rs # Tier 3: 100+ 积分器结构体 + 查找表 @@ -81,148 +61,89 @@ rest_libcint/ └── tests/ # 11 个集成测试 ``` ---- - -## 3. 模块依赖与架构 - -### 模块层级 - -``` - lib.rs (pub use prelude::*) - │ - ┌────────────────────────┼────────────────────────┐ - │ │ │ - cint.rs ffi/mod.rs gto/mod.rs - (CInt 定义) (FFI 三层) (GTO 网格求值) - │ │ │ - ┌──────────┼──────────┐ ┌──────┼──────┐ ┌─────────┼─────────┐ - │ │ │ │ │ │ │ │ │ -cint_crafter │ cint_fill_* cint_ffi cecp_ffi gto_ deriv_impl/ deriv_util -(积分构建) │ (并行填充) (C绑定) (ECP绑定) crafter (14文件) (SIMD) - │ - cint_prop cint_result - (属性查询) (错误类型) -``` - -### 关键关系 +## 3. 模块组织 -| 关系 | 说明 | +| 模块 | 职责 | |------|------| -| `cint.rs` → 6 个扩展模块 | `cint_crafter`, `cint_fill_*`, `cint_prop` 等通过 `impl CInt { ... }` 给 `CInt` 添加方法 | -| `cint_crafter` ↔ `ffi/wrapper_traits` | 通过 `Integrator` trait 分派到具体 C 函数 | -| `cint_fill_col_major.rs` 等 ↔ `util.rs` | 使用 `copy_f_*d_s*()` 从线程本地缓冲区复制到输出 | -| `gto/` ↔ `cint.rs` | `CInt` 通过 `gto_crafter.rs` 实现 GTO 网格求值方法 | -| `prelude.rs` ↔ 所有模块 | `pub(crate) use X::*` 使得常用类型在 crate 内全局可见 | - ---- +| `cint.rs` | 核心 `CInt` 结构体 + `integrate()` 方法实现 | +| `cint_crafter.rs` | 积分构建与分派,通过 `Integrator` trait 调用 C 函数;实现 `integral_block()`, `max_cache_size()` | +| `cint_fill_*.rs` (3 files) | Rayon 并行填充 (列优先/行优先/网格),线程本地 buffer 写入共享输出 | +| `cint_prop.rs` | `CInt` 属性查询: `ao_loc()`, `make_loc()` | +| `cint_result.rs` | `CIntError` 错误类型 + `cint_error!`/`cint_raise!` 宏 | +| `cint_initialize.rs` | Builder 模式构造 + serde 序列化 | +| `cint_change.rs` | `CInt` 数据修改: `with_rinv_origin`, `with_range_coulomb`, `fakemol_for_charges` 等 | +| `cint_old.rs` + `cint_old_crafter.rs` | 遗留 `CINTR2CDATA` API (独立于 CInt) | +| `ffi/` | FFI 三层封装 (详见 §5) | +| `gto/` | GTO 网格求值 (`eval_gto`, `eval_gto_spinor`)、导数实现 (14 文件)、SIMD 工具 | +| `util.rs` | 64 字节对齐分配、列优先索引计算、`copy_f_*` 函数 | +| `prelude.rs` | `pub use` 公共类型 + `pub(crate) use` crate 内全局可见项 | + +**关键依赖关系**: +- `cint.rs` 的 6 个扩展模块通过 `impl CInt { ... }` 添加方法 +- `cint_crafter.rs` ↔ `ffi/wrapper_traits.rs`: 通过 `Box` 分派到 C 函数 +- `cint_fill_*.rs` ↔ `util.rs`: 使用 `copy_f_*d_s*()` 从线程本地 buffer 复制到输出 +- `gto/` ↔ `cint.rs`: `CInt` 通过 `gto_crafter.rs` 实现 GTO 网格求值方法 ## 4. 核心类型 -### CInt (cint.rs:41) +### CInt (cint.rs:42) + +核心数据结构,映射 libcint C 库的 AOS (Array of Structs) 内存布局: ```rust pub struct CInt { - pub atm: Vec<[c_int; 6]>, // 原子插槽 (电荷, 坐标指针, 核模型, ...) - pub bas: Vec<[c_int; 8]>, // 壳层插槽 (原子, 角动量, nprim, nctr, kappa, ...) - pub ecpbas: Vec<[c_int; 8]>, // ECP 壳层插槽 - pub env: Vec, // 浮点变量 (坐标, 指数, 系数, 截断, omega, ...) - pub cint_type: CIntType, // Spheric / Cartesian / Spinor + pub atm: Vec<[c_int; 6]>, // 原子: 电荷, 坐标指针, 核模型, ... + pub bas: Vec<[c_int; 8]>, // 壳层: 原子, 角动量, nprim, nctr, 指数/系数指针 + pub ecpbas: Vec<[c_int; 8]>, // ECP 壳层 (含 RADI_POWER, SO_TYPE_OF) + pub env: Vec, // 浮点变量: 坐标, 指数, 系数, 截断, omega, ... + pub cint_type: CIntType, // Spheric(默认) / Cartesian / Spinor pub c_opt: Option>, } ``` -这是 libcint 的核心数据结构,映射 C 库的 AOS (Array of Structs) 内存布局。 - -### CIntType (cint.rs:144) - -```rust -pub enum CIntType { Spheric, Cartesian, Spinor } -``` - -决定输出类型: `Spheric`/`Cartesian` → `f64`,`Spinor` → `Complex`。 - -### CIntSymm (cint.rs:165) - -```rust -pub enum CIntSymm { S1, S2ij, S2kl, S4, S8 } -``` - -控制积分对称性利用(影响输出形状和打包方式): -- `S1`: 无对称性 (默认) -- `S2ij`: μ↔ν 对称 -- `S2kl`: κ↔λ 对称 (4 中心) -- `S4`: 前两 + 后两对称 (4 中心) -- `S8`: 完全 8 重对称 (4 中心) - -### CIntOutput (cint.rs:198) +### 其他关键枚举/结构体 -```rust -pub struct CIntOutput { - pub out: Option>, // None = 用户提供了外部缓冲区 - pub shape: Vec, // 输出形状 (列优先或行优先) -} -``` +| 类型 | 位置 | 说明 | +|------|------|------| +| `CIntKind` | `cint.rs:131` | 积分类别: `Int` (普通), `Ecp` (有效芯势) | +| `CIntType` | `cint.rs:145` | 输出类型: `Spheric`→f64, `Cartesian`→f64, `Spinor`→Complex\ | +| `CIntSymm` | `cint.rs:166` | 对称性: `S1`(无), `S2ij`(μ↔ν), `S2kl`(κ↔λ), `S4`, `S8` | +| `CIntOutput` | `cint.rs:199` | 输出: `out: Option>` + `shape: Vec`, 可 `.into()` 转元组 | +| `CIntOptimizer` | `cint.rs:188` | 优化器: `Int(*mut CINTOpt)` / `Ecp(*mut ECPOpt)`, `Drop` 调用 C 释放 | +| `CIntError` | `cint_result.rs:16` | `{ kind: CIntErrorBase, backtrace }`, 7 种变体, 通过 `cint_error!`/`cint_raise!` 构造 | -通过 `.into()` 转换为 `(Vec, Vec)`。 +### CIntSymm 细节 -### CIntOptimizer (cint.rs:187) +| 变体 | 含义 | 典型场景 | +|------|------|----------| +| `S1` | 无对称性 | 默认 | +| `S2ij` | μ↔ν 对称 | 3 中心积分 | +| `S2kl` | κ↔λ 对称 | 4 中心积分 | +| `S4` | S2ij + S2kl | 4 中心积分 | +| `S8` | 完全 8 重对称 | 4 中心 eri 积分 | -```rust -pub enum CIntOptimizer { - Int(*mut CINTOpt), // 通用积分优化器 - Ecp(*mut ECPOpt), // ECP 优化器 -} -``` +### CIntError 变体 -实现 `Drop` 时调用相应的 C 释放函数(`CINTdel_optimizer` 或 `ECPdel_optimizer`)。 +`IntegratorNotFound`, `IntegratorNotAvailable`, `RuntimeError`, `InvalidValue`, `UninitializedFieldError`, `ParseError`, `Miscellaneous` — 均包含 backtrace。 -### CIntError (cint_result.rs) +## 5. FFI 三层封装 -错误变体: `IntegratorNotFound`, `IntegratorNotAvailable`, `RuntimeError`, `InvalidValue`, `UninitializedFieldError`, `ParseError`, `Miscellaneous`。包含 backtrace。 +**三层结构**: ---- +1. **Tier 1 — 原始绑定** (`cint_ffi.rs`, `cecp_ffi.rs`): bindgen 生成的 `extern "C"` 函数声明,直接映射 C 库 API。 -## 5. FFI 三层架构 +2. **Tier 2 — 抽象 trait** (`wrapper_traits.rs`): 定义 `Integrator` trait 统一接口,提供 `impl_integrator!` 宏生成零大小结构体 + trait 实现。 -``` - ┌─────────────────────────┐ - │ cint_crafter.rs │ (积分构建/分派) - │ integral_block() │ - └────────────┬────────────┘ - │ 调用 trait 方法 - ▼ - ┌─────────────────────────┐ - Tier 2 ─────────── │ Integrator trait │ (wrapper_traits.rs) - │ + impl_integrator! 宏 │ - └────────────┬────────────┘ - │ 生成零大小结构体 - ▼ - ┌────────────────┐ ┌──────────────────┐ - │ cint_wrapper.rs│ │ cecp_wrapper.rs │ Tier 3 - │ 100+ 结构体 │ │ ECP 结构体 │ 薄胶水层 - │ get_cint_...() │ │ get_ecp_...() │ - └───────┬────────┘ └────────┬─────────┘ - │ │ - ▼ ▼ - ┌────────────────┐ ┌──────────────────┐ - │ cint_ffi.rs │ │ cecp_ffi.rs │ Tier 1 - │ extern "C" │ │ extern "C" │ 原始绑定 - │ (bindgen) │ │ (bindgen) │ - └───────┬────────┘ └────────┬─────────┘ - │ │ - ▼ ▼ - ┌───────────────────────────────────────┐ - │ C 库 libcint + libcecp │ - └───────────────────────────────────────┘ -``` +3. **Tier 3 — 胶水层** (`cint_wrapper.rs`, `cecp_wrapper.rs`): 100+ 积分器结构体(每个对应一种 C 积分函数)+ `get_cint_integrator(name)` / `get_ecp_integrator(name)` 查找函数。 ### Integrator trait 核心方法 ```rust pub trait Integrator: Send + Sync { - unsafe fn optimizer(&self, opt: *mut *mut c_void, atm: *const c_int, ...); + unsafe fn optimizer(&self, opt: *mut *mut c_void, atm: *const c_int, natm: c_int, + bas: *const c_int, nbas: c_int, env: *const f64); unsafe fn integral_sph(&self, out: *mut f64, dims: *const c_int, shls: *const c_int, ...) -> c_int; - unsafe fn integral_cart(&self, ...) -> c_int; + unsafe fn integral_cart(&self, out: *mut f64, ...) -> c_int; unsafe fn integral_spinor(&self, out: *mut c_void, ...) -> c_int; fn is_sph_available(&self) -> bool; fn is_cart_available(&self) -> bool; @@ -234,178 +155,113 @@ pub trait Integrator: Send + Sync { fn integrator_category(&self) -> &'static str; fn name(&self) -> &'static str; fn kind(&self) -> CIntKind; + fn as_any(&self) -> &dyn Any; } ``` -### impl_integrator! 宏示例 +### impl_integrator! 宏 ```rust impl_integrator!( - int2e, // 结构体名称 - int2e_optimizer, // C 优化器函数 - int2e_sph, // C 球形积分函数 - int2e_cart, // C 笛卡尔积分函数 - int2e_spinor, // C 旋量积分函数 - true, // sph 可用 - true, // cart 可用 - true, // spinor 可用 - 1, // n_comp - 1, // n_spinor_comp - 4, // n_center - vec![0,0,0,0,0,1,1,1], // ng (导数阶数) - "int2e", // 类别 - "int2e", // 名称 - CIntKind::Int // 种别 + int2e, // 结构体名 (零大小) + int2e_optimizer, // C 优化器函数 + int2e_sph, int2e_cart, // C sph/cart 积分函数 + int2e_spinor, // C spinor 积分函数 + true, true, true, // sph/cart/spinor 可用 + 1, 1, 4, // n_comp, n_spinor_comp, n_center + vec![0,0,0,0,0,1,1,1], // ng (导数阶数) + "int2e", "int2e", // 类别, 名称 + CIntKind::Int // 种别 ); ``` -宏展开生成一个零大小结构体和 `Integrator` trait 实现。`integral_sph` 等方法直接调用对应的 C FFI 函数。 +展开生成 `pub struct int2e` + `impl Integrator for int2e` + `impl Default`。`integral_sph` 等方法直接调用对应 C FFI 函数。 ---- +## 6. 积分调用流程 -## 6. 调用流程 +### 主 API 入口 -以 `cint.integrate("int2e", "s1", None)` 为例: +PR #3 将普通积分、ECP 积分、spinor 积分统一为一致的 API 家族。每个函数均有 `_f` (fallible) 和 `_spinor` 变体: -``` -CInt::integrate("int2e", "s1", None) cint.rs - │ - ├─► integrate_with_args_inner() cint_crafter.rs:145 - │ - ├─► 查找积分器: get_cint_integrator("int2e") - │ → Box (int2e 结构体) - │ - ├─► 创建优化器: integrator.optimizer() - │ → 调用 C 函数 int2e_optimizer(...) - │ - ├─► 并行填充: integral_s1_inplace() cint_fill_col_major.rs - │ │ - │ ├─► 计算缓冲区大小: - │ │ max_cache_size() = 查询 libcint (null 输出指针) - │ │ max_buffer_size() = n_comp × ∏ max(cgto_i) - │ │ - │ ├─► 使用 Rayon 并行迭代 shell pairs - │ │ 每个线程拥有 (cache: Vec, buf: Vec) - │ │ - │ ├─► 对每个 shell pair: - │ │ │ - │ │ ├─►integral_block(out=buf, shls, cache) cint_crafter.rs:1195 - │ │ │ ├─ 根据 CIntType 分派: - │ │ │ │ Spheric → integrator.integral_sph(out as *mut f64, ...) - │ │ │ │ Cartesian→ integrator.integral_cart(...) - │ │ │ │ Spinor → integrator.integral_spinor(out as *mut c_void, ...) - │ │ │ └─ C 函数将积分值写入 buf - │ │ │ - │ │ └─► copy_f_3d_s1() / copy_f_4d_s1() 等 - │ │ 将 buf 中结果复制到 output 正确位置 (列优先索引) - │ │ - │ └─► Rayon 完成后,返回 Ok(()) - │ - └─► 返回 CIntOutput { out: Some(out_vec), shape } -``` +| 函数 | 返回类型 | 说明 | +|------|----------|------| +| `integrate(intor, aosym, shls_slice)` | `CIntOutput` | 主积分驱动,列优先。统一处理普通/ECP/含 grids 的 1e 积分 | +| `integrate_spinor(...)` | `CIntOutput>` | Spinor 积分,列优先 | +| `integrate_row_major(...)` | `CIntOutput` | 行优先变体,形状与 PySCF 一致 | +| `integrate_cross(intor, mols, ...)` | `CIntOutput` | 跨分子积分 (**静态方法**),多 `CInt` 实例间计算(如 dimers) | +| `integrate_cross_spinor(...)` | `CIntOutput>` | 跨分子 spinor 积分 | +| `integrate_with_args(args)` | `CIntOutput` | 高级积分,Builder (`IntegrateArgs`) 支持自定义输出 buffer、网格 | +| `integrate_cross_with_args(args)` | `CIntOutput` | 跨分子高级积分 (Builder: `IntorCrossArgs`) | +| `eval_gto(eval_name, coord)` | `CIntOutput` | GTO 网格求值,列优先 shape `(ngrid, nao, ncomp)` | +| `eval_gto_spinor(eval_name, coord)` | `CIntOutput>` | Spinor GTO 网格求值 | -### 关键要点 +### 临时修改分子属性 -1. **`cache`** 是 libcint 的临时缓冲区。通过调用带有 `null_mut()` 输出指针的积分函数来查询大小(返回值为所需的 doubles 数量)。 -2. **`buf`** 是线程本地的输出缓冲区。大小由 `max_buffer_size()` 计算,保证 ≥ 任何单个 shell block 的输出大小。 -3. **`integral_block`** 将被调用时的 `out: &mut [F]` 通过 `as *mut f64`/`as *mut c_void` 传递给 C 函数 — 仅当 `F = f64` (sph/cart) 或 `F = Complex` (spinor) 时才是正确的。 -4. **副本函数** (`copy_f_*d_s*`) 使用 Fortran (列优先) 索引从 `buf` 复制到已打包的对称输出(对称性变体如 `s2ij` 使用特定的打包索引函数)。 +`cint_change.rs` 提供 `with_*` 模式 — 临时修改 `CInt` 字段,在闭包内执行操作后自动恢复原始值: ---- +| 函数 | 用途 | +|------|------| +| `with_rinv_origin(origin, func)` | 临时设置 $1/r$ 原点 | +| `with_rinv_origin_atom(atm_id, func)` | 临时指认原子为 $1/r$ 原点 (ECP 导数) | +| `with_range_coulomb(range, func)` | 临时设置 range-separated Coulomb ω 参数 | +| `fakemol_for_charges(atm_id, charges, func)` | 临时修改原子电荷 | -## 7. 关键代码模式 +### 调用流程剖析 -### 7.1 线程本地缓冲区 (cint_fill_col_major.rs 等) +以 `cint.integrate("int2e", "s1", None)` 为例: -```rust -let thread_init = || { - let cache = unsafe { aligned_uninitialized_vec::(cache_size) }; - let buf = unsafe { aligned_uninitialized_vec::(buffer_size) }; - (cache, buf) -}; - -iter_par.for_each_init(thread_init, |(cache, buf), indices| { - // cache: &mut Vec → &mut [f64] (自动解引用) - // buf: &mut Vec → &mut [F] - // 在迭代间作为临时空间重用 -}); -``` +1. **查找积分器**: `get_cint_integrator("int2e")` → `Box` (返回 `int2e` 结构体) -Rayon 的 `for_each_init` 为每个线程调用一次 `thread_init`,然后在所有迭代中重用该值。`(cache, buf)` 元组在 Rayon 工作线程结束时被丢弃。 +2. **创建优化器** (`cint_crafter.rs:33`): `integrator.optimizer()` → 调用 C 函数 `int2e_optimizer(...)`,返回 `CIntOptimizer` -### 7.2 共享可变输出 (所有 fill 模块) +3. **计算缓冲区**: `max_cache_size()` 通过 null 输出指针查询 libcint 所需 doubles 数;`max_buffer_size()` = `n_comp × ∏ max(cgto_i)` -```rust -// out 是 &mut [F],由并行迭代器的所有线程共享 -let out = unsafe { cast_mut_slice(&*out) }; +4. **并行填充** (`cint_fill_col_major.rs`): Rayon `for_each_init` 迭代 shell pairs,每个线程持有 `(cache: Vec, buf: Vec)` 复用 -// cast_mut_slice 将 &[T] 转换为 &mut [T] -pub(crate) unsafe fn cast_mut_slice(slc: &[T]) -> &mut [T] { - let len = slc.len(); - let ptr = slc.as_ptr() as *mut T; - unsafe { std::slice::from_raw_parts_mut(ptr, len) } -} -``` +5. **单 block 计算** (`cint_crafter.rs:1195`): `integral_block(out=buf, shls, cache)` → 根据 `CIntType` 分派: + - `Spheric` → `integrator.integral_sph(out as *mut f64, ...)` + - `Cartesian` → `integrator.integral_cart(...)` + - `Spinor` → `integrator.integral_spinor(out as *mut c_void, ...)` -**安全前提**: 每次迭代使用 `copy_f_*` 函数写入 `out` 的不重叠区域,使得共享可变性在实际上是安全的。 +6. **复制到输出**: `copy_f_3d_s1()` / `copy_f_4d_s1()` 等按列优先 (Fortran) 索引将 `buf` 复制到共享 `output` -### 7.3 对齐分配 (util.rs:843) +7. **返回** `CIntOutput { out: Some(vec), shape }` -```rust -pub unsafe fn aligned_uninitialized_vec(size: usize) -> Vec { - const MIN_ALIGN: usize = 64; - const ALIGNMENT: usize = 64; // AVX-512 最小要求 - - if size < MIN_ALIGN { - unsafe { unaligned_uninitialized_vec(size) } // 标准 Vec 分配 - } else { - // 通过 alloc::alloc::alloc 分配 64 字节对齐的内存 - // 通过 Vec::from_raw_parts 包装 - // ⚠️ 已知 UB: 释放布局与分配布局不匹配 (见 §10) - } -} -``` +**关键约定**: cache 为 libcint 临时空间;buf 为线程本地输出;列优先为内部表示,行优先由 `_row_major` 变体处理。 + +## 7. 关键代码模式 -### 7.4 列优先 (Fortran 顺序) 索引 +### 线程本地缓冲区 (所有 fill 模块) ```rust -pub(crate) fn get_f_index_3d(indices: &[usize; 3], shape: &[usize; 3]) -> usize { - indices[0] + shape[0] * (indices[1] + shape[1] * (indices[2])) -} +let thread_init = || (aligned_uninitialized_vec::(cache_size), aligned_uninitialized_vec::(buffer_size)); +iter_par.for_each_init(thread_init, |(cache, buf), indices| { /* 迭代间复用 */ }); ``` -libcint 将积分写入 Fortran 顺序的缓冲区。所有 `get_f_index_*` 函数实现此布局。对称性变体(`get_f_index_3d_s2ij` 等)添加了下三角打包。 - -### 7.5 对称性打包 (S2ij 示例) +### 共享可变输出 (cast_mut_slice) ```rust -// S2ij: 仅 μ ≥ ν 被存储和计算 -let nidx_ij = nidx_i * (nidx_i + 1) / 2; // 三角数 - -// 解包: 从扁平三角索引恢复 (i, j) -fn unravel_s2_indices(x: usize) -> [usize; 2] { - let j = (((x * 2 + 1) as f64).sqrt() - 0.5) as usize; - let i = x - j * (j + 1) / 2; - [i, j] -} +let out = unsafe { cast_mut_slice(&*out) }; ``` +从 `&[F]` 转为 `&mut [F]`,所有线程共享。安全前提: 各迭代写入不重叠区域。 + +### 对齐分配 (util.rs:843) -### 7.6 S8 跳过 (cint_fill_col_major.rs:583) +`aligned_uninitialized_vec` 分配 **64 字节对齐**内存 (AVX-512 最低要求)。**已知 UB**: 分配布局 (align=64) 与释放布局 (align=align_of::\()) 不匹配 (见 §10)。 + +### 列优先索引 (util.rs) ```rust -iter_par.for_each_init(thread_init, |(cache, buf), ([idx_ij, idx_kl], _)| { - let [idx_i, idx_j] = unravel_s2_indices(idx_ij); - let [idx_k, idx_l] = unravel_s2_indices(idx_kl); - if idx_l < idx_j { - return; // 跳过冗余迭代 (l ≥ j 约束) - } - // ... -}); +fn get_f_index_3d(i: &[usize; 3], s: &[usize; 3]) -> usize { + i[0] + s[0] * (i[1] + s[1] * i[2]) +} ``` +libcint 按 Fortran 顺序写积分。对称性变体 (`get_f_index_3d_s2ij` 等) 添加下三角打包。 -S8 对称性执行冗余迭代但提前返回以避免重复工作。这比完全消除它们具有更好的 Rayon 并行化效果。 +### 对称性打包 ---- +- **S2ij**: 仅存储 μ ≥ ν,三角数 `n*(n+1)/2`,`unravel_s2_indices()` 解包 +- **S8**: 生成所有 `(ij, kl)` 但跳过 `l < j` 时冗余迭代 (更好的 Rayon 并行化) ## 8. 构建系统 @@ -414,25 +270,18 @@ S8 对称性执行冗余迭代但提前返回以避免重复工作。这比完 ```rust fn build_ecp() { cc::Build::new() - .file("src/cecp/f2c_dgemm.c") - .file("src/cecp/nr_ecp.c") - .file("src/cecp/nr_ecp_deriv.c") + .file("src/cecp/f2c_dgemm.c").file("src/cecp/nr_ecp.c").file("src/cecp/nr_ecp_deriv.c") .compile("cecp"); } - fn main() { - build_ecp(); - if std::env::var("CINT_DEV").unwrap_or_default() == "1" { - let cint_dir = std::env::var("CINT_DIR").unwrap(); - println!("cargo:rustc-link-search=native={}", cint_dir); + build_ecp(); // 始终编译 ECP C 源码 + if env::var("CINT_DEV").unwrap_or_default() == "1" { // 开发捷径: 链接预构建 libcint + println!("cargo:rustc-link-search=native={}", env::var("CINT_DIR").unwrap()); println!("cargo:rustc-link-lib=cint"); } } ``` -- **始终**: 将 3 个 ECP C 文件编译为静态库 `cecp` -- **开发捷径**: `CINT_DEV=1` + `CINT_DIR=` 链接预构建的 `libcint` - ### Feature Flags | Feature | 效果 | @@ -441,108 +290,53 @@ fn main() { | `with_4c1e` | 启用 4 分量 1 电子积分 | | `build_from_source` | 从源码构建 libcint | | `static` | 静态链接 | -| `qcint` | Q-Chem CINT 接口变体 | +| `qcint` | QCINT 接口变体 | -### 开发配置 - -```toml -[profile.dev] -opt-level = 3 # 调试构建需要优化以获得可接受的积分性能 -``` - ---- +### 开发配置: `[profile.dev] opt-level = 3` — debug 构建需优化以获可接受的积分性能。 ## 9. 测试 -### 集成测试 (`tests/` — 11 个文件) +### 集成测试 (tests/ — 11 个文件) -| 类别 | 文件 | -|------|------| -| H2O def2-TZVP | `intor_h2o_tzvp.rs`, `intor_h2o_tzvp_row_major.rs` | -| Sb2Me4 (ECP) | `intor_sb2me4.rs` | -| 网格积分 | `intor_grids.rs` | -| 压力测试 | `stress_integral_c15_tzvp.rs` | -| GTO 测试 | `gto_sb2me4_pvtz.rs`, `gto_c10h22_qzvp.rs` | -| 验证 | `valid_integral_h2o_tzvp.rs`, `valid_ecp_sb2me4_tzvp.rs` | -| 特殊 | `special_na_ecpso.rs`, `special_change.rs` | - -### 测试分子 (src/test_mol.rs) - -三个测试分子,用于文档测试: -- `init_h2o_def2_tzvp()` — H2O, def2-TZVP 基组 -- `init_h2o_def2_jk()` — H2O, def2-universal-jkfit 辅助基组 -- `init_sb2me4_cc_pvtz()` — Sb2Me4 配合 ECP +测试使用 4 个测试分子 (定义于 `src/test_mol.rs`, 通过 prelude 导出): +- `init_h2o_def2_tzvp()` — H2O, def2-TZVP +- `init_h2o_def2_jk()` — H2O, def2-universal-jkfit +- `init_sb2me4_cc_pvtz()` — Sb2Me4 + ECP - `init_c10h22_def2_qzvp()` — C10H22 (压力测试) -### 运行测试 +数值验证通过预计算参考值 + `FingerPrint` trait 指纹计算。 ```bash -cd $REST_HOME/rest_libcint -cargo test +cd $REST_HOME/rest_libcint && cargo test ``` -测试数值结果使用预计算参考值进行验证(通过 `FingerPrint` trait 的指纹计算)。 - ---- - -## 10. 已知问题与注意事项 - -### 10.1 `aligned_uninitialized_vec` 布局不匹配 (⚠️ 活动 Bug) +## 10. 已知问题 -**位置**: `src/util.rs:843-862` +### aligned_uninitialized_vec 布局不匹配 (⚠️ 活动 Bug) -**问题**: 该函数分配内存时使用 `Layout { align: 64, size: padded }`,但通过 `Vec::from_raw_parts` 包装。当 `Vec` 被丢弃时,使用 `Layout { align: align_of::() }` (对于 f64 通常为 8) 进行释放。布局不匹配违反了 Rust 的 `GlobalAlloc` 契约。 +`src/util.rs:843` — 分配时 `Layout { align: 64 }`,释放时 `Layout { align: align_of::() }` (f64 为 8)。Windows 上可能导致堆损坏 (分配器特殊对齐处理);Linux glibc 上 `free()` 忽略对齐故无表现。 -**影响**: 在 Windows 上,分配器可能会对对齐分配使用特殊处理(例如,额外分配 + 指针调整,或 CRT 的 `_aligned_malloc`)。当使用不匹配的布局进行释放时,分配器可能无法恢复原始指针,导致堆损坏。在 Linux (glibc) 上,`free()` 忽略对齐信息,因此此问题无表现。 - -**修正方向**: 用一个自定义类型替换 `Vec` 的返回,该类型存储原始的分配 Layout 并实现自定义 `Drop` 以使用正确的布局。 - -### 10.2 调用的外部 C 代码需要 64 字节对齐 (AVX-512) - -代码注释说明需要 64 字节对齐 "AVX-512 最小要求"。缓冲区 (`cache` 和 `buf`) 通过 `aligned_uninitialized_vec` 分配以满足此要求。libcint 的 C 函数接收这些缓冲区作为临时空间。如果这些指针没有正确对齐,C 代码中的 AVX-512 `vmovaps` 指令将引发对齐错误 (SIGSEGV)。 - -### 10.3 `cast_mut_slice` UB 风险 - -```rust -let out = unsafe { cast_mut_slice(&*out) }; -``` - -在并行迭代中,这创建了从 `&[F]` 到 `&mut [F]` 的多个可变引用,违反了 Rust 的别名规则。在实践中是安全的,因为每次迭代写入不重叠的区域,但如果 `cgto_locs` 或 `out_shape` 计算错误,可能导致数据竞争。 - -### 10.4 set_len 在未初始化内存上执行 - -`unaligned_uninitialized_vec` 对未初始化的内存调用 `v.set_len(size)`。注释承认这是 UB,但也指出对于 `MaybeUninit` 类型不会发生。在实践中,libcint C 函数会在读取之前写入所有元素。 - -### 10.5 检查 shls_slice 后缺少 n_center 约束 - -在 `integral_s2kl_inplace` 和 `integral_s4_inplace` 中,`n_center` 被注释为必须为 4,并声称由 `check_shls_slice` 保证,但函数中没有对该假设的显式运行时断言。 - -### 10.6 S8 约束中的冗余迭代被跳过而非完全消除 - -S8 并行生成迭代所有 `(ij, kl)` 对但跳过那些 `l < j` 的对(以及其他约束)。这比消除非规范对具有更好的并行化效果,但增加了潜在的索引边界问题。 - ---- ## 11. 扩展开发指南 -### 添加新的积分器 +### 添加新积分器 -1. 在 `src/ffi/cint_ffi.rs` 中添加 `extern "C"` 声明 (如果尚未由 bindgen 生成) -2. 在 `src/ffi/cint_wrapper.rs` 中添加 `impl_integrator!` 调用 -3. 在 `src/ffi/cint_wrapper.rs` 的 `get_cint_integrator()` 中添加查找条目 +1. 在 `src/ffi/cint_ffi.rs` 添加 `extern "C"` 声明 (如 bindgen 未生成) +2. 在 `src/ffi/cint_wrapper.rs` 添加 `impl_integrator!` 调用 +3. 在 `get_cint_integrator()` 中添加查找条目 ### 修改积分输出布局 -1. 在相应的 `cint_fill_*.rs` 文件中修改并行填充逻辑 -2. 如果需要新的打包方案,在 `src/util.rs` 中添加新的 `get_f_index_*` + `copy_f_*` 函数 -3. 更新 `cgto_shape_*` 以计算正确的输出形状 +1. 修改对应 `cint_fill_*.rs` 中的并行填充逻辑 +2. 在 `src/util.rs` 添加新的 `get_f_index_*` + `copy_f_*` 函数 (如有新打包方案) +3. 更新对应 `cgto_shape_*` 计算正确输出形状 ### 代码约定 | 方面 | 约定 | |------|------| -| 错误处理 | `CIntError` 及 `cint_error!` / `cint_raise!` 宏 | -| 并行 | 始终使用 Rayon + `for_each_init` 作为线程本地缓存 | -| 索引 | 列优先 (Fortran 顺序) 作为内部表示;行优先由单独的 `_row_major` 变体处理 | -| 不安全 | 每个不安全块都注释说明安全前置条件;所有 FFI 调用都是 `unsafe` | +| 错误处理 | `CIntError` + `cint_error!` / `cint_raise!` 宏 | +| 并行 | Rayon `for_each_init` + 线程本地缓存 | +| 索引 | 列优先 (Fortran) 内部表示;行优先由 `_row_major` 变体处理 | +| 不安全 | 所有 `unsafe` 块注释安全前置条件;FFI 调用均为 `unsafe` | | 命名 | libcint 风格: `n_center`, `cgto_i`, `shl_i`, `idx_ij` | -- Gitee