Conversation
dd396c2 to
5881a76
Compare
1d61b38 to
83841ad
Compare
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
- remove matrix class - directly expose ReplicatedXCIntegrator API
4c63cdc to
4d683b7
Compare
There was a problem hiding this comment.
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.
| static inline void gauxc_status_init(C::GauXCStatus* status) { | ||
| if (status != nullptr) { | ||
| status->code = 0; | ||
| status->message = nullptr; | ||
| } |
There was a problem hiding this comment.
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.
| #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); |
There was a problem hiding this comment.
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.
| 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; | ||
|
|
There was a problem hiding this comment.
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.
| * @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, |
There was a problem hiding this comment.
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.
| * @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, |
| 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 |
There was a problem hiding this comment.
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.
| #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> | ||
|
|
There was a problem hiding this comment.
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).
| 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()); |
There was a problem hiding this comment.
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.
| 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()); |
There was a problem hiding this comment.
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.
| * @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 ); |
There was a problem hiding this comment.
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.
| * @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 ); |
| */ | ||
| extern GauXCBasisSet gauxc_basisset_new_from_shells( | ||
| GauXCStatus* status, | ||
| GauXCShell* shells, |
There was a problem hiding this comment.
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.
| GauXCShell* shells, | |
| const GauXCShell* shells, |
GauXC::Moleculeingauxc/molecule.hppGauXC::Atomingauxc/atom.hppGauXC::BasisSetingauxc/basisset.hpp(double only)GauXC::Shellingauxc/shell.hppset_shell_tolerancemethodGauXC::RuntimeEnvironmentandGauXC::DeviceRuntimeEnvironmentset_buffer,comm_rank,comm_sizeGauXC::AtomicGridSizeDefault,GauXC::PruningScheme,GauXC::RadialQuadingauxc/enum.hppGauXC::MolGridFactorycreate_default_molgridGauXC::MolGridGauXC::ExecutionSpaceingauxc/enum.hppGauXC::LoadBalancerFactoryget_shared_instancemethodGauXC::MolecularWeightsFactoryget_instancemethodGauXC::MolecularWeightsSettingsGauXC::functional_type(from ExchCXX)GauXC::ReplicatedXCIntegratoreval_exc,eval_exc_vxc, etc. methodsCloses #171