cp2k-rs 0.1.3

Rust bindings for CP2K with Python interface
Documentation
//! Rust bindings for CP2K
//!
//! This crate provides Rust bindings for the CP2K quantum chemistry package,
//! with additional Python bindings through PyO3.

mod ffi;
#[cfg(feature = "extended")]
mod ffi_extended;
mod force_env;

/// IPC protocol shared between the Python frontend and the MPI worker binary.
pub mod worker_protocol;

/// Pure-Rust worker management (spawning, IPC, lifecycle). GIL-free.
pub mod worker;

#[cfg(feature = "mpi")]
use mpi::{ffi::MPI_Comm_c2f, raw::AsRaw};

pub use force_env::{CP2KError, CP2KResult, ForceEnv};

#[cfg(feature = "python")]
pub mod python;

/// Initialize CP2K with MPI support
///
/// This function automatically detects if MPI is already initialized (e.g., by mpi4py
/// in Python or mpi crate in Rust) and uses the existing MPI environment if available.
/// Otherwise, it initializes MPI as part of CP2K initialization.
///
/// This is the recommended initialization function when using MPI, as it avoids
/// double MPI initialization conflicts.
pub fn init() -> CP2KResult<()> {
    unsafe {
        let result = ffi::cp2k_init_with_mpi_check();
        if result != 0 {
            return Err(CP2KError::InitializationError(format!(
                "CP2K initialization failed with code {}",
                result
            )));
        }
    }
    Ok(())
}

/// Initialize CP2K without MPI support
pub fn init_without_mpi() -> CP2KResult<()> {
    unsafe {
        ffi::cp2k_init_without_mpi();
    }
    Ok(())
}

/// Finalize CP2K with MPI support
///
/// This function finalizes CP2K without finalizing MPI if MPI was already
/// initialized by the host application (e.g., mpi4py or mpi crate).
/// The host application is responsible for finalizing MPI.
pub fn finalize() -> CP2KResult<()> {
    unsafe {
        let result = ffi::cp2k_finalize_with_mpi_check();
        if result != 0 {
            return Err(CP2KError::FinalizationError(format!(
                "CP2K finalization failed with code {}",
                result
            )));
        }
    }
    Ok(())
}

/// Finalize CP2K without MPI support
pub fn finalize_without_mpi() -> CP2KResult<()> {
    unsafe {
        ffi::cp2k_finalize_without_mpi();
    }
    Ok(())
}

/// Get the CP2K version string
pub fn get_version() -> CP2KResult<String> {
    const MAX_VERSION_LEN: usize = 256;
    let mut version_buf = vec![0u8; MAX_VERSION_LEN];

    unsafe {
        ffi::cp2k_get_version(version_buf.as_mut_ptr() as *mut i8, MAX_VERSION_LEN as i32);
    }

    // Find the null terminator
    let end = version_buf
        .iter()
        .position(|&c| c == 0)
        .unwrap_or(version_buf.len());
    version_buf.truncate(end);

    // Convert to String
    Ok(String::from_utf8(version_buf)?)
}

/// Run a CP2K input file
pub fn run_input(input_file: &str, output_file: &str) -> CP2KResult<()> {
    let input_c = std::ffi::CString::new(input_file)?;
    let output_c = std::ffi::CString::new(output_file)?;

    unsafe {
        ffi::cp2k_run_input(input_c.as_ptr(), output_c.as_ptr());
    }

    Ok(())
}

/// Run a CP2K input file with custom MPI communicator
#[cfg(feature = "mpi")]
pub fn run_input_comm(
    input_file: &str,
    output_file: &str,
    comm: &mpi::topology::SimpleCommunicator,
) -> CP2KResult<()> {
    let input_c = std::ffi::CString::new(input_file)?;
    let output_c = std::ffi::CString::new(output_file)?;
    let raw_comm = comm.as_raw();
    let fortran_comm = unsafe { MPI_Comm_c2f(raw_comm) };

    unsafe {
        ffi::cp2k_run_input_comm(input_c.as_ptr(), output_c.as_ptr(), fortran_comm);
    }

    Ok(())
}