diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
index 85c0235..ae0a57d 100644
--- a/.github/workflows/examples.yml
+++ b/.github/workflows/examples.yml
@@ -17,6 +17,7 @@ jobs:
matrix:
toolchain: ["openmp", "mpi"]
gauxc: ["release", "main"]
+ example: ["cpp", "c", "fortran"]
steps:
- uses: actions/checkout@v4
@@ -26,7 +27,7 @@ jobs:
- name: Setup micromamba
uses: mamba-org/setup-micromamba@v1
with:
- environment-file: skala/examples/cpp/gauxc_integration/environment-${{ matrix.toolchain }}.yml
+ environment-file: skala/examples/${{ matrix.example }}/gauxc_integration/environment-${{ matrix.toolchain }}.yml
environment-name: gauxc-dev
cache-environment: true
cache-downloads: true
@@ -48,6 +49,8 @@ jobs:
-DGAUXC_ENABLE_MPI=${{ matrix.toolchain == 'mpi' }}
-DGAUXC_ENABLE_CUDA=${{ matrix.toolchain == 'cuda' }}
-DGAUXC_ENABLE_ONEDFT=ON
+ -DGAUXC_ENABLE_C=${{ matrix.example == 'c' || matrix.example == 'fortran' }}
+ -DGAUXC_ENABLE_FORTRAN=${{ matrix.example == 'fortran' }}
-DBUILD_SHARED_LIBS=ON
-DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX
shell: micromamba-shell {0}
@@ -66,7 +69,7 @@ jobs:
run: >-
cmake
-B build_example
- -S skala/examples/cpp/gauxc_integration
+ -S skala/examples/${{ matrix.example }}/gauxc_integration
-G Ninja
-DSkala_GauXC_ENABLE_OPENMP=${{ matrix.toolchain == 'openmp' }}
-DSkala_GauXC_ENABLE_MPI=${{ matrix.toolchain == 'mpi' }}
@@ -84,6 +87,7 @@ jobs:
- name: Run example
run: >-
+ ${{ matrix.toolchain == 'mpi' && 'mpirun -np 1 --map-by ppr:1:numa:pe=2 --bind-to core' || '' }}
Skala
./gauxc/tests/ref_data/onedft_he_def2qzvp_tpss_uks.hdf5
--model TPSS
diff --git a/docs/gauxc/c-library.rst b/docs/gauxc/c-library.rst
new file mode 100644
index 0000000..17f4f9f
--- /dev/null
+++ b/docs/gauxc/c-library.rst
@@ -0,0 +1,506 @@
+GauXC in C
+==========
+
+This guide shows how to use the GauXC library in C applications.
+We will cover
+
+* setting up a CMake project to include GauXC as dependency
+* initializing the GauXC runtime environment
+* reading molecule, basis set, and density matrix from an HDF5 input file
+* setting up the integration grid, load balancer, and exchange-correlation integrator
+* performing the exchange-correlation evaluation and outputting the result
+
+Setting up CMake
+----------------
+
+GauXC can be most conveniently used via CMake, therefore we will setup a minimal CMake project for a command line driver to use GauXC.
+Next to GauXC we will use other dependencies as needed.
+
+The directory structure for the project will be
+
+.. code-block:: text
+
+ ├── CMakeLists.txt
+ ├── app
+ │ └── main.c
+ └── cmake
+ ├── skala-argtable3.cmake
+ ├── skala-dep-versions.cmake
+ └── skala-gauxc.cmake
+
+First we create the main ``CMakeLists.txt`` to define our project, include our dependencies, and declare our executable.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/CMakeLists.txt
+ :language: cmake
+ :caption: CMakeLists.txt
+
+For handling the dependencies, we create a separate file in the ``cmake/`` subdirectory to include the path and checksums for all our dependencies.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/cmake/skala-dep-versions.cmake
+ :language: cmake
+ :caption: cmake/skala-dep-versions.cmake
+
+For each dependency we will create a separate CMake include file for finding and making it available.
+First, we define how we will include GauXC our main dependency.
+For this we can rely in most cases to discover the GauXC config file, however we also provide a fallback to download and build GauXC in case it is not available in the environment.
+The options we defined in the main CMake file will be passed through to GauXC to ensure the library provides the requested features.
+Furthermore, after having GauXC available, we double check whether our requirements for GauXC are satisfied, this is especially necessary for the Skala implementation, which requires the ``GAUXC_HAS_ONEDFT`` feature flag.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/cmake/skala-gauxc.cmake
+ :language: cmake
+ :caption: cmake/skala-gauxc.cmake
+
+For our command line driver, we will be using `Argtable3 `__ to create the command line interface.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/cmake/skala-argtable3.cmake
+ :language: cmake
+ :caption: cmake/skala-argtable3.cmake
+
+With this we have the full CMake setup we need for creating our command line driver.
+
+
+Setting up headers
+------------------
+
+For our main driver program we include the relevant headers from GauXC, next to the ones needed for the HDF5 I/O and command line interface.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 1-24
+ :caption: app/main.c (header includes)
+
+For each of the GauXC components we will be using, we include the respective header file from GauXC.
+
+`gauxc/status.h`
+ For handling GauXC status codes and errors.
+
+`gauxc/molecule.h`
+ For handling molecular data structures, like atomic numbers and coordinates.
+
+`gauxc/basisset.h`
+ For handling basis set data, like basis function definitions.
+
+`gauxc/molgrid.h`
+ For setting up and managing the integration grid.
+
+`gauxc/runtime_environment.h`
+ For interacting with MPI and device runtime environments.
+
+`gauxc/load_balancer.h`
+ For setting up and managing the load balancer for distributing grid points.
+
+`gauxc/molecular_weights.h`
+ For computing molecular weights needed for grid generation.
+
+`gauxc/functional.h`
+ For handling exchange-correlation functionals.
+
+`gauxc/xc_integrator.h`
+ For setting up and managing the exchange-correlation integrator.
+
+`gauxc/matrix.h`
+ For handling matrix data structures, like density matrices and potentials.
+
+We start our main driver with initializing the MPI environment, if GauXC was built with MPI support.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 104-109
+ :caption: app/main.c (MPI initialize)
+
+For the finalization of the MPI environment we also add a guarded call to the MPI finalize function at the end of our main program.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 335-337,339
+ :caption: app/main.c (MPI finalize)
+
+GauXC provides a macro ``GAUXC_HAS_MPI`` to inform users whether GauXC was built with MPI support.
+We can use this macro to conditionally compile the MPI initialization and finalization code only when GauXC was built with MPI support.
+
+
+Creating the Command Line Driver
+--------------------------------
+
+We will provide our input data mainly via an HDF5 input file.
+This file will contain the molecular structure, basis set, and density matrix.
+Additionally, we have parameters for defining the integration grid and also the parallelization strategy.
+Our main parameters for the input will therefore be
+
+``input_file``
+ an HDF5 input file to provide the molecule, basis and density matrix
+
+``model``
+ the model checkpoint we want to evaluate
+
+``grid_spec``
+ the grid size specification which defines the number of angular and radial integration points
+
+``rad_quad_spec``
+ the radial quadrature scheme which defines the spacing of radial integration points
+
+``prune_spec``
+ the pruning scheme for combining the atomic grids to a molecular one
+
+For the command line driver we will use Argtable3 to handle the command line arguments concisely.
+We define the command line arguments for the input HDF5 file, model type, and other parameters using Argtable3.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 110-135
+ :caption: app/main.c (command line arguments)
+
+.. note::
+
+ The settings for the molecular grid (``grid_spec``, ``rad_quad_spec``, ``prune_spec``) are defined in more detail in :ref:`gauxc_molecular_grid_settings` reference.
+
+With this we can parse the command line and handle potential errors.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 137-157
+ :caption: app/main.c (parse command line arguments)
+
+Finally, we can extract the values of the command line arguments and store them in variables for later use.
+For this purpose we will define two helper functions, one for copying the values from the Argtable3 structs and a normalization function to ensure all inputs are lowercase.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 86-102
+ :caption: app/main.c (helper functions for command line arguments)
+
+With this we can extract the command line argument values.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 159-167
+ :caption: app/main.c (extract command line argument values)
+
+At this point we can already free the Argtable3 structures as we do not need them anymore.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 169-170
+ :caption: app/main.c (free Argtable3 structures)
+
+Also, we want to ensure our input string variables will get freed at the end of our program.
+We add the respective ``free()`` calls at the end of our main program, before the MPI finalization.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 319-326
+ :caption: app/main.c (free input strings)
+
+Before adding any further implementation, we will create a first build of the project.
+To configure the CMake build run
+
+.. code-block:: shell
+
+ cmake -B build -G Ninja -S .
+ cmake --build build
+
+After we build our project successfully, we can run our driver directly from the build directory with
+
+.. code-block:: shell
+
+ ./build/Skala --help
+
+As output you should see the help page generated by our command-line interface
+
+.. code-block:: text
+
+ Usage: ./_build/Skala --model= [--grid-spec=] [--radial-quad=] [--prune-scheme=] [--lb-exec-space=] [--int-exec-space=] [--batch-size=] [--basis-tol=] [--help]
+
+ Options:
+ Input file containing molecular geometry and density matrix
+ --model= OneDFT model to use, can be a path to a checkpoint
+ --grid-spec= Atomic grid size specification (default: Fine)
+ Possible values are: Fine, UltraFine, SuperFine, GM3, GM5
+ --radial-quad= Radial quadrature scheme (default: MuraKnowles)
+ Possible values are: Becke, MuraKnowles, TreutlerAhlrichs, MurrayHandyLaming
+ --prune-scheme= Pruning scheme (default: Robust)
+ Possible values are: Unpruned, Robust, Treutler
+ --lb-exec-space= Load balancer execution space
+ Possible values are: Host, Device
+ --int-exec-space= Integrator execution space
+ Possible values are: Host, Device
+ --batch-size= Batch size for grid point processing (default: 512)
+ --basis-tol= Basis function evaluation tolerance (default: 1e-10)
+ --help Print this help and exit
+
+With this we are able to change our configuration conveniently from the command line.
+
+.. tip::
+
+ If you encounter any issues when running the driver, like segmentation faults, rerun your binary with a debugger, like gdb.
+
+ .. code-block:: shell
+
+ gdb --args ./build/Skala --help
+
+ This way you can inspect the stack trace and find the source of the error more easily.
+
+
+As first step for any interaction with GauXC, we need to initialize the GauXC status object and runtime environment.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 172-186
+ :caption: app/main.c (GauXC status and runtime environment)
+
+.. tip::
+
+ We also define an array of pointers to GauXC objects we create during our program.
+ This will help us to free all allocated memory at the end of our program.
+
+The ``world_size`` and ``world_rank`` variables describe our MPI environment and contain dummy values if we do not use MPI.
+
+At this point we want to show the configuration we are using before proceeding further.
+We can use the ``world_rank`` variable to ensure that only the root process outputs the configuration in case of MPI parallel execution.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :lines: 188-196
+ :caption: app/main.c (configuration summary)
+
+At the end of our program we add a label for handling errors and process the status code and message accordingly.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (handle status code)
+ :lines: 312-318
+
+Finally, we also add calls to free the runtime environment at the end of our main program.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (free runtime)
+ :lines: 327-333
+
+We can use the error code from the status to adjust our own program return code.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (return exit code)
+ :lines: 335-339
+
+Molecule data
+-------------
+
+For reading the molecule we will use GauXC's built-in functionality to read from an HDF5 dataset.
+
+.. note::
+
+ GauXC stores the information about the atomic numbers and their coordinates as an array of structs, defined in C++ as
+
+ .. code-block:: c++
+
+ struct GauXC::Atom {
+ int64_t Z; ///< Atomic number
+ double x; ///< X coordinate (bohr)
+ double y; ///< Y coordinate (bohr)
+ double z; ///< Z coordinate (bohr)
+ };
+ class GauXC::Molecule : public std::vector {
+ ...
+ };
+
+ The HDF5 wrapper directly maps this struct representation to an HDF5 dataset.
+
+We use ``gauxc_molecule_read_hdf5_record`` function which implements the reading of the molecule data.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (read molecule)
+ :lines: 198-204
+
+By registering the molecule object in our GauXC objects array, we ensure that it will be freed at the end of our main program.
+
+
+Basis set data
+--------------
+
+For the basis set we will use the same approach as for the molecule and use GauXC's built-in HDF5 reading functionality.
+
+.. note::
+
+ Similar to the molecule the basis set object is built as an array of shell objects.
+ The shell object contains the information about the primitives, contraction coefficients, angular momentum, and center of the shell.
+
+ .. code-block:: c++
+
+ template
+ class alignas(256) GauXC::Shell {
+ std::array alpha; ///< exponents of primitives
+ std::array coeff; ///< contraction coefficients
+ std::array O; ///< origin of the shell
+ int32_t nprim; ///< number of primitives
+ int32_t l; ///< angular moment of the shell
+ int32_t pure; ///< pure=1: spherical Gaussianss; pure=0: cartesian Gaussianss
+ };
+
+ template
+ struct GauXC::BasisSet : public std::vector> {
+ ...
+ };
+
+ Again, this allows to directly map the object's representation to an HDF5 dataset.
+
+With GauXC's ``gauxc_basisset_read_hdf5_record`` function we can read the basis set data conveniently from the HDF5 file.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (read basisset)
+ :lines: 206-212
+
+Integration grid
+----------------
+
+To setup the integration grid, which is the part of the input to GauXC for computing the exchange-correlation functional, we create a molecular grid.
+We have three main input parameters here, the grid size which defines the density of angular and radial points, the radial quadrature scheme which defines the spacing of the radial points, and the pruning scheme which defines how atomic grids are combined to a molecular grid.
+In GauXC these are defined as enumerators and we add a number of helper functions for turning the input strings from the command-line to the respective enumerator values.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (enumerator conversion functions)
+ :lines: 38-84
+
+For the main program we can now create the molecular grid based on our input parameters.
+We also have to define the batch size for the grid, the default is 512 points per batch, however larger values up around 10000 are recommended for better performance.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (grid setup)
+ :lines: 214-229
+
+Exchange-correlation integrator
+-------------------------------
+
+To distribute the work of evaluating the exchange-correlation functional on the grid points, we create a load balancer.
+The load balancer will take care of distributing the grid points to the available resources, either host or device, based on the execuation space we provide.
+Again we have a helper function to convert the input string to the respective enumerator value.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (execuation space enumerator)
+ :lines: 26-36
+
+Note that the load balancer will provide access to the molecule, basis and grid data for all further usage in GauXC.
+We can now create the load balancer based on our input parameters.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (load balancer setup)
+ :lines: 231-256
+
+Finally, we can create the main GauXC integrator, for this we setup the exchange-correlation integrator factory for producing an instance of the integrator.
+To configure the integrator we create an additional settings object which holds the model checkpoint we want to evaluate.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (integrator setup)
+ :lines: 258-268
+
+Density matrix
+--------------
+
+The final input we need to provide to GauXC is the density matrix.
+Similar to the molecule and basis set we will read it from our HDF5 input file.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (read density matrix)
+ :lines: 274-284
+
+Exchange-correlation evaluation
+-------------------------------
+
+With all inputs provided we can now perform the exchange-correlation evaluation.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (exchange-correlation evaluation)
+ :lines: 290-300
+
+After the evaluation we can output the computed exchange-correlation energy.
+
+.. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c (exchange-correlation output)
+ :lines: 306-310
+
+Now we can rebuild our project with
+
+.. code-block:: shell
+
+ cmake --build build
+
+After we build our project successfully, we run the driver again from the build directory
+
+.. code-block:: shell
+
+ ./build/Skala He_def2-svp.h5 --model PBE
+
+.. note::
+
+ The ``He_def2-svp.h5`` input file can be created with the ``skala`` package.
+
+ .. literalinclude:: scripts/export-h5.py
+ :language: python
+
+As output we can see the results for the PBE functional
+
+.. code-block:: text
+
+ Configuration
+ -> Input file : He_def2-svp.h5
+ -> Model : PBE
+ -> Grid : fine
+ -> Radial quadrature : muraknowles
+ -> Pruning scheme : robust
+
+ Results
+ -> EXC : -1.0540318683
+
+Download checkpoint from HuggingFace
+------------------------------------
+
+To evaluate Skala we first need to download the model checkpoint from HuggingFace.
+For this we can use the ``hf`` command line tool from the ``huggingface_hub`` Python package.
+After downloading the model checkpoint we can run our driver again with the new model
+
+.. code-block:: shell
+
+ hf download microsoft/skala skala-1.0.fun --local-dir .
+ ./build/Skala He_def2-svp.h5 --model ./skala-1.0.fun
+
+In the output we can see the results for the Skala functional
+
+.. code-block:: text
+
+ Configuration
+ -> Input file : He_def2-svp.h5
+ -> Model : ./skala-1.0.fun
+ -> Grid : fine
+ -> Radial quadrature : muraknowles
+ -> Pruning scheme : robust
+
+ Results
+ -> EXC : -1.0712560886
+
+Full source code
+----------------
+
+.. dropdown:: Full source code of the main driver
+
+ .. literalinclude:: ../../examples/c/gauxc_integration/app/main.c
+ :language: c
+ :caption: app/main.c
+
+Summary
+-------
+
+Within this guide the usage of GauXC in C applications was demonstrated.
+A minimal CMake project was created to setup the build environment and include GauXC.
+We created a command line driver which reads the molecule, basis set, and density matrix from an HDF5 input file and sets up the integration grid, load balancer, and exchange-correlation integrator.
+Finally, we evaluated the exchange-correlation energy and potential and output the result.
\ No newline at end of file
diff --git a/docs/gauxc/cpp-library.rst b/docs/gauxc/cpp-library.rst
index c2a4aee..2f5b879 100644
--- a/docs/gauxc/cpp-library.rst
+++ b/docs/gauxc/cpp-library.rst
@@ -56,7 +56,7 @@ Furthermore, after having GauXC available, we double check whether our requireme
:caption: cmake/skala-gauxc.cmake
While GauXC provides the implementation to evaluate the exchange-correlation functional, it is independent to the library used for storing matrices.
-For our example here we will be using Eigen3.
+For our example here we will be using `Eigen3 `__.
Similar to GauXC we will attempt to find Eigen3 via its config file and fallback to downloading it.
Since Eigen3 is a header-only library, we just need to reexport the include directory of the project.
@@ -64,7 +64,7 @@ Since Eigen3 is a header-only library, we just need to reexport the include dire
:language: cmake
:caption: cmake/skala-eigen3.cmake
-For our command line driver, we will be using CLI11 to create the command line interface.
+For our command line driver, we will be using `CLI11 `__ to create the command line interface.
Similar to Eigen3, CLI11 is a header-only library and we will use the same approach for including its headers if the dependency can not be found in the environment.
.. literalinclude:: ../../examples/cpp/gauxc_integration/cmake/skala-cli11.cmake
@@ -420,7 +420,7 @@ In the output we can see the results for the Skala functional
Configuration
-> Input file : He_def2-svp.h5
- -> Model : ./skala-v1.0.fun
+ -> Model : ./skala-1.0.fun
-> Grid : fine
-> Radial quadrature : muraknowles
-> Pruning scheme : robust
diff --git a/docs/gauxc/fortran-library.rst b/docs/gauxc/fortran-library.rst
new file mode 100644
index 0000000..3c7984a
--- /dev/null
+++ b/docs/gauxc/fortran-library.rst
@@ -0,0 +1,558 @@
+GauXC in Fortran
+================
+
+This guide demonstrates how to use the GauXC library from Fortran to evaluate exchange-correlation functionals, including the Skala neural network functional.
+By the end of this tutorial, you will:
+
+* Set up a CMake project with GauXC as a dependency
+* Initialize the GauXC runtime environment
+* Read molecule, basis set, and density matrix from an HDF5 input file
+* Configure the integration grid, load balancer, and XC integrator
+* Evaluate the exchange-correlation energy and potential
+
+Setting up CMake
+----------------
+
+GauXC integrates most conveniently via CMake.
+We will set up a minimal CMake project for a command-line driver that uses GauXC.
+In addition to GauXC, we will include other dependencies as needed.
+
+The directory structure for the project will be
+
+.. code-block:: text
+
+ ├── CMakeLists.txt
+ ├── app
+ │ └── main.F90
+ └── cmake
+ ├── skala-flap.cmake
+ ├── skala-dep-versions.cmake
+ └── skala-gauxc.cmake
+
+First, we create the main ``CMakeLists.txt`` to define our project, include dependencies, and declare the executable.
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/CMakeLists.txt
+ :language: cmake
+ :caption: CMakeLists.txt
+
+To manage dependencies cleanly, we create a separate file in the ``cmake/`` subdirectory that stores URLs and checksums for all external packages.
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/cmake/skala-dep-versions.cmake
+ :language: cmake
+ :caption: cmake/skala-dep-versions.cmake
+
+Each dependency gets its own CMake include file.
+First, we define how to include GauXC, our main dependency.
+CMake will first attempt to discover an installed GauXC via its config file; if that fails, it will download and build GauXC from source.
+The options defined in the main CMake file are passed through to GauXC to ensure the library provides the requested features.
+After GauXC is available, we verify that our requirements are satisfied—this is especially important for the Skala implementation, which requires the ``GAUXC_HAS_ONEDFT`` feature flag.
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/cmake/skala-gauxc.cmake
+ :language: cmake
+ :caption: cmake/skala-gauxc.cmake
+
+For the command-line interface, we use the `FLAP library `__ (Fortran command Line Arguments Parser).
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/cmake/skala-flap.cmake
+ :language: cmake
+ :caption: cmake/skala-flap.cmake
+
+This completes the CMake setup required for our command-line driver.
+
+Module imports
+--------------
+
+The main driver program imports the relevant GauXC modules.
+We also use GauXC's HDF5 I/O module and the FLAP module for command-line parsing.
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 1-31
+
+Each GauXC component has a corresponding Fortran module:
+
+`gauxc_status`
+ Handles GauXC status codes and error messages.
+
+`gauxc_molecule`
+ Manages molecular data structures (atomic numbers and Cartesian coordinates).
+
+`gauxc_basisset`
+ Manages basis set data (shell definitions, exponents, contraction coefficients).
+
+`gauxc_molgrid`
+ Sets up and manages the numerical integration grid.
+
+`gauxc_runtime_environment`
+ Interfaces with MPI and device (GPU) runtime environments.
+
+`gauxc_load_balancer`
+ Distributes grid points across available compute resources.
+
+`gauxc_molecular_weights`
+ Computes Becke-style partitioning weights for the molecular grid.
+
+`gauxc_xc_functional`
+ Handles exchange-correlation functional definitions.
+
+`gauxc_integrator`
+ Performs the numerical integration of the exchange-correlation energy and potential.
+
+`gauxc_matrix`
+ Manages matrix data structures (density matrices, potentials).
+
+Next, we declare variables for the GauXC-specific types:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 33-45
+
+We also declare variables for input parameters and intermediate values:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 47-55
+
+When compiled with MPI support, we initialize MPI at program startup.
+The ``gauxc/gauxc_config.f`` header provides the ``GAUXC_HAS_MPI`` preprocessor macro for guarding MPI-specific calls.
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 57-59
+
+Similarly, at the end of the program we finalize MPI:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 277-279
+
+Command line interface
+----------------------
+
+This guide uses an HDF5 input file to provide molecular data.
+(GauXC also supports constructing these objects programmatically, which is more convenient when embedding GauXC in a library.)
+We also expose parameters for configuring the integration grid and parallelization strategy.
+The main command-line arguments are:
+
+``input_file``
+ Path to an HDF5 file containing the molecule, basis set, and density matrix.
+
+``model``
+ The model checkpoint to evaluate (e.g., ``PBE`` for a traditional functional or a path to a Skala checkpoint).
+
+``grid_spec``
+ The grid size specification, which controls the number of angular and radial integration points.
+
+``rad_quad_spec``
+ The radial quadrature scheme, which determines the spacing of radial integration points.
+
+``prune_spec``
+ The pruning scheme, which controls how atomic grids are combined into a molecular grid.
+
+We use the ``command_line_interface`` type from the FLAP library to define the CLI.
+First, we initialize variables with their default values:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 61-66
+
+Next, we initialize the CLI and define the available arguments.
+A named ``block`` statement provides convenient error handling:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 68-94,134-142
+
+.. note::
+
+ The molecular grid settings (``grid_spec``, ``rad_quad_spec``, ``prune_spec``) are documented in detail in the :ref:`gauxc_molecular_grid_settings` reference.
+
+After defining the CLI, we parse it within the ``input`` block and retrieve the values:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 96-133
+
+Before adding further implementation, let's build and test the project.
+Configure and compile with:
+
+.. code-block:: shell
+
+ cmake -B build -G Ninja -S .
+ cmake --build build
+
+Once built, run the driver with ``--help`` to verify it works:
+
+.. code-block:: shell
+
+ ./build/Skala --help
+
+You should see the help page generated by FLAP:
+
+.. code-block:: text
+
+ usage: ./build/Skala value --model value [--grid value] [--radial-quadrature value] [--pruning-scheme value] [--lb-exec-space value] [--int-exec-space value] [--batch-size value] [--help] [--markdown] [--version]
+
+ Driver for using Skala
+
+
+ Required switches:
+ value
+ 1-th argument
+ Input HDF5 file containing molecule, basis set and density matrix
+ --model value
+ Model to use for the calculation
+
+ Optional switches:
+ --grid value, value in: `fine,ultrafine,superfine,gm3,gm5`
+ default value fine
+ Molecular grid specification
+ --radial-quadrature value, value in: `becke,muraknowles,treutlerahlrichs,murrayhandylaming`
+ default value muraknowles
+ Radial quadrature to use
+ --pruning-scheme value, value in: `unpruned,robust,treutler`
+ default value robust
+ Pruning scheme to use
+ --lb-exec-space value, value in: `host,device`
+ default value host
+ Execution space for load balancer
+ --int-exec-space value, value in: `host,device`
+ default value host
+ Execution space for integrator
+ --batch-size value
+ default value 512
+ Batch size for grid point processing
+ --help, -h
+ Print this help message
+ --markdown, -md
+ Save this help message in a Markdown file
+ --version, -v
+ Print version
+
+The CLI is now fully functional and allows flexible configuration from the command line.
+
+Initializing GauXC
+------------------
+
+We begin by initializing the GauXC runtime environment.
+All GauXC-related calls are placed inside a named ``block`` for streamlined error handling:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 148-159
+
+At the end of the block, we check the status and clean up the runtime environment:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 255-261
+
+The runtime environment provides the MPI world rank and size (for both MPI and non-MPI builds).
+We print the configuration obtained from the command line:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+ :lines: 161-170
+
+From here on, we use ``world_rank`` to ensure only rank 0 produces output.
+
+
+Molecule data
+-------------
+
+We use GauXC's built-in HDF5 reader to load the molecule data.
+
+.. note::
+
+ GauXC stores the information about the atomic numbers and their coordinates as an array of structs, defined in C++ as
+
+ .. code-block:: c++
+
+ struct GauXC::Atom {
+ int64_t Z; ///< Atomic number
+ double x; ///< X coordinate (bohr)
+ double y; ///< Y coordinate (bohr)
+ double z; ///< Z coordinate (bohr)
+ };
+ class GauXC::Molecule : public std::vector {
+ ...
+ };
+
+ The HDF5 wrapper maps this struct representation directly to the HDF5 dataset.
+
+The ``gauxc_read_hdf5_record`` subroutine loads the molecule data:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (read molecule)
+ :lines: 172-177
+
+For proper memory management, we free the molecule object at program end:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (free molecule)
+ :lines: 262
+
+
+Basis set data
+--------------
+
+We load the basis set using the same HDF5 approach as for the molecule.
+
+.. note::
+
+ The basis set is represented as an array of shell objects, each containing primitive exponents,
+ contraction coefficients, angular momentum, and shell center:
+
+ .. code-block:: c++
+
+ template
+ class alignas(256) GauXC::Shell {
+ std::array alpha; ///< exponents of primitives
+ std::array coeff; ///< contraction coefficients
+ std::array O; ///< origin of the shell
+ int32_t nprim; ///< number of primitives
+ int32_t l; ///< angular moment of the shell
+ int32_t pure; ///< pure=1: spherical Gaussians; pure=0: cartesian Gaussians
+ };
+
+ template
+ struct GauXC::BasisSet : public std::vector> {
+ ...
+ };
+
+ Again, this allows the object's representation to map directly to an HDF5 dataset.
+
+We read the basis set using ``gauxc_read_hdf5_record``:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (read basisset)
+ :lines: 179-184
+
+We free the basis set at program end:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (free basisset)
+ :lines: 263
+
+Integration grid
+----------------
+
+The integration grid defines the spatial points for evaluating the exchange-correlation functional.
+Three parameters control grid construction:
+
+- **Grid size**: Density of angular and radial points (e.g., ``fine``, ``ultrafine``)
+- **Radial quadrature**: Spacing scheme for radial points (e.g., ``muraknowles``)
+- **Pruning scheme**: How atomic grids combine into the molecular grid (e.g., ``robust``)
+
+GauXC uses enumerators for these settings. We define helper functions to convert CLI strings:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (enumerator conversion functions)
+ :lines: 281,294-337
+
+We create the molecular grid from our input parameters.
+The batch size controls how many grid points are processed together; larger values (up to ~10000)
+improve performance, though the default is 512:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (grid setup)
+ :lines: 186-191
+
+We free the grid at program end:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (free grid)
+ :lines: 264
+
+Exchange-correlation integrator
+-------------------------------
+
+The load balancer distributes XC functional evaluation across available resources (host or device).
+A helper function converts the execution-space CLI string to an enumerator:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (execution space enumerator)
+ :lines: 281-292
+
+The load balancer manages access to the molecule, basis, and grid data for all subsequent GauXC operations.
+We create it from our input parameters:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (load balancer setup)
+ :lines: 193-210
+
+Finally, we create the XC integrator using a factory pattern.
+A settings object specifies the model checkpoint to evaluate:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (integrator setup)
+ :lines: 212-218
+
+We free the integrator and associated objects at program end:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (free integrator)
+ :lines: 265-271
+
+Density matrix
+--------------
+
+The density matrix is the final input for GauXC.
+We read it from the HDF5 file:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (read density matrix)
+ :lines: 220-226
+
+We free the density matrix at program end:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (free density matrix)
+ :lines: 272-273
+
+Exchange-correlation evaluation
+-------------------------------
+
+With all inputs ready, we perform the XC evaluation:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (exchange-correlation evaluation)
+ :lines: 233-237
+
+.. tip::
+
+ To measure evaluation time, define a helper function:
+
+ .. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (time helper function)
+ :lines: 339-344
+
+ Use it to wrap the evaluation and print elapsed time:
+
+ .. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (timed exchange-correlation evaluation)
+ :lines: 228-243
+
+We output the computed XC energy:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (exchange-correlation output)
+ :lines: 245-253
+
+We free the allocated XC potential matrices at program end:
+
+.. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90 (free exchange-correlation potential)
+ :lines: 274-275
+
+Rebuild the project:
+
+.. code-block:: shell
+
+ cmake --build build
+
+Run the driver from the build directory:
+
+.. code-block:: shell
+
+ ./build/Skala He_def2-svp.h5 --model PBE
+
+.. note::
+
+ Create the ``He_def2-svp.h5`` input file using the ``skala`` package:
+
+ .. literalinclude:: scripts/export-h5.py
+ :language: python
+
+The output shows results for the PBE functional:
+
+.. code-block:: text
+
+ Configuration
+ -> Input file : He_def2-svp.h5
+ -> Model : PBE
+ -> Grid : fine
+ -> Radial quadrature : muraknowles
+ -> Pruning scheme : robust
+
+ Results
+ Exc = -1.0540318683E+00 Eh
+ |VXC(a+b)|_F = 1.4559829661E+00
+ |VXC(a-b)|_F = 0.0000000000E+00
+ Runtime XC = 2.5566819200E-01
+
+Download checkpoint from HuggingFace
+------------------------------------
+
+To evaluate Skala, download the model checkpoint from HuggingFace using the ``hf`` CLI
+from the ``huggingface_hub`` package:
+
+.. code-block:: shell
+
+ hf download microsoft/skala skala-1.0.fun --local-dir .
+ ./build/Skala He_def2-svp.h5 --model ./skala-1.0.fun
+
+The output shows results for the Skala functional:
+
+.. code-block:: text
+
+ Configuration
+ -> Input file : He_def2-svp.h5
+ -> Model : ./skala-1.0.fun
+ -> Grid : fine
+ -> Radial quadrature : muraknowles
+ -> Pruning scheme : robust
+
+ Results
+ Exc = -1.0712560874E+00 Eh
+ |VXC(a+b)|_F = 1.5002997546E+00
+ |VXC(a-b)|_F = 0.0000000000E+00
+ Runtime XC = 1.5986489670E+00
+
+Full source code
+----------------
+
+.. dropdown:: Full source code of the main driver
+
+ .. literalinclude:: ../../examples/fortran/gauxc_integration/app/main.F90
+ :language: fortran
+ :caption: app/main.F90
+
+Summary
+-------
+
+This guide demonstrated GauXC usage in Fortran applications. We:
+
+1. Created a minimal CMake project with GauXC as a dependency
+2. Built a CLI driver that reads molecule, basis set, and density matrix from HDF5
+3. Configured the integration grid, load balancer, and XC integrator
+4. Evaluated the exchange-correlation energy and potential
\ No newline at end of file
diff --git a/docs/gauxc/index.rst b/docs/gauxc/index.rst
index 9a923d9..db896d3 100644
--- a/docs/gauxc/index.rst
+++ b/docs/gauxc/index.rst
@@ -12,4 +12,6 @@ The following sections provide instructions on how to install GauXC with Skala s
installation
standalone
cpp-library
+ c-library
+ fortran-library
settings
\ No newline at end of file
diff --git a/docs/gauxc/installation.rst b/docs/gauxc/installation.rst
index 4574362..38383c2 100644
--- a/docs/gauxc/installation.rst
+++ b/docs/gauxc/installation.rst
@@ -231,8 +231,8 @@ options chosen above.
.. code-block:: cmake
- set(Skala_GauXC_URL "https://github.com/microsoft/skala/releases/download/v1.1.0/gauxc-skala.tar.gz")
- set(Skala_GauXC_SHA256 "e0346c62453eef58ba3ee52c257370ee8abcbf00fbb1b4ea2e0bb879225e06be")
+ set(Skala_GauXC_URL "https://github.com/microsoft/skala/releases/download/v1.1.1/gauxc-skala.tar.gz")
+ set(Skala_GauXC_SHA256 "0a64b2623be99894d3c61bd78aec1759abf3f476656695fab76c5e3fbb372616")
option(Skala_GauXC_ENABLE_OPENMP "Enable OpenMP support in GauXC" ON)
option(Skala_GauXC_ENABLE_MPI "Enable MPI support in GauXC" OFF)
diff --git a/examples/c/gauxc_integration/.gitignore b/examples/c/gauxc_integration/.gitignore
new file mode 100644
index 0000000..07b9832
--- /dev/null
+++ b/examples/c/gauxc_integration/.gitignore
@@ -0,0 +1,3 @@
+*build/
+*.tar.gz
+*.fun
diff --git a/examples/c/gauxc_integration/CMakeLists.txt b/examples/c/gauxc_integration/CMakeLists.txt
new file mode 100644
index 0000000..568736e
--- /dev/null
+++ b/examples/c/gauxc_integration/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
+
+project("Skala" LANGUAGES C CXX)
+
+include(GNUInstallDirs)
+include(FetchContent)
+set(FETCHCONTENT_UPDATES_DISCONNECTED ON CACHE BOOL "Disable FC Updates")
+
+list(PREPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
+
+option(Skala_GauXC_ENABLE_OPENMP "Enable OpenMP support in GauXC" ON)
+option(Skala_GauXC_ENABLE_MPI "Enable MPI support in GauXC" OFF)
+option(Skala_GauXC_ENABLE_CUDA "Enable CUDA support in GauXC" OFF)
+
+include(skala-dep-versions)
+include(skala-gauxc)
+include(skala-argtable3)
+
+add_executable(
+ "${PROJECT_NAME}"
+ "app/main.c"
+)
+target_link_libraries(
+ "${PROJECT_NAME}"
+ PRIVATE
+ gauxc::gauxc
+ argtable3::argtable3
+)
+install(
+ TARGETS
+ "${PROJECT_NAME}"
+ DESTINATION
+ "${CMAKE_INSTALL_BINDIR}"
+)
+
+list(REMOVE_AT CMAKE_MODULE_PATH 0)
diff --git a/examples/c/gauxc_integration/app/main.c b/examples/c/gauxc_integration/app/main.c
new file mode 100644
index 0000000..4a7e731
--- /dev/null
+++ b/examples/c/gauxc_integration/app/main.c
@@ -0,0 +1,339 @@
+// Standard C libraries
+#include
+#include
+#include
+#include
+
+// For GauXC core functionality
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// For HDF5 I/O
+#include
+
+// For command line interface
+#include
+
+enum GauXC_ExecutionSpace
+read_execution_space(GauXCStatus* status, const char* exec_space_str)
+{
+ status->code = 0;
+ if(strcmp(exec_space_str, "host") == 0)
+ return GauXC_ExecutionSpace_Host;
+ if(strcmp(exec_space_str, "device") == 0)
+ return GauXC_ExecutionSpace_Device;
+ status->message = "Invalid execution space specification";
+ status->code = 1;
+}
+
+enum GauXC_RadialQuad
+read_radial_quad(GauXCStatus* status, const char* rad_quad_spec)
+{
+ status->code = 0;
+ if(strcmp(rad_quad_spec, "becke") == 0)
+ return GauXC_RadialQuad_Becke;
+ if(strcmp(rad_quad_spec, "muraknowles") == 0)
+ return GauXC_RadialQuad_MuraKnowles;
+ if(strcmp(rad_quad_spec, "treutlerahlrichs") == 0)
+ return GauXC_RadialQuad_TreutlerAhlrichs;
+ if(strcmp(rad_quad_spec, "murrayhandylaming") == 0)
+ return GauXC_RadialQuad_MurrayHandyLaming;
+ status->message = "Invalid radial quadrature specification";
+ status->code = 1;
+}
+
+enum GauXC_AtomicGridSizeDefault
+read_atomic_grid_size(GauXCStatus* status, const char* spec)
+{
+ status->code = 0;
+ if(strcmp(spec, "fine") == 0)
+ return GauXC_AtomicGridSizeDefault_FineGrid;
+ if(strcmp(spec, "ultrafine") == 0)
+ return GauXC_AtomicGridSizeDefault_UltraFineGrid;
+ if(strcmp(spec, "superfine") == 0)
+ return GauXC_AtomicGridSizeDefault_SuperFineGrid;
+ if(strcmp(spec, "gm3") == 0)
+ return GauXC_AtomicGridSizeDefault_GM3;
+ if(strcmp(spec, "gm5") == 0)
+ return GauXC_AtomicGridSizeDefault_GM5;
+ status->message = "Invalid atomic grid size specification";
+ status->code = 1;
+}
+
+enum GauXC_PruningScheme
+read_pruning_scheme(GauXCStatus* status, const char* spec)
+{
+ status->code = 0;
+ if(strcmp(spec, "unpruned") == 0)
+ return GauXC_PruningScheme_Unpruned;
+ if(strcmp(spec, "robust") == 0)
+ return GauXC_PruningScheme_Robust;
+ if(strcmp(spec, "treutler") == 0)
+ return GauXC_PruningScheme_Treutler;
+ status->message = "Invalid pruning scheme specification";
+ status->code = 1;
+}
+
+inline static char * arg_strcpy(struct arg_str * arg, char * default_str)
+{
+ if(arg->count == 0 && default_str != NULL) {
+ return strcpy(malloc(strlen(default_str) + 1), default_str);
+ }
+ if(arg->count > 0) {
+ const char* sval = arg->sval[arg->count - 1];
+ return strcpy(malloc(strlen(sval) + 1), sval);
+ }
+ return NULL;
+}
+
+inline static char * lowercase(char * str)
+{
+ for(char * p = str; *p; ++p) *p = tolower(*p);
+ return str;
+}
+
+int
+main(int argc, char** argv)
+{
+#ifdef GAUXC_HAS_MPI
+ MPI_Init(NULL, NULL);
+#endif
+ struct arg_lit *help;
+ struct arg_file *input_file_;
+ struct arg_int *batch_size_;
+ struct arg_dbl *basis_tol_;
+ struct arg_str *model_, *grid_spec_, *rad_quad_spec_, *prune_spec_;
+ struct arg_str *lb_exec_space_, *int_exec_space_;
+ struct arg_end *end;
+
+ void* argtable[] = {
+ input_file_ = arg_filen(NULL, NULL, "", 1, 1, "Input file containing molecular geometry and density matrix"),
+ model_ = arg_strn(NULL, "model", "", 1, 1, "OneDFT model to use, can be a path to a checkpoint"),
+ grid_spec_ = arg_strn(NULL, "grid-spec", "", 0, 1, "Atomic grid size specification (default: Fine)"),
+ arg_rem(NULL, "Possible values are: Fine, UltraFine, SuperFine, GM3, GM5"),
+ rad_quad_spec_ = arg_strn(NULL, "radial-quad", "", 0, 1, "Radial quadrature scheme (default: MuraKnowles)"),
+ arg_rem(NULL, "Possible values are: Becke, MuraKnowles, TreutlerAhlrichs, MurrayHandyLaming"),
+ prune_spec_ = arg_strn(NULL, "prune-scheme", "", 0, 1, "Pruning scheme (default: Robust)"),
+ arg_rem(NULL, "Possible values are: Unpruned, Robust, Treutler"),
+ lb_exec_space_ = arg_strn(NULL, "lb-exec-space", "", 0, 1, "Load balancer execution space"),
+ arg_rem(NULL, "Possible values are: Host, Device"),
+ int_exec_space_= arg_strn(NULL, "int-exec-space", "", 0, 1, "Integrator execution space"),
+ arg_rem(NULL, "Possible values are: Host, Device"),
+ batch_size_ = arg_intn(NULL, "batch-size", "", 0, 1, "Batch size for grid point processing (default: 512)"),
+ basis_tol_ = arg_dbln(NULL, "basis-tol", "", 0, 1, "Basis function evaluation tolerance (default: 1e-10)"),
+ help = arg_litn(NULL, "help", 0, 1, "Print this help and exit"),
+ end = arg_end(20)
+ };
+
+ int nerrors = arg_parse(argc, argv, argtable);
+ if(help->count > 0) {
+ printf("Usage: %s", argv[0]);
+ arg_print_syntax(stdout, argtable, "\n\n");
+ printf("Options:\n");
+ arg_print_glossary(stdout, argtable, " %-25s %s\n");
+#ifdef GAUXC_HAS_MPI
+ MPI_Finalize();
+#endif
+ return EXIT_SUCCESS;
+ }
+
+ if (nerrors > 0 || input_file_->count == 0 || model_->count == 0) {
+ printf("Usage: %s", argv[0]);
+ arg_print_syntax(stdout, argtable, "\n");
+ arg_print_errors(stderr, end, argv[0]);
+#ifdef GAUXC_HAS_MPI
+ MPI_Abort(MPI_COMM_WORLD, 1);
+#endif
+ return EXIT_FAILURE;
+ }
+
+ char* input_file = strcpy(malloc(strlen(input_file_->filename[0]) + 1), input_file_->filename[0]);
+ char* model = arg_strcpy(model_, NULL);
+ char* grid_spec = lowercase(arg_strcpy(grid_spec_, "Fine"));
+ char* rad_quad_spec = lowercase(arg_strcpy(rad_quad_spec_, "MuraKnowles"));
+ char* prune_spec = lowercase(arg_strcpy(prune_spec_, "Robust"));
+ char* lb_exec_space_str = lowercase(arg_strcpy(lb_exec_space_, "Host"));
+ char* int_exec_space_str = lowercase(arg_strcpy(int_exec_space_, "Host"));
+ int batch_size = batch_size_->count > 0 ? batch_size_->ival[0] : 512;
+ double basis_tol = basis_tol_->count > 0 ? basis_tol_->dval[0] : 1e-10;
+
+ // Clean up argtable memory
+ arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
+
+ // Memory management for GauXC
+ void* objs[16];
+ size_t nobj = 0;
+
+ // Add handler for events like exceptions
+ GauXCStatus status = {0, NULL};
+
+ // Create runtime
+ GauXCRuntimeEnvironment rt = gauxc_runtime_environment_new(&status GAUXC_MPI_CODE(, MPI_COMM_WORLD));
+ objs[nobj++] = &rt;
+ if (status.code) goto err;
+ int world_rank = gauxc_runtime_environment_comm_rank(&status, rt);
+ if (status.code) goto err;
+ int world_size = gauxc_runtime_environment_comm_size(&status, rt);
+ if (status.code) goto err;
+
+ if(!world_rank && !status.code) {
+ printf("Configuration\n");
+ printf("-> Input file : %s\n", input_file);
+ printf("-> Model : %s\n", model);
+ printf("-> Grid : %s\n", grid_spec);
+ printf("-> Radial quadrature : %s\n", rad_quad_spec);
+ printf("-> Pruning scheme : %s\n", prune_spec);
+ printf("\n");
+ }
+
+ // Get molecule (atomic numbers and cartesian coordinates)
+ GauXCMolecule mol = gauxc_molecule_new(&status);
+ objs[nobj++] = &mol;
+ if (status.code) goto err;
+ // Load molecule from HDF5 dataset
+ gauxc_molecule_read_hdf5_record(&status, mol, input_file, "/MOLECULE");
+ if (status.code) goto err;
+
+ // Get basis set
+ GauXCBasisSet basis = gauxc_basisset_new(&status);
+ objs[nobj++] = &basis;
+ if (status.code) goto err;
+ // Load basis set from HDF5 dataset
+ gauxc_basisset_read_hdf5_record(&status, basis, input_file, "/BASIS");
+ if (status.code) goto err;
+
+ // Define molecular grid from grid size, radial quadrature and pruning scheme
+ enum GauXC_AtomicGridSizeDefault grid_type = read_atomic_grid_size(&status, grid_spec);
+ if (status.code) goto err;
+ enum GauXC_RadialQuad radial_quad = read_radial_quad(&status, rad_quad_spec);
+ if (status.code) goto err;
+ enum GauXC_PruningScheme pruning_scheme = read_pruning_scheme(&status, prune_spec);
+ if (status.code) goto err;
+ GauXCMolGrid grid = gauxc_molgrid_new_default(
+ &status,
+ mol,
+ pruning_scheme,
+ batch_size,
+ radial_quad,
+ grid_type);
+ objs[nobj++] = &grid;
+ if (status.code) goto err;
+
+ // Choose whether we run on host or device
+ enum GauXC_ExecutionSpace lb_exec_space, int_exec_space;
+ lb_exec_space = read_execution_space(&status, lb_exec_space_str);
+ if (status.code) goto err;
+ int_exec_space = read_execution_space(&status, int_exec_space_str);
+ if (status.code) goto err;
+
+ // Setup load balancer based on molecule, grid and basis set
+ GauXCLoadBalancerFactory lb_factory = gauxc_load_balancer_factory_new(&status, lb_exec_space, "Replicated");
+ objs[nobj++] = &lb_factory;
+ if (status.code) goto err;
+ GauXCLoadBalancer lb = gauxc_load_balancer_factory_get_shared_instance(&status, lb_factory, rt, mol, grid, basis);
+ objs[nobj++] = &lb;
+ if (status.code) goto err;
+
+ // Apply partitioning weights to the molecule grid
+ GauXCMolecularWeightsSettings settings = {GauXC_XCWeightAlg_SSF, false};
+ GauXCMolecularWeightsFactory mw_factory = gauxc_molecular_weights_factory_new(&status, int_exec_space,
+ "Default", settings);
+ objs[nobj++] = &mw_factory;
+ if (status.code) goto err;
+ GauXCMolecularWeights mw = gauxc_molecular_weights_factory_get_instance(&status, mw_factory);
+ if (status.code) goto err;
+ gauxc_molecular_weights_modify_weights(&status, mw, lb);
+ objs[nobj++] = &mw;
+ if (status.code) goto err;
+
+ // Setup exchange-correlation integrator
+ GauXCFunctional func = gauxc_functional_from_string(&status, "PBE", true);
+ objs[nobj++] = &func;
+ if (status.code) goto err;
+ GauXCIntegratorFactory integrator_factory = gauxc_integrator_factory_new(&status, int_exec_space,
+ "Replicated", "Default", "Default", "Default");
+ objs[nobj++] = &integrator_factory;
+ if (status.code) goto err;
+ GauXCIntegrator integrator = gauxc_integrator_factory_get_instance(&status, integrator_factory, func, lb);
+ objs[nobj++] = &integrator;
+ if (status.code) goto err;
+
+ // Configure model checkpoint
+ // GauXCOneDFTSettings onedft_settings;
+ // onedft_settings.model = model;
+
+ // Load density matrix from input
+ GauXCMatrix P_s = gauxc_matrix_empty(&status);
+ objs[nobj++] = &P_s;
+ if (status.code) goto err;
+ GauXCMatrix P_z = gauxc_matrix_empty(&status);
+ objs[nobj++] = &P_z;
+ if (status.code) goto err;
+ gauxc_matrix_read_hdf5_record(&status, P_s, input_file, "/DENSITY_SCALAR");
+ if (status.code) goto err;
+ gauxc_matrix_read_hdf5_record(&status, P_z, input_file, "/DENSITY_Z");
+ if (status.code) goto err;
+
+#ifdef GAUXC_HAS_MPI
+ MPI_Barrier(MPI_COMM_WORLD);
+#endif
+
+ // Integrate exchange correlation energy
+ double EXC;
+ GauXCMatrix VXC_s = gauxc_matrix_empty(&status);
+ objs[nobj++] = &VXC_s;
+ if (status.code) goto err;
+ GauXCMatrix VXC_z = gauxc_matrix_empty(&status);
+ objs[nobj++] = &VXC_z;
+ if (status.code) goto err;
+
+ gauxc_integrator_eval_exc_vxc_onedft_uks(&status, integrator, P_s, P_z, model, &EXC, &VXC_s, &VXC_z);
+ if (status.code) goto err;
+
+#ifdef GAUXC_HAS_MPI
+ MPI_Barrier(MPI_COMM_WORLD);
+#endif
+
+ if(!world_rank && !status.code) {
+ printf("Results\n");
+ printf("-> EXC : %.10f\n", EXC);
+ printf("\n");
+ }
+
+err:
+ int error_code = status.code;
+ if (error_code) {
+ fprintf(stderr, "Error (code %d)", error_code);
+ if (status.message) fprintf(stderr, ": %s", status.message);
+ fprintf(stderr, "\n");
+ }
+ // Clean up memory
+ free(input_file);
+ free(model);
+ free(grid_spec);
+ free(rad_quad_spec);
+ free(prune_spec);
+ free(lb_exec_space_str);
+ free(int_exec_space_str);
+ gauxc_objects_delete(&status, objs, nobj);
+ if (status.code) {
+ fprintf(stderr, "Error during cleanup (code %d)", status.code);
+ if (status.message) fprintf(stderr, ": %s", status.message);
+ fprintf(stderr, "\n");
+ error_code = status.code;
+ }
+
+#ifdef GAUXC_HAS_MPI
+ MPI_Finalize();
+#endif
+ return error_code ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/examples/c/gauxc_integration/cmake/skala-argtable3.cmake b/examples/c/gauxc_integration/cmake/skala-argtable3.cmake
new file mode 100644
index 0000000..67c17b8
--- /dev/null
+++ b/examples/c/gauxc_integration/cmake/skala-argtable3.cmake
@@ -0,0 +1,18 @@
+if(NOT DEFINED Skala_Argtable3_URL)
+ include(skala-dep-versions)
+endif()
+find_package(Argtable3 QUIET CONFIG)
+if(NOT Argtable3_FOUND)
+ include(FetchContent)
+
+ message(STATUS "Could not find Argtable3... Building Argtable3 from source")
+ message(STATUS "Argtable3 URL: ${Skala_Argtable3_URL}")
+
+ FetchContent_Declare(
+ argtable3
+ URL ${Skala_Argtable3_URL}
+ URL_HASH SHA256=${Skala_Argtable3_SHA256}
+ DOWNLOAD_EXTRACT_TIMESTAMP ON
+ )
+ FetchContent_MakeAvailable(Argtable3)
+endif()
diff --git a/examples/c/gauxc_integration/cmake/skala-dep-versions.cmake b/examples/c/gauxc_integration/cmake/skala-dep-versions.cmake
new file mode 100644
index 0000000..46a89e6
--- /dev/null
+++ b/examples/c/gauxc_integration/cmake/skala-dep-versions.cmake
@@ -0,0 +1,5 @@
+set(Skala_GauXC_URL "https://github.com/microsoft/skala/releases/download/v1.1.1/gauxc-skala.tar.gz")
+set(Skala_GauXC_SHA256 "0a64b2623be99894d3c61bd78aec1759abf3f476656695fab76c5e3fbb372616")
+
+set(Skala_Argtable3_URL "https://github.com/argtable/argtable3/releases/download/v3.3.1/argtable-v3.3.1.tar.gz")
+set(Skala_Argtable3_SHA256 "c08bca4b88ddb9234726b75455b3b1670d7c864d8daf198eaa7a3b4d41addf2c")
diff --git a/examples/c/gauxc_integration/cmake/skala-gauxc.cmake b/examples/c/gauxc_integration/cmake/skala-gauxc.cmake
new file mode 100644
index 0000000..bf2fbab
--- /dev/null
+++ b/examples/c/gauxc_integration/cmake/skala-gauxc.cmake
@@ -0,0 +1,41 @@
+if(NOT DEFINED Skala_GauXC_URL)
+ include(skala-dep-versions)
+endif()
+find_package(gauxc QUIET CONFIG)
+if(NOT gauxc_FOUND)
+ include(FetchContent)
+
+ message(STATUS "Could not find GauXC... Building GauXC from source")
+ message(STATUS "GAUXC URL: ${Skala_GauXC_URL}")
+
+ set(GAUXC_ENABLE_ONEDFT ON CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_C ON CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_TESTS OFF CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_OPENMP ${Skala_GauXC_ENABLE_OPENMP} CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_MPI ${Skala_GauXC_ENABLE_MPI} CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_CUDA ${Skala_GauXC_ENABLE_CUDA} CACHE BOOL "" FORCE)
+
+ FetchContent_Declare(
+ gauxc
+ URL ${Skala_GauXC_URL}
+ # URL_HASH SHA256=${Skala_GauXC_SHA256}
+ DOWNLOAD_EXTRACT_TIMESTAMP ON
+ )
+ FetchContent_MakeAvailable(gauxc)
+
+endif()
+if(NOT GAUXC_HAS_ONEDFT)
+ message(FATAL_ERROR "GauXC found but without Skala support enabled")
+endif()
+if(NOT GAUXC_HAS_C)
+ message(FATAL_ERROR "GauXC found but without C API support enabled")
+endif()
+if(Skala_GauXC_ENABLE_OPENMP AND NOT GAUXC_HAS_OPENMP)
+ message(FATAL_ERROR "GauXC found without OpenMP support but Skala_GauXC_ENABLE_OPENMP is ON")
+endif()
+if(Skala_GauXC_ENABLE_MPI AND NOT GAUXC_HAS_MPI)
+ message(FATAL_ERROR "GauXC found without MPI support but Skala_GauXC_ENABLE_MPI is ON")
+endif()
+if(Skala_GauXC_ENABLE_CUDA AND NOT GAUXC_HAS_CUDA)
+ message(FATAL_ERROR "GauXC found without CUDA support but Skala_GauXC_ENABLE_CUDA is ON")
+endif()
diff --git a/examples/c/gauxc_integration/environment-mpi.yml b/examples/c/gauxc_integration/environment-mpi.yml
new file mode 100644
index 0000000..f8e5004
--- /dev/null
+++ b/examples/c/gauxc_integration/environment-mpi.yml
@@ -0,0 +1,17 @@
+name: gauxc-dev
+channels:
+ - conda-forge
+dependencies:
+ # build requirements
+ - c-compiler
+ - cxx-compiler
+ - cmake >=3.15,<4
+ - ninja
+ - nlohmann_json >=3.9
+ # host/runtime requirements
+ - openmpi # pick mpich if that matches your stack
+ - exchcxx >=1.0
+ - gau2grid >=2.0.6
+ - hdf5 * mpi_*
+ - libblas
+ - pytorch >=2.0 cpu_*
diff --git a/examples/c/gauxc_integration/environment-openmp.yml b/examples/c/gauxc_integration/environment-openmp.yml
new file mode 100644
index 0000000..dbdf2c7
--- /dev/null
+++ b/examples/c/gauxc_integration/environment-openmp.yml
@@ -0,0 +1,16 @@
+name: gauxc-dev
+channels:
+ - conda-forge
+dependencies:
+ # build requirements
+ - c-compiler
+ - cxx-compiler
+ - cmake >=3.15,<4
+ - ninja
+ - nlohmann_json >=3.9
+ # host/runtime requirements
+ - exchcxx >=1.0
+ - gau2grid >=2.0.6
+ - hdf5
+ - libblas
+ - pytorch >=2.0 cpu_*
diff --git a/examples/cpp/gauxc_integration/app/main.cxx b/examples/cpp/gauxc_integration/app/main.cxx
index 6618719..62057a0 100644
--- a/examples/cpp/gauxc_integration/app/main.cxx
+++ b/examples/cpp/gauxc_integration/app/main.cxx
@@ -245,4 +245,4 @@ main(int argc, char** argv)
#ifdef GAUXC_HAS_MPI
MPI_Finalize();
#endif
-}
\ No newline at end of file
+}
diff --git a/examples/cpp/gauxc_integration/cmake/skala-dep-versions.cmake b/examples/cpp/gauxc_integration/cmake/skala-dep-versions.cmake
index bbc2f27..d35a2bf 100644
--- a/examples/cpp/gauxc_integration/cmake/skala-dep-versions.cmake
+++ b/examples/cpp/gauxc_integration/cmake/skala-dep-versions.cmake
@@ -1,8 +1,8 @@
-set(Skala_GauXC_URL "https://github.com/microsoft/skala/releases/download/v1.1.0/gauxc-skala.tar.gz")
-set(Skala_GauXC_SHA256 "e0346c62453eef58ba3ee52c257370ee8abcbf00fbb1b4ea2e0bb879225e06be")
+set(Skala_GauXC_URL "https://github.com/microsoft/skala/releases/download/v1.1.1/gauxc-skala.tar.gz")
+set(Skala_GauXC_SHA256 "0a64b2623be99894d3c61bd78aec1759abf3f476656695fab76c5e3fbb372616")
set(Skala_CLI11_URL "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.6.1.tar.gz")
set(Skala_CLI11_SHA256 "377691f3fac2b340f12a2f79f523c780564578ba3d6eaf5238e9f35895d5ba95")
set(Skala_Eigen3_URL "https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz")
-set(Skala_Eigen3_SHA256 "8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72")
\ No newline at end of file
+set(Skala_Eigen3_SHA256 "8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72")
diff --git a/examples/cpp/gauxc_integration/cmake/skala-gauxc.cmake b/examples/cpp/gauxc_integration/cmake/skala-gauxc.cmake
index 228012a..ae63c49 100644
--- a/examples/cpp/gauxc_integration/cmake/skala-gauxc.cmake
+++ b/examples/cpp/gauxc_integration/cmake/skala-gauxc.cmake
@@ -22,21 +22,16 @@ if(NOT gauxc_FOUND)
)
FetchContent_MakeAvailable(gauxc)
-else()
- if(NOT ${GAUXC_HAS_ONEDFT})
- message(FATAL_ERROR "GauXC found but without Skala support enabled")
- endif()
- if(${Skala_GauXC_ENABLE_OPENMP} AND NOT ${GAUXC_HAS_OPENMP})
- message(FATAL_ERROR "GauXC found without OpenMP support but Skala_GauXC_ENABLE_OPENMP is ON")
- endif()
- if(${Skala_GauXC_ENABLE_MPI} AND NOT ${GAUXC_HAS_MPI})
- message(FATAL_ERROR "GauXC found without MPI support but Skala_GauXC_ENABLE_MPI is ON")
- endif()
- if(${Skala_GauXC_ENABLE_CUDA} AND NOT ${GAUXC_HAS_CUDA})
- message(FATAL_ERROR "GauXC found without CUDA support but Skala_GauXC_ENABLE_CUDA is ON")
- endif()
endif()
-
-if(GAUXC_HAS_GAU2GRID AND NOT TARGET gau2grid::gg)
- find_package(gau2grid CONFIG REQUIRED)
+if(NOT GAUXC_HAS_ONEDFT)
+ message(FATAL_ERROR "GauXC found but without Skala support enabled")
+endif()
+if(Skala_GauXC_ENABLE_OPENMP AND NOT GAUXC_HAS_OPENMP)
+ message(FATAL_ERROR "GauXC found without OpenMP support but Skala_GauXC_ENABLE_OPENMP is ON")
+endif()
+if(Skala_GauXC_ENABLE_MPI AND NOT GAUXC_HAS_MPI)
+ message(FATAL_ERROR "GauXC found without MPI support but Skala_GauXC_ENABLE_MPI is ON")
+endif()
+if(Skala_GauXC_ENABLE_CUDA AND NOT GAUXC_HAS_CUDA)
+ message(FATAL_ERROR "GauXC found without CUDA support but Skala_GauXC_ENABLE_CUDA is ON")
endif()
\ No newline at end of file
diff --git a/examples/fortran/gauxc_integration/.gitignore b/examples/fortran/gauxc_integration/.gitignore
new file mode 100644
index 0000000..07b9832
--- /dev/null
+++ b/examples/fortran/gauxc_integration/.gitignore
@@ -0,0 +1,3 @@
+*build/
+*.tar.gz
+*.fun
diff --git a/examples/fortran/gauxc_integration/CMakeLists.txt b/examples/fortran/gauxc_integration/CMakeLists.txt
new file mode 100644
index 0000000..577c342
--- /dev/null
+++ b/examples/fortran/gauxc_integration/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
+
+project("Skala" LANGUAGES Fortran C CXX)
+
+include(GNUInstallDirs)
+include(FetchContent)
+set(FETCHCONTENT_UPDATES_DISCONNECTED ON CACHE BOOL "Disable FC Updates")
+
+list(PREPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
+
+option(Skala_GauXC_ENABLE_OPENMP "Enable OpenMP support in GauXC" ON)
+option(Skala_GauXC_ENABLE_MPI "Enable MPI support in GauXC" OFF)
+option(Skala_GauXC_ENABLE_CUDA "Enable CUDA support in GauXC" OFF)
+
+include(skala-dep-versions)
+include(skala-gauxc)
+include(skala-flap)
+
+add_executable(
+ "${PROJECT_NAME}"
+ "app/main.F90"
+)
+target_link_libraries(
+ "${PROJECT_NAME}"
+ PRIVATE
+ gauxc::gauxc
+ FLAP::FLAP
+)
+if(GAUXC_HAS_MPI)
+ find_package(MPI REQUIRED)
+ target_link_libraries(
+ "${PROJECT_NAME}"
+ PRIVATE
+ MPI::MPI_Fortran
+ )
+endif()
+
+install(
+ TARGETS
+ "${PROJECT_NAME}"
+ DESTINATION
+ "${CMAKE_INSTALL_BINDIR}"
+)
+
+list(REMOVE_AT CMAKE_MODULE_PATH 0)
diff --git a/examples/fortran/gauxc_integration/app/main.F90 b/examples/fortran/gauxc_integration/app/main.F90
new file mode 100644
index 0000000..d6c874b
--- /dev/null
+++ b/examples/fortran/gauxc_integration/app/main.F90
@@ -0,0 +1,345 @@
+#include "gauxc/gauxc_config.f"
+
+program skala
+ use iso_c_binding, only : c_int, c_int64_t, c_double, c_bool
+ use flap, only : command_line_interface
+ use gauxc_status, only : gauxc_status_type, gauxc_status_message
+ use gauxc_enums, only : gauxc_executionspace, gauxc_radialquad, &
+ & gauxc_atomicgridsizedefault, gauxc_pruningscheme
+ use gauxc_runtime_environment, only : gauxc_runtime_environment_type, &
+ & gauxc_runtime_environment_comm_rank, gauxc_runtime_environment_comm_size, &
+ & gauxc_runtime_environment_new, gauxc_delete
+ use gauxc_molecule, only : gauxc_molecule_type, gauxc_molecule_new, gauxc_delete
+ use gauxc_basisset, only : gauxc_basisset_type, gauxc_basisset_new, gauxc_delete
+ use gauxc_molgrid, only : gauxc_molgrid_type, gauxc_molgrid_new_default, &
+ & gauxc_delete
+ use gauxc_load_balancer, only : gauxc_load_balancer_type, gauxc_load_balancer_factory_type, &
+ & gauxc_load_balancer_factory_new, gauxc_get_shared_instance, gauxc_delete
+ use gauxc_molecular_weights, only : gauxc_molecular_weights_type, &
+ & gauxc_molecular_weights_factory_type, gauxc_molecular_weights_factory_new, &
+ & gauxc_molecular_weights_modify_weights, gauxc_get_instance, gauxc_delete, &
+ & gauxc_molecular_weights_settings
+ use gauxc_xc_functional, only : gauxc_functional_type, gauxc_functional_from_string, gauxc_delete
+ use gauxc_integrator, only : gauxc_integrator_type, gauxc_integrator_factory_type, &
+ & gauxc_integrator_factory_new, gauxc_get_instance, gauxc_eval_exc_vxc, gauxc_delete
+ use gauxc_matrix, only : gauxc_matrix_type, gauxc_matrix_empty, gauxc_matrix_data, gauxc_delete
+ use gauxc_external_hdf5_read, only : gauxc_read_hdf5_record
+#ifdef GAUXC_HAS_MPI
+ use mpi
+#endif
+
+ implicit none(type, external)
+
+ type(gauxc_status_type) :: status
+ type(gauxc_runtime_environment_type) :: rt
+ type(gauxc_molecule_type) :: mol
+ type(gauxc_basisset_type) :: basis
+ type(gauxc_molgrid_type) :: grid
+ type(gauxc_load_balancer_factory_type) :: lbf
+ type(gauxc_load_balancer_type) :: lb
+ type(gauxc_molecular_weights_factory_type) :: mwf
+ type(gauxc_molecular_weights_type) :: mw
+ type(gauxc_functional_type) :: func
+ type(gauxc_integrator_factory_type) :: intf
+ type(gauxc_integrator_type) :: integrator
+ type(gauxc_matrix_type) :: p_s, p_z, vxc_s, vxc_z
+
+ character(len=:), allocatable :: input_file, model, grid_spec, rad_quad_spec, prune_spec, &
+ & lb_exec_space_str, int_exec_space_str
+ integer(c_int) :: grid_type, radial_quad, pruning_scheme
+ integer(c_int) :: lb_exec_space, int_exec_space
+ integer(c_int) :: world_rank, world_size
+ integer(c_int64_t) :: batch_size
+ real(c_double) :: exc, t_exc, t_start, t_end
+ type(command_line_interface) :: cli
+ integer :: error
+
+#ifdef GAUXC_HAS_MPI
+ call MPI_Init(error)
+#endif
+
+ grid_spec = "fine"
+ rad_quad_spec = "muraknowles"
+ prune_spec = "robust"
+ lb_exec_space_str = "host"
+ int_exec_space_str = "host"
+ batch_size = 512_c_int64_t
+
+ input: block
+ character(len=512) :: dummy
+ call cli%init(description="Driver for using Skala")
+ call cli%add(position=1, required=.true., act="store", error=error, positional=.true., &
+ & help="Input HDF5 file containing molecule, basis set and density matrix")
+ if (error /= 0) exit input
+ call cli%add(switch="--model", required=.true., act="store", error=error, &
+ & help="Model to use for the calculation")
+ if (error /= 0) exit input
+ call cli%add(switch="--grid", act="store", error=error, def=grid_spec, required=.false., &
+ & help="Molecular grid specification", choices="fine,ultrafine,superfine,gm3,gm5")
+ if (error /= 0) exit input
+ call cli%add(switch="--radial-quadrature", act="store", error=error, def=rad_quad_spec, &
+ & help="Radial quadrature to use", choices="becke,muraknowles,treutlerahlrichs,murrayhandylaming")
+ if (error /= 0) exit input
+ call cli%add(switch="--pruning-scheme", act="store", error=error, def=prune_spec, &
+ & help="Pruning scheme to use", choices="unpruned,robust,treutler")
+ if (error /= 0) exit input
+ call cli%add(switch="--lb-exec-space", act="store", error=error, def=lb_exec_space_str, &
+ & help="Execution space for load balancer", choices="host,device")
+ if (error /= 0) exit input
+ call cli%add(switch="--int-exec-space", act="store", error=error, def=int_exec_space_str, &
+ & help="Execution space for integrator", choices="host,device")
+ if (error /= 0) exit input
+ call cli%add(switch="--batch-size", act="store", error=error, def="512", &
+ & help="Batch size for grid point processing")
+ if (error /= 0) exit input
+
+ call cli%parse(error=error)
+ if (error /= 0) exit input
+
+ call cli%get(position=1, val=dummy, error=error)
+ input_file = trim(dummy)
+ if (error /= 0) exit input
+ call cli%get(switch="--model", val=dummy, error=error)
+ model = trim(dummy)
+ if (error /= 0) exit input
+ if (cli%is_passed(switch="--grid")) then
+ call cli%get(switch="--grid", val=dummy, error=error)
+ if (error /= 0) exit input
+ grid_spec = trim(dummy)
+ end if
+ if (cli%is_passed(switch="--radial-quadrature")) then
+ call cli%get(switch="--radial-quadrature", val=dummy, error=error)
+ if (error /= 0) exit input
+ rad_quad_spec = trim(dummy)
+ end if
+ if (cli%is_passed(switch="--pruning-scheme")) then
+ call cli%get(switch="--pruning-scheme", val=dummy, error=error)
+ if (error /= 0) exit input
+ prune_spec = trim(dummy)
+ end if
+ if (cli%is_passed(switch="--lb-exec-space")) then
+ call cli%get(switch="--lb-exec-space", val=dummy, error=error)
+ if (error /= 0) exit input
+ lb_exec_space_str = trim(dummy)
+ end if
+ if (cli%is_passed(switch="--int-exec-space")) then
+ call cli%get(switch="--int-exec-space", val=dummy, error=error)
+ if (error /= 0) exit input
+ int_exec_space_str = trim(dummy)
+ end if
+ if (cli%is_passed(switch="--batch-size")) then
+ call cli%get(switch="--batch-size", val=batch_size, error=error)
+ if (error /= 0) exit input
+ end if
+ end block input
+ if (error /= 0) then
+ print '(a)', cli%error_message
+#ifdef GAUXC_HAS_MPI
+ call MPI_Abort(MPI_COMM_WORLD, 1, error)
+#else
+ stop 1
+#endif
+ end if
+
+#ifdef GAUXC_HAS_MPI
+ call MPI_Barrier(MPI_COMM_WORLD, error)
+#endif
+
+ main: block
+ ! Create runtime
+#ifdef GAUXC_HAS_MPI
+ rt = gauxc_runtime_environment_new(status, MPI_COMM_WORLD)
+#else
+ rt = gauxc_runtime_environment_new(status)
+#endif
+ if (status%code /= 0) exit main
+ world_rank = gauxc_runtime_environment_comm_rank(status, rt)
+ if (status%code /= 0) exit main
+ world_size = gauxc_runtime_environment_comm_size(status, rt)
+ if (status%code /= 0) exit main
+
+ if (world_rank == 0) then
+ print '(a)', &
+ & "Configuration", &
+ & "-> Input file : "//input_file, &
+ & "-> Model : "//model, &
+ & "-> Grid : "//grid_spec, &
+ & "-> Radial quadrature : "//rad_quad_spec, &
+ & "-> Pruning scheme : "//prune_spec, &
+ & ""
+ end if
+
+ ! Get molecule (atomic numbers and cartesian coordinates)
+ mol = gauxc_molecule_new(status)
+ if (status%code /= 0) exit main
+ ! Load molecule from HDF5 dataset
+ call gauxc_read_hdf5_record(status, mol, input_file, "/MOLECULE")
+ if (status%code /= 0) exit main
+
+ ! Get basis set
+ basis = gauxc_basisset_new(status)
+ if (status%code /= 0) exit main
+ ! Load basis set from HDF5 dataset
+ call gauxc_read_hdf5_record(status, basis, input_file, "/BASIS")
+ if (status%code /= 0) exit main
+
+ ! Define molecular grid from grid size, radial quadrature and pruning scheme
+ grid_type = read_atomic_grid_size(grid_spec)
+ radial_quad = read_radial_quad(rad_quad_spec)
+ pruning_scheme = read_pruning_scheme(prune_spec)
+ grid = gauxc_molgrid_new_default(status, mol, pruning_scheme, batch_size, radial_quad, grid_type)
+ if (status%code /= 0) exit main
+
+ ! Choose whether we run on host or device
+ lb_exec_space = read_execution_space(lb_exec_space_str)
+ int_exec_space = read_execution_space(int_exec_space_str)
+
+ ! Setup load balancer based on molecule, grid and basis set
+ lbf = gauxc_load_balancer_factory_new(status, lb_exec_space, "Replicated")
+ if (status%code /= 0) exit main
+ lb = gauxc_get_shared_instance(status, lbf, rt, mol, grid, basis)
+ if (status%code /= 0) exit main
+
+ ! Apply partitioning weights to the molecule grid
+ mwf = gauxc_molecular_weights_factory_new(status, int_exec_space, "Default", &
+ & gauxc_molecular_weights_settings())
+ if (status%code /= 0) exit main
+ mw = gauxc_get_instance(status, mwf)
+ if (status%code /= 0) exit main
+ call gauxc_molecular_weights_modify_weights(status, mw, lb)
+ if (status%code /= 0) exit main
+
+ ! Setup exchange-correlation integrator
+ func = gauxc_functional_from_string(status, "PBE", .true._c_bool)
+ intf = gauxc_integrator_factory_new(status, int_exec_space, "Replicated", &
+ & "Default", "Default", "Default")
+ if (status%code /= 0) exit main
+ integrator = gauxc_get_instance(status, intf, func, lb)
+ if (status%code /= 0) exit main
+
+ ! Load density matrix from input
+ p_s = gauxc_matrix_empty(status)
+ p_z = gauxc_matrix_empty(status)
+ call gauxc_read_hdf5_record(status, p_s, input_file, "/DENSITY_SCALAR")
+ if (status%code /= 0) exit main
+ call gauxc_read_hdf5_record(status, p_z, input_file, "/DENSITY_Z")
+ if (status%code /= 0) exit main
+
+#ifdef GAUXC_HAS_MPI
+ call MPI_Barrier(MPI_COMM_WORLD, error)
+#endif
+ t_start = timing()
+
+ ! Integrate exchange-correlation energy
+ vxc_s = gauxc_matrix_empty(status)
+ vxc_z = gauxc_matrix_empty(status)
+ call gauxc_eval_exc_vxc(status, integrator, p_s, p_z, model, exc, vxc_s, vxc_z)
+ if (status%code /= 0) exit main
+
+#ifdef GAUXC_HAS_MPI
+ call MPI_Barrier(MPI_COMM_WORLD, error)
+#endif
+ t_end = timing()
+ t_exc = t_end - t_start
+
+ if (world_rank == 0) then
+ associate(vxc_s_ => gauxc_matrix_data(status, vxc_s), vxc_z_ => gauxc_matrix_data(status, vxc_z))
+ print '(a)', "Results"
+ print '(a,1x,es17.10,:1x,a)', "Exc =", exc, "Eh"
+ print '(a,1x,es17.10,:1x,a)', "|VXC(a+b)|_F =", sqrt(sum(vxc_s_**2))
+ print '(a,1x,es17.10,:1x,a)', "|VXC(a-b)|_F =", sqrt(sum(vxc_z_**2))
+ print '(a,1x,es17.10,:1x,a)', "Runtime XC =", t_exc
+ end associate
+ end if
+
+ end block main
+ if (world_rank == 0 .and. status%code /= 0) then
+ print '(a,1x,i0)', "GauXC returned with status code", status%code
+ print '(a)', gauxc_status_message(status)
+ end if
+
+ call gauxc_delete(status, rt)
+ call gauxc_delete(status, mol)
+ call gauxc_delete(status, basis)
+ call gauxc_delete(status, grid)
+ call gauxc_delete(status, lbf)
+ call gauxc_delete(status, lb)
+ call gauxc_delete(status, mwf)
+ call gauxc_delete(status, mw)
+ call gauxc_delete(status, func)
+ call gauxc_delete(status, intf)
+ call gauxc_delete(status, integrator)
+ call gauxc_delete(status, p_s)
+ call gauxc_delete(status, p_z)
+ call gauxc_delete(status, vxc_s)
+ call gauxc_delete(status, vxc_z)
+
+#ifdef GAUXC_HAS_MPI
+ call MPI_Finalize(error)
+#endif
+
+contains
+
+ pure function read_execution_space(spec) result(val)
+ character(len=*), intent(in) :: spec
+ integer(c_int) :: val
+ select case(spec)
+ case("host")
+ val = gauxc_executionspace%host
+ case("device")
+ val = gauxc_executionspace%device
+ end select
+ end function read_execution_space
+
+ pure function read_atomic_grid_size(spec) result(val)
+ character(len=*), intent(in) :: spec
+ integer(c_int) :: val
+ select case(spec)
+ case("fine")
+ val = gauxc_atomicgridsizedefault%finegrid
+ case("ultrafine")
+ val = gauxc_atomicgridsizedefault%ultrafinegrid
+ case("superfine")
+ val = gauxc_atomicgridsizedefault%superfinegrid
+ case("gm3")
+ val = gauxc_atomicgridsizedefault%gm3
+ case("gm5")
+ val = gauxc_atomicgridsizedefault%gm5
+ end select
+ end function read_atomic_grid_size
+
+ pure function read_radial_quad(spec) result(val)
+ character(len=*), intent(in) :: spec
+ integer(c_int) :: val
+ select case(spec)
+ case("becke")
+ val = gauxc_radialquad%becke
+ case("muraknowles")
+ val = gauxc_radialquad%mura_knowles
+ case("treutlerahlrichs")
+ val = gauxc_radialquad%treutler_ahlrichs
+ case("murrayhandylaming")
+ val = gauxc_radialquad%murray_handy_laming
+ end select
+ end function read_radial_quad
+
+ pure function read_pruning_scheme(spec) result(val)
+ character(len=*), intent(in) :: spec
+ integer(c_int) :: val
+ select case(spec)
+ case("unpruned")
+ val = gauxc_pruningscheme%unpruned
+ case("robust")
+ val = gauxc_pruningscheme%robust
+ case("treutler")
+ val = gauxc_pruningscheme%treutler
+ end select
+ end function read_pruning_scheme
+
+ function timing() result(time)
+ real(c_double) :: time
+ integer(c_int64_t) :: time_count, time_rate, time_max
+ call system_clock(time_count, time_rate, time_max)
+ time = real(time_count, c_double) / real(time_rate, c_double)
+ end function timing
+end program skala
\ No newline at end of file
diff --git a/examples/fortran/gauxc_integration/cmake/skala-dep-versions.cmake b/examples/fortran/gauxc_integration/cmake/skala-dep-versions.cmake
new file mode 100644
index 0000000..4916baf
--- /dev/null
+++ b/examples/fortran/gauxc_integration/cmake/skala-dep-versions.cmake
@@ -0,0 +1,5 @@
+set(Skala_GauXC_URL "https://github.com/microsoft/skala/releases/download/v1.1.1/gauxc-skala.tar.gz")
+set(Skala_GauXC_SHA256 "0a64b2623be99894d3c61bd78aec1759abf3f476656695fab76c5e3fbb372616")
+
+set(Skala_FLAP_URL "https://github.com/szaghi/FLAP/releases/download/v1.2.5/FLAP.tar.gz")
+set(Skala_FLAP_SHA256 "f2a388898a1ee49a8559822280647eb112d884484cb6169710011daab6dbb501")
diff --git a/examples/fortran/gauxc_integration/cmake/skala-flap.cmake b/examples/fortran/gauxc_integration/cmake/skala-flap.cmake
new file mode 100644
index 0000000..9a8324a
--- /dev/null
+++ b/examples/fortran/gauxc_integration/cmake/skala-flap.cmake
@@ -0,0 +1,23 @@
+if(NOT DEFINED Skala_FLAP_URL)
+ include(skala-dep-versions)
+endif()
+find_package(FLAP QUIET CONFIG)
+if(NOT FLAP_FOUND)
+ include(FetchContent)
+
+ message(STATUS "Could not find FLAP... Building FLAP from source")
+ message(STATUS "FLAP URL: ${Skala_FLAP_URL}")
+
+ FetchContent_Declare(
+ flap
+ URL ${Skala_FLAP_URL}
+ URL_HASH SHA256=${Skala_FLAP_SHA256}
+ DOWNLOAD_EXTRACT_TIMESTAMP ON
+ )
+ FetchContent_MakeAvailable(flap)
+endif()
+
+if(NOT TARGET FLAP::FLAP)
+ add_library(FLAP::FLAP INTERFACE IMPORTED)
+ target_link_libraries(FLAP::FLAP INTERFACE FLAP)
+endif()
\ No newline at end of file
diff --git a/examples/fortran/gauxc_integration/cmake/skala-gauxc.cmake b/examples/fortran/gauxc_integration/cmake/skala-gauxc.cmake
new file mode 100644
index 0000000..91e6fc1
--- /dev/null
+++ b/examples/fortran/gauxc_integration/cmake/skala-gauxc.cmake
@@ -0,0 +1,45 @@
+if(NOT DEFINED Skala_GauXC_URL)
+ include(skala-dep-versions)
+endif()
+find_package(gauxc QUIET CONFIG)
+if(NOT gauxc_FOUND)
+ include(FetchContent)
+
+ message(STATUS "Could not find GauXC... Building GauXC from source")
+ message(STATUS "GAUXC URL: ${Skala_GauXC_URL}")
+
+ set(GAUXC_ENABLE_ONEDFT ON CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_C ON CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_FORTRAN ON CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_TESTS OFF CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_OPENMP ${Skala_GauXC_ENABLE_OPENMP} CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_MPI ${Skala_GauXC_ENABLE_MPI} CACHE BOOL "" FORCE)
+ set(GAUXC_ENABLE_CUDA ${Skala_GauXC_ENABLE_CUDA} CACHE BOOL "" FORCE)
+
+ FetchContent_Declare(
+ gauxc
+ URL ${Skala_GauXC_URL}
+ # URL_HASH SHA256=${Skala_GauXC_SHA256}
+ DOWNLOAD_EXTRACT_TIMESTAMP ON
+ )
+ FetchContent_MakeAvailable(gauxc)
+
+endif()
+if(NOT GAUXC_HAS_ONEDFT)
+ message(FATAL_ERROR "GauXC found but without Skala support enabled")
+endif()
+if(NOT GAUXC_HAS_C)
+ message(FATAL_ERROR "GauXC found but without C API support enabled")
+endif()
+if(NOT GAUXC_HAS_FORTRAN)
+ message(FATAL_ERROR "GauXC found but without Fortran API support enabled")
+endif()
+if(Skala_GauXC_ENABLE_OPENMP AND NOT GAUXC_HAS_OPENMP)
+ message(FATAL_ERROR "GauXC found without OpenMP support but Skala_GauXC_ENABLE_OPENMP is ON")
+endif()
+if(Skala_GauXC_ENABLE_MPI AND NOT GAUXC_HAS_MPI)
+ message(FATAL_ERROR "GauXC found without MPI support but Skala_GauXC_ENABLE_MPI is ON")
+endif()
+if(Skala_GauXC_ENABLE_CUDA AND NOT GAUXC_HAS_CUDA)
+ message(FATAL_ERROR "GauXC found without CUDA support but Skala_GauXC_ENABLE_CUDA is ON")
+endif()
diff --git a/examples/fortran/gauxc_integration/environment-mpi.yml b/examples/fortran/gauxc_integration/environment-mpi.yml
new file mode 100644
index 0000000..13416a2
--- /dev/null
+++ b/examples/fortran/gauxc_integration/environment-mpi.yml
@@ -0,0 +1,18 @@
+name: gauxc-dev
+channels:
+ - conda-forge
+dependencies:
+ # build requirements
+ - fortran-compiler
+ - c-compiler
+ - cxx-compiler
+ - cmake >=3.15,<4
+ - ninja
+ - nlohmann_json >=3.9
+ # host/runtime requirements
+ - openmpi # pick mpich if that matches your stack
+ - exchcxx >=1.0
+ - gau2grid >=2.0.6
+ - hdf5 * mpi_*
+ - libblas
+ - pytorch >=2.0 cpu_*
diff --git a/examples/fortran/gauxc_integration/environment-openmp.yml b/examples/fortran/gauxc_integration/environment-openmp.yml
new file mode 100644
index 0000000..c9c1248
--- /dev/null
+++ b/examples/fortran/gauxc_integration/environment-openmp.yml
@@ -0,0 +1,17 @@
+name: gauxc-dev
+channels:
+ - conda-forge
+dependencies:
+ # build requirements
+ - fortran-compiler
+ - c-compiler
+ - cxx-compiler
+ - cmake >=3.15,<4
+ - ninja
+ - nlohmann_json >=3.9
+ # host/runtime requirements
+ - exchcxx >=1.0
+ - gau2grid >=2.0.6
+ - hdf5
+ - libblas
+ - pytorch >=2.0 cpu_*