Skip to content

Basic C API for GauXC#172

Open
awvwgk wants to merge 6 commits intowavefunction91:masterfrom
awvwgk:c-api
Open

Basic C API for GauXC#172
awvwgk wants to merge 6 commits intowavefunction91:masterfrom
awvwgk:c-api

Conversation

@awvwgk
Copy link
Copy Markdown
Collaborator

@awvwgk awvwgk commented Jan 19, 2026

  • GauXC::Molecule in gauxc/molecule.hpp
  • GauXC::Atom in gauxc/atom.hpp
  • GauXC::BasisSet in gauxc/basisset.hpp (double only)
  • GauXC::Shell in gauxc/shell.hpp
    • set_shell_tolerance method
  • GauXC::RuntimeEnvironment and GauXC::DeviceRuntimeEnvironment
    • set_buffer, comm_rank, comm_size
  • GauXC::AtomicGridSizeDefault, GauXC::PruningScheme, GauXC::RadialQuad in gauxc/enum.hpp
  • GauXC::MolGridFactory
    • create_default_molgrid
  • GauXC::MolGrid
  • GauXC::ExecutionSpace in gauxc/enum.hpp
  • GauXC::LoadBalancerFactory
    • get_shared_instance method
  • GauXC::MolecularWeightsFactory
    • get_instance method
  • GauXC::MolecularWeightsSettings
  • GauXC::functional_type (from ExchCXX)
  • GauXC::ReplicatedXCIntegrator
    • eval_exc, eval_exc_vxc, etc. methods

Closes #171

@awvwgk awvwgk self-assigned this Jan 19, 2026
@awvwgk awvwgk force-pushed the c-api branch 3 times, most recently from dd396c2 to 5881a76 Compare January 26, 2026 09:54
@awvwgk awvwgk force-pushed the c-api branch 3 times, most recently from 1d61b38 to 83841ad Compare January 27, 2026 10:05
@awvwgk awvwgk changed the title [WIP] Basic C API for GauXC Basic C API for GauXC Jan 27, 2026
@awvwgk awvwgk added the enhancement New feature or request label Jan 27, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a comprehensive C API for the GauXC library, enabling integration with C codebases and other languages that interface with C. The implementation wraps core GauXC C++ functionality including molecular structures, basis sets, integration grids, load balancing, and XC integrators.

Changes:

  • Added C API bindings for core classes (Molecule, Atom, BasisSet, Shell, etc.)
  • Implemented factory patterns for runtime environments, load balancers, molecular weights, and XC integrators
  • Created C/C++ compatible enum definitions with mappings between C and C++ versions
  • Added HDF5 I/O support for C API
  • Refactored configuration headers to separate C and C++ concerns

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
include/gauxc/*.h New C API header files defining opaque handles and functions for core types
include/gauxc/util/c_*.hpp Helper headers for casting between C handles and C++ objects
src/c_*.cxx C API implementation files wrapping C++ functionality
include/gauxc/enum.h C-compatible enum definitions
include/gauxc/enums.hpp C++ enum class definitions mapped to C enums
include/gauxc/gauxc_config.h.in C-compatible configuration header
include/gauxc/gauxc_config.hpp C++ configuration header including C header
tests/moltypes_test.cxx Basic C API interoperability test
src/CMakeLists.txt CMake integration for conditional C API compilation
src/external/c_hdf5_*.cxx HDF5 I/O functions for C API

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 48 changed files in this pull request and generated 17 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- provide C enums
- add atom and molecule types
- add basis set and shell definitions
- add molecule grid and runtime environment
- add load balancer to C API
- add molecular weights for C API
- add functional class wrapping ExchCXX
- add xc integrator and matrix type
- add references for functionals
- add support for reading and writing from HDF5 in C
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 49 out of 49 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@awvwgk awvwgk force-pushed the c-api branch 2 times, most recently from 4c63cdc to 4d683b7 Compare March 2, 2026 16:31
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 49 out of 49 changed files in this pull request and generated 13 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +22 to +26
static inline void gauxc_status_init(C::GauXCStatus* status) {
if (status != nullptr) {
status->code = 0;
status->message = nullptr;
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gauxc_status_init overwrites status->message with nullptr without freeing any previously allocated message. This will leak if callers reuse a GauXCStatus across multiple API calls after an error. Consider freeing status->message in gauxc_status_init (or calling gauxc_status_delete internally) before resetting fields.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +36
#include <cstring>

#include <gauxc/c/status.h>

#include <gauxc/exceptions.hpp>

namespace GauXC::detail {

static inline void gauxc_status_init(C::GauXCStatus* status) {
if (status != nullptr) {
status->code = 0;
status->message = nullptr;
}
}

static inline void gauxc_status_handle(C::GauXCStatus* status, int code, const char* message) {
if (status != nullptr) {
status->code = code;
if (status->message != nullptr) {
std::free(status->message);
}
status->message = (char*)std::malloc(std::strlen(message) + 1);
std::strcpy(status->message, message);
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header uses std::malloc/std::free but does not include <cstdlib>, relying on transitive includes for declarations. Add the proper include to make the header self-contained and avoid build failures under stricter compilers.

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +38
void gauxc_object_delete(GauXCStatus* status, void** obj) {
detail::gauxc_status_init(status);
if(obj == nullptr) return;

struct GauXCObject {
GauXCHeader hdr;
};

GauXCHeader* header = &reinterpret_cast<struct GauXCObject*>(*obj)->hdr;

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gauxc_object_delete dereferences *obj unconditionally (reinterpret_cast<...>(*obj)), so passing a non-null obj whose value is nullptr will segfault. Add a if (*obj == nullptr) return; (or set an error) before reading the header.

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +64
* @param status Status object to capture any errors.
* @param ptr Pointer to the GauXC object to delete.
*/
extern void gauxc_object_delete(
GauXCStatus* status,
void** ptr
);

