diff --git a/CMakeLists.txt b/CMakeLists.txt index d680079..dbbbb6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.16) # Initialize the CMake project. project(Trexio - VERSION 2.4.0 + VERSION 2.4.2 DESCRIPTION "TREX I/O library" LANGUAGES C Fortran ) diff --git a/ChangeLog b/ChangeLog index c5b9e36..abbe095 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,9 @@ CHANGES - Added state/energy - Made state/id an index instead of an int +- Added JSON configuration data as a C variable in trexio.h +- Added JSON configuration file in tar.gz release +- Added Rust interface 2.3 --- diff --git a/Makefile.am b/Makefile.am index f64ff11..cfc6217 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,10 +34,9 @@ ACLOCAL_AMFLAGS = -I m4 CLEANFILES = trexio.mod +BUILT_SOURCES = trex.json if HAVE_FORTRAN -BUILT_SOURCES = trexio.mod -else -BUILT_SOURCES = +BUILT_SOURCES += trexio.mod endif EXTRA_DIST = .git_hash @@ -87,14 +86,63 @@ src_libtrexio_la_SOURCES = $(trexio_h) $(SOURCES) # Include CMake-related files in the distribution. EXTRA_DIST += CMakeLists.txt \ + trex.json \ src/CMakeLists.txt \ tests/CMakeLists.txt \ + tests/test_macros.h \ cmake/cmake_uninstall.cmake.in \ cmake/FindTREXIO.cmake # =============== TESTS =============== # +TEST_FILES = \ + tests/delete_group.c \ + tests/delete_group_hdf5.c \ + tests/delete_group_text.c \ + tests/io_all.c \ + tests/io_determinant.c \ + tests/io_determinant_hdf5.c \ + tests/io_determinant_text.c \ + tests/io_dset_float.c \ + tests/io_dset_float_hdf5.c \ + tests/io_dset_float_text.c \ + tests/io_dset_int.c \ + tests/io_dset_int_hdf5.c \ + tests/io_dset_int_text.c \ + tests/io_dset_sparse.c \ + tests/io_dset_sparse_hdf5.c \ + tests/io_dset_sparse_text.c \ + tests/io_dset_str.c \ + tests/io_dset_str_hdf5.c \ + tests/io_dset_str_text.c \ + tests/io_jastrow.c \ + tests/io_jastrow_hdf5.c \ + tests/io_jastrow_text.c \ + tests/io_num.c \ + tests/io_num_hdf5.c \ + tests/io_num_text.c \ + tests/io_safe_dset_float.c \ + tests/io_safe_dset_float_hdf5.c \ + tests/io_safe_dset_float_text.c \ + tests/io_str.c \ + tests/io_str_hdf5.c \ + tests/io_str_text.c \ + tests/open.c \ + tests/open_hdf5.c \ + tests/open_text.c \ + tests/overwrite_all.c \ + tests/overwrite_all_hdf5.c \ + tests/overwrite_all_text.c \ + tests/pre_close.c \ + tests/template_hdf5.c \ + tests/template_text.c \ + tests/test_f.f90 \ + tests/test_macros.h \ + tests/trexio_f.f90 + +EXTRA_DIST += $(TEST_FILES) + TESTS_C = \ tests/open_text \ tests/io_num_text \ @@ -221,6 +269,8 @@ src/trexio.c: $(trexio_h) $(trexio_h): $(ORG_FILES) $(GENERATOR_FILES) cd $(srcdir)/tools && ./build_trexio.sh +trex.json: $(trexio_h) + $(htmlizer): $(ORG_FILES) $(srcdir)/src/README.org touch $(htmlizer) cd $(srcdir)/tools && ./build_doc.sh diff --git a/README.md b/README.md index 4073fd8..74ba86f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![build](https://github.com/TREX-CoE/trexio/actions/workflows/actions.yml/badge.svg)](https://github.com/TREX-CoE/trexio/actions/workflows/actions.yml) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/TREX-CoE/trexio) -TREXIO is an open-source file format and library developed for the storage and manipulation of data produced by quantum chemistry calculations. It is designed with the goal of providing a reliable and efficient method of storing and exchanging wave function parameters and matrix elements, making it an important tool for researchers in the field of quantum chemistry. In this work, we present an overview of the TREXIO file format and library. The library consists of a front-end implemented in the C programming language and two different back-ends: a text back-end and a binary back-end utilizing the HDF5 library which enables fast read and write operations. It is compatible with a variety of platforms and has interfaces for the Fortran, Python, and OCaml programming languages. In addition, a suite of tools has been developed to facilitate the use of the TREXIO format and library, including converters for popular quantum chemistry codes and utilities for validating and manipulating data stored in TREXIO files. The simplicity, versatility, and ease of use of TREXIO make it a valuable resource for researchers working with quantum chemistry data. +TREXIO is an open-source file format and library developed for the storage and manipulation of data produced by quantum chemistry calculations. It is designed with the goal of providing a reliable and efficient method of storing and exchanging wave function parameters and matrix elements. The library consists of a front-end implemented in the C programming language and two different back-ends: a text back-end and a binary back-end utilizing the HDF5 library which enables fast read and write operations. It is compatible with a variety of platforms and has interfaces for the Fortran, Python, OCaml and Rust programming languages. ## Minimal requirements (for users): @@ -191,6 +191,34 @@ make python-test We highly recommend to use virtual environments to avoid compatibility issues and to improve reproducibility. +## Rust API + +The Rust API is available on Crates.io, so you can simply run +``` +cargo add trexio +``` +to your Rust project. + +If you prefer to install the Rust API provided with this repository: +``` +cargo add --path /path/to/trexio/rust/trexio +``` + +## OCaml API + +The TREXIO OCaml API is available in OPAM: +``` +opam install trexio +``` + +If you prefer to install it from this repository, + +``` +cd ocaml/trexio +make +opam install . +``` + ## Tutorial TREXIO tutorials in Jupyter notebook format can be found in the diff --git a/configure.ac b/configure.ac index b8f26a7..ecd13ae 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([trexio],[2.4.0],[https://github.com/TREX-CoE/trexio/issues]) +AC_INIT([trexio],[2.4.2],[https://github.com/TREX-CoE/trexio/issues]) AC_CONFIG_SRCDIR([Makefile.in]) AC_CONFIG_HEADERS([include/config.h]) diff --git a/ocaml/trexio/Makefile b/ocaml/trexio/Makefile index 8783f1a..f01743b 100644 --- a/ocaml/trexio/Makefile +++ b/ocaml/trexio/Makefile @@ -10,6 +10,7 @@ lib/trexio.h: sources: lib/trexio.ml lib/trexio.h clean: + rm lib/trexio.h lib/trexio.ml lib/trexio.mli lib/trexio_stubs.c dune clean .PHONY: sources default diff --git a/ocaml/trexio/dune-project b/ocaml/trexio/dune-project index f37f9f6..5d32174 100644 --- a/ocaml/trexio/dune-project +++ b/ocaml/trexio/dune-project @@ -1,7 +1,7 @@ (lang dune 3.1) (name trexio) -(version 2.3.2) +(version 2.4.2) (generate_opam_files false) diff --git a/ocaml/trexio/read_json.py b/ocaml/trexio/read_json.py index d41bc00..ef4e598 100755 --- a/ocaml/trexio/read_json.py +++ b/ocaml/trexio/read_json.py @@ -7,6 +7,21 @@ stubs_file= "trexio_stubs.c" ml_file = "trexio.ml" mli_file = ml_file+"i" +def check_version(): + with open('trexio.opam','r') as f: + for line in f: + if line.startswith("version"): + ocaml_version = line.split(':')[1].strip()[1:-1] + break + with open('../../configure.ac','r') as f: + for line in f: + if line.startswith("AC_INIT"): + trexio_version = line.split(',')[1].strip()[1:-1] + break + if ocaml_version != trexio_version: + print(f"Inconsistent versions:\nTREXIO:{trexio_version}\nOCaml: {ocaml_version}\n") + raise + def write_stubs(data): with open("src/"+stubs_file,'r') as f: @@ -643,10 +658,12 @@ def write_ml(data): def main(): + check_version() with open(json_file,'r') as f: data = json.load(f) for group in data: for element in data[group]: + print(f"{group}_{element}") if data[group][element][0] == "str": data[group][element][0] = "string" diff --git a/ocaml/trexio/trexio-2.3.2.tar.gz b/ocaml/trexio/trexio-2.3.2.tar.gz deleted file mode 100644 index 8fa17bb..0000000 Binary files a/ocaml/trexio/trexio-2.3.2.tar.gz and /dev/null differ diff --git a/ocaml/trexio/trexio.opam b/ocaml/trexio/trexio.opam index d3a6a46..5d41dca 100644 --- a/ocaml/trexio/trexio.opam +++ b/ocaml/trexio/trexio.opam @@ -1,6 +1,6 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" -version: "2.3.2" +version: "2.4.2" synopsis: "Binding for the TREXIO Input/Output library" description: "TREXIO is a file format and library for storing wave functions and integrals for quantum chemistry." diff --git a/python/pytrexio/_version.py b/python/pytrexio/_version.py index 3d67cd6..60be088 100644 --- a/python/pytrexio/_version.py +++ b/python/pytrexio/_version.py @@ -1 +1 @@ -__version__ = "2.4.0" +__version__ = "2.4.2" diff --git a/rust/trexio/.gitignore b/rust/trexio/.gitignore new file mode 100644 index 0000000..1027d3c --- /dev/null +++ b/rust/trexio/.gitignore @@ -0,0 +1,5 @@ +Cargo.lock +src/generated.rs +target/ +wrapper.h + diff --git a/rust/trexio/Cargo.toml b/rust/trexio/Cargo.toml new file mode 100644 index 0000000..5ca5289 --- /dev/null +++ b/rust/trexio/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "trexio" +version = "2.4.2" +edition = "2021" +license = "BSD-3-Clause" +authors = ["Anthony Scemama ", "Evgeny Posenitskiy"] +description = "TREXIO is an open-source file format and library developed for the storage and manipulation of data produced by quantum chemistry calculations. It is designed with the goal of providing a reliable and efficient method of storing and exchanging wave function parameters and matrix elements." +repository = "https://github.com/trex-coe/trexio" +keywords = ["quantum", "chemistry"] +readme = "README.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +bindgen = "0.65.1" +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +reqwest = { version = "0.11", features = ["blocking", "rustls-tls"] } +tar = "0.4" +flate2 = "1.0" + +[lib] +doctest = false + +[dependencies] +hdf5 = "0.8.1" diff --git a/rust/trexio/Makefile b/rust/trexio/Makefile new file mode 100644 index 0000000..bb6fc05 --- /dev/null +++ b/rust/trexio/Makefile @@ -0,0 +1,10 @@ +default: src/generated.rs + cargo build + cargo test + +src/generated.rs: build.py + python3 build.py + +test: default + - cargo test -- --show-output + diff --git a/rust/trexio/README.md b/rust/trexio/README.md new file mode 100644 index 0000000..0774ebe --- /dev/null +++ b/rust/trexio/README.md @@ -0,0 +1,42 @@ +# TREXIO + + +TREXIO is an open-source file format and library developed for the storage and +manipulation of data produced by quantum chemistry calculations. It is designed +with the goal of providing a reliable and efficient method of storing and +exchanging wave function parameters and matrix elements. + +This crate is the Rust binding for the TREXIO C library: +![GitHub release (latest by date)](https://img.shields.io/github/v/release/TREX-CoE/trexio) + + +## Documentation + +[TREXIO Documentation.](https://trex-coe.github.io/trexio/) + + +## Citation + +The journal article reference describing TREXIO can be cited as follows: + +``` +@article{10.1063/5.0148161, + author = {Posenitskiy, Evgeny and Chilkuri, Vijay Gopal and Ammar, Abdallah and Hapka, Michał and Pernal, Katarzyna and Shinde, Ravindra and Landinez Borda, Edgar Josué and Filippi, Claudia and Nakano, Kosuke and Kohulák, Otto and Sorella, Sandro and de Oliveira Castro, Pablo and Jalby, William and Ríos, Pablo López and Alavi, Ali and Scemama, Anthony}, + title = "{TREXIO: A file format and library for quantum chemistry}", + journal = {The Journal of Chemical Physics}, + volume = {158}, + number = {17}, + year = {2023}, + month = {05}, + issn = {0021-9606}, + doi = {10.1063/5.0148161}, + url = {https://doi.org/10.1063/5.0148161}, + note = {174801}, + eprint = {https://pubs.aip.org/aip/jcp/article-pdf/doi/10.1063/5.0148161/17355866/174801\_1\_5.0148161.pdf}, +} +``` + +Journal paper: [![doi](https://img.shields.io/badge/doi-10.1063/5.0148161-5077AB.svg)](https://doi.org/10.1063/5.0148161) + +ArXiv paper: [![arXiv](https://img.shields.io/badge/arXiv-2302.14793-b31b1b.svg)](https://arxiv.org/abs/2302.14793) + diff --git a/rust/trexio/build.rs b/rust/trexio/build.rs new file mode 100644 index 0000000..7625038 --- /dev/null +++ b/rust/trexio/build.rs @@ -0,0 +1,729 @@ +extern crate reqwest; +extern crate tar; +extern crate flate2; + +const WRAPPER_H: &str = "wrapper.h"; +const GENERATED_RS: &str = "generated.rs"; + +use std::env; +use std::path::PathBuf; +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, BufRead, BufReader, Write}; +use serde_json::Value; +use flate2::read::GzDecoder; +use tar::Archive; + + + +fn download_trexio() -> PathBuf { + let version = env::var("CARGO_PKG_VERSION").unwrap(); + println!("Version : {}", version); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + let trexio_url = format!("https://github.com/TREX-CoE/trexio/releases/download/v{version}/trexio-{version}.tar.gz"); + + // Download the .tar.gz archive + let tar_gz = out_path.join("trexio.tar.gz"); + let trexio_dir= out_path.join("trexio_dir"); + let mut resp = reqwest::blocking::get(trexio_url).expect("Failed to download the archive"); + let mut out = File::create(tar_gz.clone()).expect("Failed to create archive file"); + std::io::copy(&mut resp, &mut out).expect("Failed to copy content"); + + // Unpack the .tar.gz archive + let tar_gz = File::open(tar_gz).unwrap(); + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + archive.unpack(trexio_dir.clone()).expect("Failed to unpack"); + + // Assume that the archive extracts to a directory named 'trexio-0.1.0' + trexio_dir.join(format!("trexio-{}", version)) +} + + +fn install_trexio(trexio_dir: &PathBuf) -> PathBuf { + println!("{}", trexio_dir.display()); + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + let install_path = out_path.join("trexio_install"); + + // Run configure script + let configure_status = std::process::Command::new("./configure") + .arg(format!("--prefix={}",install_path.display())) + .arg("--without-fortran") + .current_dir(&trexio_dir) + .status() + .unwrap(); + + assert!(configure_status.success()); + + // Run make + let make_status = std::process::Command::new("make") + .arg("install") + .current_dir(&trexio_dir) + .status() + .unwrap(); + + assert!(make_status.success()); + install_path +} + + +/// This function reads from `trexio.h`, extracts the exit codes and backends, and writes them to `wrapper.h`. +fn make_interface(trexio_h: &PathBuf) -> io::Result<()> { + let mut err = HashMap::new(); + let mut be = HashMap::new(); + + let trexio_file = File::open(trexio_h)?; + let trexio_reader = BufReader::new(trexio_file); + + for line in trexio_reader.lines() { + let line = line?; + let buf = line.trim_start(); + + if buf.starts_with("#define TREXIO_") && buf.contains("(trexio_exit_code)") { + let buf2 = buf.replace(")", ""); + let buf2 = buf2.replace("(", ""); + let buf2: Vec<&str> = buf2.split_whitespace().collect(); + err.insert(buf2[1].to_string(), buf2[3].trim().parse::().unwrap()); + } + + if buf.starts_with("#define TREXIO_") && buf.contains("(back_end_t)") { + let buf2 = buf.replace(")", ""); + let buf2 = buf2.replace("(", ""); + let buf2: Vec<&str> = buf2.split_whitespace().collect(); + be.insert(buf2[1].to_string(), buf2[3].trim().parse::().unwrap()); + } + } + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + let wrapper_h = out_path.join(WRAPPER_H); + let mut wrapper_file = File::create(wrapper_h)?; + write!(&mut wrapper_file, "#include \n")?; + + for (k, v) in &err { + write!(&mut wrapper_file, "#undef {}\n", k)?; + write!(&mut wrapper_file, "const trexio_exit_code {} = {};\n", k, v)?; + } + + for (k, v) in &be { + write!(&mut wrapper_file, "#undef {}\n", k)?; + write!(&mut wrapper_file, "const back_end_t {} = {};\n", k, v)?; + } + + write!(&mut wrapper_file, "#undef TREXIO_AUTO\n")?; + write!(&mut wrapper_file, "const back_end_t TREXIO_AUTO = TREXIO_INVALID_BACK_END;\n")?; + + Ok(()) +} + + +/// Type conversions for Rust API +fn convert_r(typ: &str) -> String { + match typ { + "int" => "i64", + "int special" => "usize", + "float" | "float sparse" | "float buffered" => "f64", + "dim" | "dim readonly" | "index" => "usize", + "str" => "str", + _ => panic!("Unknown type to convert: {}", typ) + }.to_string() +} + +/// Type conversion to call C functions +fn convert_c(typ: &str) -> String { + match typ { + "int" | "int special" | "dim" | "dim readonly" | "index" => "i64", + "float" | "float sparse" | "float buffered" => "f64", + "str" => "str", + _ => panic!("Unknown type to convert: {}", typ) + }.to_string() +} + + +/// Generate has-functions for checking the existence of groups and elements in a TREXIO file. +/// +/// # Parameters +/// * `data` - The JSON-like data containing the groups and elements. +/// +/// # Returns +/// A `Vec` containing the generated Rust code as strings. +fn make_has_functions(data: &Value) -> Vec { + let mut r = Vec::new(); + + if let Value::Object(groups) = data { + for (group, elements) in groups.iter() { + let group_l = group.to_lowercase(); + + let has_group_func = format!( + "/// Checks if the group `{group}` exists in the file. +/// # Parameters +/// +/// None +/// +/// # Returns +/// +/// * `Result` - Returns `Ok(true)` if the element exists in the file, +/// otherwise returns `Ok(false)`. An error during the execution results in `Err(ExitCode)`. +pub fn has_{group_l}(&self) -> Result {{ + let rc = unsafe {{ c::trexio_has_{group}(self.ptr) }}; + match rc {{ + c::TREXIO_SUCCESS => Ok(true), + c::TREXIO_HAS_NOT => Ok(false), + x => Err(ExitCode::from(x)), + }} +}}"); + + r.push(has_group_func); + + if let Value::Object(elements_map) = elements { + for (element, _types_value) in elements_map.iter() { + let element_l = element.to_lowercase(); + + let has_element_func = format!( + "/// Checks if the element `{element}` of the group `{group}` exists in the file. +/// +/// # Parameters +/// +/// None +/// +/// # Returns +/// +/// * `Result` - Returns `Ok(true)` if the element exists in the file, +/// otherwise returns `Ok(false)`. An error during the execution results in `Err(ExitCode)`. +pub fn has_{group_l}_{element_l}(&self) -> Result {{ + let rc = unsafe {{ c::trexio_has_{group}_{element}(self.ptr) }}; + match rc {{ + c::TREXIO_SUCCESS => Ok(true), + c::TREXIO_HAS_NOT => Ok(false), + x => Err(ExitCode::from(x)), + }} +}}"); + + r.push(has_element_func); + } + } + } + } + r +} + + + + +fn make_scalar_functions(data: &serde_json::Value) -> Vec { + let mut r: Vec = Vec::new(); + + for group in data.as_object().unwrap().keys() { + let group_l = group.to_lowercase(); + + for (element, attributes) in data[group].as_object().unwrap() { + let typ = attributes[0].as_str().unwrap(); + let type_c = convert_c(typ); + let type_r = convert_r(typ); + let element_l = element.to_lowercase(); + + if attributes[1].as_array().unwrap().is_empty() { + match typ { + "int" | "float" | "dim" | "index" => { + let s = format!(r#" +/// Reads the scalar element `{element}` from the group `{group}` in the file. +/// +/// # Parameters +/// +/// None +/// +/// # Returns +/// +/// * `Result<{type_r}, ExitCode>` - Returns the scalar element as a `{type_r}` upon successful +/// operation. If the operation fails, it returns `Err(ExitCode)`. +pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> {{ + let mut data_c: {type_c} = 0{type_c}; + let (rc, data) = unsafe {{ + let rc = c::trexio_read_{group}_{element}_64(self.ptr, &mut data_c); + (rc, data_c.try_into().expect("try_into failed in read_{group_l}_{element_l}")) + }}; + rc_return(data, rc) +}} + +/// Writes the scalar element `{element}` into the group `{group}` in the file. +/// +/// # Parameters +/// +/// * `data: {type_r}` - A `{type_r}` scalar element that will be written into `{element}` in the group `{group}`. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` upon successful operation, otherwise returns `Err(ExitCode)`. +pub fn write_{group_l}_{element_l}(&self, data: {type_r}) -> Result<(), ExitCode> {{ + let data: {type_c} = data.try_into().expect("try_into failed in write_{group_l}_{element_l}"); + let rc = unsafe {{ c::trexio_write_{group}_{element}_64(self.ptr, data) }}; + rc_return((), rc) +}} +"#); + r.push(s); + }, + "str" => { + let s = format!(r#" +/// Reads the string attribute `{element}` contained in the group `{group}`. +/// # Parameters +/// +/// * `capacity: usize` - The maximum buffer size allocated for the string to be read. +/// +/// # Returns +/// +/// * `Result` - Returns the attribute as a `String` upon successful operation. +/// If the operation fails, it returns `Err(ExitCode)`. +pub fn read_{group_l}_{element_l}(&self, capacity: usize) -> Result {{ + let data_c = CString::new(vec![ b' ' ; capacity]).expect("CString::new failed"); + let (rc, data) = unsafe {{ + let data_c = data_c.into_raw() as *mut c_char; + let rc = c::trexio_read_{group}_{element}(self.ptr, data_c, capacity.try_into().expect("try_into failed in read_{group_l}_{element_l}")); + (rc, CString::from_raw(data_c)) + }}; + let result : String = CString::into_string(data).expect("into_string failed in read_{group_l}_{element_l}"); + rc_return(result, rc) +}} + + +/// Writes the string attribute `{element}` into the group `{group}`. +/// +/// # Parameters +/// +/// * `data: &str` - The string attribute that will be written into the `{element}` field in the `{group}` group. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` upon successful operation. +/// If the operation fails, it returns `Err(ExitCode)`. +pub fn write_{group_l}_{element_l}(&self, data: &str) -> Result<(), ExitCode> {{ + let size : i32 = data.len().try_into().expect("try_into failed in write_{group_l}_{element_l}"); + let data = string_to_c(data); + let data = data.as_ptr() as *const c_char; + let rc = unsafe {{ c::trexio_write_{group}_{element}(self.ptr, data, size) }}; + rc_return((), rc) +}} +"#); + r.push(s); + }, + "dim readonly" => { + let s = format!(r#" +/// Reads the dimensioning variable `{element}` from the group `{group}`. +/// +/// # Parameters +/// +/// None. +/// +/// # Returns +/// +/// * `Result<{type_r}, ExitCode>` - Returns the dimensioning variable `{element}` of type `{type_r}` +/// upon successful operation. If the operation fails, it returns `Err(ExitCode)`. +pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> {{ + let mut data_c: {type_c} = 0{type_c}; + let (rc, data) = unsafe {{ + let rc = c::trexio_read_{group}_{element}_64(self.ptr, &mut data_c); + (rc, data_c.try_into().expect("try_into failed in read_{group_l}_{element_l}")) + }}; + rc_return(data, rc) +}} +"#); + r.push(s); + }, + _ => {} + } + } + } + } + r +} + + + +fn make_array_functions(data: &serde_json::Value) -> Vec { + let mut r: Vec = Vec::new(); + + for group in data.as_object().unwrap().keys() { + let group_l = group.to_lowercase(); + + for (element, attributes) in data[group].as_object().unwrap() { + let typ = attributes[0].as_str().unwrap(); + let type_c = convert_c(typ); + let type_r = convert_r(typ); + let element_l = element.to_lowercase(); + let dimensions = attributes[1].as_array().unwrap(); + let dimensions: Vec<&str> = dimensions.iter().map(|x| x.as_str().unwrap()).collect(); + let dimensions_str = format!("{:?}", dimensions).replace("\"",""); + if ! dimensions.is_empty() { + match typ { + "int" | "float" | "dim" | "index" => { + r.push(format!(r#" +/// Reads the `{element}` array from the group `{group}` in the file. +/// +/// # Dimensions +/// +/// The array is of dimension `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result, ExitCode>` - Returns a flattened one-dimensional vector that contains +/// the elements of the `{element}` array. If the operation is unsuccessful, it returns `Err(ExitCode)`. +/// +///"#)); + if dimensions.len() > 1 { + r.push(format!(r#" +/// # Example +/// +/// To reshape the one-dimensional vector back into a two-dimensional array, you can use the [`chunks`] method: +/// +/// ```text +/// let one_d_array = trexio_file.read_{}_{}()?;"#, group_l, element_l)); + if let Some(dim) = dimensions.first() { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!("/// let {}_{} = trexio_file.read_{}_{}()?;", parts[0], parts[1], parts[0], parts[1])); + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}_{}).collect();", parts[0], parts[1])); + } else { + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}).collect();", dim)); + } + } + r.push(String::from("/// ```")); + r.push(String::from("///\n/// [`chunks`]: slice::chunks")); + } + r.push(format!(r#"pub fn read_{}_{}(&self) -> Result, ExitCode> {{ + let mut size = 1;"#, group_l, element_l, type_r)); + + for dim in &dimensions { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!(" size *= self.read_{}_{}()?;", parts[0], parts[1])); + } else { + r.push(format!(" size *= {};", dim)); + } + } + r.push(format!(r#" let mut data: Vec<{type_r}> = Vec::with_capacity(size); + let rc = unsafe {{ + let data_c = data.as_mut_ptr() as *mut {type_c}; + let rc = c::trexio_read_safe_{group}_{element}_64(self.ptr, data_c, size.try_into().expect("try_into failed in read_{group}_{element} (size)")); + data.set_len(size); + rc + }}; + rc_return(data, rc) +}}"#)); + r.push(format!(r#" +/// Writes the `{element}` array into the group `{group}` in the file. +/// +/// # Parameters +/// +/// * `data: &[{type_r}]` - A one-dimensional vector that contains the elements of the `{element}` array +/// to be written into the file. The vector should be flattened from a two-dimensional array with +/// dimensions `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, +/// otherwise returns `Err(ExitCode)`.""", """\ +pub fn write_{group_l}_{element_l}(&self, data: &[{type_r}]) -> Result<(), ExitCode> {{ + let size: i64 = data.len().try_into().expect("try_into failed in write_{group_l}_{element_l}"); + let data = data.as_ptr() as *const {type_c}; + let rc = unsafe {{ c::trexio_write_safe_{group}_{element}_64(self.ptr, data, size) }}; + rc_return((), rc) +}} +"#)); + } + , + "str" => { + r.push(format!(r#" +/// Reads the `{element}` array from the group `{group}` in the file. +/// +/// # Dimensions +/// +/// The array is of dimension `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result, ExitCode>` - Returns a flattened one-dimensional vector that contains +/// the elements of the `{element}` array. If the operation is unsuccessful, it returns `Err(ExitCode)`. +/// +/// "#)); + if dimensions.len() > 1 { + r.push(format!(r#"/// # Example +/// +/// To reshape the one-dimensional vector back into a two-dimensional array, you can use the [`chunks`] method: +/// +/// ```text +/// let one_d_array = trexio_file.read_{}_{}()?;"#, group_l, element_l)); + if let Some(dim) = dimensions.first() { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!("/// let {}_{} = trexio_file.read_{}_{}()?;", parts[0], parts[1], parts[0], parts[1])); + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}_{}).collect();", parts[0], parts[1])); + } else { + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}).collect();", dim)); + } + } + r.push(String::from("/// ```")); + r.push(String::from("///\n/// [`chunks`]: slice::chunks")); + } + r.push(format!(r#"pub fn read_{}_{}(&self, capacity: usize) -> Result, ExitCode> {{ + let mut size = 1;"#, group_l, element_l)); + for dim in &dimensions { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!(" size *= self.read_{}_{}()?;", parts[0], parts[1])); + } else { + r.push(format!(" size *= {};", dim)); + } + } + r.push(format!(r#" // Allocate an array of *mut i8 pointers (initialized to null) + let mut dset_out: Vec<*mut i8> = vec![std::ptr::null_mut(); size]; + + // Allocate C-style strings and populate dset_out + for item in dset_out.iter_mut().take(size) {{ + let c_str: *mut i8 = unsafe {{ std::alloc::alloc_zeroed(std::alloc::Layout::array::(capacity).unwrap()) as *mut i8 }}; + if c_str.is_null() {{ + return Err(ExitCode::AllocationFailed); + }} + *item = c_str; + }} + + + let rc = unsafe {{ + c::trexio_read_{group}_{element}(self.ptr, dset_out.as_mut_ptr(), capacity.try_into().expect("try_into failed in read_{group}_{element} (capacity)") ) + }}; + + // Convert the populated C strings to Rust Strings + let mut rust_strings = Vec::new(); + for &c_str in &dset_out {{ + let rust_str = unsafe {{ + std::ffi::CStr::from_ptr(c_str) + .to_string_lossy() + .into_owned() + }}; + rust_strings.push(rust_str); + }} + + // Clean up allocated C strings + for &c_str in &dset_out {{ + unsafe {{ std::alloc::dealloc(c_str as *mut u8, std::alloc::Layout::array::(capacity).unwrap()) }}; + }} + + rc_return(rust_strings, rc) +}} + +/// Writes the `{element}` array into the group `{group}` in the file. +/// +/// # Parameters +/// +/// * `data: &[{type_r}]` - A one-dimensional vector that contains the elements of the `{element}` array +/// to be written into the file. The vector should be flattened from a two-dimensional array with +/// dimensions `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, +/// otherwise returns `Err(ExitCode)`. +pub fn write_{group_l}_{element_l}(&self, data: &[&str]) -> Result<(), ExitCode> {{ + let mut size = 0; + // Find longest string + for s in data {{ + let l = s.len(); + size = if l>size {{l}} else {{size}}; + }} + size += 1; + let data_c : Vec = data.iter().map(|&x| string_to_c(x)).collect::>(); + let data_c : Vec<*const c_char> = data_c.iter().map(|x| x.as_ptr() as *const c_char).collect::>(); + let size : i32 = size.try_into().expect("try_into failed in write_{group}_{element} (size)"); + let data_c = data_c.as_ptr() as *mut *const c_char; + let rc = unsafe {{ c::trexio_write_{group}_{element}(self.ptr, data_c, size) }}; + rc_return((), rc) +}} +"#)); + }, + "float sparse" => { + let size = dimensions.len(); + let typ = [ "(", (vec![ "usize" ; size ]).join(", ").as_str(),", f64)"].join(""); + r.push(format!(r#" +/// Reads a buffer of {element} from group {group}. +/// +/// # Parameters +/// +/// * `offset: usize` - The starting point in the array from which data will be read. +/// * `buffer_size: usize` - The size of the buffer in which read data will be stored. +/// +/// # Returns +/// +/// * `Result, ExitCode>` - Returns a vector of tuples containing +/// the indices and the value of the element. The vector has a length of at most `buffer_size`. +/// +/// # Notes +/// +/// The reading process is a buffered operation, meaning that only a segment of the full array +/// is read into the memory. +pub fn read_{group_l}_{element_l}(&self, offset: usize, buffer_size:usize) -> Result, ExitCode> {{ + let mut idx = Vec::::with_capacity({size}*buffer_size); + let mut val = Vec::::with_capacity(buffer_size); + let idx_ptr = idx.as_ptr() as *mut i32; + let val_ptr = val.as_ptr() as *mut f64; + let offset: i64 = offset.try_into().expect("try_into failed in read_{group}_{element} (offset)"); + let mut buffer_size_read: i64 = buffer_size.try_into().expect("try_into failed in read_{group}_{element} (buffer_size)"); + let rc = unsafe {{ c::trexio_read_safe_{group}_{element}(self.ptr, + offset, &mut buffer_size_read, idx_ptr, buffer_size_read, val_ptr, buffer_size_read) + }}; + let rc = match ExitCode::from(rc) {{ + ExitCode::End => ExitCode::to_c(&ExitCode::Success), + _ => rc + }}; + let buffer_size_read: usize = buffer_size_read.try_into().expect("try_into failed in read_{group}_{element} (buffer_size)"); + unsafe {{ idx.set_len({size}*buffer_size_read) }}; + unsafe {{ val.set_len(buffer_size_read) }}; + let idx: Vec::<&[i32]> = idx.chunks({size}).collect(); + + let mut result = Vec::<{typ}>::with_capacity(buffer_size); + for (i, v) in zip(idx, val) {{ + result.push( ("#)); + let mut x = Vec::new(); + for k in 0..size { + x.push(format!("i[{k}].try_into().unwrap()")) + }; + x.push("v));\n }\n rc_return(result, rc)\n}".to_string()); + r.push(x.join(", ")); + r.push(format!(r#"/// Writes a buffer of {element} from group {group}. +/// +/// # Parameters +/// +/// * `offset: usize` - The starting point in the array at which data will be written. +/// * `data: &[{typ}]` - A slice of tuples containing the indices and the value of the element. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` if the writing operation is successful, +/// otherwise returns `Err(ExitCode)`. +/// +/// # Notes +/// +/// The writing process is a buffered operation, meaning that only a segment of the full array +/// is written into the file. +pub fn write_{group_l}_{element_l}(&self, offset: usize, data: &[{typ}]) -> Result<(), ExitCode> {{ + let mut idx = Vec::::with_capacity({size}*data.len()); + let mut val = Vec::::with_capacity(data.len()); + + for d in data {{ "#)); + let mut x = Vec::new(); + for k in 0..size { + x.push(format!(" idx.push(d.{k}.try_into().unwrap());")) + }; + r.push(x.join("\n")); + r.push(format!(r#" + val.push(d.{size}); + }} + + let size_max: i64 = data.len().try_into().expect("try_into failed in write_{group}_{element} (size_max)"); + let buffer_size = size_max; + let idx_ptr = idx.as_ptr() as *const i32; + let val_ptr = val.as_ptr() as *const f64; + let offset: i64 = offset.try_into().expect("try_into failed in write_{group}_{element} (offset)"); + let rc = unsafe {{ c::trexio_write_safe_{group}_{element}(self.ptr, + offset, buffer_size, idx_ptr, size_max, val_ptr, size_max) }}; + rc_return((), rc) +}}"#)); + }, + _ => {} + } + } + } + } + r +} + + + + + + + + + +/// Reads the JSON file, processes its contents, and generates Rust functions according to the specifications in the JSON data. +fn make_functions(json_path: &PathBuf) -> std::io::Result<()> { + let file = File::open(json_path).unwrap(); + let data: Value = serde_json::from_reader(file).unwrap(); + + let mut r: Vec = vec![ + String::from(" +use std::ffi::CString; +use std::iter::zip; + +/// This implementation block includes additional functions automatically generated from tables. +/// For more details, refer to [TREXIO tables documentation](https://trex-coe.github.io/trexio/trex.html). +impl File { + #![allow(clippy::unnecessary_cast)] + #![allow(clippy::useless_conversion)] + #![allow(clippy::type_complexity)] +"), + ]; + + r.append(&mut make_has_functions(&data)); + r.append(&mut make_scalar_functions(&data)); + r.append(&mut make_array_functions(&data)); + + r.push(String::from("}")); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + let generated_rs = out_path.join(GENERATED_RS); + let mut f = File::create(&generated_rs)?; + f.write_all(r.join("\n").as_bytes())?; + Ok(()) +} + + + + + +fn main() { + let source_path = download_trexio(); + println!("source path: {}", source_path.display()); + + let install_path = install_trexio(&source_path); + println!("install path: {}", install_path.display()); + + // Tell cargo to look for shared libraries in the specified directory + println!("cargo:rustc-link-search={}/lib", install_path.display()); + + // Tell cargo to tell rustc to link the system trexio shared library. + println!("cargo:rustc-link-lib=trexio"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + let trexio_h = install_path.join("include").join("trexio.h"); + println!("trexio.h: {}", trexio_h.display()); + + make_interface(&trexio_h).unwrap(); + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let wrapper_h = out_path.join(WRAPPER_H); + println!("wrapper.h: {}", wrapper_h.display()); + + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header(wrapper_h.to_str().unwrap()) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let bindings_path = out_path.join("bindings.rs"); + println!("bindings.rs: {}", bindings_path.display()); + + bindings + .write_to_file(&bindings_path) + .expect("Couldn't write bindings!"); + + let json_path = source_path.join("trex.json"); + println!("json path: {}", json_path.display()); + + make_functions(&json_path).unwrap(); +} diff --git a/rust/trexio/src/back_end.rs b/rust/trexio/src/back_end.rs new file mode 100644 index 0000000..6349b88 --- /dev/null +++ b/rust/trexio/src/back_end.rs @@ -0,0 +1,35 @@ +use crate::c; + +#[derive(Debug, PartialEq)] +pub enum BackEnd { + /// The TREXIO "file" is a directory with text files for each group. + /// A fallback when HDF5 is not available. + Text, + + /// Should be used for production. The TREXIO file is a single HDF5 file. + Hdf5, + + /// Automatic discovery of the appropriate backend + Auto, +} + +impl BackEnd { + /// Creation from a C value + pub fn from(b: c::back_end_t) -> Self { + match b { + c::TREXIO_TEXT => Self::Text, + c::TREXIO_HDF5 => Self::Hdf5, + c::TREXIO_AUTO => Self::Auto, + _ => panic!("Invalid backend"), + } + } + + /// Conversion to a C value + pub fn to_c(self) -> c::back_end_t { + match self { + Self::Text => c::TREXIO_TEXT, + Self::Hdf5 => c::TREXIO_HDF5, + Self::Auto => c::TREXIO_AUTO, + } + } +} diff --git a/rust/trexio/src/bitfield.rs b/rust/trexio/src/bitfield.rs new file mode 100644 index 0000000..dd0119c --- /dev/null +++ b/rust/trexio/src/bitfield.rs @@ -0,0 +1,208 @@ +#[derive(Debug, PartialEq)] +pub struct Bitfield { + data: Vec, +} + +use crate::c; +use crate::ExitCode; + +impl Bitfield { + /// Creates a new bitfield , using a number of i64 elements consistent + /// with the number of MOs in the TREXIO file. + pub fn from(n_int: usize, orb_list: &[usize]) -> (Self, f64) { + let orb_list: Vec = orb_list.iter().map(|&x| x as i32).collect(); + let occ_num = orb_list + .len() + .try_into() + .expect("try_into failed in Bitfield::from"); + let orb_list_ptr: *const i32 = orb_list.as_ptr(); + let n_int32: i32 = n_int.try_into().expect("try_into failed in Bitfield::from"); + let mut b = vec![0i64; n_int]; + let bit_list = b.as_mut_ptr() as *mut c::bitfield_t; + std::mem::forget(b); + let rc = unsafe { c::trexio_to_bitfield_list(orb_list_ptr, occ_num, bit_list, n_int32) }; + + let data = unsafe { Vec::from_raw_parts(bit_list, n_int, n_int) }; + + let result = Bitfield { data }; + + match ExitCode::from(rc) { + ExitCode::Success => (result, 1.0), + ExitCode::PhaseChange => (result, -1.0), + x => panic!("TREXIO Error {}", x), + } + } + + pub fn from_vec(v: &[i64]) -> Bitfield { + Bitfield { data: v.to_vec() } + } + + pub fn from_alpha_beta(alpha: &Bitfield, beta: &Bitfield) -> Bitfield { + if alpha.data.len() != beta.data.len() { + panic!("alpha and beta parts have different lengths"); + }; + let mut data = alpha.data.clone(); + data.extend_from_slice(&beta.data); + Bitfield { data } + } + + /// Returns the alpha part + pub fn alpha(&self) -> Bitfield { + let n_int = self.data.len() / 2; + Bitfield { + data: (self.data[0..n_int]).to_vec(), + } + } + + /// Returns the beta part + pub fn beta(&self) -> Bitfield { + let n_int = self.data.len() / 2; + Bitfield { + data: (self.data[n_int..2 * n_int]).to_vec(), + } + } + + /// Converts to a format usable in the C library + pub fn as_ptr(&self) -> *const c::bitfield_t { + let len = self.data.len(); + let result = &self.data[0..len]; + result.as_ptr() as *const c::bitfield_t + } + + /// Converts to a format usable in the C library + pub fn as_mut_ptr(&mut self) -> *mut c::bitfield_t { + let len = self.data.len(); + let result = &mut self.data[0..len]; + result.as_mut_ptr() as *mut c::bitfield_t + } + + /// Converts the bitfield into a list of orbital indices (0-based) + pub fn to_orbital_list(&self) -> Vec { + let n_int: i32 = self + .data + .len() + .try_into() + .expect("try_into failed in to_orbital_list"); + let d1 = self.as_ptr(); + let cap = self.data.len() * 64; + let mut list = vec![0i32; cap]; + let list_c: *mut i32 = list.as_mut_ptr(); + std::mem::forget(list); + + let mut occ_num: i32 = 0; + + let rc = unsafe { c::trexio_to_orbital_list(n_int, d1, list_c, &mut occ_num) }; + match ExitCode::from(rc) { + ExitCode::Success => (), + x => panic!("TREXIO Error {}", x), + }; + + let occ_num = occ_num as usize; + let list = unsafe { Vec::from_raw_parts(list_c, occ_num, cap) }; + + let mut result: Vec = Vec::with_capacity(occ_num); + for i in list.iter() { + result.push(*i as usize); + } + result + } + + /// Converts the bitfield into a vector + pub fn as_vec(&self) -> &[i64] { + &self.data + } + + /// Converts the determinant into a list of orbital indices (0-based) + pub fn to_orbital_list_up_dn(&self) -> (Vec, Vec) { + let n_int: i32 = (self.data.len() / 2) + .try_into() + .expect("try_into failed in to_orbital_list"); + let d1 = self.as_ptr(); + let cap = self.data.len() / 2 * 64; + let mut b = vec![0i32; cap]; + let list_up_c: *mut i32 = b.as_mut_ptr(); + std::mem::forget(b); + let mut b = vec![0i32; cap]; + let list_dn_c: *mut i32 = b.as_mut_ptr(); + std::mem::forget(b); + + let mut occ_num_up: i32 = 0; + let mut occ_num_dn: i32 = 0; + + let rc = unsafe { + c::trexio_to_orbital_list_up_dn( + n_int, + d1, + list_up_c, + list_dn_c, + &mut occ_num_up, + &mut occ_num_dn, + ) + }; + match ExitCode::from(rc) { + ExitCode::Success => (), + x => panic!("TREXIO Error {}", x), + }; + + let occ_num_up = occ_num_up as usize; + let occ_num_dn = occ_num_dn as usize; + let list_up = unsafe { Vec::from_raw_parts(list_up_c, occ_num_up, cap) }; + let list_dn = unsafe { Vec::from_raw_parts(list_dn_c, occ_num_dn, cap) }; + + let mut result_up: Vec = Vec::with_capacity(occ_num_up); + for i in list_up.iter() { + result_up.push(*i as usize); + } + + let mut result_dn: Vec = Vec::with_capacity(occ_num_dn); + for i in list_dn.iter() { + result_dn.push(*i as usize); + } + (result_up, result_dn) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn creation_from_list() { + let list0 = vec![0, 1, 2, 3, 4]; + let list1 = vec![0, 1, 2, 4, 3]; + let list2 = vec![0, 1, 4, 2, 3]; + + let (alpha0, phase0) = Bitfield::from(2, &list0); + let list = alpha0.to_orbital_list(); + assert_eq!(list, list0); + + let (alpha1, phase1) = Bitfield::from(2, &list1); + let list = alpha1.to_orbital_list(); + assert_eq!(list, list0); + assert_eq!(phase1, -phase0); + + let (alpha2, phase2) = Bitfield::from(2, &list2); + let list = alpha2.to_orbital_list(); + assert_eq!(list, list0); + assert_eq!(phase2, phase0); + } + + #[test] + fn creation_alpha_beta() { + let (alpha, _) = Bitfield::from(2, &[0, 1, 2, 3, 4]); + let (beta, _) = Bitfield::from(2, &[0, 1, 2, 4, 5]); + let det = Bitfield::from_alpha_beta(&alpha, &beta); + let list = det.to_orbital_list(); + assert_eq!(list, [0, 1, 2, 3, 4, 128, 129, 130, 132, 133]); + assert_eq!(det.alpha(), alpha); + assert_eq!(det.beta(), beta); + } + + #[test] + #[should_panic] + fn creation_alpha_beta_with_different_nint() { + let (alpha, _) = Bitfield::from(1, &[0, 1, 2, 3, 4]); + let (beta, _) = Bitfield::from(2, &[0, 1, 2, 4, 5]); + let _ = Bitfield::from_alpha_beta(&alpha, &beta); + } +} diff --git a/rust/trexio/src/c.rs b/rust/trexio/src/c.rs new file mode 100644 index 0000000..cd503e4 --- /dev/null +++ b/rust/trexio/src/c.rs @@ -0,0 +1,6 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/rust/trexio/src/exit_code.rs b/rust/trexio/src/exit_code.rs new file mode 100644 index 0000000..dce0467 --- /dev/null +++ b/rust/trexio/src/exit_code.rs @@ -0,0 +1,219 @@ +use crate::c; + +#[derive(Debug, PartialEq)] +pub enum ExitCode { + /// Unknown failure + Failure, + + /// Everything went fine + Success, + + /// Invalid argument + InvalidArg(usize), + + /// End of file + End, + + /// Read-only file + ReadOnly, + + /// Error returned by Errno + Errno, + + /// Invalid ID + InvalidId, + + /// Allocation failed + AllocationFailed, + + /// Element absent + HasNot, + + /// Invalid (negative or 0) dimension + InvalidNum, + + /// Attribute already exists + AttrAlreadyExists, + + /// Dataset already exists + DsetAlreadyExists, + + /// Error opening file + OpenError, + + /// Error locking file + LockError, + + /// Error unlocking file + UnlockError, + + /// Invalid file + FileError, + + /// Error reading group + GroupReadError, + + /// Error writing group + GroupWriteError, + + /// Error reading element + ElemReadError, + + /// Error writing element + ElemWriteError, + + /// Access to memory beyond allocated + UnsafeArrayDim, + + /// Attribute does not exist in the file + AttrMissing, + + /// Dataset does not exist in the file + DsetMissing, + + /// Requested back end is disabled + BackEndMissing, + + /// Invalid max_str_len + InvalidStrLen, + + /// Possible integer overflow + IntSizeOverflow, + + /// Unsafe operation in safe mode + SafeMode, + + /// Inconsistent number of electrons + InvalidElectronNum, + + /// Inconsistent number of determinants + InvalidDeterminantNum, + + /// Inconsistent state of the file + InvalidState, + + /// Failed to parse package_version + VersionParsingIssue, + + /// The function succeeded with a change of sign + PhaseChange, +} + +impl ExitCode { + /// Creation from a C value + pub fn from(rc: c::trexio_exit_code) -> Self { + match rc { + c::TREXIO_FAILURE => Self::Failure, + c::TREXIO_SUCCESS => Self::Success, + c::TREXIO_INVALID_ARG_1 => Self::InvalidArg(1), + c::TREXIO_INVALID_ARG_2 => Self::InvalidArg(2), + c::TREXIO_INVALID_ARG_3 => Self::InvalidArg(3), + c::TREXIO_INVALID_ARG_4 => Self::InvalidArg(4), + c::TREXIO_INVALID_ARG_5 => Self::InvalidArg(5), + c::TREXIO_END => Self::End, + c::TREXIO_READONLY => Self::ReadOnly, + c::TREXIO_ERRNO => Self::Errno, + c::TREXIO_INVALID_ID => Self::InvalidId, + c::TREXIO_ALLOCATION_FAILED => Self::AllocationFailed, + c::TREXIO_HAS_NOT => Self::HasNot, + c::TREXIO_INVALID_NUM => Self::InvalidNum, + c::TREXIO_ATTR_ALREADY_EXISTS => Self::AttrAlreadyExists, + c::TREXIO_DSET_ALREADY_EXISTS => Self::DsetAlreadyExists, + c::TREXIO_OPEN_ERROR => Self::OpenError, + c::TREXIO_LOCK_ERROR => Self::LockError, + c::TREXIO_UNLOCK_ERROR => Self::UnlockError, + c::TREXIO_FILE_ERROR => Self::FileError, + c::TREXIO_GROUP_READ_ERROR => Self::GroupReadError, + c::TREXIO_GROUP_WRITE_ERROR => Self::GroupWriteError, + c::TREXIO_ELEM_READ_ERROR => Self::ElemReadError, + c::TREXIO_ELEM_WRITE_ERROR => Self::ElemWriteError, + c::TREXIO_UNSAFE_ARRAY_DIM => Self::UnsafeArrayDim, + c::TREXIO_ATTR_MISSING => Self::AttrMissing, + c::TREXIO_DSET_MISSING => Self::DsetMissing, + c::TREXIO_BACK_END_MISSING => Self::BackEndMissing, + c::TREXIO_INVALID_ARG_6 => Self::InvalidArg(6), + c::TREXIO_INVALID_ARG_7 => Self::InvalidArg(7), + c::TREXIO_INVALID_ARG_8 => Self::InvalidArg(8), + c::TREXIO_INVALID_STR_LEN => Self::InvalidStrLen, + c::TREXIO_INT_SIZE_OVERFLOW => Self::IntSizeOverflow, + c::TREXIO_SAFE_MODE => Self::SafeMode, + c::TREXIO_INVALID_ELECTRON_NUM => Self::InvalidElectronNum, + c::TREXIO_INVALID_DETERMINANT_NUM => Self::InvalidDeterminantNum, + c::TREXIO_INVALID_STATE => Self::InvalidState, + c::TREXIO_VERSION_PARSING_ISSUE => Self::VersionParsingIssue, + c::TREXIO_PHASE_CHANGE => Self::PhaseChange, + _ => panic!("Unknown exit code"), + } + } + + /// Conversion to a C value + pub fn to_c(&self) -> c::trexio_exit_code { + match self { + Self::Failure => c::TREXIO_FAILURE, + Self::Success => c::TREXIO_SUCCESS, + Self::InvalidArg(1) => c::TREXIO_INVALID_ARG_1, + Self::InvalidArg(2) => c::TREXIO_INVALID_ARG_2, + Self::InvalidArg(3) => c::TREXIO_INVALID_ARG_3, + Self::InvalidArg(4) => c::TREXIO_INVALID_ARG_4, + Self::InvalidArg(5) => c::TREXIO_INVALID_ARG_5, + Self::End => c::TREXIO_END, + Self::ReadOnly => c::TREXIO_READONLY, + Self::Errno => c::TREXIO_ERRNO, + Self::InvalidId => c::TREXIO_INVALID_ID, + Self::AllocationFailed => c::TREXIO_ALLOCATION_FAILED, + Self::HasNot => c::TREXIO_HAS_NOT, + Self::InvalidNum => c::TREXIO_INVALID_NUM, + Self::AttrAlreadyExists => c::TREXIO_ATTR_ALREADY_EXISTS, + Self::DsetAlreadyExists => c::TREXIO_DSET_ALREADY_EXISTS, + Self::OpenError => c::TREXIO_OPEN_ERROR, + Self::LockError => c::TREXIO_LOCK_ERROR, + Self::UnlockError => c::TREXIO_UNLOCK_ERROR, + Self::FileError => c::TREXIO_FILE_ERROR, + Self::GroupReadError => c::TREXIO_GROUP_READ_ERROR, + Self::GroupWriteError => c::TREXIO_GROUP_WRITE_ERROR, + Self::ElemReadError => c::TREXIO_ELEM_READ_ERROR, + Self::ElemWriteError => c::TREXIO_ELEM_WRITE_ERROR, + Self::UnsafeArrayDim => c::TREXIO_UNSAFE_ARRAY_DIM, + Self::AttrMissing => c::TREXIO_ATTR_MISSING, + Self::DsetMissing => c::TREXIO_DSET_MISSING, + Self::BackEndMissing => c::TREXIO_BACK_END_MISSING, + Self::InvalidArg(6) => c::TREXIO_INVALID_ARG_6, + Self::InvalidArg(7) => c::TREXIO_INVALID_ARG_7, + Self::InvalidArg(8) => c::TREXIO_INVALID_ARG_8, + Self::InvalidStrLen => c::TREXIO_INVALID_STR_LEN, + Self::IntSizeOverflow => c::TREXIO_INT_SIZE_OVERFLOW, + Self::SafeMode => c::TREXIO_SAFE_MODE, + Self::InvalidElectronNum => c::TREXIO_INVALID_ELECTRON_NUM, + Self::InvalidDeterminantNum => c::TREXIO_INVALID_DETERMINANT_NUM, + Self::InvalidState => c::TREXIO_INVALID_STATE, + Self::VersionParsingIssue => c::TREXIO_VERSION_PARSING_ISSUE, + Self::PhaseChange => c::TREXIO_PHASE_CHANGE, + _ => panic!("Unknown exit code"), + } + } + + pub fn to_str(&self) -> Result<&'static str, Utf8Error> { + let c_error = self.to_c(); + let c_buf: *const c_char = unsafe { c::trexio_string_of_error(c_error) }; + let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) }; + c_str.to_str() + } +} + +use std::error::Error; +use std::ffi::c_char; +use std::ffi::CStr; +use std::fmt; +use std::str::Utf8Error; + +impl fmt::Display for ExitCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_str().unwrap()) + } +} + +impl Error for ExitCode { + fn description(&self) -> &str { + self.to_str().unwrap() + } +} diff --git a/rust/trexio/src/lib.rs b/rust/trexio/src/lib.rs new file mode 100644 index 0000000..e82974b --- /dev/null +++ b/rust/trexio/src/lib.rs @@ -0,0 +1,261 @@ +//! TREXIO is an open-source file format and library developed for the storage and manipulation of +//! data produced by quantum chemistry calculations. It was designed with the goal of providing a +//! reliable and efficient method of storing and exchanging wave function parameters and matrix +//! elements. +//! +//! For comprehensive documentation, consult: [TREXIO Documentation](https://trex-coe.github.io/trexio/) +//! +//! The C library source code is available on GitHub: [TREXIO GitHub Repository](https://github.com/trex-coe/trexio) +//! + +use ::std::os::raw::c_char; + +mod c; + +/// Enumeration representing the various error codes that might be returned by function calls. +/// These error codes are mapped to those defined in the original C TREXIO library. +pub mod exit_code; +pub use exit_code::ExitCode; +use exit_code::ExitCode::InvalidArg; + +/// Enum representing the different backends that TREXIO can employ for data storage. +pub mod back_end; +pub use back_end::BackEnd; + +/// Structure representing bit fields. Mainly utilized for encapsulating determinant descriptions. +pub mod bitfield; +pub use bitfield::Bitfield; + +/// A constant string representing the package version of the linked C TREXIO library. +pub const PACKAGE_VERSION: &str = + unsafe { std::str::from_utf8_unchecked(c::TREXIO_PACKAGE_VERSION) }; + +/// Utility function to convert Rust results into TREXIO exit codes. +fn rc_return(result: T, rc: c::trexio_exit_code) -> Result { + let rc = ExitCode::from(rc); + match rc { + ExitCode::Success => Ok(result), + x => Err(x), + } +} + +/// Utility function to convert Rust string to C-compatible string. +fn string_to_c(s: &str) -> std::ffi::CString { + std::ffi::CString::new(s).unwrap() +} + +/// Function to print out diagnostic information about the linked C TREXIO library. +pub fn info() -> Result<(), ExitCode> { + let rc = unsafe { c::trexio_info() }; + rc_return((), rc) +} + +/// Type representing a TREXIO file. Wraps a pointer to the underlying C structure. +pub struct File { + ptr: *mut c::trexio_t, +} + +impl File { + /// Opens a TREXIO file. Returns a `File` instance that can be used for subsequent I/O operations. + /// + /// # Parameters + /// - `file_name`: The path to the TREXIO file. + /// - `mode`: Specifies the file access mode. `'r'` for read-only, `'w'` for safe write (write + /// if the data doesn't exist), `'u'` for unsafe write (update existing data). + /// - `back_end`: Specifies the backend used for data storage. + /// + /// # Returns + /// - `Result`: `File` instance or an error code. + pub fn open(file_name: &str, mode: char, back_end: BackEnd) -> Result { + let file_name_c = string_to_c(file_name); + let file_name_c = file_name_c.as_ptr() as *const c_char; + let mode = mode as c_char; + let back_end = back_end.to_c(); + let rc: *mut c::trexio_exit_code = &mut c::TREXIO_SUCCESS.clone(); + let result = unsafe { c::trexio_open(file_name_c, mode, back_end, rc) }; + let rc = unsafe { *rc }; + rc_return(File { ptr: result }, rc) + } + + /// Closes the current TREXIO file and releases associated resources. + /// + /// # Returns + /// - `Result<(), ExitCode>`: An error code in case of failure. + pub fn close(self) -> Result<(), ExitCode> { + let rc = unsafe { c::trexio_close(self.ptr) }; + rc_return((), rc) + } + + /// Inquires if a file with the specified name exists. + /// + /// # Parameters + /// + /// * `file_name: &str` - The name of the file to inquire about. + /// + /// # Returns + /// + /// * `Result` - Returns `Ok(true)` if the file exists, + /// `Ok(false)` otherwise. Returns `Err(ExitCode)` if an error occurs + /// during the operation. + pub fn inquire(file_name: &str) -> Result { + let file_name_c = string_to_c(file_name); + let file_name_c = file_name_c.as_ptr() as *const c_char; + let rc = unsafe { c::trexio_inquire(file_name_c) }; + match ExitCode::from(rc) { + ExitCode::Failure => Ok(false), + ExitCode::Success => Ok(true), + x => Err(x), + } + } + + /// Retrieves the ID of the electronic state stored in the file. + /// + /// # Parameters + /// + /// None + /// + /// # Returns + /// + /// * `Result` - Returns the ID as `Ok(usize)` if the operation is successful, + /// otherwise returns `Err(ExitCode)`. + pub fn get_state(&self) -> Result { + let mut num = 0i32; + let rc = unsafe { c::trexio_get_state(self.ptr, &mut num) }; + let result: usize = num.try_into().expect("try_into failed in get_state"); + rc_return(result, rc) + } + + /// Sets the ID of the electronic state to be stored in the file. + /// + /// # Parameters + /// + /// * `num: usize` - The ID of the electronic state. + /// + /// # Returns + /// + /// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, + /// otherwise returns `Err(ExitCode)`. + pub fn set_state(&self, num: usize) -> Result<(), ExitCode> { + let num: i32 = num.try_into().expect("try_into failed in set_state"); + let rc = unsafe { c::trexio_set_state(self.ptr, num) }; + rc_return((), rc) + } + + /// Retrieves the number of `i64` required to store a determinant as a bit-field. + /// This corresponds to \(\frac{\text{mo\_num}}{64}+1\). + /// + /// # Parameters + /// + /// None + /// + /// # Returns + /// + /// * `Result` - Returns the number of `i64` as `Ok(usize)` if the operation is successful, + /// otherwise returns `Err(ExitCode)`. + pub fn get_int64_num(&self) -> Result { + let mut num = 0i32; + let rc = unsafe { c::trexio_get_int64_num(self.ptr, &mut num) }; + let num: usize = num.try_into().expect("try_into failed in get_int64_num"); + rc_return(num, rc) + } + + /// Writes a vector of determinants, represented as [Bitfield] objects. + /// + /// # Parameters + /// + /// * `offset: usize` - The number of determinants to skip in the file before writing. + /// * `determinants: &[Bitfield]` - The array of determinants to write. + /// + /// # Returns + /// + /// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, + /// otherwise returns `Err(ExitCode)`. + pub fn write_determinant_list( + &self, + offset: usize, + determinants: &[Bitfield], + ) -> Result<(), ExitCode> { + let n_int = self.get_int64_num()?; + match determinants.len() { + 0 => return Ok(()), + _ => { + if determinants[0].as_vec().len() != 2 * n_int { + return Err(InvalidArg(3)); + } + } + }; + let offset: i64 = offset + .try_into() + .expect("try_into failed in write_determinant_list"); + let buffer_size: i64 = determinants + .len() + .try_into() + .expect("try_into failed in write_determinant_list"); + let mut one_d_array: Vec = Vec::with_capacity(determinants.len() * n_int); + for det in determinants { + for i in det.as_vec() { + one_d_array.push(*i); + } + } + let dset: *const i64 = one_d_array.as_ptr(); + let rc = unsafe { c::trexio_write_determinant_list(self.ptr, offset, buffer_size, dset) }; + rc_return((), rc) + } + + /// Reads a vector of determinants, represented as [Bitfield] objects. + /// + /// # Parameters + /// + /// * `offset: usize` - The number of determinants to skip in the file before reading. + /// * `buffer_size: usize` - The number of determinants to read. + /// + /// # Returns + /// + /// * `Result, ExitCode>` - Returns the read determinants as `Ok(Vec)` + /// if the operation is successful, otherwise returns `Err(ExitCode)`. + pub fn read_determinant_list( + &self, + offset: usize, + buffer_size: usize, + ) -> Result, ExitCode> { + let n_int = self.get_int64_num()?; + let mut one_d_array: Vec = Vec::with_capacity(buffer_size * 2 * n_int); + let one_d_array_ptr = one_d_array.as_ptr() as *mut i64; + let rc = unsafe { + let offset: i64 = offset + .try_into() + .expect("try_into failed in read_determinant_list (offset)"); + let mut buffer_size_read: i64 = buffer_size + .try_into() + .expect("try_into failed in read_determinant_list (buffer_size)"); + let rc = c::trexio_read_determinant_list( + self.ptr, + offset, + &mut buffer_size_read, + one_d_array_ptr, + ); + let buffer_size_read: usize = buffer_size_read + .try_into() + .expect("try_into failed in read_determinant_list (buffer_size)"); + one_d_array.set_len(n_int * 2usize * buffer_size_read); + match ExitCode::from(rc) { + ExitCode::End => ExitCode::to_c(&ExitCode::Success), + ExitCode::Success => { + assert_eq!(buffer_size_read, buffer_size); + rc + } + _ => rc, + } + }; + let result: Vec = one_d_array + .chunks(2 * n_int) + .collect::>() + .iter() + .map(|x| (Bitfield::from_vec(x))) + .collect::>(); + rc_return(result, rc) + } +} + +include!(concat!(env!("OUT_DIR"), "/generated.rs")); + diff --git a/rust/trexio/tests/read_write.rs b/rust/trexio/tests/read_write.rs new file mode 100644 index 0000000..ebdd88f --- /dev/null +++ b/rust/trexio/tests/read_write.rs @@ -0,0 +1,292 @@ +use trexio::back_end::BackEnd; +use trexio::bitfield::Bitfield; + +fn write(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> { + // Prepare data to be written + + let nucleus_num = 12; + let state_id = 2; + let charge = vec![6., 6., 6., 6., 6., 6., 1., 1., 1., 1., 1., 1.0f64]; + let coord = vec![ + [0.00000000f64, 1.39250319, 0.00], + [-1.20594314, 0.69625160, 0.00], + [-1.20594314, -0.69625160, 0.00], + [0.00000000, -1.39250319, 0.00], + [1.20594314, -0.69625160, 0.00], + [1.20594314, 0.69625160, 0.00], + [-2.14171677, 1.23652075, 0.00], + [-2.14171677, -1.23652075, 0.00], + [0.00000000, -2.47304151, 0.00], + [2.14171677, -1.23652075, 0.00], + [2.14171677, 1.23652075, 0.00], + [0.00000000, 2.47304151, 0.00], + ]; + let flat_coord = coord.into_iter().flatten().collect::>(); + let mo_num = 150; + let ao_num = 1000; + let basis_shell_num = 24; + let basis_nucleus_index: Vec = (0..24).collect(); + + let label = vec![ + "C", "Na", "C", "C 66", "C", "C", "H 99", "Ru", "H", "H", "H", "H", + ]; + + let sym_str = "B3U with some comments"; + + println!("Write {}", file_name); + assert!(!trexio::File::inquire(file_name)?); + + let trex_file = trexio::File::open(file_name, 'w', back_end)?; + + assert!(!trex_file.has_nucleus()?); + assert!(!trex_file.has_nucleus_num()?); + assert!(!trex_file.has_nucleus_charge()?); + assert!(!trex_file.has_ao_2e_int()?); + assert!(!trex_file.has_ao_2e_int_eri()?); + assert!(!trex_file.has_determinant_list()?); + + trex_file.write_nucleus_num(nucleus_num)?; + trex_file.write_nucleus_charge(&charge)?; + trex_file.write_nucleus_point_group(sym_str)?; + trex_file.write_nucleus_coord(&flat_coord)?; + trex_file.write_nucleus_label(&label)?; + trex_file.write_basis_shell_num(basis_shell_num)?; + trex_file.write_basis_nucleus_index(&basis_nucleus_index)?; + trex_file.write_state_id(state_id)?; + + if !trex_file.has_ao_num()? { + trex_file.write_ao_num(ao_num)?; + } + + if !trex_file.has_mo_num()? { + trex_file.write_mo_num(mo_num)?; + } + + let mut energy = Vec::with_capacity(mo_num); + for i in 0..mo_num { + let e: f64 = i as f64 - 100.0f64; + energy.push(e); + } + trex_file.write_mo_energy(&energy)?; + + let mut spin = vec![0; mo_num]; + for i in mo_num / 2..mo_num { + spin[i] = 1; + } + trex_file.write_mo_spin(&spin)?; + + // Integrals + let nmax = 100; + let mut ao_2e_int_eri = Vec::<(usize, usize, usize, usize, f64)>::with_capacity(nmax); + + let n_buffers = 5; + let bufsize = nmax / n_buffers; + + for i in 0..100 { + // Quadruplet of indices + value + let data = (4 * i, 4 * i + 1, 4 * i + 2, 4 * i + 3, 3.14 + (i as f64)); + ao_2e_int_eri.push(data); + } + + let mut offset = 0; + for _ in 0..n_buffers { + trex_file.write_ao_2e_int_eri(offset, &ao_2e_int_eri[offset..offset + bufsize])?; + offset += bufsize; + } + + // Determinants + let det_num = 50; + let mut det_list = Vec::with_capacity(det_num); + for i in 0..det_num { + let mut d = [0i64; 6]; + for j in 0..6 { + d[j] = 6 * (i as i64) + (j as i64); + } + det_list.push(Bitfield::from_vec(&d)); + } + + let n_buffers = 5; + let bufsize = 50 / n_buffers; + let mut offset = 0; + for _ in 0..n_buffers { + trex_file.write_determinant_list(offset, &det_list[offset..offset + bufsize])?; + offset += bufsize; + } + + trex_file.close() +} + +fn read(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> { + println!("Read {}", file_name); + assert!(trexio::File::inquire(file_name)?); + + let trex_file = trexio::File::open(file_name, 'r', back_end)?; + + assert!(trex_file.has_nucleus()?); + assert!(trex_file.has_nucleus_num()?); + assert!(trex_file.has_nucleus_charge()?); + assert!(trex_file.has_ao_2e_int()?); + assert!(trex_file.has_ao_2e_int_eri()?); + assert!(trex_file.has_determinant_list()?); + + let nucleus_num = trex_file.read_nucleus_num()?; + assert_eq!(nucleus_num, 12); + + let sym_str = trex_file.read_nucleus_point_group(64)?; + assert_eq!(sym_str, "B3U with some comments"); + + let charge = trex_file.read_nucleus_charge()?; + assert_eq!( + charge, + vec![6., 6., 6., 6., 6., 6., 1., 1., 1., 1., 1., 1.0f64] + ); + + let coord = trex_file.read_nucleus_coord()?; + assert_eq!( + coord, + vec![ + 0.00000000f64, + 1.39250319, + 0.00, + -1.20594314, + 0.69625160, + 0.00, + -1.20594314, + -0.69625160, + 0.00, + 0.00000000, + -1.39250319, + 0.00, + 1.20594314, + -0.69625160, + 0.00, + 1.20594314, + 0.69625160, + 0.00, + -2.14171677, + 1.23652075, + 0.00, + -2.14171677, + -1.23652075, + 0.00, + 0.00000000, + -2.47304151, + 0.00, + 2.14171677, + -1.23652075, + 0.00, + 2.14171677, + 1.23652075, + 0.00, + 0.00000000, + 2.47304151, + 0.00 + ] + ); + + let label = trex_file.read_nucleus_label(6)?; + assert_eq!( + label, + vec!["C", "Na", "C", "C 66", "C", "C", "H 99", "Ru", "H", "H", "H", "H"] + ); + + let basis_shell_num = trex_file.read_basis_shell_num()?; + assert_eq!(basis_shell_num, 24); + + let basis_nucleus_index = trex_file.read_basis_nucleus_index()?; + let ref_val: Vec = (0..24).collect(); + assert_eq!(basis_nucleus_index, ref_val); + + let state_id = trex_file.read_state_id()?; + assert_eq!(state_id, 2); + + let ao_num = trex_file.read_ao_num()?; + assert_eq!(ao_num, 1000); + + let mo_num = trex_file.read_mo_num()?; + assert_eq!(mo_num, 150); + + let mut energy_ref = Vec::with_capacity(mo_num); + for i in 0..mo_num { + let e: f64 = i as f64 - 100.0f64; + energy_ref.push(e); + } + let energy = trex_file.read_mo_energy()?; + assert_eq!(energy, energy_ref); + + let mut spin_ref = vec![0; mo_num]; + for i in mo_num / 2..mo_num { + spin_ref[i] = 1; + } + let spin = trex_file.read_mo_spin()?; + assert_eq!(spin, spin_ref); + + // Integrals + let nmax = 100; + let mut ao_2e_int_eri_ref = Vec::<(usize, usize, usize, usize, f64)>::with_capacity(nmax); + + let n_buffers = 8; + let bufsize = nmax / n_buffers + 10; + + for i in 0..100 { + // Quadruplet of indices + value + let data = (4 * i, 4 * i + 1, 4 * i + 2, 4 * i + 3, 3.14 + (i as f64)); + ao_2e_int_eri_ref.push(data); + } + + let mut offset = 0; + let mut ao_2e_int_eri = Vec::<(usize, usize, usize, usize, f64)>::with_capacity(nmax); + for _ in 0..n_buffers { + let buffer = trex_file.read_ao_2e_int_eri(offset, bufsize)?; + offset += buffer.len(); + ao_2e_int_eri.extend(buffer); + } + assert_eq!(ao_2e_int_eri_ref, ao_2e_int_eri); + + // Determinants + let det_num = trex_file.read_determinant_num()?; + assert_eq!(det_num, 50); + + let mut det_list_ref = Vec::with_capacity(det_num); + for i in 0..det_num { + let mut d = [0i64; 6]; + for j in 0..6 { + d[j] = 6 * (i as i64) + (j as i64); + } + det_list_ref.push(Bitfield::from_vec(&d)); + } + + let n_buffers = 8; + let bufsize = det_num / n_buffers + 20; + let mut offset = 0; + let mut det_list: Vec = Vec::with_capacity(det_num); + for _ in 0..n_buffers { + let buffer = trex_file.read_determinant_list(offset, bufsize)?; + offset += buffer.len(); + det_list.extend(buffer); + } + assert_eq!(det_list_ref, det_list); + + trex_file.close() +} + +#[test] +pub fn info() { + let _ = trexio::info(); +} + +use std::fs; + +#[test] +pub fn text_backend() { + let _ = write("tmp/test_write.dir", trexio::BackEnd::Text).unwrap(); + let _ = read("tmp/test_write.dir", trexio::BackEnd::Text).unwrap(); + fs::remove_dir_all("tmp/test_write.dir").unwrap() +} + +#[test] +pub fn hdf5_backend() { + let _ = write("tmp/test_write.hdf5", trexio::BackEnd::Hdf5).unwrap(); + let _ = read("tmp/test_write.hdf5", trexio::BackEnd::Hdf5).unwrap(); + fs::remove_file("tmp/test_write.hdf5").unwrap() +} diff --git a/rust/trexio/tmp/.gitignore b/rust/trexio/tmp/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/rust/trexio/tmp/.gitignore @@ -0,0 +1 @@ +* diff --git a/src/templates_front/templator_front.org b/src/templates_front/templator_front.org index 6663430..001c2cc 100644 --- a/src/templates_front/templator_front.org +++ b/src/templates_front/templator_front.org @@ -20,6 +20,258 @@ ** C + We include in thr trexio.h file a text variable that contains the + contents of the ~trex.json~ confguration file, generated from the + ~trex.org~ file. + + #+NAME: trex_json + #+begin_src python :results drawer +res = "/* JSON configuration\n" +with open('../../trex.json','r') as f: + for line in f: + res += line.rstrip()+'\n' +res += "*/" +return res + #+end_src + + #+RESULTS: trex_json + :results: + /* JSON configuration + { + + "metadata": { + "code_num" : [ "dim", [] ] + , "code" : [ "str", [ "metadata.code_num" ] ] + , "author_num" : [ "dim", [] ] + , "author" : [ "str", [ "metadata.author_num" ] ] + , "package_version" : [ "str", [] ] + , "description" : [ "str", [] ] + , "unsafe" : [ "int", [] ] + } , + + "nucleus": { + "num" : [ "dim" , [] ] + , "charge" : [ "float", [ "nucleus.num" ] ] + , "coord" : [ "float", [ "nucleus.num", "3" ] ] + , "label" : [ "str" , [ "nucleus.num" ] ] + , "point_group" : [ "str" , [] ] + , "repulsion" : [ "float", [] ] + } , + + "cell": { + "a" : [ "float", [ "3" ] ] + , "b" : [ "float", [ "3" ] ] + , "c" : [ "float", [ "3" ] ] + , "G_a" : [ "float", [ "3" ] ] + , "G_b" : [ "float", [ "3" ] ] + , "G_c" : [ "float", [ "3" ] ] + , "two_pi" : [ "int" , [] ] + } , + + "pbc": { + "periodic" : [ "int" , [] ] + , "k_point" : [ "float", [ "3" ] ] + } , + + "electron": { + "num" : [ "dim", [] ] + , "up_num" : [ "int", [] ] + , "dn_num" : [ "int", [] ] + } , + + "state": { + "num" : [ "dim" , [] ] + , "id" : [ "index", [] ] + , "energy" : [ "float", [] ] + , "current_label" : [ "str" , [] ] + , "label" : [ "str" , [ "state.num" ] ] + , "file_name" : [ "str" , [ "state.num" ] ] + } , + + "basis": { + "type" : [ "str" , [] ] + , "prim_num" : [ "dim" , [] ] + , "shell_num" : [ "dim" , [] ] + , "nao_grid_num" : [ "dim" , [] ] + , "interp_coeff_cnt" : [ "dim" , [] ] + , "nucleus_index" : [ "index", [ "basis.shell_num" ] ] + , "shell_ang_mom" : [ "int" , [ "basis.shell_num" ] ] + , "shell_factor" : [ "float", [ "basis.shell_num" ] ] + , "r_power" : [ "int" , [ "basis.shell_num" ] ] + , "nao_grid_start" : [ "index", [ "basis.shell_num" ] ] + , "nao_grid_size" : [ "dim" , [ "basis.shell_num" ] ] + , "shell_index" : [ "index", [ "basis.prim_num" ] ] + , "exponent" : [ "float", [ "basis.prim_num" ] ] + , "coefficient" : [ "float", [ "basis.prim_num" ] ] + , "prim_factor" : [ "float", [ "basis.prim_num" ] ] + , "e_cut" : [ "float", [] ] + , "nao_grid_radius" : [ "float", [ "basis.nao_grid_num" ] ] + , "nao_grid_phi" : [ "float", [ "basis.nao_grid_num" ] ] + , "nao_grid_grad" : [ "float", [ "basis.nao_grid_num" ] ] + , "nao_grid_lap" : [ "float", [ "basis.nao_grid_num" ] ] + , "interpolator_kind" : [ "str" , [] ] + , "interpolator_phi" : [ "float", [ "basis.nao_grid_num", "basis.interp_coeff_cnt" ] ] + , "interpolator_grad" : [ "float", [ "basis.nao_grid_num", "basis.interp_coeff_cnt" ] ] + , "interpolator_lap" : [ "float", [ "basis.nao_grid_num", "basis.interp_coeff_cnt" ] ] + } , + + "ecp": { + "max_ang_mom_plus_1" : [ "int" , [ "nucleus.num" ] ] + , "z_core" : [ "int" , [ "nucleus.num" ] ] + , "num" : [ "dim" , [] ] + , "ang_mom" : [ "int" , [ "ecp.num" ] ] + , "nucleus_index" : [ "index", [ "ecp.num" ] ] + , "exponent" : [ "float", [ "ecp.num" ] ] + , "coefficient" : [ "float", [ "ecp.num" ] ] + , "power" : [ "int" , [ "ecp.num" ] ] + } , + + "grid": { + "description" : [ "str" , [] ] + , "rad_precision" : [ "float", [] ] + , "num" : [ "dim" , [] ] + , "max_ang_num" : [ "int" , [] ] + , "min_ang_num" : [ "int" , [] ] + , "coord" : [ "float", [ "grid.num" ] ] + , "weight" : [ "float", [ "grid.num" ] ] + , "ang_num" : [ "dim" , [] ] + , "ang_coord" : [ "float", [ "grid.ang_num" ] ] + , "ang_weight" : [ "float", [ "grid.ang_num" ] ] + , "rad_num" : [ "dim" , [] ] + , "rad_coord" : [ "float", [ "grid.rad_num" ] ] + , "rad_weight" : [ "float", [ "grid.rad_num" ] ] + } , + + "ao": { + "cartesian" : [ "int" , [] ] + , "num" : [ "dim" , [] ] + , "shell" : [ "index", [ "ao.num" ] ] + , "normalization" : [ "float", [ "ao.num" ] ] + } , + + "ao_1e_int": { + "overlap" : [ "float", [ "ao.num", "ao.num" ] ] + , "kinetic" : [ "float", [ "ao.num", "ao.num" ] ] + , "potential_n_e" : [ "float", [ "ao.num", "ao.num" ] ] + , "ecp" : [ "float", [ "ao.num", "ao.num" ] ] + , "core_hamiltonian" : [ "float", [ "ao.num", "ao.num" ] ] + , "overlap_im" : [ "float", [ "ao.num", "ao.num" ] ] + , "kinetic_im" : [ "float", [ "ao.num", "ao.num" ] ] + , "potential_n_e_im" : [ "float", [ "ao.num", "ao.num" ] ] + , "ecp_im" : [ "float", [ "ao.num", "ao.num" ] ] + , "core_hamiltonian_im" : [ "float", [ "ao.num", "ao.num" ] ] + } , + + "ao_2e_int": { + "eri" : [ "float sparse", [ "ao.num", "ao.num", "ao.num", "ao.num" ] ] + , "eri_lr" : [ "float sparse", [ "ao.num", "ao.num", "ao.num", "ao.num" ] ] + , "eri_cholesky_num" : [ "dim" , [] ] + , "eri_cholesky" : [ "float sparse", [ "ao_2e_int.eri_cholesky_num", "ao.num", "ao.num" ] ] + , "eri_lr_cholesky_num" : [ "dim" , [] ] + , "eri_lr_cholesky" : [ "float sparse", [ "ao_2e_int.eri_lr_cholesky_num", "ao.num", "ao.num" ] ] + } , + + "mo": { + "type" : [ "str" , [] ] + , "num" : [ "dim" , [] ] + , "coefficient" : [ "float", [ "mo.num", "ao.num" ] ] + , "coefficient_im" : [ "float", [ "mo.num", "ao.num" ] ] + , "class" : [ "str" , [ "mo.num" ] ] + , "symmetry" : [ "str" , [ "mo.num" ] ] + , "occupation" : [ "float", [ "mo.num" ] ] + , "energy" : [ "float", [ "mo.num" ] ] + , "spin" : [ "int" , [ "mo.num" ] ] + } , + + "mo_1e_int": { + "overlap" : [ "float", [ "mo.num", "mo.num" ] ] + , "kinetic" : [ "float", [ "mo.num", "mo.num" ] ] + , "potential_n_e" : [ "float", [ "mo.num", "mo.num" ] ] + , "ecp" : [ "float", [ "mo.num", "mo.num" ] ] + , "core_hamiltonian" : [ "float", [ "mo.num", "mo.num" ] ] + , "overlap_im" : [ "float", [ "mo.num", "mo.num" ] ] + , "kinetic_im" : [ "float", [ "mo.num", "mo.num" ] ] + , "potential_n_e_im" : [ "float", [ "mo.num", "mo.num" ] ] + , "ecp_im" : [ "float", [ "mo.num", "mo.num" ] ] + , "core_hamiltonian_im" : [ "float", [ "mo.num", "mo.num" ] ] + } , + + "mo_2e_int": { + "eri" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "eri_lr" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "eri_cholesky_num" : [ "dim" , [] ] + , "eri_cholesky" : [ "float sparse", [ "mo_2e_int.eri_cholesky_num", "mo.num", "mo.num" ] ] + , "eri_lr_cholesky_num" : [ "dim" , [] ] + , "eri_lr_cholesky" : [ "float sparse", [ "mo_2e_int.eri_lr_cholesky_num", "mo.num", "mo.num" ] ] + } , + + "determinant": { + "num" : [ "dim readonly" , [] ] + , "list" : [ "int special" , [ "determinant.num" ] ] + , "coefficient" : [ "float buffered", [ "determinant.num" ] ] + } , + + "csf": { + "num" : [ "dim readonly" , [] ] + , "coefficient" : [ "float buffered", [ "csf.num" ] ] + , "det_coefficient" : [ "float sparse" , [ "csf.num", "determinant.num" ] ] + } , + + "amplitude": { + "single" : [ "float sparse", [ "mo.num", "mo.num" ] ] + , "single_exp" : [ "float sparse", [ "mo.num", "mo.num" ] ] + , "double" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "double_exp" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "triple" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "triple_exp" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "quadruple" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "quadruple_exp" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num", "mo.num" ] ] + } , + + "rdm": { + "1e" : [ "float" , [ "mo.num", "mo.num" ] ] + , "1e_up" : [ "float" , [ "mo.num", "mo.num" ] ] + , "1e_dn" : [ "float" , [ "mo.num", "mo.num" ] ] + , "2e" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "2e_upup" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "2e_dndn" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "2e_updn" : [ "float sparse", [ "mo.num", "mo.num", "mo.num", "mo.num" ] ] + , "2e_cholesky_num" : [ "dim" , [] ] + , "2e_cholesky" : [ "float sparse", [ "rdm.2e_cholesky_num", "mo.num", "mo.num" ] ] + , "2e_upup_cholesky_num" : [ "dim" , [] ] + , "2e_upup_cholesky" : [ "float sparse", [ "rdm.2e_upup_cholesky_num", "mo.num", "mo.num" ] ] + , "2e_dndn_cholesky_num" : [ "dim" , [] ] + , "2e_dndn_cholesky" : [ "float sparse", [ "rdm.2e_dndn_cholesky_num", "mo.num", "mo.num" ] ] + , "2e_updn_cholesky_num" : [ "dim" , [] ] + , "2e_updn_cholesky" : [ "float sparse", [ "rdm.2e_updn_cholesky_num", "mo.num", "mo.num" ] ] + } , + + "jastrow": { + "type" : [ "str" , [] ] + , "en_num" : [ "dim" , [] ] + , "ee_num" : [ "dim" , [] ] + , "een_num" : [ "dim" , [] ] + , "en" : [ "float" , [ "jastrow.en_num" ] ] + , "ee" : [ "float" , [ "jastrow.ee_num" ] ] + , "een" : [ "float" , [ "jastrow.een_num" ] ] + , "en_nucleus" : [ "index" , [ "jastrow.en_num" ] ] + , "een_nucleus" : [ "index" , [ "jastrow.een_num" ] ] + , "ee_scaling" : [ "float" , [] ] + , "en_scaling" : [ "float" , [ "nucleus.num" ] ] + } , + + "qmc": { + "num" : [ "dim" , [] ] + , "point" : [ "float", [ "qmc.num", "electron.num", "3" ] ] + , "psi" : [ "float", [ "qmc.num" ] ] + , "e_loc" : [ "float", [ "qmc.num" ] ] + } + + } + */ + :end: + + #+begin_src c :tangle prefix_front.h :noweb yes <
> #ifndef TREXIO_H @@ -32,6 +284,7 @@ extern "C" { #include #include +<> typedef int32_t trexio_exit_code; #+end_src diff --git a/trex.org b/trex.org index 769a172..9d296eb 100644 --- a/trex.org +++ b/trex.org @@ -1327,7 +1327,7 @@ power = [ #+name: jastrow | Variable | Type | Dimensions | Description | |---------------+----------+---------------------+-----------------------------------------------------------------| - | ~type~ | ~string~ | | Type of Jastrow factor: ~CHAMP~ or ~Mu~ | + | ~type~ | ~str~ | | Type of Jastrow factor: ~CHAMP~ or ~Mu~ | | ~en_num~ | ~dim~ | | Number of Electron-nucleus parameters | | ~ee_num~ | ~dim~ | | Number of Electron-electron parameters | | ~een_num~ | ~dim~ | | Number of Electron-electron-nucleus parameters | @@ -1345,7 +1345,7 @@ power = [ :results: #+begin_src python :tangle trex.json "jastrow": { - "type" : [ "string", [] ] + "type" : [ "str" , [] ] , "en_num" : [ "dim" , [] ] , "ee_num" : [ "dim" , [] ] , "een_num" : [ "dim" , [] ] diff --git a/version_memo.txt b/version_memo.txt new file mode 100644 index 0000000..67fcce7 --- /dev/null +++ b/version_memo.txt @@ -0,0 +1,8 @@ +To update the version, change: + +- configure.ac +- CMakeLists.txt +- ocaml/trexio/trexio.opam +- python/pytrexio/_version.py +- rust/trexio/Cargo.toml +