/**
* @brief Delete all GauXC objects.
* @param status Status object to capture any errors.
* @param ptrs Array of pointers to GauXC objects.
* @param nptrs Number of pointers in the array.
*/
extern void gauxc_objects_delete(
GauXCStatus* status,
void** ptrs,
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API docs for gauxc_object_delete are misleading: the implementation expects void** where *ptr points to a handle struct (e.g., GauXCMolecule*), not the underlying handle.ptr. Please clarify the documentation (and ideally the parameter name) to reflect the required calling convention, otherwise consumers are likely to pass the wrong pointer type and crash.

Suggested change
* @param status Status object to capture any errors.
* @param ptr Pointer to the GauXC object to delete.
*/
extern void gauxc_object_delete(
GauXCStatus* status,
void** ptr
);
/**
* @brief Delete all GauXC objects.
* @param status Status object to capture any errors.
* @param ptrs Array of pointers to GauXC objects.
* @param nptrs Number of pointers in the array.
*/
extern void gauxc_objects_delete(
GauXCStatus* status,
void** ptrs,
*
* This function expects a pointer to a GauXC handle object pointer. For example,
* for a molecule handle `GauXCMolecule* mol`, you must pass `&mol` to this
* function (type `void**` after implicit conversion), not the underlying
* implementation pointer stored inside the handle.
*
* The function will delete the underlying GauXC object associated with the
* handle and may set the handle to `NULL` on success.
*
* @param status Status object to capture any errors.
* @param handle Address of the GauXC handle object pointer to delete
* (e.g., `(void**)&mol` where `mol` is a `GauXCMolecule*`).
*/
extern void gauxc_object_delete(
GauXCStatus* status,
void** handle
);
/**
* @brief Delete all GauXC objects.
*
* This function is the batched counterpart of ::gauxc_object_delete. It expects
* an array of pointers to GauXC handle object pointers. Each element of
* `handles` should be the address of a handle object pointer (e.g., entries of
* type `GauXCMolecule**`, passed as `void**`).
*
* For each element, the underlying GauXC object will be deleted and the
* corresponding handle pointer may be set to `NULL` on success.
*
* @param status Status object to capture any errors.
* @param handles Array of addresses of GauXC handle object pointers.
* @param nptrs Number of pointers in the array.
*/
extern void gauxc_objects_delete(
GauXCStatus* status,
void** handles,

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +52
void gauxc_runtime_environment_delete(GauXCStatus* status, GauXCRuntimeEnvironment* env) {
detail::gauxc_status_init(status);
if (env == nullptr) return;
if (env->ptr != nullptr)
delete detail::get_runtime_environment_ptr(*env);
env->ptr = nullptr;
#ifdef GAUXC_HAS_DEVICE
if (env->device_ptr != nullptr)
delete detail::get_device_runtime_environment_ptr(*env);
env->device_ptr = nullptr;
#endif
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gauxc_runtime_environment_delete deletes get_runtime_environment_ptr(*env), but get_runtime_environment_ptr prefers device_ptr when set. If a consumer ever populates both ptr and device_ptr (the handle struct is public in the C header), this can lead to deleting device_ptr twice (once via ptr path and once via the explicit device_ptr delete). Delete env->ptr and env->device_ptr independently without routing through the selector helper.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +17
#include <gauxc/c/gauxc_config.h>
#ifdef GAUXC_HAS_HDF5
#include <gauxc/c/status.h>
#include <gauxc/c/basisset.h>
#include <gauxc/c/molecule.h>

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header is missing an include guard (#pragma once or #ifndef/#define). Without it, including gauxc/c/hdf5.h from multiple translation units (or indirectly via other headers) can cause duplicate declaration / redefinition issues depending on build settings. Please add #pragma once (consistent with the other C headers).

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +41
void gauxc_molecule_write_hdf5_record(
GauXCStatus* status,
GauXCMolecule mol,
const char* fname,
const char* dset
) {
detail::gauxc_status_init(status);
try {
write_hdf5_record( *detail::get_molecule_ptr(mol), std::string(fname), std::string(dset) );
} catch(std::exception& e) {
detail::gauxc_status_handle(status, 1, e.what());
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These HDF5 C-API functions dereference the internal mol.ptr/basis.ptr and construct std::string(fname) / std::string(dset) without validating inputs. Passing a default/invalid handle or a null fname/dset will crash/UB rather than setting status. Please add explicit null checks and fail via gauxc_status_handle before dereferencing or constructing strings.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +41
void gauxc_molecule_read_hdf5_record(
GauXCStatus* status,
GauXCMolecule mol,
const char* fname,
const char* dset
) {
detail::gauxc_status_init(status);
try {
read_hdf5_record( *detail::get_molecule_ptr(mol), std::string(fname), std::string(dset) );
} catch(std::exception& e) {
detail::gauxc_status_handle(status, 1, e.what());
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These HDF5 C-API functions dereference the internal mol.ptr/basis.ptr and construct std::string(fname) / std::string(dset) without validating inputs. Passing a default/invalid handle or a null fname/dset will crash/UB rather than setting status. Please add explicit null checks and fail via gauxc_status_handle before dereferencing or constructing strings.

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +54
* @param atoms Pointer to an array of GauXCAtom.
* @param natoms Number of atoms in the array.
* @return Handle to the created Molecule.
*/
extern GauXCMolecule gauxc_molecule_new_from_atoms(GauXCStatus* status, GauXCAtom* atoms, size_t natoms );
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gauxc_molecule_new_from_atoms does not modify the atoms array, but the API takes a non-const GauXCAtom*. Consider making this const GauXCAtom* (and updating the implementation signature) to better express ownership/immutability and allow callers to pass const data.

Suggested change
* @param atoms Pointer to an array of GauXCAtom.
* @param natoms Number of atoms in the array.
* @return Handle to the created Molecule.
*/
extern GauXCMolecule gauxc_molecule_new_from_atoms(GauXCStatus* status, GauXCAtom* atoms, size_t natoms );
* @param atoms Pointer to an array of GauXCAtom (not modified).
* @param natoms Number of atoms in the array.
* @return Handle to the created Molecule.
*/
extern GauXCMolecule gauxc_molecule_new_from_atoms(GauXCStatus* status, const GauXCAtom* atoms, size_t natoms );

Copilot uses AI. Check for mistakes.
*/
extern GauXCBasisSet gauxc_basisset_new_from_shells(
GauXCStatus* status,
GauXCShell* shells,
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gauxc_basisset_new_from_shells does not modify the shells array, but the API takes a non-const GauXCShell*. Consider making this const GauXCShell* (and updating the implementation signature) to improve const-correctness for C callers.

Suggested change
GauXCShell* shells,
const GauXCShell* shells,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C API Related to the C API enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Basic C API

4 participants