Make GRPP an internal dependency

Co-authored-by: Mathieu Taillefumier <mathieu.taillefumier@free.fr>
This commit is contained in:
Taillefumier Mathieu 2025-02-28 03:36:16 -05:00 committed by GitHub
parent a60d89c369
commit cbdeaffe0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 54582 additions and 319 deletions

View file

@ -50,8 +50,8 @@ endif()
# set language and standard
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_C_STANDARD 11)
set(CMAKE_HIP_STANDARD 11)
set(CMAKE_CUDA_STANDARD 11)
set(CMAKE_HIP_STANDARD 14)
set(CMAKE_CUDA_STANDARD 14)
# remove NDEBUG flag
string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE})
string(REPLACE "-DNDEBUG" "" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
@ -724,10 +724,6 @@ if(CP2K_USE_TREXIO)
find_package(TrexIO REQUIRED)
endif()
if(CP2K_USE_GRPP)
# we need grpp when TREXIO is on
find_package(grpp REQUIRED CONFIG)
endif()
# OPTION HANDLING
# make sure that the default build type is RELEASE
@ -931,9 +927,7 @@ if(CP2K_USE_DLAF)
endif()
if(CP2K_USE_GRPP)
message(" - grpp\n" # let below line separate
" - include directories: ${GRPP_INCLUDE_DIRS}\n"
" - libraries: ${GRPP_LINK_LIBRARIES}\n\n")
message(" - grpp\n")
endif()
if(CP2K_USE_TREXIO)

View file

@ -454,12 +454,10 @@ Calls to `offload_dgemm` also accept pointers on GPU or a combination of them.
### 2y. libgrpp (optional, enables calculations with ECPs)
- libgrpp is a library for the calculation of integrals with GTOs and ECPs
- The libgrpp library can be found under <https://github.com/aoleynichenko/libgrpp>
- During the installation, the directories `$(LIBGRPP_DIR)/lib` and `$(LIBGRPP_DIR)/include` are
created.
- Add `-D__LIBGRPP` to DFLAGS, `-I$(LIBGRPP_DIR)/include` to FCFLAGS and
`-L$(LIBGRPP_DIR)/lib -llibgrpp` to LIBS
libgrpp is a library for the calculation of integrals with GTOs and ECPs. The source code of the
library is part of cp2k.
- Add `-D__LIBGRPP` to DFLAGS.
<!---
### 2y. LibMaxwell (External Maxwell Solver)

View file

@ -161,12 +161,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -144,12 +144,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -168,12 +168,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -165,12 +165,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -102,12 +102,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -121,12 +121,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -223,12 +223,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -128,12 +128,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -248,12 +248,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/libgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -202,12 +202,7 @@ endif
ifneq ($(USE_LIBGRPP),)
USE_LIBGRPP := $(strip $(USE_LIBGRPP))
LIBGRPP_INC := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include
LIBGRPP_INC += -I$(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/include/libgrpp/GNU-$(GNU_VER)/fortran_modules
LIBGRPP_LIB := $(INSTALL_PATH)/libgrpp-main-$(USE_LIBGRPP)/lib
CFLAGS += -I$(LIBGRPP_INC)
DFLAGS += -D__LIBGRPP
LIBS += $(LIBGRPP_LIB)/liblibgrpp.a
endif
ifneq ($(USE_LIBINT),)

View file

@ -61,7 +61,7 @@ if [[ "${PROFILE}" == "spack" ]] && [[ "${VERSION}" == "psmp" ]]; then
-DCP2K_USE_COSMA=ON \
-DCP2K_USE_SIRIUS=ON \
-DCP2K_USE_LIBVDWXC=ON \
-DCP2K_USE_GRPP=OFF \
-DCP2K_USE_GRPP=ON \
-DCP2K_USE_TREXIO=ON \
-DCP2K_USE_LIBTORCH=ON \
-DCP2K_USE_DLAF=ON \
@ -82,7 +82,7 @@ elif [[ "${PROFILE}" == "toolchain" ]] && [[ "${VERSION}" == "ssmp" ]]; then
-DCP2K_USE_DFTD4=ON \
-DCP2K_USE_DLAF=OFF \
-DCP2K_USE_FFTW3=ON \
-DCP2K_USE_GRPP=OFF \
-DCP2K_USE_GRPP=ON \
-DCP2K_USE_HDF5=ON \
-DCP2K_USE_LIBINT2=ON \
-DCP2K_USE_LIBTORCH=ON \
@ -129,7 +129,7 @@ elif [[ "${PROFILE}" == "toolchain" ]] && [[ "${VERSION}" == "psmp" ]]; then
-DCP2K_USE_DLAF=OFF \
-DCP2K_USE_ELPA=ON \
-DCP2K_USE_FFTW3=ON \
-DCP2K_USE_GRPP=OFF \
-DCP2K_USE_GRPP=ON \
-DCP2K_USE_HDF5=ON \
-DCP2K_USE_LIBINT2=ON \
-DCP2K_USE_LIBSMEAGOL=ON \

View file

@ -1404,6 +1404,46 @@ set(CP2K_GRID_SRCS_C
grid/cpu/grid_cpu_task_list.c
grid/grid_replay.c)
set(CP2K_GRPP_SRCS_C
grpp/grpp_angular_integrals.c
grpp/grpp_binomial.c
grpp/grpp_diff_gaussian.c
grpp/grpp_factorial.c
grpp/grpp_fortran.c
grpp/grpp_full_grpp_integrals.c
grpp/grpp.c
grpp/grpp_integrals_gradient.c
grpp/grpp_init_finalize.c
grpp/grpp_kinetic.c
grpp/grpp_lmatrix.c
grpp/grpp_momentum.c
grpp/grpp_norm_gaussian.c
grpp/grpp_nuclear_attraction.c
grpp/grpp_nuclear_models.c
grpp/grpp_outercore_integrals.c
grpp/grpp_overlap.c
grpp/grpp_overlap_gradient.c
grpp/grpp_parameters.c
grpp/grpp_potential.c
grpp/grpp_radial_type1_integral.c
grpp/grpp_radial_type2_integral.c
grpp/grpp_screening.c
grpp/grpp_shell.c
grpp/grpp_specfunc_bessel_table.c
grpp/grpp_specfunc_boys.c
grpp/grpp_specfunc_dawson.c
grpp/grpp_specfunc_fermi_sk.c
grpp/grpp_specfunc_gfun.c
grpp/grpp_specfunc_scaled_mod_sph_bessel.c
grpp/grpp_spherical_harmonics.c
grpp/grpp_spin_orbit_integrals.c
grpp/grpp_type1_integrals.c
grpp/grpp_type1_mcmurchie_davidson.c
grpp/grpp_type2_integrals.c
grpp/grpp_utils.c)
set(CP2K_GRPP_SRCS_F grpp/libgrpp.F)
set(CP2K_FPGA_SRC_C pw/fpga/fft_fpga.c pw/fpga/opencl_utils.c)
set(CP2K_PW_SRCS_C pw/gpu/pw_gpu_internal.c)
set(CP2K_OFFLOAD_SRCS_C offload/offload_buffer.c offload/offload_library.c)
@ -1489,6 +1529,10 @@ endif()
list(APPEND CP2K_SRCS_C ${CP2K_DBM_SRCS_C} ${CP2K_GRID_SRCS_C})
if(CP2K_USE_GRPP)
list(APPEND CP2K_SRCS_C ${CP2K_GRPP_SRCS_C})
list(APPEND CP2K_SRCS_F ${CP2K_GRPP_SRCS_F})
endif()
# ##############################################################################
# check that the files registered in CMakeLists.txt are all present and return
# an error otherwise. cmake only runs this test nothing else below
@ -1496,12 +1540,12 @@ list(APPEND CP2K_SRCS_C ${CP2K_DBM_SRCS_C} ${CP2K_GRID_SRCS_C})
if(CP2K_ENABLE_CONSISTENCY_CHECKS)
cp2k_compare_src_with_list(
"${CP2K_FPGA_SRC_C};${CP2K_SRCS_C};${CP2K_OFFLOAD_SRCS_C};${CP2K_DBM_SRCS_C};${CP2K_GRID_SRCS_C};${CP2K_PW_SRCS_C};${CP2K_DBM_SRCS_GPU_C};${CP2K_PROGS_C}"
"${CP2K_FPGA_SRC_C};${CP2K_SRCS_C};${CP2K_OFFLOAD_SRCS_C};${CP2K_DBM_SRCS_C};${CP2K_GRID_SRCS_C};${CP2K_PW_SRCS_C};${CP2K_DBM_SRCS_GPU_C};${CP2K_PROGS_C};${CP2K_GRPP_SRCS_C}"
"*.c"
"${CP2K_EXCLUDED_SRCS_C}")
cp2k_compare_src_with_list(
"${CP2K_SRCS_F};${CP2K_OFFLOAD_SRCS_F};${CP2K_PROGS_F}" "*.F"
"${CP2K_EXCLUDED_SRCS_F}")
"${CP2K_GRPP_SRCS_F};${CP2K_SRCS_F};${CP2K_OFFLOAD_SRCS_F};${CP2K_PROGS_F}"
"*.F" "${CP2K_EXCLUDED_SRCS_F}")
cp2k_compare_src_with_list(
"${CP2K_GRID_SRCS_GPU};${CP2K_GRID_SRCS_HIP};${CP2K_DBM_SRCS_GPU};${CP2K_PW_SRCS_GPU}"
"*.cu"
@ -1658,7 +1702,6 @@ target_link_libraries(
$<$<BOOL:${CP2K_USE_COSMA}>:cosma::cosma>
$<$<BOOL:${CP2K_USE_DLAF}>:DLAF::Fortran>
$<$<BOOL:${CP2K_USE_TREXIO}>:cp2k::trexio::trexio>
$<$<BOOL:${CP2K_USE_GRPP}>:cp2k::grpp::grpp>
$<$<BOOL:${CP2K_USE_HDF5}>:HDF5::HDF5
hdf5::hdf5_fortran>
DBCSR::dbcsr

View file

@ -19,6 +19,7 @@
"shg_int",
"arnoldi",
"grid",
"grpp",
"dbm",
"dbt",
"dbx",

21
src/grpp/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Alexander Oleynichenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5
src/grpp/PACKAGE Normal file
View file

@ -0,0 +1,5 @@
{
"description": "grpp library",
"requires": [],
"public": ["libgrpp.F"],
}

140
src/grpp/README.md Normal file
View file

@ -0,0 +1,140 @@
# libgrpp
A library for the evaluation of molecular integrals of the generalized relativistic pseudopotential
operator (GRPP) over Gaussian functions.
# Features
- basis functions:
- Cartesian contracted GTOs
- max angular momentum of basis functions $l_{max} = 10$ (up to $n$-functions, can be increased by
hands)
- RPP integrals:
- scalar-relativistic part: integrals over the local potential (type 1 integrals)
- scalar-relativistic part: integrals with angular projectors (type 2 integrals)
- integrals over the effective spin-orbit (SO) interaction operator
- integrals over GRPP-specific non-local terms (with projectors onto subvalence shells)
- analytic gradients of GRPP integrals
- other one-electron integrals:
- overlap integrals
- nuclear attraction integrals
- C and Fortran 90 interfaces
- no dependence on external libraries
- thread safety
# What is a generalized pseudopotential?
[Generalized relativistic pseudopotentials (GRPPs)](https://onlinelibrary.wiley.com/doi/10.1002/%28SICI%291097-461X%281999%2971%3A5%3C359%3A%3AAID-QUA1%3E3.0.CO%3B2-U)
of atomic cores imply the use of different potentials for atomic electronic shells with different
principal quantum numbers. GRPPs give rise to accurate and reliable relativistic electronic
structure models of [atoms](https://onlinelibrary.wiley.com/doi/abs/10.1002/qua.26076),
[molecules](https://www.mdpi.com/2073-8994/15/1/197),
[clusters](https://pubs.rsc.org/en/content/articlelanding/2022/CP/D2CP01738E) and
[solids](https://journals.aps.org/prb/abstract/10.1103/PhysRevB.103.205105). GRPPs readily
incorporate the effects of
[Breit electronelectron interactions](https://iopscience.iop.org/article/10.1088/0953-4075/37/23/004)
and
[one-loop quantum electrodynamics effects](https://onlinelibrary.wiley.com/doi/abs/10.1002/qua.27077).
GRPPs are one of the most precise relativistic Hamiltonians at the moment, allowing one to
completely bypass any complicated four-component calculations.
Library of generalized pseudopotentials:
[http://qchem.pnpi.spb.ru/recp](http://qchem.pnpi.spb.ru/recp)
# How to compile examples and run tests
```
mkdir build
cd build
CC=icc FC=ifort cmake ..
make
make test
```
# Citation
A. V. Oleynichenko, A. Zaitsevskii, N. S. Mosyagin, A. N. Petrov, E. Eliav, A. V. Titov.
LIBGRPP: A Library for the Evaluation of Molecular Integrals of the Generalized Relativistic
Pseudopotential Operator over Gaussian Functions.
<i>Symmetry</i>, 15(1), 197 (2023)
doi: [10.3390/sym15010197](https://doi.org/10.3390/sym15010197)
```
@article{Oleynichenko2023,
title = {{LIBGRPP}: A library for the evaluation of molecular integrals of the generalized relativistic pseudopotential operator over {G}aussian functions},
author = {A. V. Oleynichenko and A. Zaitsevskii and N. S. Mosyagin and A. N. Petrov and E. Eliav and A. V. Titov},
year = {2022},
journal = {Symmetry},
volume = {15},
year = {2023},
number = {1},
article-number = {197},
url = {https://www.mdpi.com/2073-8994/15/1/197},
doi = {10.3390/sym15010197}
}
```
# Bug report
Alexander Oleynichenko, alexvoleynichenko@gmail.com
# References: more on algorithms used
- type 1 integrals (local part):
- L. E. McMurchie, E. R. Davidson. One- and two-electron integrals over Cartesian Gaussian
functions.
[<i>J. Comput. Phys.</i> 26, 218 (1978)](<https://doi.org/10.1016/0021-9991(78)90092-X>)
- J. O. Jensen, A. H. Carrieri, C. P. Vlahacos, D. Zeroka, H. F. Hameka, C. N. Merrow. Evaluation
of one-electron integrals for arbitrary operators $V(r)$ over Cartesian Gaussians: Application
to inverse-square distance and Yukawa operators.
[<i>J. Comput. Chem.</i> 14, 986 (1993)](https://doi.org/10.1002/jcc.540140814)
- B. Gao, A. J. Thorvaldsen, K. Ruud. GEN1INT: A unified procedure for the evaluation of
one-electron integrals over Gaussian basis functions and their geometric derivatives.
[<i>Int. J. Quantum Chem.</i> 111, 858 (2011)](https://doi.org/10.1002/qua.22886)
- type 2 integrals (semilocal part):
- L. E. McMurchie, E. R. Davidson. Calculation of integrals over ab initio pseudopotentials.
[<i>J. Comput. Phys.</i> 44, 289 (1981)](<https://doi.org/10.1016/0021-9991(81)90053-X>)
- C. K. Skylaris, L. Gagliardi, N. C. Handy, A. G. Ioannou, S. Spencer, A. Willetts, A. M. Simper.
An efficient method for calculating effective core potential integrals which involve projection
operators.
[<i>Chem. Phys. Lett.</i> 296, 445 (1998)](<https://doi.org/10.1016/S0009-2614(98)01077-X>)
- R. Flores-Moreno, R. J. Alvarez-Mendez, A. Vela, A. M. Köster. Half-numerical evaluation of
pseudopotential integrals.
[<i>J. Comput. Chem.</i> 27, 1009 (2006)](https://doi.org/10.1002/jcc.20410)
- C. van Wüllen. Numerical instabilities in the computation of pseudopotential matrix elements.
[<i>J. Comput. Chem.</i> 27, 135 (2006)](https://doi.org/10.1002/jcc.20325)
- R. A. Shaw, J. G. Hill. Prescreening and efficiency in the evaluation of integrals over ab
initio effective core potentials.
[<i>J. Chem. Phys.</i> 147, 074108 (2017)](https://doi.org/10.1063/1.4986887)
- spin-orbit integrals:
- R. M. Pitzer, N. W. Winter. Spin-orbit (core) and core potential integrals.
[<i>Int. J. Quantum Chem.</i> 40, 773 (1991)](https://doi.org/10.1002/qua.560400606)
- integrals non-local terms (with projectors onto subvalence shells):
- A. V. Oleynichenko, A. Zaitsevskii, N. S. Mosyagin, A. N. Petrov, E. Eliav, A. V. Titov.
LIBGRPP: A library for the evaluation of molecular integrals of the generalized relativistic
pseudopotential operator over Gaussian functions.
[<i>Symmetry</i>, 15(1), 197 (2023).](https://doi.org/10.3390/sym15010197)

113
src/grpp/grpp.c Normal file
View file

@ -0,0 +1,113 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include "libgrpp.h"
#include <stdlib.h>
#define LIBGRPP_MAX_NUMBER_POTENTIALS 100
libgrpp_grpp_t *libgrpp_new_grpp() {
return (libgrpp_grpp_t *)calloc(1, sizeof(libgrpp_grpp_t));
}
void libgrpp_grpp_set_local_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot) {
if (grpp->U_L != NULL) {
libgrpp_delete_potential(grpp->U_L);
}
grpp->U_L = pot;
}
void libgrpp_grpp_add_averaged_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot) {
if (grpp->n_arep == 0) {
grpp->U_arep = (libgrpp_potential_t **)calloc(
LIBGRPP_MAX_NUMBER_POTENTIALS, sizeof(libgrpp_potential_t *));
}
grpp->U_arep[grpp->n_arep++] = pot;
}
void libgrpp_grpp_add_spin_orbit_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot) {
if (grpp->n_esop == 0) {
grpp->U_esop = (libgrpp_potential_t **)calloc(
LIBGRPP_MAX_NUMBER_POTENTIALS, sizeof(libgrpp_potential_t *));
}
grpp->U_esop[grpp->n_esop++] = pot;
}
void libgrpp_grpp_add_outercore_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot,
libgrpp_shell_t *oc_shell) {
if (grpp->n_oc_shells == 0) {
grpp->U_oc = (libgrpp_potential_t **)calloc(LIBGRPP_MAX_NUMBER_POTENTIALS,
sizeof(libgrpp_potential_t *));
grpp->oc_shells = (libgrpp_shell_t **)calloc(LIBGRPP_MAX_NUMBER_POTENTIALS,
sizeof(libgrpp_shell_t *));
}
grpp->U_oc[grpp->n_oc_shells] = pot;
grpp->oc_shells[grpp->n_oc_shells] = oc_shell;
grpp->n_oc_shells++;
}
void libgrpp_delete_grpp(libgrpp_grpp_t *grpp) {
if (grpp == NULL) {
return;
}
/*
* scalar-relativistic part
*/
if (grpp->U_L != NULL) {
libgrpp_delete_potential(grpp->U_L);
}
for (int i = 0; i < grpp->n_arep; i++) {
if (grpp->U_arep[i] != NULL) {
libgrpp_delete_potential(grpp->U_arep[i]);
}
}
free(grpp->U_arep);
/*
* effective spin-orbit operator
*/
for (int i = 0; i < grpp->n_esop; i++) {
if (grpp->U_esop[i] != NULL) {
libgrpp_delete_potential(grpp->U_esop[i]);
}
}
free(grpp->U_esop);
/*
* outercore shells and potentials
*/
for (int i = 0; i < grpp->n_oc_shells; i++) {
if (grpp->U_oc[i] != NULL) {
libgrpp_delete_potential(grpp->U_oc[i]);
}
if (grpp->oc_shells[i] != NULL) {
libgrpp_delete_shell(grpp->oc_shells[i]);
}
}
free(grpp->U_oc);
free(grpp->oc_shells);
free(grpp);
}

View file

@ -0,0 +1,141 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* This file contains subroutines for evaluation of angular integrals of the 1st
* and 2nd type. It also contains functions for construction of matrices of the
* angular momentum operator in the bases of either real or complex spherical
* harmonics.
*
* For more details on the angular integrals used in RPP integration, see:
*
* L. E. McMurchie, E. R. Davidson. Calculation of integrals over ab initio
* pseudopotentials. J. Comput. Phys. 44(2), 289 (1981).
* doi: 10.1016/0021-9991(81)90053-x
*/
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_angular_integrals.h"
#include "grpp_factorial.h"
#include "grpp_spherical_harmonics.h"
#include "libgrpp.h"
static double integrate_unitary_sphere_polynomial(int i, int j, int k);
/**
* Type 1 angular integral.
* (see MrMurchie & Davidson, formula (28))
*/
double libgrpp_angular_type1_integral(int lambda, int II, int JJ, int KK,
double *k) {
double sum = 0.0;
for (int mu = -lambda; mu <= lambda; mu++) {
double sum2 = 0.0;
for (int r = 0; r <= lambda; r++) {
for (int s = 0; s <= lambda; s++) {
for (int t = 0; t <= lambda; t++) {
if (r + s + t == lambda) {
double y_lm_rst =
libgrpp_spherical_to_cartesian_coef(lambda, mu, r, s);
double usp_int =
integrate_unitary_sphere_polynomial(II + r, JJ + s, KK + t);
sum2 += y_lm_rst * usp_int;
}
}
}
}
sum += sum2 * libgrpp_evaluate_real_spherical_harmonic(lambda, mu, k);
}
return sum;
}
/**
* Type 2 angular integral.
* (see MrMurchie & Davidson, formula (29))
*/
double libgrpp_angular_type2_integral(const int lambda, const int L,
const int m, const int a, const int b,
const int c, const double *rsh_values) {
double sum = 0.0;
rsh_coef_table_t *rsh_coef_lambda =
libgrpp_get_real_spherical_harmonic_table(lambda);
rsh_coef_table_t *rsh_coef_L = libgrpp_get_real_spherical_harmonic_table(L);
int ncomb_rst = rsh_coef_lambda->n_cart_comb;
int ncomb_uvw = rsh_coef_L->n_cart_comb;
for (int mu = -lambda; mu <= lambda; mu++) {
double sum2 = 0.0;
double rsh_value_k = rsh_values[mu + lambda];
if (fabs(rsh_value_k) < LIBGRPP_ZERO_THRESH) {
continue;
}
for (int icomb_uvw = 0; icomb_uvw < ncomb_uvw; icomb_uvw++) {
int u = rsh_coef_L->cartesian_comb[3 * icomb_uvw];
int v = rsh_coef_L->cartesian_comb[3 * icomb_uvw + 1];
int w = rsh_coef_L->cartesian_comb[3 * icomb_uvw + 2];
double y_lm_uvw = rsh_coef_L->coeffs[(m + L) * ncomb_uvw + icomb_uvw];
if (fabs(y_lm_uvw) < LIBGRPP_ZERO_THRESH) {
continue;
}
for (int icomb_rst = 0; icomb_rst < ncomb_rst; icomb_rst++) {
int r = rsh_coef_lambda->cartesian_comb[3 * icomb_rst];
int s = rsh_coef_lambda->cartesian_comb[3 * icomb_rst + 1];
int t = rsh_coef_lambda->cartesian_comb[3 * icomb_rst + 2];
double y_lam_mu_rst =
rsh_coef_lambda->coeffs[(mu + lambda) * ncomb_rst + icomb_rst];
if (fabs(y_lam_mu_rst) < LIBGRPP_ZERO_THRESH) {
continue;
}
double usp_int = integrate_unitary_sphere_polynomial(
a + r + u, b + s + v, c + t + w);
sum2 += y_lam_mu_rst * y_lm_uvw * usp_int;
}
}
sum += sum2 * rsh_value_k;
}
return sum;
}
/**
* Integral of the unitary sphere polynomial over full solid angle.
* (see MrMurchie & Davidson, formula (30))
*/
static double integrate_unitary_sphere_polynomial(int i, int j, int k) {
if ((i % 2 == 0) && (j % 2 == 0) && (k % 2 == 0)) {
double dfac_i = (double)libgrpp_double_factorial(i - 1);
double dfac_j = (double)libgrpp_double_factorial(j - 1);
double dfac_k = (double)libgrpp_double_factorial(k - 1);
double dfac_ijk = (double)libgrpp_double_factorial(i + j + k + 1);
return 4 * M_PI * dfac_i * dfac_j * dfac_k / dfac_ijk;
} else {
return 0.0;
}
}

View file

@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_ANGULAR_INTEGRALS_H
#define LIBGRPP_ANGULAR_INTEGRALS_H
double libgrpp_angular_type1_integral(int lambda, int II, int JJ, int KK,
double *k);
double libgrpp_angular_type2_integral(int lambda, int L, int m, int a, int b,
int c, const double *rsh_values);
#endif // LIBGRPP_ANGULAR_INTEGRALS_H

82
src/grpp/grpp_binomial.c Normal file
View file

@ -0,0 +1,82 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include "grpp_binomial.h"
#include <stdint.h>
/* The code is borrowed from RosettaCode:
* https://rosettacode.org/wiki/Evaluate_binomial_coefficients#C
* We go to some effort to handle overflow situations.
*/
static uint64_t gcd_ui(uint64_t x, uint64_t y);
/*
* returns binomial coefficient:
* ( n )
* ( k )
*/
uint64_t libgrpp_binomial(uint64_t n, uint64_t k) {
uint64_t d, g, r = 1;
if (k == 0) {
return 1;
}
if (k == 1) {
return n;
}
if (k >= n) {
return (k == n);
}
if (k > n / 2) {
k = n - k;
}
for (d = 1; d <= k; d++) {
if (r >= UINT64_MAX / n) { /* Possible overflow */
uint64_t nr, dr; /* reduced numerator / denominator */
g = gcd_ui(n, d);
nr = n / g;
dr = d / g;
g = gcd_ui(r, dr);
r = r / g;
dr = dr / g;
if (r >= UINT64_MAX / nr)
return 0; /* Unavoidable overflow */
r *= nr;
r /= dr;
n--;
} else {
r *= n--;
r /= d;
}
}
return r;
}
static uint64_t gcd_ui(uint64_t x, uint64_t y) {
uint64_t t;
if (y < x) {
t = x;
x = y;
y = t;
}
while (y > 0) {
t = y;
y = x % y;
x = t; /* y1 <- x0 % y0 ; x1 <- y0 */
}
return x;
}

22
src/grpp/grpp_binomial.h Normal file
View file

@ -0,0 +1,22 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_BINOMIAL_H
#define LIBGRPP_BINOMIAL_H
#include <stdint.h>
uint64_t libgrpp_binomial(uint64_t n, uint64_t k);
#endif // LIBGRPP_BINOMIAL_H

View file

@ -0,0 +1,83 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Differentiation of contracted Gaussian functions.
* Derivatives are then used to calculate analytic gradients of 1-el integrals.
*/
#include <math.h>
#include <stdlib.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_diff_gaussian.h"
#include "libgrpp.h"
static double norm_factor(double alpha, int L);
/**
* Performs differentiation of a contracted Gaussian.
*
* Note that the "2 alpha" factors are absorbed into coefficients, while the 'n'
* factor is not. The latter must be accounted for explicitly at the stage of
* gradient construction. For more details, see: T. Helgaker, P. Jorgensen, J.
* Olsen, Molecular Electronic-Structure Theory, John Wiley & Sons Ltd, 2000.
* Chapter 9.2.2, "Recurrence relations for Cartesian Gaussians"
*
*/
void libgrpp_differentiate_shell(libgrpp_shell_t *shell,
libgrpp_shell_t **shell_minus,
libgrpp_shell_t **shell_plus) {
// downwards
if (shell->L > 0) {
*shell_minus =
libgrpp_new_shell(shell->origin, shell->L - 1, shell->num_primitives,
shell->coeffs, shell->alpha);
for (int i = 0; i < shell->num_primitives; i++) {
double alpha = shell->alpha[i];
double L = shell->L;
(*shell_minus)->coeffs[i] *=
norm_factor(alpha, L) / norm_factor(alpha, L - 1);
}
} else {
*shell_minus = NULL;
}
// upwards
*shell_plus =
libgrpp_new_shell(shell->origin, shell->L + 1, shell->num_primitives,
shell->coeffs, shell->alpha);
for (int i = 0; i < shell->num_primitives; i++) {
double alpha = shell->alpha[i];
double L = shell->L;
(*shell_plus)->coeffs[i] *=
2.0 * alpha * norm_factor(alpha, L) / norm_factor(alpha, L + 1);
}
}
/**
* Calculates normalization factor for the primitive Gaussian
* with the exponential parameter 'alpha' and angular momentum L.
*/
static double norm_factor(double alpha, int L) {
return pow(2 * alpha / M_PI, 0.75) * pow(4 * alpha, 0.5 * L);
}

View file

@ -0,0 +1,29 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Differentiation of contracted Gaussian functions.
* Derivatives are then used to calculate analytic gradients of 1-el integrals.
*/
#ifndef LIBGRPP_DIFF_GAUSSIAN_H
#define LIBGRPP_DIFF_GAUSSIAN_H
#include "libgrpp_types.h"
void libgrpp_differentiate_shell(libgrpp_shell_t *shell,
libgrpp_shell_t **shell_minus,
libgrpp_shell_t **shell_plus);
#endif // LIBGRPP_DIFF_GAUSSIAN_H

116
src/grpp/grpp_factorial.c Normal file
View file

@ -0,0 +1,116 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include "grpp_factorial.h"
#include <assert.h>
#include <stdint.h>
static uint64_t pretabulated_factorials[] = {1,
1,
2,
6,
24,
120,
720,
5040,
40320,
362880,
3628800,
39916800,
479001600,
6227020800,
87178291200,
1307674368000,
20922789888000,
355687428096000,
6402373705728000,
121645100408832000,
2432902008176640000};
double libgrpp_factorial(int n) {
if (n < 0) {
return 1;
} else if (n <= 20) {
return (double)pretabulated_factorials[n];
} else {
return n * libgrpp_factorial(n - 1);
}
}
/*
* Calculates ratio of two factorials:
* n!
* ----
* m!
*/
double libgrpp_factorial_ratio(int n, int m) {
if (n == m) {
return 1.0;
}
if (n < m) {
return 1.0 / libgrpp_factorial_ratio(m, n);
} else { // n > m
double prod = 1.0;
for (int i = m + 1; i <= n; i++) {
prod *= i;
}
return prod;
}
}
static uint64_t pretabulated_double_factorials[] = {
1, // 0!!
1,
2,
3,
8,
15, // 5!!
48,
105,
384,
945,
3840, // 10!!
10395,
46080,
135135,
645120,
2027025, // 15!!
10321920,
34459425,
185794560,
654729075,
3715891200, // 20!!
13749310575,
81749606400,
316234143225,
1961990553600,
7905853580625, // 25!!
51011754393600,
213458046676875,
1428329123020800,
6190283353629375,
42849873690624000 // 30!!
};
double libgrpp_double_factorial(int n) {
assert(n >= -1 && n <= 30);
if (n == -1) {
return 1;
} else if (n <= 30) {
return (double)pretabulated_double_factorials[n];
} else {
return n * libgrpp_double_factorial(n - 2);
}
}

24
src/grpp/grpp_factorial.h Normal file
View file

@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_FACTORIAL_H
#define LIBGRPP_FACTORIAL_H
double libgrpp_factorial(int n);
double libgrpp_factorial_ratio(int n, int m);
double libgrpp_double_factorial(int n);
#endif // LIBGRPP_FACTORIAL_H

859
src/grpp/grpp_fortran.c Normal file
View file

@ -0,0 +1,859 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Wrappers for the LIBGRPP subroutines to be used from Fortran projects.
*
* C99 Fortran
* --------------------
* int32_t integer(4)
* double real(8)
*/
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "grpp_factorial.h"
#include "libgrpp.h"
/*
* Fine-tuning of the LIBGRPP internal parameters.
*/
void libgrpp_set_default_parameters_() { libgrpp_set_default_parameters(); }
void libgrpp_set_radial_tolerance_(const double *tolerance) {
libgrpp_set_radial_tolerance(*tolerance);
}
void libgrpp_set_angular_screening_tolerance_(const double *tolerance) {
libgrpp_set_angular_screening_tolerance(*tolerance);
}
void libgrpp_set_modified_bessel_tolerance_(const double *tolerance) {
libgrpp_set_modified_bessel_tolerance(*tolerance);
}
void libgrpp_set_cartesian_order_(const int32_t *order) {
libgrpp_set_cartesian_order(*order);
}
/*
* initialization and finalization
*/
void libgrpp_init_() { libgrpp_init(); }
void libgrpp_finalize_() { libgrpp_finalize(); }
/*
* Type 1 RPP integrals (local term)
*/
void libgrpp_type1_integrals_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// pseudopotential
double *rpp_origin, int32_t *rpp_num_primitives, int32_t *rpp_powers,
double *rpp_coeffs, double *rpp_alpha,
// answer
double *matrix) {
int *pot_powers_int = (int *)calloc(*rpp_num_primitives, sizeof(int));
for (int i = 0; i < *rpp_num_primitives; i++) {
pot_powers_int[i] = rpp_powers[i];
}
libgrpp_potential_t *pot = libgrpp_new_potential(
0, 0, *rpp_num_primitives, pot_powers_int, rpp_coeffs, rpp_alpha);
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_type1_integrals(shell_A, shell_B, rpp_origin, pot, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(pot);
free(pot_powers_int);
}
/*
* Type 2 RPP integrals (semilocal terms with projectors)
*/
void libgrpp_type2_integrals_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A, double *origin_B,
// contracted Gaussian B
int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B, double *alpha_B,
// pseudopotential
double *pot_origin, int32_t *pot_L, int32_t *pot_num_primitives,
int32_t *pot_powers, double *pot_coeffs, double *pot_alpha,
// answer
double *matrix) {
int *pot_powers_int = (int *)calloc(*pot_num_primitives, sizeof(int));
for (int i = 0; i < *pot_num_primitives; i++) {
pot_powers_int[i] = pot_powers[i];
}
libgrpp_potential_t *pot = libgrpp_new_potential(
*pot_L, 0, *pot_num_primitives, pot_powers_int, pot_coeffs, pot_alpha);
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_type2_integrals(shell_A, shell_B, pot_origin, pot, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(pot);
free(pot_powers_int);
}
/*
* Effective spin-orbit operator ("Type 3") RPP integrals (semilocal terms with
* projectors)
*/
void libgrpp_spin_orbit_integrals_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// pseudopotential
double *pot_origin, int32_t *pot_L, int32_t *pot_num_primitives,
int32_t *pot_powers, double *pot_coeffs, double *pot_alpha,
// answer
double *so_x_matrix, double *so_y_matrix, double *so_z_matrix) {
int *pot_powers_int = (int *)calloc(*pot_num_primitives, sizeof(int));
for (int i = 0; i < *pot_num_primitives; i++) {
pot_powers_int[i] = pot_powers[i];
}
/*
* construct RPP structure
*/
libgrpp_potential_t *pot = libgrpp_new_potential(
*pot_L, 0, *pot_num_primitives, pot_powers_int, pot_coeffs, pot_alpha);
/*
* construct shells
*/
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_spin_orbit_integrals(shell_A, shell_B, pot_origin, pot, so_x_matrix,
so_y_matrix, so_z_matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(pot);
free(pot_powers_int);
}
/**
* Outercore RPP integrals (non-local terms with projectors onto outercore
* spinors)
*
* Part 1: integration of the first non-local term:
* U*|nlj><nlj| + |nlj><nlj|*U
*/
void libgrpp_outercore_potential_integrals_part_1_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A, double *origin_B,
// contracted Gaussian B
int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B, double *alpha_B,
// pseudopotential for the outercore shell LJ
double *pot_origin, int32_t *pot_L, int32_t *pot_J,
int32_t *pot_num_primitives, int32_t *pot_powers, double *pot_coeffs,
double *pot_alpha,
// expansion of the outercore shell LJ
int32_t *oc_shell_num_primitives, double *oc_shell_coeffs,
double *oc_shell_alpha,
// answer
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix) {
void libgrpp_outercore_potential_integrals_part_1(
libgrpp_shell_t * shell_A, libgrpp_shell_t * shell_B, double *C,
libgrpp_potential_t *oc_potential, libgrpp_shell_t *oc_shell,
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix);
/*
* array conversion: Fortran -> C
*/
int *pot_powers_int = (int *)calloc(*pot_num_primitives, sizeof(int));
for (int i = 0; i < *pot_num_primitives; i++) {
pot_powers_int[i] = pot_powers[i];
}
/*
* construct shells
*/
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
/*
* construct pseudopotential for the given L,J numbers
*/
libgrpp_potential_t *oc_potential =
libgrpp_new_potential(*pot_L, *pot_J, *pot_num_primitives, pot_powers_int,
pot_coeffs, pot_alpha);
/*
* construct outercore shell associated with the pseudopotential
*/
libgrpp_shell_t *oc_shell =
libgrpp_new_shell(pot_origin, *pot_L, *oc_shell_num_primitives,
oc_shell_coeffs, oc_shell_alpha);
/*
* evaluate RPP integrals
*/
libgrpp_outercore_potential_integrals_part_1(
shell_A, shell_B, pot_origin, oc_potential, oc_shell, arep_matrix,
so_x_matrix, so_y_matrix, so_z_matrix);
/*
* clean-up
*/
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(oc_potential);
libgrpp_delete_shell(oc_shell);
free(pot_powers_int);
}
/**
* Outercore RPP integrals (non-local terms with projectors onto outercore
* spinors)
*
* Part 2: integration of the second non-local term:
* |nlj><nlj| U |n'lj><n'lj|
*/
void libgrpp_outercore_potential_integrals_part_2_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// origin of the RPP
double *pot_origin,
// outercore shell 1:
int32_t *oc_shell_1_L, int32_t *oc_shell_1_J, int32_t *pot1_num_primitives,
int32_t *pot1_powers, double *pot1_coeffs, double *pot1_alpha,
int32_t *oc_shell_1_num_primitives, double *oc_shell_1_coeffs,
double *oc_shell_1_alpha,
// outercore shell 2:
int32_t *oc_shell_2_L, int32_t *oc_shell_2_J, int32_t *pot2_num_primitives,
int32_t *pot2_powers, double *pot2_coeffs, double *pot2_alpha,
int32_t *oc_shell_2_num_primitives, double *oc_shell_2_coeffs,
double *oc_shell_2_alpha,
// answer:
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix) {
void libgrpp_outercore_potential_integrals_part_2(
libgrpp_shell_t * shell_A, libgrpp_shell_t * shell_B, double *C,
libgrpp_potential_t *oc_potential_1, libgrpp_shell_t *oc_shell_1,
libgrpp_potential_t *oc_potential_2, libgrpp_shell_t *oc_shell_2,
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix);
/*
* array conversion: Fortran -> C
*/
int *pot1_powers_int = (int *)calloc(*pot1_num_primitives, sizeof(int));
int *pot2_powers_int = (int *)calloc(*pot2_num_primitives, sizeof(int));
for (int i = 0; i < *pot1_num_primitives; i++) {
pot1_powers_int[i] = pot1_powers[i];
}
for (int i = 0; i < *pot2_num_primitives; i++) {
pot2_powers_int[i] = pot2_powers[i];
}
/*
* construct shells
*/
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
/*
* the first outercore pseudopotential U_{n,L,J} and the corresponding
* outercore shell
*/
libgrpp_potential_t *oc_potential_1 =
libgrpp_new_potential(*oc_shell_1_L, *oc_shell_1_J, *pot1_num_primitives,
pot1_powers_int, pot1_coeffs, pot1_alpha);
libgrpp_shell_t *oc_shell_1 =
libgrpp_new_shell(pot_origin, *oc_shell_1_L, *oc_shell_1_num_primitives,
oc_shell_1_coeffs, oc_shell_1_alpha);
/*
* the second outercore pseudopotential U_{n',L',J'} and the corresponding
* outercore shell
*/
libgrpp_potential_t *oc_potential_2 =
libgrpp_new_potential(*oc_shell_2_L, *oc_shell_2_J, *pot2_num_primitives,
pot2_powers_int, pot2_coeffs, pot2_alpha);
libgrpp_shell_t *oc_shell_2 =
libgrpp_new_shell(pot_origin, *oc_shell_2_L, *oc_shell_2_num_primitives,
oc_shell_2_coeffs, oc_shell_2_alpha);
/*
* evaluate integrals
*/
libgrpp_outercore_potential_integrals_part_2(
shell_A, shell_B, pot_origin, oc_potential_1, oc_shell_1, oc_potential_2,
oc_shell_2, arep_matrix, so_x_matrix, so_y_matrix, so_z_matrix);
/*
* clean-up
*/
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(oc_potential_1);
libgrpp_delete_potential(oc_potential_2);
libgrpp_delete_shell(oc_shell_1);
libgrpp_delete_shell(oc_shell_2);
free(pot1_powers_int);
free(pot2_powers_int);
}
/**
* Analytic calculation of gradients of LOCAL potential integrals for a given
* shell pair with respect to the point 'point_3d'.
*/
void libgrpp_type1_integrals_gradient_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// pseudopotential
double *rpp_origin, int32_t *rpp_num_primitives, int32_t *rpp_powers,
double *rpp_coeffs, double *rpp_alpha,
// differentiation wrt the 3d point (x,y,z)
double *point_3d,
// answer: matrices d<Int>/dx, d<Int>/dy, d<Int>/dZ
double *grad_arep_x, double *grad_arep_y, double *grad_arep_z) {
int *pot_powers_int = (int *)calloc(*rpp_num_primitives, sizeof(int));
double *grad_array[3];
grad_array[0] = grad_arep_x;
grad_array[1] = grad_arep_y;
grad_array[2] = grad_arep_z;
for (int i = 0; i < *rpp_num_primitives; i++) {
pot_powers_int[i] = rpp_powers[i];
}
libgrpp_potential_t *pot = libgrpp_new_potential(
0, 0, *rpp_num_primitives, pot_powers_int, rpp_coeffs, rpp_alpha);
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_type1_integrals_gradient(shell_A, shell_B, rpp_origin, pot, point_3d,
grad_array);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(pot);
free(pot_powers_int);
}
/**
* Analytic calculation of gradients of SEMI-LOCAL potential integrals for a
* given shell pair with respect to the point 'point_3d'.
*/
void libgrpp_type2_integrals_gradient_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A, double *origin_B,
// contracted Gaussian B
int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B, double *alpha_B,
// pseudopotential
double *pot_origin, int32_t *pot_L, int32_t *pot_num_primitives,
int32_t *pot_powers, double *pot_coeffs, double *pot_alpha,
// differentiation wrt the 3d point (x,y,z)
double *point_3d,
// answer: matrices d<Int>/dx, d<Int>/dy, d<Int>/dZ
double *grad_arep_x, double *grad_arep_y, double *grad_arep_z) {
int *pot_powers_int = (int *)calloc(*pot_num_primitives, sizeof(int));
for (int i = 0; i < *pot_num_primitives; i++) {
pot_powers_int[i] = pot_powers[i];
}
double *grad_array[3];
grad_array[0] = grad_arep_x;
grad_array[1] = grad_arep_y;
grad_array[2] = grad_arep_z;
libgrpp_potential_t *pot = libgrpp_new_potential(
*pot_L, 0, *pot_num_primitives, pot_powers_int, pot_coeffs, pot_alpha);
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_type2_integrals_gradient(shell_A, shell_B, pot_origin, pot, point_3d,
grad_array);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(pot);
free(pot_powers_int);
}
/**
* Analytic calculation of gradients of integrals over the effective spin-orbit
* operator (potential) for a given shell pair (with respect to the point
* 'point_3d').
*/
void libgrpp_spin_orbit_integrals_gradient_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// pseudopotential
double *pot_origin, int32_t *pot_L, int32_t *pot_num_primitives,
int32_t *pot_powers, double *pot_coeffs, double *pot_alpha,
// differentiation wrt the 3d point (x,y,z)
double *point_3d,
// answer: 9 matrices
// d<SO-x>/dx, d<SO-x>/dy, d<SO-x>/dZ
double *grad_sox_x, double *grad_sox_y, double *grad_sox_z,
// d<SO-y>/dx, d<SO-y>/dy, d<SO-y>/dZ
double *grad_soy_x, double *grad_soy_y, double *grad_soy_z,
// d<SO-z>/dx, d<SO-z>/dy, d<SO-z>/dZ
double *grad_soz_x, double *grad_soz_y, double *grad_soz_z) {
int *pot_powers_int = (int *)calloc(*pot_num_primitives, sizeof(int));
double *grad_array_SO_x[3];
grad_array_SO_x[0] = grad_sox_x;
grad_array_SO_x[1] = grad_sox_y;
grad_array_SO_x[2] = grad_sox_z;
double *grad_array_SO_y[3];
grad_array_SO_y[0] = grad_soy_x;
grad_array_SO_y[1] = grad_soy_y;
grad_array_SO_y[2] = grad_soy_z;
double *grad_array_SO_z[3];
grad_array_SO_z[0] = grad_soz_x;
grad_array_SO_z[1] = grad_soz_y;
grad_array_SO_z[2] = grad_soz_z;
for (int i = 0; i < *pot_num_primitives; i++) {
pot_powers_int[i] = pot_powers[i];
}
/*
* construct RPP structure
*/
libgrpp_potential_t *pot = libgrpp_new_potential(
*pot_L, 0, *pot_num_primitives, pot_powers_int, pot_coeffs, pot_alpha);
/*
* construct shells
*/
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_spin_orbit_integrals_gradient(shell_A, shell_B, pot_origin, pot,
point_3d, grad_array_SO_x,
grad_array_SO_y, grad_array_SO_z);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
libgrpp_delete_potential(pot);
free(pot_powers_int);
}
/**
* Overlap integrals between two contracted Gaussians with given cartesian parts
* x^n y^l z^m (auxiliary function)
*/
void evaluate_overlap_integral_contracted_(
// contracted Gaussian A
double *origin_A, int32_t *n_A, int32_t *l_A, int32_t *m_A,
int32_t *num_primitives_A, double *coeffs_A, double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *n_B, int32_t *l_B, int32_t *m_B,
int32_t *num_primitives_B, double *coeffs_B, double *alpha_B,
// answer
double *overlap_integral) {
void libgrpp_overlap_integrals(libgrpp_shell_t * shell_A,
libgrpp_shell_t * shell_B,
double *overlap_matrix);
libgrpp_shell_t *shell_A = libgrpp_new_shell(
origin_A, *n_A + *l_A + *m_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B = libgrpp_new_shell(
origin_B, *n_B + *l_B + *m_B, *num_primitives_B, coeffs_B, alpha_B);
shell_A->cart_size = 1;
shell_A->cart_list[0] = *n_A;
shell_A->cart_list[1] = *l_A;
shell_A->cart_list[2] = *m_A;
shell_B->cart_size = 1;
shell_B->cart_list[0] = *n_B;
shell_B->cart_list[1] = *l_B;
shell_B->cart_list[2] = *m_B;
libgrpp_overlap_integrals(shell_A, shell_B, overlap_integral);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
/*
* calculates normalization factor for the given contracted Gaussians
* (auxiliary function)
*/
void radial_gto_norm_factor_(int32_t *L, int32_t *num_primitives,
double *coeffs, double *alpha, double *norm) {
*norm = 0.0;
double S = 0.0;
double origin[] = {0, 0, 0};
int zero = 0;
evaluate_overlap_integral_contracted_(origin, L, &zero, &zero, num_primitives,
coeffs, alpha, origin, L, &zero, &zero,
num_primitives, coeffs, alpha, &S);
*norm = sqrt(libgrpp_double_factorial(2 * (*L) - 1)) / sqrt(S);
}
/*
* overlap integrals (for the shell pair)
*/
void libgrpp_overlap_integrals_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_overlap_integrals(shell_A, shell_B, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
/*
* kinetic-energy integrals (for the shell pair)
*/
void libgrpp_kinetic_energy_integrals_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_kinetic_energy_integrals(shell_A, shell_B, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
/*
* momentum operator integrals (for the shell pair)
*/
void libgrpp_momentum_integrals_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// answer
double *matrix_x, double *matrix_y, double *matrix_z) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_momentum_integrals(shell_A, shell_B, matrix_x, matrix_y, matrix_z);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
/*
* nuclear attraction integrals
*/
void libgrpp_nuclear_attraction_integrals_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// potential definition
double *charge_origin, int32_t *charge, int32_t *nuclear_model,
double *model_params,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_nuclear_attraction_integrals(shell_A, shell_B, charge_origin, *charge,
*nuclear_model, model_params, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
void libgrpp_nuclear_attraction_integrals_point_charge_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// potential definition
double *charge_origin, int32_t *charge,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_nuclear_attraction_integrals_point_charge(
shell_A, shell_B, charge_origin, *charge, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
void libgrpp_nuclear_attraction_integrals_charged_ball_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// potential definition
double *charge_origin, int32_t *charge, double *r_rms,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_nuclear_attraction_integrals_charged_ball(
shell_A, shell_B, charge_origin, *charge, *r_rms, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
void libgrpp_nuclear_attraction_integrals_gaussian_model_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// potential definition
double *charge_origin, int32_t *charge, double *r_rms,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_nuclear_attraction_integrals_gaussian_model(
shell_A, shell_B, charge_origin, *charge, *r_rms, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
void libgrpp_nuclear_attraction_integrals_fermi_model_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// potential definition
double *charge_origin, int32_t *charge, double *fermi_param_c,
double *fermi_param_a,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_nuclear_attraction_integrals_fermi_model(
shell_A, shell_B, charge_origin, *charge, *fermi_param_c, *fermi_param_a,
matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
void libgrpp_nuclear_attraction_integrals_fermi_bubble_model_(
// contracted Gaussian A
double *origin_A, int32_t *L_A, int32_t *num_primitives_A, double *coeffs_A,
double *alpha_A,
// contracted Gaussian B
double *origin_B, int32_t *L_B, int32_t *num_primitives_B, double *coeffs_B,
double *alpha_B,
// potential definition
double *charge_origin, int32_t *charge, double *fermi_param_c,
double *fermi_param_a, double *param_k,
// answer
double *matrix) {
libgrpp_shell_t *shell_A =
libgrpp_new_shell(origin_A, *L_A, *num_primitives_A, coeffs_A, alpha_A);
libgrpp_shell_t *shell_B =
libgrpp_new_shell(origin_B, *L_B, *num_primitives_B, coeffs_B, alpha_B);
libgrpp_nuclear_attraction_integrals_fermi_bubble_model(
shell_A, shell_B, charge_origin, *charge, *fermi_param_c, *fermi_param_a,
*param_k, matrix);
libgrpp_delete_shell(shell_A);
libgrpp_delete_shell(shell_B);
}
/*
* Fortran interface to the nuclear models
*/
void libgrpp_estimate_nuclear_rms_radius_johnson_1985_(int32_t *A,
double *R_rms) {
*R_rms = libgrpp_estimate_nuclear_rms_radius_johnson_1985(*A);
}
void libgrpp_estimate_nuclear_rms_radius_golovko_2008_(int32_t *A,
double *R_rms) {
*R_rms = libgrpp_estimate_nuclear_rms_radius_golovko_2008(*A);
}
void libgrpp_estimate_fermi_model_parameters_(double *R_rms, double *c,
double *a, int32_t *err_code) {
*err_code = (int32_t)libgrpp_estimate_fermi_model_parameters(*R_rms, c, a);
}
void libgrpp_charge_density_ball_(double *r, double *Z, double *R_rms,
double *rho) {
*rho = libgrpp_charge_density_ball(*r, *Z, *R_rms);
}
void libgrpp_charge_density_gaussian_(double *r, double *Z, double *R_rms,
double *rho) {
*rho = libgrpp_charge_density_gaussian(*r, *Z, *R_rms);
}
void libgrpp_charge_density_fermi_(double *r, double *Z, double *c, double *a,
double *rho) {
*rho = libgrpp_charge_density_fermi(*r, *Z, *c, *a);
}
void libgrpp_charge_density_fermi_bubble_(double *r, double *Z, double *c,
double *a, double *k, double *rho) {
*rho = libgrpp_charge_density_fermi_bubble(*r, *Z, *c, *a, *k);
}
void libgrpp_coulomb_potential_point_(double *r, double *Z, double *potential) {
*potential = libgrpp_coulomb_potential_point(*r, *Z);
}
void libgrpp_coulomb_potential_ball_(double *r, double *Z, double *R_rms,
double *potential) {
*potential = libgrpp_coulomb_potential_ball(*r, *Z, *R_rms);
}
void libgrpp_coulomb_potential_gaussian_(double *r, double *Z, double *R_rms,
double *potential) {
*potential = libgrpp_coulomb_potential_gaussian(*r, *Z, *R_rms);
}
void libgrpp_coulomb_potential_fermi_(double *r, double *Z, double *c,
double *a, double *potential) {
*potential = libgrpp_coulomb_potential_fermi(*r, *Z, *c, *a);
}
void libgrpp_coulomb_potential_fermi_bubble_(double *r, double *Z, double *c,
double *a, double *k,
double *potential) {
*potential = libgrpp_coulomb_potential_fermi_bubble(*r, *Z, *c, *a, *k);
}
void libgrpp_rms_radius_fermi_(int32_t *Z, double *c, double *a,
double *r_rms) {
*r_rms = libgrpp_rms_radius_fermi(*Z, *c, *a);
}
void libgrpp_rms_radius_fermi_bubble_(int32_t *Z, double *c, double *a,
double *k, double *r_rms) {
*r_rms = libgrpp_rms_radius_fermi_bubble(*Z, *c, *a, *k);
}

View file

@ -0,0 +1,106 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include "libgrpp.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "grpp_utils.h"
/**
* Evaluates integrals over the full GRPP operator consisting of three parts:
* - scalar relativistic (local)
* - scalar relativistic (semi-local)
* - effective spin-orbit (semi-local)
* - outercore potentials (non-local)
*
* See libgrpp.h for the definition of the libgrpp_grpp_t structure.
*/
void libgrpp_full_grpp_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator,
double *grpp_origin, double *arep_matrix,
double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix) {
assert(libgrpp_is_initialized());
size_t size = shell_A->cart_size * shell_B->cart_size;
double *buf_arep = (double *)calloc(size, sizeof(double));
double *buf_so_x = (double *)calloc(size, sizeof(double));
double *buf_so_y = (double *)calloc(size, sizeof(double));
double *buf_so_z = (double *)calloc(size, sizeof(double));
memset(arep_matrix, 0, sizeof(double) * size);
memset(so_x_matrix, 0, sizeof(double) * size);
memset(so_y_matrix, 0, sizeof(double) * size);
memset(so_z_matrix, 0, sizeof(double) * size);
/*
* radially-local ("type-1") integrals
*/
libgrpp_type1_integrals(shell_A, shell_B, grpp_origin, grpp_operator->U_L,
buf_arep);
libgrpp_daxpy(size, 1.0, buf_arep, arep_matrix);
/*
* semilocal AREP ("type-2") integrals
*/
for (int L = 0; L < grpp_operator->n_arep; L++) {
libgrpp_type2_integrals(shell_A, shell_B, grpp_origin,
grpp_operator->U_arep[L], buf_arep);
libgrpp_daxpy(size, 1.0, buf_arep, arep_matrix);
}
/*
* semilocal SO ("type-3") integrals
*/
for (int i_so = 0; i_so < grpp_operator->n_esop; i_so++) {
libgrpp_potential_t *so_potential = grpp_operator->U_esop[i_so];
libgrpp_spin_orbit_integrals(shell_A, shell_B, grpp_origin, so_potential,
buf_so_x, buf_so_y, buf_so_z);
int L = so_potential->L;
libgrpp_daxpy(size, 2.0 / (2 * L + 1), buf_so_x, so_x_matrix);
libgrpp_daxpy(size, 2.0 / (2 * L + 1), buf_so_y, so_y_matrix);
libgrpp_daxpy(size, 2.0 / (2 * L + 1), buf_so_z, so_z_matrix);
}
/*
* integrals over outercore non-local potentials,
* the part specific for GRPP.
*
* note that proper pre-factors for the SO part are calculated inside
* the libgrpp_outercore_potential_integrals() procedure.
*/
libgrpp_outercore_potential_integrals(
shell_A, shell_B, grpp_origin, grpp_operator->n_oc_shells,
grpp_operator->U_oc, grpp_operator->oc_shells, buf_arep, buf_so_x,
buf_so_y, buf_so_z);
libgrpp_daxpy(size, 1.0, buf_arep, arep_matrix);
libgrpp_daxpy(size, 1.0, buf_so_x, so_x_matrix);
libgrpp_daxpy(size, 1.0, buf_so_y, so_y_matrix);
libgrpp_daxpy(size, 1.0, buf_so_z, so_z_matrix);
/*
* cleanup
*/
free(buf_arep);
free(buf_so_x);
free(buf_so_y);
free(buf_so_z);
}

View file

@ -0,0 +1,49 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include "libgrpp.h"
void libgrpp_create_real_spherical_harmonic_coeffs_tables(int Lmax);
void libgrpp_pretabulate_bessel();
static int libgrpp_initialized = 0;
/**
* thread-safe initialization
*/
void libgrpp_init() {
#pragma omp critical
{
if (libgrpp_initialized == 0) {
libgrpp_create_real_spherical_harmonic_coeffs_tables(40);
libgrpp_pretabulate_bessel();
libgrpp_initialized = 1;
}
}
}
int libgrpp_is_initialized() { return libgrpp_initialized; }
/**
* thread-safe finalization
*/
void libgrpp_finalize() {
#pragma omp critical
{
if (libgrpp_initialized == 1) {
libgrpp_initialized = 0;
}
}
}

View file

@ -0,0 +1,917 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include "libgrpp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "grpp_diff_gaussian.h"
#include "grpp_utils.h"
void grpp_gradient_diff_bra_contribution(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor);
void grpp_gradient_diff_bra_grpp_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin,
double **arep_matrix_down, double **so_x_matrix_down,
double **so_y_matrix_down, double **so_z_matrix_down,
double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
double **so_z_matrix_up, int *cart_size_down, int *cart_size_up);
void grpp_gradient_diff_ket_contribution(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor);
void grpp_gradient_diff_ket_grpp_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin,
double **arep_matrix_down, double **so_x_matrix_down,
double **so_y_matrix_down, double **so_z_matrix_down,
double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
double **so_z_matrix_up, int *cart_size_down, int *cart_size_up);
void grpp_gradient_contribution(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator,
double *grpp_origin, double **grad_arep,
double **grad_so_x, double **grad_so_y,
double **grad_so_z, int diff_bra,
double factor);
void grpp_gradient_diff_gaussian(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin,
double **arep_matrix_down, double **so_x_matrix_down,
double **so_y_matrix_down, double **so_z_matrix_down,
double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
double **so_z_matrix_up, int *cart_size_down, int *cart_size_up,
int diff_bra);
extern int libgrpp_nlm_to_linear(int *nlm);
double **libgrpp_alloc_gradients(libgrpp_shell_t *bra, libgrpp_shell_t *ket);
void libgrpp_dealloc_gradients(double **grad);
/**
* Analytic calculation of gradients of LOCAL potential integrals for a given
* shell pair with respect to the point 'point_3d'.
*/
void libgrpp_type1_integrals_gradient(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *grpp_origin,
libgrpp_potential_t *potential,
double *point_3d, double **grad_arep) {
libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
libgrpp_grpp_set_local_potential(grpp_operator, potential);
/*
* these arrays are not actually used.
* they are needed only in order to use the
* libgrpp_full_grpp_integrals_gradient() routine.
*/
double **stub_grad_so_x = libgrpp_alloc_gradients(shell_A, shell_B);
double **stub_grad_so_y = libgrpp_alloc_gradients(shell_A, shell_B);
double **stub_grad_so_z = libgrpp_alloc_gradients(shell_A, shell_B);
libgrpp_full_grpp_integrals_gradient(
shell_A, shell_B, grpp_operator, grpp_origin, point_3d, grad_arep,
stub_grad_so_x, stub_grad_so_y, stub_grad_so_z);
libgrpp_dealloc_gradients(stub_grad_so_x);
libgrpp_dealloc_gradients(stub_grad_so_y);
libgrpp_dealloc_gradients(stub_grad_so_z);
grpp_operator->U_L = NULL;
libgrpp_delete_grpp(grpp_operator);
}
/**
* Analytic calculation of gradients of SEMI-LOCAL potential integrals for a
* given shell pair with respect to the point 'point_3d'.
*/
void libgrpp_type2_integrals_gradient(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *grpp_origin,
libgrpp_potential_t *potential,
double *point_3d, double **grad_arep) {
libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
libgrpp_grpp_add_averaged_potential(grpp_operator, potential);
/*
* these arrays are not actually used.
* they are needed only in order to use the
* libgrpp_full_grpp_integrals_gradient() routine.
*/
double **stub_grad_so_x = libgrpp_alloc_gradients(shell_A, shell_B);
double **stub_grad_so_y = libgrpp_alloc_gradients(shell_A, shell_B);
double **stub_grad_so_z = libgrpp_alloc_gradients(shell_A, shell_B);
libgrpp_full_grpp_integrals_gradient(
shell_A, shell_B, grpp_operator, grpp_origin, point_3d, grad_arep,
stub_grad_so_x, stub_grad_so_y, stub_grad_so_z);
libgrpp_dealloc_gradients(stub_grad_so_x);
libgrpp_dealloc_gradients(stub_grad_so_y);
libgrpp_dealloc_gradients(stub_grad_so_z);
grpp_operator->n_arep = 0;
libgrpp_delete_grpp(grpp_operator);
}
/**
* Analytic calculation of gradients of integrals over the effective spin-orbit
* operator (potential) for a given shell pair (with respect to the point
* 'point_3d').
*/
void libgrpp_spin_orbit_integrals_gradient(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *grpp_origin,
libgrpp_potential_t *potential, double *point_3d, double **grad_so_x,
double **grad_so_y, double **grad_so_z) {
libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
libgrpp_grpp_add_spin_orbit_potential(grpp_operator, potential);
/*
* this array is not actually used and is needed only in order
* to use the libgrpp_full_grpp_integrals_gradient() routine.
*/
double **stub_grad_arep = libgrpp_alloc_gradients(shell_A, shell_B);
libgrpp_full_grpp_integrals_gradient(shell_A, shell_B, grpp_operator,
grpp_origin, point_3d, stub_grad_arep,
grad_so_x, grad_so_y, grad_so_z);
/*
* inside the libgrpp_full_grpp_integrals_gradient() function
* the SO potential was scaled by 2/(2L+1). Thus the result has to be
* re-scaled by (2L+1)/2 to get rid of any problems with pre-factor
*/
int L = potential->L;
int buf_size = shell_A->cart_size * shell_B->cart_size;
for (int icoord = 0; icoord < 3; icoord++) {
for (int i = 0; i < buf_size; i++) {
grad_so_x[icoord][i] *= (2.0 * L + 1.0) / 2.0;
grad_so_y[icoord][i] *= (2.0 * L + 1.0) / 2.0;
grad_so_z[icoord][i] *= (2.0 * L + 1.0) / 2.0;
}
}
libgrpp_dealloc_gradients(stub_grad_arep);
grpp_operator->n_esop = 0;
libgrpp_delete_grpp(grpp_operator);
}
void libgrpp_outercore_potential_integrals_gradient(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *rpp_origin,
int num_oc_shells, libgrpp_potential_t **oc_potentials,
libgrpp_shell_t **oc_shells, double *point_3d, double **grad_arep,
double **grad_so_x, double **grad_so_y, double **grad_so_z) {
libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
for (int ioc = 0; ioc < num_oc_shells; ioc++) {
libgrpp_grpp_add_outercore_potential(grpp_operator, oc_potentials[ioc],
oc_shells[ioc]);
}
libgrpp_full_grpp_integrals_gradient(shell_A, shell_B, grpp_operator,
rpp_origin, point_3d, grad_arep,
grad_so_x, grad_so_y, grad_so_z);
grpp_operator->n_oc_shells = 0;
libgrpp_delete_grpp(grpp_operator);
}
/**
* Analytic calculation of gradients of GRPP integrals for a given shell pair
* with respect to the point 'point_3d'.
* (for the full GRPP operator which includes local, semi-local and non-local
* parts)
*/
void libgrpp_full_grpp_integrals_gradient(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin, double *point_3d,
double **grad_arep, double **grad_so_x, double **grad_so_y,
double **grad_so_z) {
int cart_size_A = shell_A->cart_size;
int cart_size_B = shell_B->cart_size;
int buf_size = cart_size_A * cart_size_B;
/*
* initialization: set all gradients to zero
*/
for (int icoord = 0; icoord < 3; icoord++) {
memset(grad_arep[icoord], 0, sizeof(double) * buf_size);
memset(grad_so_x[icoord], 0, sizeof(double) * buf_size);
memset(grad_so_y[icoord], 0, sizeof(double) * buf_size);
memset(grad_so_z[icoord], 0, sizeof(double) * buf_size);
}
/*
* d<AAA>/d... = 0
*/
if (points_are_equal(shell_A->origin, grpp_origin) &&
points_are_equal(shell_B->origin, grpp_origin)) {
return;
}
/*
* d<ACB>/dD = 0
*/
if (!points_are_equal(shell_A->origin, point_3d) &&
!points_are_equal(shell_B->origin, point_3d) &&
!points_are_equal(grpp_origin, point_3d)) {
return;
}
double *A = shell_A->origin;
double *B = shell_B->origin;
double *C = grpp_origin;
double *D = point_3d;
const int diff_bra = 1;
const int diff_ket = 0;
/*
* Type ACB
*/
if (!points_are_equal(A, C) && !points_are_equal(C, B) &&
!points_are_equal(A, B)) {
if (points_are_equal(A, D)) {
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_bra, +1.0);
}
if (points_are_equal(B, D)) {
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_ket, +1.0);
}
if (points_are_equal(C, D)) {
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_bra, -1.0);
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_ket, -1.0);
}
}
/*
* Type ACA
*/
if (points_are_equal(A, B) && !points_are_equal(A, C)) {
if (points_are_equal(A, D)) {
grpp_gradient_diff_bra_contribution(shell_A, shell_B, grpp_operator,
grpp_origin, grad_arep, grad_so_x,
grad_so_y, grad_so_z, +1.0);
grpp_gradient_diff_ket_contribution(shell_A, shell_B, grpp_operator,
grpp_origin, grad_arep, grad_so_x,
grad_so_y, grad_so_z, +1.0);
} else {
grpp_gradient_diff_bra_contribution(shell_A, shell_B, grpp_operator,
grpp_origin, grad_arep, grad_so_x,
grad_so_y, grad_so_z, -1.0);
grpp_gradient_diff_ket_contribution(shell_A, shell_B, grpp_operator,
grpp_origin, grad_arep, grad_so_x,
grad_so_y, grad_so_z, -1.0);
}
}
/*
* Type ACC
*/
if (!points_are_equal(A, C) && points_are_equal(C, B)) {
if (points_are_equal(A, D)) {
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_bra, +1.0);
} else {
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_bra, -1.0);
}
}
/*
* Type CCB
*/
if (points_are_equal(A, C) && !points_are_equal(C, B)) {
if (points_are_equal(B, D)) {
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_ket, +1.0);
} else {
grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
grad_arep, grad_so_x, grad_so_y, grad_so_z,
diff_ket, -1.0);
}
}
}
/**
* Calculates contribution to gradients arising from the < df/dA | V | g > term:
*
* grad += factor * < df/dA | V | g >
*
* (bra basis function is differentiated).
*/
void grpp_gradient_diff_bra_contribution(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor) {
/*
* calculate integrals < df/dA | V | B >
*/
double *arep_matrix_down = NULL;
double *so_x_matrix_down = NULL;
double *so_y_matrix_down = NULL;
double *so_z_matrix_down = NULL;
double *arep_matrix_up = NULL;
double *so_x_matrix_up = NULL;
double *so_y_matrix_up = NULL;
double *so_z_matrix_up = NULL;
int cart_size_down = 0;
int cart_size_up = 0;
grpp_gradient_diff_bra_grpp_integrals(
shell_A, shell_B, grpp_operator, grpp_origin, &arep_matrix_down,
&so_x_matrix_down, &so_y_matrix_down, &so_z_matrix_down, &arep_matrix_up,
&so_x_matrix_up, &so_y_matrix_up, &so_z_matrix_up, &cart_size_down,
&cart_size_up);
/*
* construct contributions to gradients:
* d<A|V|B>/dA += < df/dA | V | B >
*/
for (int icoord = 0; icoord < 3; icoord++) {
for (int i = 0; i < shell_A->cart_size; i++) {
for (int j = 0; j < shell_B->cart_size; j++) {
int bra_nlm[3];
bra_nlm[0] = shell_A->cart_list[3 * i + 0];
bra_nlm[1] = shell_A->cart_list[3 * i + 1];
bra_nlm[2] = shell_A->cart_list[3 * i + 2];
int ket_nlm[3];
ket_nlm[0] = shell_B->cart_list[3 * j + 0];
ket_nlm[1] = shell_B->cart_list[3 * j + 1];
ket_nlm[2] = shell_B->cart_list[3 * j + 2];
int index = i * shell_B->cart_size + j;
/*
* contribution from the L-1 gaussian
*/
if (shell_A->L > 0) {
bra_nlm[icoord] -= 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
bra_nlm[icoord] += 1;
grad_arep[icoord][index] -=
factor * bra_nlm[icoord] *
arep_matrix_down[shell_B->cart_size * bra_index + ket_index];
grad_so_x[icoord][index] -=
factor * bra_nlm[icoord] *
so_x_matrix_down[shell_B->cart_size * bra_index + ket_index];
grad_so_y[icoord][index] -=
factor * bra_nlm[icoord] *
so_y_matrix_down[shell_B->cart_size * bra_index + ket_index];
grad_so_z[icoord][index] -=
factor * bra_nlm[icoord] *
so_z_matrix_down[shell_B->cart_size * bra_index + ket_index];
}
/*
* contribution from the L+1 gaussian
*/
bra_nlm[icoord] += 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
bra_nlm[icoord] -= 1;
grad_arep[icoord][index] +=
factor * arep_matrix_up[shell_B->cart_size * bra_index + ket_index];
grad_so_x[icoord][index] +=
factor * so_x_matrix_up[shell_B->cart_size * bra_index + ket_index];
grad_so_y[icoord][index] +=
factor * so_y_matrix_up[shell_B->cart_size * bra_index + ket_index];
grad_so_z[icoord][index] +=
factor * so_z_matrix_up[shell_B->cart_size * bra_index + ket_index];
}
}
}
if (arep_matrix_down) {
free(arep_matrix_down);
free(so_x_matrix_down);
free(so_y_matrix_down);
free(so_z_matrix_down);
}
free(arep_matrix_up);
free(so_x_matrix_up);
free(so_y_matrix_up);
free(so_z_matrix_up);
}
/**
* To assemble the contribution < df/dA | V | g > to gradients, one have to
* differentiate a Gaussian function. Such a differentiation yields two
* Gaussians with angular momenta L-1 ("down") and L+1 ("up"): dG/dA -> G(L-1)
* and G(L+1)
*
* This function constructs overlap matrices with these "downgraded" and
* "upgraded" Gaussian functions: < G(L-1) | V | G' > and < G(L+1) | V | G' >
*
*/
void grpp_gradient_diff_bra_grpp_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin,
double **arep_matrix_down, double **so_x_matrix_down,
double **so_y_matrix_down, double **so_z_matrix_down,
double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
double **so_z_matrix_up, int *cart_size_down, int *cart_size_up) {
/*
* differentiation of contracted Gaussian functions
*/
libgrpp_shell_t *shell_A_down = NULL;
libgrpp_shell_t *shell_A_up = NULL;
libgrpp_differentiate_shell(shell_A, &shell_A_down, &shell_A_up);
*cart_size_down = 0;
if (shell_A_down != NULL) {
*cart_size_down = shell_A_down->cart_size;
}
*cart_size_up = shell_A_up->cart_size;
/*
* matrix < L-1 | V | L >
*/
if (shell_A_down != NULL) {
size_t mat_size_down = shell_A_down->cart_size * shell_B->cart_size;
*arep_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_x_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_y_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_z_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
libgrpp_full_grpp_integrals(
shell_A_down, shell_B, grpp_operator, grpp_origin, *arep_matrix_down,
*so_x_matrix_down, *so_y_matrix_down, *so_z_matrix_down);
} else {
*arep_matrix_down = NULL;
*so_x_matrix_down = NULL;
*so_y_matrix_down = NULL;
*so_z_matrix_down = NULL;
}
/*
* matrix < L+1 | V | L >
*/
size_t mat_size_up = shell_A_up->cart_size * shell_B->cart_size;
*arep_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_x_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_y_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_z_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
libgrpp_full_grpp_integrals(shell_A_up, shell_B, grpp_operator, grpp_origin,
*arep_matrix_up, *so_x_matrix_up, *so_y_matrix_up,
*so_z_matrix_up);
/*
* clean up
*/
if (shell_A_down) {
libgrpp_delete_shell(shell_A_down);
}
libgrpp_delete_shell(shell_A_up);
}
/**
* Calculates contribution to gradients arising from the < df/dA | V | g > term:
*
* grad += factor * < f | V | dg/dA >
*
* (bra basis function is differentiated).
*/
void grpp_gradient_diff_ket_contribution(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor) {
/*
* calculate integrals < df/dA | V | B >
*/
double *arep_matrix_down = NULL;
double *so_x_matrix_down = NULL;
double *so_y_matrix_down = NULL;
double *so_z_matrix_down = NULL;
double *arep_matrix_up = NULL;
double *so_x_matrix_up = NULL;
double *so_y_matrix_up = NULL;
double *so_z_matrix_up = NULL;
int cart_size_down = 0;
int cart_size_up = 0;
grpp_gradient_diff_ket_grpp_integrals(
shell_A, shell_B, grpp_operator, grpp_origin, &arep_matrix_down,
&so_x_matrix_down, &so_y_matrix_down, &so_z_matrix_down, &arep_matrix_up,
&so_x_matrix_up, &so_y_matrix_up, &so_z_matrix_up, &cart_size_down,
&cart_size_up);
/*
* construct contributions to gradients:
* d<A|B>/dA += < df/dA | V | B >
*/
for (int icoord = 0; icoord < 3; icoord++) {
for (int i = 0; i < shell_A->cart_size; i++) {
for (int j = 0; j < shell_B->cart_size; j++) {
int bra_nlm[3];
bra_nlm[0] = shell_A->cart_list[3 * i + 0];
bra_nlm[1] = shell_A->cart_list[3 * i + 1];
bra_nlm[2] = shell_A->cart_list[3 * i + 2];
int ket_nlm[3];
ket_nlm[0] = shell_B->cart_list[3 * j + 0];
ket_nlm[1] = shell_B->cart_list[3 * j + 1];
ket_nlm[2] = shell_B->cart_list[3 * j + 2];
int index = i * shell_B->cart_size + j;
/*
* contribution from the L-1 gaussian
*/
if (shell_B->L > 0) {
ket_nlm[icoord] -= 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
ket_nlm[icoord] += 1;
grad_arep[icoord][index] -=
factor * ket_nlm[icoord] *
arep_matrix_down[cart_size_down * bra_index + ket_index];
grad_so_x[icoord][index] -=
factor * ket_nlm[icoord] *
so_x_matrix_down[cart_size_down * bra_index + ket_index];
grad_so_y[icoord][index] -=
factor * ket_nlm[icoord] *
so_y_matrix_down[cart_size_down * bra_index + ket_index];
grad_so_z[icoord][index] -=
factor * ket_nlm[icoord] *
so_z_matrix_down[cart_size_down * bra_index + ket_index];
}
/*
* contribution from the L+1 gaussian
*/
ket_nlm[icoord] += 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
ket_nlm[icoord] -= 1;
grad_arep[icoord][index] +=
factor * arep_matrix_up[cart_size_up * bra_index + ket_index];
grad_so_x[icoord][index] +=
factor * so_x_matrix_up[cart_size_up * bra_index + ket_index];
grad_so_y[icoord][index] +=
factor * so_y_matrix_up[cart_size_up * bra_index + ket_index];
grad_so_z[icoord][index] +=
factor * so_z_matrix_up[cart_size_up * bra_index + ket_index];
}
}
}
if (arep_matrix_down) {
free(arep_matrix_down);
free(so_x_matrix_down);
free(so_y_matrix_down);
free(so_z_matrix_down);
}
free(arep_matrix_up);
free(so_x_matrix_up);
free(so_y_matrix_up);
free(so_z_matrix_up);
}
/**
* To assemble the contribution < df/dA | V | g > to gradients, one have to
* differentiate Gaussian function. Such a differentiation yields two Gaussians
* with angular momenta L-1 ("down") and L+1 ("up"): dG/dA -> G(L-1) and G(L+1)
*
* This function constructs matrices with these "downgraded" and "upgraded"
* Gaussian functions:
* < G(L-1) | V | G' > and < G(L+1) | V | G' >
*
*/
void grpp_gradient_diff_ket_grpp_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin,
double **arep_matrix_down, double **so_x_matrix_down,
double **so_y_matrix_down, double **so_z_matrix_down,
double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
double **so_z_matrix_up, int *cart_size_down, int *cart_size_up) {
/*
* differentiation of contracted Gaussian functions
*/
libgrpp_shell_t *shell_B_down = NULL;
libgrpp_shell_t *shell_B_up = NULL;
libgrpp_differentiate_shell(shell_B, &shell_B_down, &shell_B_up);
*cart_size_down = 0;
if (shell_B_down != NULL) {
*cart_size_down = shell_B_down->cart_size;
}
*cart_size_up = shell_B_up->cart_size;
/*
* matrix < L-1 | L>
*/
if (shell_B_down != NULL) {
size_t mat_size_down = shell_A->cart_size * shell_B_down->cart_size;
*arep_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_x_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_y_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_z_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
libgrpp_full_grpp_integrals(
// evaluate_grpp_integrals_shell_pair(
shell_A, shell_B_down, grpp_operator, grpp_origin, *arep_matrix_down,
*so_x_matrix_down, *so_y_matrix_down, *so_z_matrix_down);
} else {
*arep_matrix_down = NULL;
*so_x_matrix_down = NULL;
*so_y_matrix_down = NULL;
*so_z_matrix_down = NULL;
}
/*
* matrix < L+1 | L>
*/
size_t mat_size_up = shell_A->cart_size * shell_B_up->cart_size;
*arep_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_x_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_y_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_z_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
libgrpp_full_grpp_integrals(
// evaluate_grpp_integrals_shell_pair(
shell_A, shell_B_up, grpp_operator, grpp_origin, *arep_matrix_up,
*so_x_matrix_up, *so_y_matrix_up, *so_z_matrix_up);
/*
* clean up
*/
if (shell_B_down) {
libgrpp_delete_shell(shell_B_down);
}
libgrpp_delete_shell(shell_B_up);
}
void grpp_gradient_contribution(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator,
double *grpp_origin, double **grad_arep,
double **grad_so_x, double **grad_so_y,
double **grad_so_z, int diff_bra,
double factor) {
// int diff_ket = 0;
if (diff_bra == 0) {
diff_bra = 0;
// diff_ket = 1;
} else {
diff_bra = 1;
// diff_ket = 0;
}
/*
* calculate overlap integrals < df/dA | V | B >
*/
double *arep_matrix_down = NULL;
double *so_x_matrix_down = NULL;
double *so_y_matrix_down = NULL;
double *so_z_matrix_down = NULL;
double *arep_matrix_up = NULL;
double *so_x_matrix_up = NULL;
double *so_y_matrix_up = NULL;
double *so_z_matrix_up = NULL;
int cart_size_down = 0;
int cart_size_up = 0;
grpp_gradient_diff_gaussian(
shell_A, shell_B, grpp_operator, grpp_origin, &arep_matrix_down,
&so_x_matrix_down, &so_y_matrix_down, &so_z_matrix_down, &arep_matrix_up,
&so_x_matrix_up, &so_y_matrix_up, &so_z_matrix_up, &cart_size_down,
&cart_size_up, diff_bra);
/*
* construct contributions to gradients:
* d<A|U|B>/dA += < df/dA | U | B >
*/
for (int icoord = 0; icoord < 3; icoord++) {
for (int i = 0; i < shell_A->cart_size; i++) {
for (int j = 0; j < shell_B->cart_size; j++) {
int bra_nlm[3];
bra_nlm[0] = shell_A->cart_list[3 * i + 0];
bra_nlm[1] = shell_A->cart_list[3 * i + 1];
bra_nlm[2] = shell_A->cart_list[3 * i + 2];
int ket_nlm[3];
ket_nlm[0] = shell_B->cart_list[3 * j + 0];
ket_nlm[1] = shell_B->cart_list[3 * j + 1];
ket_nlm[2] = shell_B->cart_list[3 * j + 2];
int index = i * shell_B->cart_size + j;
int *diff_nlm = diff_bra ? bra_nlm : ket_nlm;
/*
* contribution from the L-1 gaussian
*/
if (cart_size_down > 0) {
diff_nlm[icoord] -= 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
diff_nlm[icoord] += 1;
int n = diff_nlm[icoord];
int row_len = diff_bra ? shell_B->cart_size : cart_size_down;
int index_down = row_len * bra_index + ket_index;
grad_arep[icoord][index] -= factor * n * arep_matrix_down[index_down];
grad_so_x[icoord][index] -= factor * n * so_x_matrix_down[index_down];
grad_so_y[icoord][index] -= factor * n * so_y_matrix_down[index_down];
grad_so_z[icoord][index] -= factor * n * so_z_matrix_down[index_down];
}
/*
* contribution from the L+1 gaussian
*/
diff_nlm[icoord] += 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
diff_nlm[icoord] -= 1;
int row_len = diff_bra ? shell_B->cart_size : cart_size_up;
int index_up = row_len * bra_index + ket_index;
grad_arep[icoord][index] += factor * arep_matrix_up[index_up];
grad_so_x[icoord][index] += factor * so_x_matrix_up[index_up];
grad_so_y[icoord][index] += factor * so_y_matrix_up[index_up];
grad_so_z[icoord][index] += factor * so_z_matrix_up[index_up];
}
}
}
if (arep_matrix_down) {
free(arep_matrix_down);
free(so_x_matrix_down);
free(so_y_matrix_down);
free(so_z_matrix_down);
}
free(arep_matrix_up);
free(so_x_matrix_up);
free(so_y_matrix_up);
free(so_z_matrix_up);
}
/**
* To assemble the contribution < df/dA | V | g > to gradients, one have to
* differentiate Gaussian function. Such a differentiation yields two Gaussians
* with angular momenta L-1 ("down") and L+1 ("up"): dG/dA -> G(L-1) and G(L+1)
*
* This function constructs matrices with these "downgraded" and "upgraded"
* Gaussian functions:
* < G(L-1) | V | G' > and < G(L+1) | V | G' >
*
*/
void grpp_gradient_diff_gaussian(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin,
double **arep_matrix_down, double **so_x_matrix_down,
double **so_y_matrix_down, double **so_z_matrix_down,
double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
double **so_z_matrix_up, int *cart_size_down, int *cart_size_up,
int diff_bra) {
// int diff_ket = 0;
if (diff_bra == 0) {
diff_bra = 0;
// diff_ket = 1;
} else {
diff_bra = 1;
// diff_ket = 0;
}
/*
* which shell should be differentiated, bra or ket
*/
libgrpp_shell_t *const_shell = NULL;
libgrpp_shell_t *diff_shell = NULL;
if (diff_bra) {
diff_shell = shell_A;
const_shell = shell_B;
} else {
diff_shell = shell_B;
const_shell = shell_A;
}
/*
* differentiation of contracted Gaussian functions
*/
libgrpp_shell_t *shell_down = NULL;
libgrpp_shell_t *shell_up = NULL;
libgrpp_differentiate_shell(diff_shell, &shell_down, &shell_up);
*cart_size_down = 0;
if (shell_down != NULL) {
*cart_size_down = shell_down->cart_size;
}
*cart_size_up = shell_up->cart_size;
/*
* GRPP matrix:
* < L-1 | U | L > or < L | U | L-1 >
*/
if (shell_down != NULL) {
size_t mat_size_down = const_shell->cart_size * shell_down->cart_size;
*arep_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_x_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_y_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
*so_z_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
libgrpp_full_grpp_integrals(
diff_bra ? shell_down : shell_A, diff_bra ? shell_B : shell_down,
grpp_operator, grpp_origin, *arep_matrix_down, *so_x_matrix_down,
*so_y_matrix_down, *so_z_matrix_down);
} else {
*arep_matrix_down = NULL;
*so_x_matrix_down = NULL;
*so_y_matrix_down = NULL;
*so_z_matrix_down = NULL;
}
/*
* GRPP matrix:
* < L+1 | U | L > or < L | U | L+1 >
*/
size_t mat_size_up = const_shell->cart_size * shell_up->cart_size;
*arep_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_x_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_y_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
*so_z_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
libgrpp_full_grpp_integrals(diff_bra ? shell_up : shell_A,
diff_bra ? shell_B : shell_up, grpp_operator,
grpp_origin, *arep_matrix_up, *so_x_matrix_up,
*so_y_matrix_up, *so_z_matrix_up);
/*
* clean up
*/
if (shell_down) {
libgrpp_delete_shell(shell_down);
}
libgrpp_delete_shell(shell_up);
}
/**
* Allocates memory for gradients for a given shell pair.
*/
double **libgrpp_alloc_gradients(libgrpp_shell_t *bra, libgrpp_shell_t *ket) {
size_t size = bra->cart_size * ket->cart_size;
double **grad = (double **)calloc(3, sizeof(double *));
for (int icoord = 0; icoord < 3; icoord++) {
grad[icoord] = (double *)calloc(size, sizeof(double));
}
return grad;
}
/**
* Deallocates arrays containing gradients of AO integrals.
*/
void libgrpp_dealloc_gradients(double **grad) {
free(grad[0]);
free(grad[1]);
free(grad[2]);
free(grad);
}

161
src/grpp/grpp_kinetic.c Normal file
View file

@ -0,0 +1,161 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/**
* Calculation of kinetic-energy integrals.
*
* For details, see:
* T. Helgaker, P. Jorgensen, J. Olsen, Molecular Electronic-Structure Theory,
* John Wiley & Sons Ltd, 2000.
* Chapter 9.3.4, "Momentum and kinetic-energy integrals"
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_kinetic.h"
#include "grpp_norm_gaussian.h"
#include "grpp_utils.h"
#include "libgrpp.h"
static void kinetic_energy_integrals_shell_pair_obara_saika(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double alpha_A,
double alpha_B, double *kinetic_matrix);
void libgrpp_kinetic_energy_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *kinetic_matrix) {
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double *buf = calloc(size_A * size_B, sizeof(double));
memset(kinetic_matrix, 0, size_A * size_B * sizeof(double));
// loop over primitives in contractions
for (int i = 0; i < shell_A->num_primitives; i++) {
for (int j = 0; j < shell_B->num_primitives; j++) {
double alpha_i = shell_A->alpha[i];
double alpha_j = shell_B->alpha[j];
double coef_A_i = shell_A->coeffs[i];
double coef_B_j = shell_B->coeffs[j];
kinetic_energy_integrals_shell_pair_obara_saika(shell_A, shell_B, alpha_i,
alpha_j, buf);
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf, kinetic_matrix);
}
}
free(buf);
}
static void kinetic_energy_integrals_shell_pair_obara_saika(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double alpha_A,
double alpha_B, double *kinetic_matrix) {
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
int L_A = shell_A->L;
int L_B = shell_B->L;
double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
double p = alpha_A + alpha_B;
double mu = alpha_A * alpha_B / (alpha_A + alpha_B);
double *A = shell_A->origin;
double *B = shell_B->origin;
// calculate S_ij
double S[3][LIBGRPP_MAX_BASIS_L + 2][LIBGRPP_MAX_BASIS_L + 2];
for (int coord = 0; coord < 3; coord++) {
double P = (alpha_A * A[coord] + alpha_B * B[coord]) / p;
double X_AB = A[coord] - B[coord];
double X_PA = P - A[coord];
double X_PB = P - B[coord];
double pfac = 1.0 / (2.0 * p);
for (int i = 0; i <= L_A + 2; i++) {
for (int j = 0; j <= L_B + 2; j++) {
double S_ij = 0.0;
if (i + j == 0) {
S[coord][0][0] = sqrt(M_PI / p) * exp(-mu * X_AB * X_AB);
continue;
}
if (i == 0) { // upward by j
S_ij += X_PB * S[coord][i][j - 1];
if (j - 1 > 0) {
S_ij += (j - 1) * pfac * S[coord][i][j - 2];
}
} else { // upward by i
S_ij += X_PA * S[coord][i - 1][j];
if (i - 1 > 0) {
S_ij += (i - 1) * pfac * S[coord][i - 2][j];
}
if (j > 0) {
S_ij += j * pfac * S[coord][i - 1][j - 1];
}
}
S[coord][i][j] = S_ij;
}
}
}
// calculate D^2_ij
double D2[3][LIBGRPP_MAX_BASIS_L][LIBGRPP_MAX_BASIS_L];
for (int coord = 0; coord < 3; coord++) {
for (int i = 0; i <= L_A; i++) {
for (int j = 0; j <= L_B; j++) {
double D2_ij = 0.0;
D2_ij += 4.0 * alpha_A * alpha_A * S[coord][i + 2][j];
D2_ij -= 2.0 * alpha_A * (2 * i + 1) * S[coord][i][j];
if (i >= 2) {
D2_ij += i * (i - 1) * S[coord][i - 2][j];
}
D2[coord][i][j] = D2_ij;
}
}
}
// loop over cartesian functions inside the shells
for (int m = 0; m < size_A; m++) {
for (int n = 0; n < size_B; n++) {
int n_A = shell_A->cart_list[3 * m + 0];
int l_A = shell_A->cart_list[3 * m + 1];
int m_A = shell_A->cart_list[3 * m + 2];
int n_B = shell_B->cart_list[3 * n + 0];
int l_B = shell_B->cart_list[3 * n + 1];
int m_B = shell_B->cart_list[3 * n + 2];
kinetic_matrix[m * size_B + n] =
-0.5 * N_A * N_B *
(D2[0][n_A][n_B] * S[1][l_A][l_B] * S[2][m_A][m_B] +
S[0][n_A][n_B] * D2[1][l_A][l_B] * S[2][m_A][m_B] +
S[0][n_A][n_B] * S[1][l_A][l_B] * D2[2][m_A][m_B]);
}
}
}

24
src/grpp/grpp_kinetic.h Normal file
View file

@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_KINETIC_H
#define LIBGRPP_KINETIC_H
#include "libgrpp_types.h"
void libgrpp_kinetic_energy_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *kinetic_matrix);
#endif // LIBGRPP_KINETIC_H

146
src/grpp/grpp_lmatrix.c Normal file
View file

@ -0,0 +1,146 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Functions for construction of matrices of the angular momentum operator L
* in the bases of either real or complex spherical harmonics.
*/
#include "grpp_lmatrix.h"
#include <complex.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
static void get_transformation_coeffs_csh_to_rsh(int m, double complex *a,
double complex *b);
/**
* Constructs matrices of the Lx, Ly, Lz operators for the given angular
* momentum L in the basis of real spherical harmonics (rsh).
*/
void libgrpp_construct_angular_momentum_matrices_rsh(int L, double *lx_matrix,
double *ly_matrix,
double *lz_matrix) {
int dim = 2 * L + 1;
// set all matrices to zero
memset(lx_matrix, 0, dim * dim * sizeof(double));
memset(ly_matrix, 0, dim * dim * sizeof(double));
memset(lz_matrix, 0, dim * dim * sizeof(double));
double *lx_matrix_csh = calloc(dim * dim, sizeof(double));
double *ly_matrix_csh = calloc(dim * dim, sizeof(double));
double *lz_matrix_csh = calloc(dim * dim, sizeof(double));
libgrpp_construct_angular_momentum_matrices_csh(L, lx_matrix_csh,
ly_matrix_csh, lz_matrix_csh);
for (int m1 = -L; m1 <= L; m1++) {
for (int m2 = -L; m2 <= L; m2++) {
// coefficients: S_lm = a * Y_{l,-m} + b * Y_{l,m}
double complex a1, b1;
double complex a2, b2;
get_transformation_coeffs_csh_to_rsh(m1, &a1, &b1); // bra
get_transformation_coeffs_csh_to_rsh(m2, &a2, &b2); // ket
int m1m = -abs(m1);
int m1p = +abs(m1);
int m2m = -abs(m2);
int m2p = +abs(m2);
double complex lx = 0.0 + 0.0 * I;
lx += conj(a1) * a2 * lx_matrix_csh[dim * (m1m + L) + (m2m + L)];
lx += conj(a1) * b2 * lx_matrix_csh[dim * (m1m + L) + (m2p + L)];
lx += conj(b1) * a2 * lx_matrix_csh[dim * (m1p + L) + (m2m + L)];
lx += conj(b1) * b2 * lx_matrix_csh[dim * (m1p + L) + (m2p + L)];
double complex ly = 0.0 + 0.0 * I;
ly += conj(a1) * a2 * ly_matrix_csh[dim * (m1m + L) + (m2m + L)];
ly += conj(a1) * b2 * ly_matrix_csh[dim * (m1m + L) + (m2p + L)];
ly += conj(b1) * a2 * ly_matrix_csh[dim * (m1p + L) + (m2m + L)];
ly += conj(b1) * b2 * ly_matrix_csh[dim * (m1p + L) + (m2p + L)];
double complex lz = 0.0 + 0.0 * I;
lz += conj(a1) * a2 * lz_matrix_csh[dim * (m1m + L) + (m2m + L)];
lz += conj(a1) * b2 * lz_matrix_csh[dim * (m1m + L) + (m2p + L)];
lz += conj(b1) * a2 * lz_matrix_csh[dim * (m1p + L) + (m2m + L)];
lz += conj(b1) * b2 * lz_matrix_csh[dim * (m1p + L) + (m2p + L)];
lx_matrix[(m1 + L) * dim + (m2 + L)] = cimag(lx);
ly_matrix[(m1 + L) * dim + (m2 + L)] = -creal(ly);
lz_matrix[(m1 + L) * dim + (m2 + L)] = cimag(lz);
}
}
free(lx_matrix_csh);
free(ly_matrix_csh);
free(lz_matrix_csh);
}
/**
* Constructs matrices of the Lx, Ly, Lz operators in the basis of
* complex spherical harmonics (csh) |Y_lm> for angular momentum l=L.
* Matrices of size (2*L+1) x (2*L+1) must be pre-allocated.
*/
void libgrpp_construct_angular_momentum_matrices_csh(int L, double *lx_matrix,
double *ly_matrix,
double *lz_matrix) {
int dim = 2 * L + 1;
// set all matrices to zero
memset(lx_matrix, 0, dim * dim * sizeof(double));
memset(ly_matrix, 0, dim * dim * sizeof(double));
memset(lz_matrix, 0, dim * dim * sizeof(double));
for (int m1 = -L; m1 <= L; m1++) {
for (int m2 = -L; m2 <= L; m2++) {
double lz = m2 * (m1 == m2);
double lp = sqrt((L - m2) * (L + m2 + 1)) * (m1 == m2 + 1); // L+
double lm = sqrt((L + m2) * (L - m2 + 1)) * (m1 == m2 - 1); // L-
double lx = 0.5 * (lp + lm);
double ly = 0.5 * (lp - lm);
lx_matrix[(m1 + L) * dim + (m2 + L)] = lx;
ly_matrix[(m1 + L) * dim + (m2 + L)] = ly;
lz_matrix[(m1 + L) * dim + (m2 + L)] = lz;
}
}
}
/**
* Real spherical harmonic S_{l,m} can be represented as a linear combination
* of two complex spherical harmonics:
* S_{l,m} = a * Y_{l,-m} + b * Y_{l,m}
* (except for the case m=0, where S_{l,0} = Y_{l,0})
*
* coefficients can be found elsewhere, see, for example,
* https://en.wikipedia.org/wiki/Table_of_spherical_harmonics
*/
static void get_transformation_coeffs_csh_to_rsh(int m, double complex *a,
double complex *b) {
if (m == 0) {
*a = 0.5;
*b = 0.5;
} else if (m < 0) {
*a = +1.0 * I / sqrt(2);
*b = -1.0 * I / sqrt(2) * pow(-1, abs(m));
} else { // m > 0
*a = +1.0 / sqrt(2);
*b = +1.0 / sqrt(2) * pow(-1, m);
}
}

26
src/grpp/grpp_lmatrix.h Normal file
View file

@ -0,0 +1,26 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_LMATRIX_H
#define LIBGRPP_LMATRIX_H
void libgrpp_construct_angular_momentum_matrices_rsh(int L, double *lx_matrix,
double *ly_matrix,
double *lz_matrix);
void libgrpp_construct_angular_momentum_matrices_csh(int L, double *lx_matrix,
double *ly_matrix,
double *lz_matrix);
#endif // LIBGRPP_LMATRIX_H

178
src/grpp/grpp_momentum.c Normal file
View file

@ -0,0 +1,178 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/**
* Calculation of momentum integrals.
*
* For details, see:
* T. Helgaker, P. Jorgensen, J. Olsen, Molecular Electronic-Structure Theory,
* John Wiley & Sons Ltd, 2000.
* Chapter 9.3.4, "Momentum and kinetic-energy integrals"
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_momentum.h"
#include "grpp_norm_gaussian.h"
#include "grpp_utils.h"
#include "libgrpp.h"
static void momentum_integrals_shell_pair_obara_saika(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double alpha_A,
double alpha_B, double *momentum_x_matrix, double *momentum_y_matrix,
double *momentum_z_matrix);
/**
* returns imaginary(!) part of integrals over the momentum operator p = -i
* \hbar \nabla. The "minus" sign is included.
*/
void libgrpp_momentum_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *momentum_x_matrix,
double *momentum_y_matrix,
double *momentum_z_matrix) {
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double *buf_x = calloc(size_A * size_B, sizeof(double));
double *buf_y = calloc(size_A * size_B, sizeof(double));
double *buf_z = calloc(size_A * size_B, sizeof(double));
memset(momentum_x_matrix, 0, size_A * size_B * sizeof(double));
memset(momentum_y_matrix, 0, size_A * size_B * sizeof(double));
memset(momentum_z_matrix, 0, size_A * size_B * sizeof(double));
// loop over primitives in contractions
for (int i = 0; i < shell_A->num_primitives; i++) {
for (int j = 0; j < shell_B->num_primitives; j++) {
double alpha_i = shell_A->alpha[i];
double alpha_j = shell_B->alpha[j];
double coef_A_i = shell_A->coeffs[i];
double coef_B_j = shell_B->coeffs[j];
momentum_integrals_shell_pair_obara_saika(shell_A, shell_B, alpha_i,
alpha_j, buf_x, buf_y, buf_z);
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf_x,
momentum_x_matrix);
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf_y,
momentum_y_matrix);
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf_z,
momentum_z_matrix);
}
}
free(buf_x);
free(buf_y);
free(buf_z);
}
static void momentum_integrals_shell_pair_obara_saika(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double alpha_A,
double alpha_B, double *momentum_x_matrix, double *momentum_y_matrix,
double *momentum_z_matrix) {
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
int L_A = shell_A->L;
int L_B = shell_B->L;
double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
double p = alpha_A + alpha_B;
double mu = alpha_A * alpha_B / (alpha_A + alpha_B);
double *A = shell_A->origin;
double *B = shell_B->origin;
// calculate S_ij
double S[3][LIBGRPP_MAX_BASIS_L + 1][LIBGRPP_MAX_BASIS_L + 1];
for (int coord = 0; coord < 3; coord++) {
double P = (alpha_A * A[coord] + alpha_B * B[coord]) / p;
double X_AB = A[coord] - B[coord];
double X_PA = P - A[coord];
double X_PB = P - B[coord];
double pfac = 1.0 / (2.0 * p);
for (int i = 0; i <= L_A + 1; i++) {
for (int j = 0; j <= L_B + 1; j++) {
double S_ij = 0.0;
if (i + j == 0) {
S[coord][0][0] = sqrt(M_PI / p) * exp(-mu * X_AB * X_AB);
continue;
}
if (i == 0) { // upward by j
S_ij += X_PB * S[coord][i][j - 1];
if (j - 1 > 0) {
S_ij += (j - 1) * pfac * S[coord][i][j - 2];
}
} else { // upward by i
S_ij += X_PA * S[coord][i - 1][j];
if (i - 1 > 0) {
S_ij += (i - 1) * pfac * S[coord][i - 2][j];
}
if (j > 0) {
S_ij += j * pfac * S[coord][i - 1][j - 1];
}
}
S[coord][i][j] = S_ij;
}
}
}
// calculate D^1_ij
double D1[3][LIBGRPP_MAX_BASIS_L][LIBGRPP_MAX_BASIS_L];
for (int coord = 0; coord < 3; coord++) {
for (int i = 0; i <= L_A; i++) {
for (int j = 0; j <= L_B; j++) {
double D1_ij = 0.0;
D1_ij += 2.0 * alpha_A * S[coord][i + 1][j];
if (i >= 1) {
D1_ij -= i * S[coord][i - 1][j];
}
D1[coord][i][j] = D1_ij;
}
}
}
// loop over cartesian functions inside the shells
for (int m = 0; m < size_A; m++) {
for (int n = 0; n < size_B; n++) {
int n_A = shell_A->cart_list[3 * m + 0];
int l_A = shell_A->cart_list[3 * m + 1];
int m_A = shell_A->cart_list[3 * m + 2];
int n_B = shell_B->cart_list[3 * n + 0];
int l_B = shell_B->cart_list[3 * n + 1];
int m_B = shell_B->cart_list[3 * n + 2];
momentum_x_matrix[m * size_B + n] =
-N_A * N_B * D1[0][n_A][n_B] * S[1][l_A][l_B] * S[2][m_A][m_B];
momentum_y_matrix[m * size_B + n] =
-N_A * N_B * S[0][n_A][n_B] * D1[1][l_A][l_B] * S[2][m_A][m_B];
momentum_z_matrix[m * size_B + n] =
-N_A * N_B * S[0][n_A][n_B] * S[1][l_A][l_B] * D1[2][m_A][m_B];
}
}
}

26
src/grpp/grpp_momentum.h Normal file
View file

@ -0,0 +1,26 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_MOMENTUM_H
#define LIBGRPP_MOMENTUM_H
#include "libgrpp_types.h"
void libgrpp_momentum_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *momentum_x_matrix,
double *momentum_y_matrix,
double *momentum_z_matrix);
#endif // LIBGRPP_MOMENTUM_H

View file

@ -0,0 +1,33 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_norm_gaussian.h"
/**
* Calculates normalization factor for the cartesian Gaussian x^n y^l z^m
* exp(-alpha*r^2)
*/
double libgrpp_gaussian_norm_factor(int n, int l, int m, double alpha) {
return pow(2 * alpha / M_PI, 0.75) *
pow(4 * alpha, 0.5 * (n + l + m)); /* /
sqrt((double) double_factorial(2 * n - 1) *
(double) double_factorial(2 * l - 1) *
(double) double_factorial(2 * m - 1));*/
}

View file

@ -0,0 +1,20 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_NORM_GAUSSIAN_H
#define LIBGRPP_NORM_GAUSSIAN_H
double libgrpp_gaussian_norm_factor(int n, int l, int m, double alpha);
#endif // LIBGRPP_NORM_GAUSSIAN_H

View file

@ -0,0 +1,310 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/**
* Calculation of nuclear attraction integrals.
*
* For the point charge nuclear model the recursive Obara-Saika scheme is used
* to calculate nuclear attraction integrals. For details, see
* T. Helgaker, P. Jorgensen, J. Olsen, Molecular Electronic-Structure Theory,
* John Wiley & Sons Ltd, 2000.
* Chapter 9.10.1, "The Obara-Saika scheme for one-electron Coulomb integrals"
*
* For the other three models,
* - uniformly charged ball
* - Gaussian nucleus
* - Fermi nucleus,
* the scheme is actually the same as for the type 1 (radially-local) ECP
* integrals. Electrostatic potential V(r) induced by the finite nuclear charge
* distribution is integrated numerically on a radial grid.
*/
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "grpp_norm_gaussian.h"
#include "grpp_nuclear_models.h"
#include "grpp_utils.h"
#include "libgrpp.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
void libgrpp_evaluate_radially_local_potential_integral_primitive_gaussians(
double *A, int n_cart_A, int *cart_list_A, double alpha_A, double *B,
int n_cart_B, int *cart_list_B, double alpha_B, double *C,
double (*potential)(double r, void *params), void *potential_params,
double *matrix);
void libgrpp_evaluate_rpp_type1_mmd_n1_primitive_shell_pair(
libgrpp_shell_t *shell_A, double alpha_A, libgrpp_shell_t *shell_B,
double alpha_B, double *rpp_origin, double rpp_alpha, double *rpp_matrix);
static double wrapper_coulomb_potential_point(double r, void *params);
static double wrapper_coulomb_potential_ball(double r, void *params);
static double wrapper_coulomb_potential_gaussian(double r, void *params);
static double wrapper_coulomb_potential_fermi(double r, void *params);
static double wrapper_coulomb_potential_fermi_bubble(double r, void *params);
/**
* Calculates nuclear attraction integral between two shells
* represented by contracted Gaussian functions.
*
* nuclear model should be one of:
* LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE
* LIBGRPP_NUCLEAR_MODEL_CHARGED_BALL
* LIBGRPP_NUCLEAR_MODEL_GAUSSIAN
* LIBGRPP_NUCLEAR_MODEL_FERMI
* LIBGRPP_NUCLEAR_MODEL_FERMI_BUBBLE
* LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE_NUMERICAL
*/
void libgrpp_nuclear_attraction_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *charge_origin, int charge,
int nuclear_model,
double *model_params,
double *coulomb_matrix) {
assert(libgrpp_is_initialized());
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double *buf = calloc(size_A * size_B, sizeof(double));
memset(coulomb_matrix, 0, size_A * size_B * sizeof(double));
// loop over primitives in contractions
for (int i = 0; i < shell_A->num_primitives; i++) {
double coef_A_i = shell_A->coeffs[i];
for (int j = 0; j < shell_B->num_primitives; j++) {
double coef_B_j = shell_B->coeffs[j];
if (fabs(coef_A_i * coef_B_j) < 1e-13) {
continue;
}
if (nuclear_model == LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE) {
// use code for RPP type-1 integrals with RPP exponent = 0.0
libgrpp_evaluate_rpp_type1_mmd_n1_primitive_shell_pair(
shell_A, shell_A->alpha[i], shell_B, shell_B->alpha[j],
charge_origin, 0.0, buf);
for (int k = 0; k < size_A * size_B; k++) {
buf[k] *= (-1) * charge;
}
} else if (nuclear_model == LIBGRPP_NUCLEAR_MODEL_CHARGED_BALL ||
nuclear_model == LIBGRPP_NUCLEAR_MODEL_GAUSSIAN ||
nuclear_model == LIBGRPP_NUCLEAR_MODEL_FERMI ||
nuclear_model == LIBGRPP_NUCLEAR_MODEL_FERMI_BUBBLE ||
nuclear_model ==
LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE_NUMERICAL) {
double params[10];
params[0] = charge;
/*
* choose nuclear model
*/
double (*electrostatic_potential_fun)(double, void *) = NULL;
if (nuclear_model == LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE_NUMERICAL) {
// printf("charge distribution: point\n");
electrostatic_potential_fun = wrapper_coulomb_potential_point;
} else if (nuclear_model == LIBGRPP_NUCLEAR_MODEL_CHARGED_BALL) {
params[1] = model_params[0]; // R_rms
electrostatic_potential_fun = wrapper_coulomb_potential_ball;
} else if (nuclear_model == LIBGRPP_NUCLEAR_MODEL_GAUSSIAN) {
params[1] = model_params[0]; // R_rms
electrostatic_potential_fun = wrapper_coulomb_potential_gaussian;
} else if (nuclear_model == LIBGRPP_NUCLEAR_MODEL_FERMI) {
params[1] = model_params[0]; // c
params[2] = model_params[1]; // a
electrostatic_potential_fun = wrapper_coulomb_potential_fermi;
} else {
params[1] = model_params[0]; // c
params[2] = model_params[1]; // a
params[3] = model_params[2]; // k
electrostatic_potential_fun = wrapper_coulomb_potential_fermi_bubble;
}
/*
* calculate integrals for the shell pair
*/
libgrpp_evaluate_radially_local_potential_integral_primitive_gaussians(
shell_A->origin, size_A, shell_A->cart_list, shell_A->alpha[i],
shell_B->origin, size_B, shell_B->cart_list, shell_B->alpha[j],
charge_origin, electrostatic_potential_fun, params, buf);
} else {
printf("LIBGRPP: unknown finite nuclear charge distribution model!\n");
exit(0);
}
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf, coulomb_matrix);
}
}
free(buf);
}
/**
* Calculates nuclear attraction integral between two shells
* represented by contracted Gaussian functions for the electrostatic potential
* generated by the point charge.
*/
void libgrpp_nuclear_attraction_integrals_point_charge(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *charge_origin,
int charge,
double *coulomb_matrix) {
libgrpp_nuclear_attraction_integrals(shell_A, shell_B, charge_origin, charge,
LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE, NULL,
coulomb_matrix);
}
/**
* Calculates nuclear attraction integral between two shells
* represented by contracted Gaussian functions for the electrostatic potential
* generated by the charged ball.
*
* r_rms stands for the root mean square radius (in bohrs)
*/
void libgrpp_nuclear_attraction_integrals_charged_ball(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *charge_origin,
int charge, double r_rms,
double *coulomb_matrix) {
double params[10];
params[0] = charge;
params[1] = r_rms;
libgrpp_nuclear_attraction_integrals(shell_A, shell_B, charge_origin, charge,
LIBGRPP_NUCLEAR_MODEL_CHARGED_BALL,
params, coulomb_matrix);
}
/**
* Calculates nuclear attraction integral between two shells
* represented by contracted Gaussian functions for the electrostatic potential
* generated by the Gaussian distribution.
*
* r_rms stands for the root mean square radius (in bohrs)
*/
void libgrpp_nuclear_attraction_integrals_gaussian_model(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *charge_origin,
int charge, double r_rms, double *coulomb_matrix) {
double params[10];
params[0] = charge;
params[1] = r_rms;
libgrpp_nuclear_attraction_integrals(shell_A, shell_B, charge_origin, charge,
LIBGRPP_NUCLEAR_MODEL_GAUSSIAN, params,
coulomb_matrix);
}
/**
* Calculates nuclear attraction integral between two shells
* represented by contracted Gaussian functions for the electrostatic potential
* generated by the Fermi distribution.
*
* Model parameters 'c' and 'a' must be given in bohrs.
*/
void libgrpp_nuclear_attraction_integrals_fermi_model(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *charge_origin,
int charge, double fermi_param_c, double fermi_param_a,
double *coulomb_matrix) {
double params[10];
params[0] = charge;
params[1] = fermi_param_c;
params[2] = fermi_param_a;
libgrpp_nuclear_attraction_integrals(shell_A, shell_B, charge_origin, charge,
LIBGRPP_NUCLEAR_MODEL_FERMI, params,
coulomb_matrix);
}
/**
* Calculates nuclear attraction integral between two shells
* represented by contracted Gaussian functions for the electrostatic potential
* generated by the "Fermi + bubble" distribution.
*
* Model parameters 'c' and 'a' must be given in bohrs.
* The 'k' constant is dimensionless.
*/
void libgrpp_nuclear_attraction_integrals_fermi_bubble_model(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *charge_origin,
int charge, double param_c, double param_a, double param_k,
double *coulomb_matrix) {
double params[10];
params[0] = charge;
params[1] = param_c;
params[2] = param_a;
params[3] = param_k;
libgrpp_nuclear_attraction_integrals(shell_A, shell_B, charge_origin, charge,
LIBGRPP_NUCLEAR_MODEL_FERMI_BUBBLE,
params, coulomb_matrix);
}
/**
* wrappers for charge distribution functions.
* are used to provide a unified interface to radially-local potentials.
* the 'params' argument is unpacked, then the specific routines are invoked.
*/
static double wrapper_coulomb_potential_point(double r, void *params) {
double Z = ((double *)params)[0];
return libgrpp_coulomb_potential_point(r, Z);
}
static double wrapper_coulomb_potential_ball(double r, void *params) {
double Z = ((double *)params)[0];
double R_rms = ((double *)params)[1];
return libgrpp_coulomb_potential_ball(r, Z, R_rms);
}
static double wrapper_coulomb_potential_gaussian(double r, void *params) {
double Z = ((double *)params)[0];
double R_rms = ((double *)params)[1];
return libgrpp_coulomb_potential_gaussian(r, Z, R_rms);
}
static double wrapper_coulomb_potential_fermi(double r, void *params) {
double Z = ((double *)params)[0];
double c = ((double *)params)[1];
double a = ((double *)params)[2];
return libgrpp_coulomb_potential_fermi(r, Z, c, a);
}
static double wrapper_coulomb_potential_fermi_bubble(double r, void *params) {
double Z = ((double *)params)[0];
double c = ((double *)params)[1];
double a = ((double *)params)[2];
double k = ((double *)params)[3];
return libgrpp_coulomb_potential_fermi_bubble(r, Z, c, a, k);
}

View file

@ -0,0 +1,337 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include "grpp_nuclear_models.h"
#include "grpp_specfunc.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
extern double libgrpp_fermi_model_Sk(int k, double x);
extern double libgrpp_fermi_model_norm_factor(double c, double a);
extern double libgrpp_fermi_bubble_model_norm_factor(double c, double a,
double k);
/*
* estimates for root mean square radius of nuclear charge distribution
*/
/**
* estimate for R(rms) from:
* W. R. Johnson, G. Soff. The Lamb shift in hydrogen-like atoms, 1 <= Z <=>
* 110. At. Data Nucl. Data Tables, 33(3), 405 (1985).
*
* A = mass number
* returns R(rms) in [fm] units
*/
double libgrpp_estimate_nuclear_rms_radius_johnson_1985(int A) {
return 0.836 * pow(A, 1.0 / 3.0) + 0.570;
}
/**
* estimate for R(rms) from:
* O. A. Golovko, I. A. Goidenko, I. I. Tupitsyn.
* Quantum electrodynamic corrections for valence electrons in Eka-Hg.
* Opt. Spectrosc. 104(5), 662 (2008).
*
* A = mass number
* returns R(rms) in [fm] units
*/
double libgrpp_estimate_nuclear_rms_radius_golovko_2008(int A) {
return 0.77 * pow(A, 1.0 / 3.0) + 0.98;
}
/**
* estimate parameters of the Fermi model using default formulas for 'c' and
* 'a'. return -1 if such estimate cannot be performed, 1 otherwise.
*
* units: Fermi for R_rms, c, a.
*/
int libgrpp_estimate_fermi_model_parameters(double R_rms, double *c,
double *a) {
const double t = 2.3;
*a = t / (4 * log(3));
const double c2 =
5.0 / 3.0 * R_rms * R_rms - 7.0 / 3.0 * M_PI * M_PI * (*a) * (*a);
if (c2 < 0) {
return -1;
}
*c = sqrt(c2);
return 1;
}
/*
* radially-local electrostatic potentials and density functions
* for different nuclear finite charge distribution models
*/
/**
* point charge
*/
double libgrpp_coulomb_potential_point(double r, double Z) { return -Z / r; }
/**
* uniformly charged ball: density
*/
double libgrpp_charge_density_ball(double r, double Z, double R_rms) {
const double R0 = sqrt(5.0 / 3.0) * R_rms;
if (r <= R0) {
return 3.0 * Z / (4.0 * M_PI * R0 * R0 * R0);
} else {
return 0.0;
}
}
/**
* uniformly charged ball: potential
*
* formula from:
* L. Visscher, K. G. Dyall,
* Dirac-Fock atomic electronic structure calculations using different nuclear
* charge distributions. Atomic Data and Nuclear Data Tables, 67, 207224 (1997)
*/
double libgrpp_coulomb_potential_ball(double r, double Z, double R_rms) {
const double R0 = sqrt(5.0 / 3.0) * R_rms;
if (r <= R0) {
return -Z / (2.0 * R0) * (3.0 - r * r / (R0 * R0));
} else {
return -Z / r;
}
}
/**
* Gaussian charge distribution: density
*/
double libgrpp_charge_density_gaussian(double r, double Z, double R_rms) {
const double xi = 3.0 / (2.0 * R_rms * R_rms);
const double rho0 = Z * pow(xi / M_PI, 1.5);
return rho0 * exp(-xi * r * r);
}
/**
* Gaussian charge distribution: potential
*
* formula from:
* L. Visscher, K. G. Dyall,
* Dirac-Fock atomic electronic structure calculations using different nuclear
* charge distributions. Atomic Data and Nuclear Data Tables, 67, 207224 (1997)
*/
double libgrpp_coulomb_potential_gaussian(double r, double Z, double R_rms) {
const double xi = 3.0 / (2.0 * R_rms * R_rms);
return -Z / r * erf(sqrt(xi) * r);
}
/**
* Fermi charge distribution: density
*/
double libgrpp_charge_density_fermi(double r, double Z, double c, double a) {
const double N = libgrpp_fermi_model_norm_factor(c, a);
const double c3 = c * c * c;
const double rho0 = 3.0 * Z / (4 * M_PI * c3 * N);
return rho0 / (1.0 + exp((r - c) / a));
}
/**
* Fermi charge distribution: rms radius
*/
double libgrpp_rms_radius_fermi(int Z, double c, double a) {
const double N = libgrpp_fermi_model_norm_factor(c, a);
const double c3 = c * c * c;
const double rho0 = 3.0 * Z / (4 * M_PI * c3 * N);
const double r2 =
4 * M_PI * rho0 / Z * pow(c, 5) / 5.0 *
(1.0 + 10.0 / 3.0 * a * a * M_PI * M_PI / (c * c) +
7.0 / 3.0 * pow(M_PI, 4) * pow(a, 4) / pow(c, 4) -
120.0 * pow(a, 5) / pow(c, 5) * libgrpp_specfunc_fermi_sk(5, -c / a));
return sqrt(r2);
}
/**
* Fermi charge distribution: potential
*
* formula from:
* N. S. Mosyagin, A. V. Zaitsevskii, A. V. Titov
* Generalized relativistic effective core potentials for superheavy elements
* Int. J. Quantum Chem. e26076 (2019)
* doi: https://doi.org/10.1002/qua.26076
*/
double libgrpp_coulomb_potential_fermi(double r, double Z, double c, double a) {
const double a2 = a * a;
const double a3 = a * a * a;
const double c3 = c * c * c;
const double N = libgrpp_fermi_model_norm_factor(c, a);
if (r > c) {
const double S2 = libgrpp_specfunc_fermi_sk(2, (c - r) / a);
const double S3 = libgrpp_specfunc_fermi_sk(3, (c - r) / a);
return -Z / (N * r) * (N + 3 * a2 * r / c3 * S2 + 6 * a3 / c3 * S3);
} else {
const double P2 = libgrpp_specfunc_fermi_sk(2, (r - c) / a);
const double P3 = libgrpp_specfunc_fermi_sk(3, (r - c) / a);
const double S3 = libgrpp_specfunc_fermi_sk(3, -c / a);
const double r3 = r * r * r;
return -Z / (N * r) *
(1.5 * r / c - r3 / (2 * c3) + M_PI * M_PI * a2 * r / (2 * c3) -
3 * a2 * r / c3 * P2 + 6 * a3 / c3 * (P3 - S3));
}
}
/**
* normalization factor for the Fermi nuclear charge distribution
*/
double libgrpp_fermi_model_norm_factor(double c, double a) {
const double a2 = a * a;
const double a3 = a * a * a;
const double c2 = c * c;
const double c3 = c * c * c;
return 1.0 + M_PI * M_PI * a2 / c2 -
6.0 * a3 / c3 * libgrpp_specfunc_fermi_sk(3, -c / a);
}
/**
* "Fermi bubble" charge distribution: density
*/
double libgrpp_charge_density_fermi_bubble(double r, double Z, double c,
double a, double k) {
const double Nk = libgrpp_fermi_bubble_model_norm_factor(c, a, k);
const double c3 = c * c * c;
const double rho0 = 3.0 * Z / (4 * M_PI * c3 * Nk);
return rho0 * (1 + k * pow(r / c, 2)) / (1.0 + exp((r - c) / a));
}
/**
* "Fermi bubble" charge distribution: rms radius
*/
double libgrpp_rms_radius_fermi_bubble(int Z, double c, double a, double k) {
const double Nk = libgrpp_fermi_bubble_model_norm_factor(c, a, k);
const double c3 = c * c * c;
const double rho0 = 3.0 * Z / (4 * M_PI * c3 * Nk);
const double part_r4 =
pow(c, 5) / 5.0 *
(1.0 + 10.0 / 3.0 * a * a * M_PI * M_PI / (c * c) +
7.0 / 3.0 * pow(M_PI, 4) * pow(a, 4) / pow(c, 4) -
120.0 * pow(a, 5) / pow(c, 5) * libgrpp_specfunc_fermi_sk(5, -c / a));
const double part_r6 =
pow(c, 7) / 7.0 *
(1.0 + 7.0 * a * a * M_PI * M_PI / (c * c) +
49.0 / 3.0 * pow(M_PI, 4) * pow(a, 4) / pow(c, 4) +
31.0 / 3.0 * pow(M_PI, 6) * pow(a, 6) / pow(c, 6) -
5040.0 * pow(a, 7) / pow(c, 7) * libgrpp_specfunc_fermi_sk(7, -c / a));
const double r2 = 4 * M_PI * rho0 / Z * (part_r4 + k / (c * c) * part_r6);
return sqrt(r2);
}
/**
* "Fermi bubble" charge distribution: potential.
*
* derivation of the formula is based on:
*
* F. A. Parpia and A. K. Mohanty,
* Relativistic basis-set calculations for atoms with Fermi nuclei.
* Phys. Rev. A 46, 3735 (1992)
* doi: 10.1103/PhysRevA.46.3735
*
*/
double libgrpp_coulomb_potential_fermi_bubble(double r, double Z, double c,
double a, double k) {
// const double a2 = a * a;
// const double a3 = a * a * a;
// const double c2 = c * c;
// const double c3 = c * c * c;
const double Nk = libgrpp_fermi_bubble_model_norm_factor(c, a, k);
// const double rho0 = 3 * Z / (4 * M_PI * M_PI * c * c * c * Nk);
double F0 = 0.0;
double F2 = 0.0;
if (r < c) {
const double S2 = libgrpp_specfunc_fermi_sk(2, (r - c) / a);
const double S3 = libgrpp_specfunc_fermi_sk(3, (r - c) / a);
const double S4 = libgrpp_specfunc_fermi_sk(4, (r - c) / a);
const double S5 = libgrpp_specfunc_fermi_sk(5, (r - c) / a);
// contribution from the "classical" Fermi term
F0 = -pow(r, 3) / 6.0 - r * a * a * S2 + 2.0 * pow(a, 3) * S3 +
r * c * c / 2.0 + M_PI * M_PI / 6.0 * r * a * a -
2.0 * pow(a, 3) * libgrpp_specfunc_fermi_sk(3, -c / a);
// contribution from the quadratic, "hole" term
F2 = -pow(r, 5) / 20.0 - pow(r, 3) * pow(a, 2) * S2 +
6.0 * pow(r, 2) * pow(a, 3) * S3 - 18.0 * r * pow(a, 4) * S4 +
24.0 * pow(a, 5) * S5 + r * pow(c, 4) / 4.0 +
r * M_PI * M_PI * c * c * a * a / 2.0 +
r * pow(a, 4) * pow(M_PI, 4) * 7.0 / 60.0 -
24.0 * pow(a, 5) * libgrpp_specfunc_fermi_sk(5, -c / a);
} else {
const double S2 = libgrpp_specfunc_fermi_sk(2, (c - r) / a);
const double S3 = libgrpp_specfunc_fermi_sk(3, (c - r) / a);
const double S4 = libgrpp_specfunc_fermi_sk(4, (c - r) / a);
const double S5 = libgrpp_specfunc_fermi_sk(5, (c - r) / a);
// contribution from the "classical" Fermi term
F0 = pow(c, 3) / 3.0 + M_PI * M_PI / 3.0 * c * a * a -
2.0 * pow(a, 3) * libgrpp_specfunc_fermi_sk(3, -c / a) +
r * a * a * S2 + 2.0 * pow(a, 3) * S3;
// contribution from the quadratic, "hole" term
F2 = pow(c, 5) / 5.0 + 2.0 * pow(c, 3) * a * a * M_PI * M_PI / 3.0 +
7.0 * pow(a, 4) * c * pow(M_PI, 4) / 15.0 -
24.0 * pow(a, 5) * libgrpp_specfunc_fermi_sk(5, -c / a) +
pow(a, 2) * pow(r, 3) * S2 + 6.0 * pow(a, 3) * pow(r, 2) * S3 +
18.0 * r * pow(a, 4) * S4 + 24.0 * pow(a, 5) * S5;
}
return -Z / (Nk * r) * 3.0 / pow(c, 3) * (F0 + k / (c * c) * F2);
}
/**
* normalization factor for the "Fermi bubble" nuclear charge distribution
*/
double libgrpp_fermi_bubble_model_norm_factor(double c, double a, double k) {
const double a2 = a * a;
const double a3 = a * a2;
const double a4 = a * a3;
const double a5 = a * a4;
const double c2 = c * c;
const double c3 = c * c2;
const double c4 = c * c3;
const double c5 = c * c4;
return libgrpp_fermi_model_norm_factor(c, a) + 3.0 / 5.0 * k +
2.0 * M_PI * M_PI * a2 * k / c2 +
7.0 * M_PI * M_PI * M_PI * M_PI * a4 * k / (5.0 * c4) -
72.0 * a5 * k / c5 * libgrpp_specfunc_fermi_sk(5, -c / a);
}

View file

@ -0,0 +1,50 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef TEST_LIBGRPP_F90_X_NUCLEAR_MODELS_H
#define TEST_LIBGRPP_F90_X_NUCLEAR_MODELS_H
#define FERMI_UNITS_TO_ATOMIC (1.0 / 52917.7210903)
double libgrpp_estimate_nuclear_rms_radius_johnson_1985(int A);
double libgrpp_estimate_nuclear_rms_radius_golovko_2008(int A);
int libgrpp_estimate_fermi_model_parameters(double R_rms, double *c, double *a);
double libgrpp_charge_density_ball(double r, double Z, double R_rms);
double libgrpp_charge_density_gaussian(double r, double Z, double R_rms);
double libgrpp_charge_density_fermi(double r, double Z, double c, double a);
double libgrpp_charge_density_fermi_bubble(double r, double Z, double c,
double a, double k);
double libgrpp_rms_radius_fermi(int Z, double c, double a);
double libgrpp_rms_radius_fermi_bubble(int Z, double c, double a, double k);
double libgrpp_coulomb_potential_point(double r, double Z);
double libgrpp_coulomb_potential_ball(double r, double Z, double R_rms);
double libgrpp_coulomb_potential_gaussian(double r, double Z, double R_rms);
double libgrpp_coulomb_potential_fermi(double r, double Z, double c, double a);
double libgrpp_coulomb_potential_fermi_bubble(double r, double Z, double c,
double a, double k);
#endif // TEST_LIBGRPP_F90_X_NUCLEAR_MODELS_H

View file

@ -0,0 +1,549 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Integration of the non-local terms in the GRPP operator.
* These integrals are reduced to the type 1 integrals and overlap integrals.
*/
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_factorial.h"
#include "grpp_lmatrix.h"
#include "grpp_overlap.h"
#include "grpp_spherical_harmonics.h"
#include "grpp_utils.h"
#include "libgrpp.h"
/*
* pre-definitions of function used below
*/
void libgrpp_outercore_potential_integrals_part_1(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
libgrpp_potential_t *oc_potential, libgrpp_shell_t *oc_shell,
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix);
void libgrpp_outercore_potential_integrals_part_2(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
libgrpp_potential_t *oc_potential_1, libgrpp_shell_t *oc_shell_1,
libgrpp_potential_t *oc_potential_2, libgrpp_shell_t *oc_shell_2,
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix);
static double calculate_delta_integral(libgrpp_potential_t *oc_pot_1,
libgrpp_shell_t *oc_shell_1,
libgrpp_potential_t *oc_pot_2,
libgrpp_shell_t *oc_shell_2);
static void transform_to_sph_basis_ket(int dim_bra, int dim_ket_cart,
int dim_ket_sph, double *A_in,
double *A_out, double *S_lm_coef);
static void transform_to_sph_basis_bra(int dim_bra_cart, int dim_bra_sph,
int dim_ket, double *A_in, double *A_out,
double *S_lm_coef);
static double ang_norm_factor(int lx, int ly, int lz);
static double analytic_one_center_rpp_integral_contracted(
libgrpp_shell_t *bra, libgrpp_shell_t *ket, libgrpp_potential_t *pot);
static double analytic_one_center_rpp_integral_primitive(int L, double alpha1,
double alpha2, int n,
double zeta);
static double radial_gto_norm_factor(int L, double alpha);
/**
* Calculates non-local contributions to the scalar-relativistic ECP and
* effective spin-orbit interaction matrices from the outercore (OC) potentials.
*/
void libgrpp_outercore_potential_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *rpp_origin,
int num_oc_shells, libgrpp_potential_t **oc_potentials,
libgrpp_shell_t **oc_shells, double *arep, double *esop_x, double *esop_y,
double *esop_z) {
assert(libgrpp_is_initialized());
// clear output matrices
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
memset(arep, 0, size_A * size_B * sizeof(double));
memset(esop_x, 0, size_A * size_B * sizeof(double));
memset(esop_y, 0, size_A * size_B * sizeof(double));
memset(esop_z, 0, size_A * size_B * sizeof(double));
// bind outercore shells of the GRPP to the RPP center
for (int ioc = 0; ioc < num_oc_shells; ioc++) {
oc_shells[ioc]->origin[0] = rpp_origin[0];
oc_shells[ioc]->origin[1] = rpp_origin[1];
oc_shells[ioc]->origin[2] = rpp_origin[2];
}
// 1. the U * |nlj><nlj| + |nlj><nlj| * U part
for (int ioc = 0; ioc < num_oc_shells; ioc++) {
libgrpp_potential_t *pot = oc_potentials[ioc];
libgrpp_shell_t *nlj = oc_shells[ioc];
libgrpp_outercore_potential_integrals_part_1(
shell_A, shell_B, rpp_origin, pot, nlj, arep, esop_x, esop_y, esop_z);
}
// 2. the |nlj><nlj| U |n'lj><n'lj| part
for (int ioc = 0; ioc < num_oc_shells; ioc++) {
for (int joc = 0; joc < num_oc_shells; joc++) {
libgrpp_potential_t *pot_1 = oc_potentials[ioc];
libgrpp_potential_t *pot_2 = oc_potentials[joc];
libgrpp_shell_t *nlj_1 = oc_shells[ioc];
libgrpp_shell_t *nlj_2 = oc_shells[joc];
libgrpp_outercore_potential_integrals_part_2(
shell_A, shell_B, rpp_origin, pot_1, nlj_1, pot_2, nlj_2, arep,
esop_x, esop_y, esop_z);
}
}
}
/**
* integration of the non-local outercore potential:
* the U*|nlj><nlj| + |nlj><nlj|*U part
*/
void libgrpp_outercore_potential_integrals_part_1(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
libgrpp_potential_t *oc_potential, libgrpp_shell_t *oc_shell,
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix) {
int L = oc_shell->L;
double J = oc_potential->J / 2.0;
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
int size_nlj = libgrpp_get_shell_size(oc_shell);
/*
* foolproof: bind outercore shells to the ECP center
*/
oc_shell->origin[0] = C[0];
oc_shell->origin[1] = C[1];
oc_shell->origin[2] = C[2];
/*
* Transformation coefficients: Cartesian -> Real spherical
* Rows: real spherical harmonics S_lm
* Columns: cartesians x^r y^s z^t
*/
double *S_lm_coef = (double *)calloc((2 * L + 1) * size_nlj, sizeof(double));
for (int m = -L; m <= +L; m++) {
for (int icart = 0; icart < size_nlj; icart++) {
int r = oc_shell->cart_list[3 * icart + 0];
int s = oc_shell->cart_list[3 * icart + 1];
int t = oc_shell->cart_list[3 * icart + 2];
double u = libgrpp_spherical_to_cartesian_coef(L, m, r, s) /
ang_norm_factor(r, s, t);
S_lm_coef[size_nlj * (m + L) + icart] = u;
}
}
/*
* Overlap integrals of the A and B shells with the outercore shell |nlj>:
* <A|nljm> and <nljm|B>
*
* Integrals are evaluated first for the cartesian components of |nlj>, and
* then transformed to the basis of real spherical components |nljm>.
* Resulting integrals are stored in the 'S_a_nljm' and 'S_nljm_b' arrays.
*/
double *S_nljm_b_cart =
alloc_zeros_1d(size_nlj * size_B); // in cart_list basis
double *S_a_nljm_cart = alloc_zeros_1d(size_A * size_nlj);
double *S_nljm_b = alloc_zeros_1d((2 * L + 1) * size_B); // in spherical basis
double *S_a_nljm = alloc_zeros_1d(size_A * (2 * L + 1));
libgrpp_overlap_integrals(oc_shell, shell_B, S_nljm_b_cart); // <nljm|B>
libgrpp_overlap_integrals(shell_A, oc_shell, S_a_nljm_cart); // <A|nljm>
transform_to_sph_basis_ket(size_A, size_nlj, 2 * L + 1, S_a_nljm_cart,
S_a_nljm, S_lm_coef);
transform_to_sph_basis_bra(size_nlj, 2 * L + 1, size_B, S_nljm_b_cart,
S_nljm_b, S_lm_coef);
free(S_nljm_b_cart);
free(S_a_nljm_cart);
/*
* ECP type-2 (semilocal) integrals of the A and B shells with the outercore
* shell |nlj>: <A|U(r)P_L|nljm> and <nljm|U(r)P_L|B>
*
* Integrals are evaluated first for the cartesian components of |nlj>, and
* then transformed to the basis of real spherical components |nljm>.
* Resulting integrals are stored in the 'U_a_nljm' and 'U_nljm_b' arrays.
*/
double *U_nljm_b_cart =
alloc_zeros_1d(size_nlj * size_B); // in cart_list basis
double *U_a_nljm_cart = alloc_zeros_1d(size_A * size_nlj);
double *U_nljm_b = alloc_zeros_1d((2 * L + 1) * size_B); // in spherical basis
double *U_a_nljm = alloc_zeros_1d(size_A * (2 * L + 1));
libgrpp_type1_integrals(oc_shell, shell_B, C, oc_potential,
U_nljm_b_cart); // <nljm|U(r)P_L|B>
libgrpp_type1_integrals(shell_A, oc_shell, C, oc_potential,
U_a_nljm_cart); // <A|U(r)P_L|nljm>
transform_to_sph_basis_ket(size_A, size_nlj, 2 * L + 1, U_a_nljm_cart,
U_a_nljm, S_lm_coef);
transform_to_sph_basis_bra(size_nlj, 2 * L + 1, size_B, U_nljm_b_cart,
U_nljm_b, S_lm_coef);
free(U_nljm_b_cart);
free(U_a_nljm_cart);
/*
* Construct outercore AREP matrix elements
* < a | P_nlj U(r) P_L + U(r) P_nlj P_L | b >
*/
double arep_factor =
(J < L) ? (L / (2.0 * L + 1)) : ((L + 1) / (2.0 * L + 1));
double *buf = alloc_zeros_1d(size_A * size_B);
libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, S_a_nljm, U_nljm_b, buf);
libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, U_a_nljm, S_nljm_b, buf);
libgrpp_daxpy(size_A * size_B, arep_factor, buf, arep_matrix);
free(buf);
/*
* Construct outercore effective SO potential matrix elements
* < a | P_nlj U(r) L P_L + U(r) P_nlj L P_L | b >
*/
double **L_matrices = alloc_zeros_2d(3, (2 * L + 1) * (2 * L + 1));
libgrpp_construct_angular_momentum_matrices_rsh(L, L_matrices[0],
L_matrices[1], L_matrices[2]);
double **so_buf = alloc_zeros_2d(3, size_A * size_B);
buf = alloc_zeros_1d((2 * L + 1) * int_max2(size_A, size_B));
for (int icoord = 0; icoord < 3; icoord++) {
// U(r) P_nlj
memset(buf, 0, (2 * L + 1) * size_B * sizeof(double));
libgrpp_multiply_matrices(2 * L + 1, size_B, 2 * L + 1, L_matrices[icoord],
S_nljm_b, buf);
libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, U_a_nljm, buf,
so_buf[icoord]);
// P_nlj U(r)
memset(buf, 0, (2 * L + 1) * size_A * sizeof(double));
libgrpp_multiply_matrices(size_A, 2 * L + 1, 2 * L + 1, S_a_nljm,
L_matrices[icoord], buf);
libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, buf, U_nljm_b,
so_buf[icoord]);
}
free(buf);
double esop_factor = (J < L) ? (-2.0 / (2 * L + 1)) : (+2.0 / (2 * L + 1));
libgrpp_daxpy(size_A * size_B, esop_factor, so_buf[0], so_x_matrix);
libgrpp_daxpy(size_A * size_B, esop_factor, so_buf[1], so_y_matrix);
libgrpp_daxpy(size_A * size_B, esop_factor, so_buf[2], so_z_matrix);
/*
* Cleanup
*/
free(S_lm_coef);
free(S_a_nljm);
free(S_nljm_b);
free(U_a_nljm);
free(U_nljm_b);
free_2d(L_matrices, 3);
free_2d(so_buf, 3);
}
/**
* integration of the non-local outercore potential:
* the |nlj><nlj| U |n'lj><n'lj| part
*/
void libgrpp_outercore_potential_integrals_part_2(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
libgrpp_potential_t *oc_potential_1, libgrpp_shell_t *oc_shell_1,
libgrpp_potential_t *oc_potential_2, libgrpp_shell_t *oc_shell_2,
double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix) {
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
if (oc_potential_1->L != oc_potential_2->L) {
return;
}
if (oc_potential_1->J != oc_potential_2->J) {
return;
}
/*
* foolproof: bind outercore shells to the ECP center
*/
oc_shell_1->origin[0] = C[0];
oc_shell_1->origin[1] = C[1];
oc_shell_1->origin[2] = C[2];
oc_shell_2->origin[0] = C[0];
oc_shell_2->origin[1] = C[1];
oc_shell_2->origin[2] = C[2];
/*
* just to be consistent with the MOLGEP code by A. Titov, N. Mosyagin, A.
* Petrov: off-diagonal elements <n'lj|U|n''lj> = 0
*/
/*if (!ecps_are_equal(oc_potential_1, oc_potential_2)) {
return;
}*/
int L = oc_potential_1->L;
double J = oc_potential_1->J / 2.0;
int size_nlj = libgrpp_get_shell_size(oc_shell_1);
double delta = calculate_delta_integral(oc_potential_1, oc_shell_1,
oc_potential_2, oc_shell_2);
/*
* Transformation coefficients: Cartesian -> Real spherical
* Rows: real spherical harmonics S_lm
* Columns: cartesians x^r y^s z^t
*/
double *S_lm_coef = (double *)calloc((2 * L + 1) * size_nlj, sizeof(double));
for (int m = -L; m <= +L; m++) {
for (int icart = 0; icart < size_nlj; icart++) {
int r = oc_shell_1->cart_list[3 * icart + 0];
int s = oc_shell_1->cart_list[3 * icart + 1];
int t = oc_shell_1->cart_list[3 * icart + 2];
double u = libgrpp_spherical_to_cartesian_coef(L, m, r, s) /
ang_norm_factor(r, s, t);
S_lm_coef[size_nlj * (m + L) + icart] = u;
}
}
/*
* Overlap integrals of the A and B shells with the outercore shells |nlj> and
* |n'lj>: <A|nljm> and <n'ljm|B>
*
* Integrals are evaluated first for the cartesian components of |nlj>, and
* then transformed to the basis of real spherical components |nljm>.
* Resulting integrals are stored in the 'S_a_nljm1' and 'S_nljm2_b' arrays.
*/
double *S_nljm2_b_cart =
alloc_zeros_1d(size_nlj * size_B); // in cart_list basis
double *S_a_nljm1_cart = alloc_zeros_1d(size_A * size_nlj);
double *S_nljm2_b =
alloc_zeros_1d((2 * L + 1) * size_B); // in spherical basis
double *S_a_nljm1 = alloc_zeros_1d(size_A * (2 * L + 1));
libgrpp_overlap_integrals(oc_shell_2, shell_B, S_nljm2_b_cart); // <nljm|B>
libgrpp_overlap_integrals(shell_A, oc_shell_1, S_a_nljm1_cart); // <A|nljm>
transform_to_sph_basis_ket(size_A, size_nlj, 2 * L + 1, S_a_nljm1_cart,
S_a_nljm1, S_lm_coef);
transform_to_sph_basis_bra(size_nlj, 2 * L + 1, size_B, S_nljm2_b_cart,
S_nljm2_b, S_lm_coef);
free(S_nljm2_b_cart);
free(S_a_nljm1_cart);
/*
* Construct outercore AREP matrix elements
* < a | P_nlj U(r) P_n'lj | b >
*/
double arep_factor =
(J < L) ? (L / (2.0 * L + 1)) : ((L + 1) / (2.0 * L + 1));
double *buf = alloc_zeros_1d(size_A * size_B);
libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, S_a_nljm1, S_nljm2_b,
buf);
libgrpp_daxpy(size_A * size_B, (-1.0) * delta * arep_factor, buf,
arep_matrix);
free(buf);
/*
* Construct outercore effective SO potential matrix elements
* < a | P_nlj U(r) L P_L + U(r) P_nlj L P_L | b >
*/
double **L_matrices = alloc_zeros_2d(3, (2 * L + 1) * (2 * L + 1));
libgrpp_construct_angular_momentum_matrices_rsh(L, L_matrices[0],
L_matrices[1], L_matrices[2]);
double **so_buf = alloc_zeros_2d(3, size_A * size_B);
buf = alloc_zeros_1d((2 * L + 1) * size_B);
for (int icoord = 0; icoord < 3; icoord++) {
memset(buf, 0, (2 * L + 1) * size_B * sizeof(double));
libgrpp_multiply_matrices(2 * L + 1, size_B, 2 * L + 1, L_matrices[icoord],
S_nljm2_b, buf);
libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, S_a_nljm1, buf,
so_buf[icoord]);
}
free(buf);
double esop_factor = (J < L) ? (-2.0 / (2 * L + 1)) : (+2.0 / (2 * L + 1));
libgrpp_daxpy(size_A * size_B, (-1.0) * delta * esop_factor, so_buf[0],
so_x_matrix);
libgrpp_daxpy(size_A * size_B, (-1.0) * delta * esop_factor, so_buf[1],
so_y_matrix);
libgrpp_daxpy(size_A * size_B, (-1.0) * delta * esop_factor, so_buf[2],
so_z_matrix);
free_2d(so_buf, 3);
free_2d(L_matrices, 3);
free(S_nljm2_b);
free(S_a_nljm1);
free(S_lm_coef);
}
/**
* Calculation of radial "delta" integrals.
* Is performed analytically.
*/
static double calculate_delta_integral(libgrpp_potential_t *oc_pot_1,
libgrpp_shell_t *oc_shell_1,
libgrpp_potential_t *oc_pot_2,
libgrpp_shell_t *oc_shell_2) {
// both shells must have equal L,J quantum numbers, otherwise
// the < nlj | U | n'l'j' > integral is strictly zero
if (oc_pot_1->L != oc_pot_2->L || oc_pot_1->J != oc_pot_2->J) {
return 0.0;
}
double U1 = analytic_one_center_rpp_integral_contracted(oc_shell_1,
oc_shell_2, oc_pot_1);
double U2 = analytic_one_center_rpp_integral_contracted(oc_shell_1,
oc_shell_2, oc_pot_2);
return 0.5 * (U1 + U2);
}
/**
* analytic formula for one-center RPP integral between two contracted gaussian
* functions.
*/
static double analytic_one_center_rpp_integral_contracted(
libgrpp_shell_t *bra, libgrpp_shell_t *ket, libgrpp_potential_t *pot) {
double sum = 0.0;
int L = pot->L;
for (int i = 0; i < bra->num_primitives; i++) {
double coef_i = bra->coeffs[i];
double alpha_i = bra->alpha[i];
double N_i = radial_gto_norm_factor(L, alpha_i);
for (int j = 0; j < ket->num_primitives; j++) {
double coef_j = ket->coeffs[j];
double alpha_j = ket->alpha[j];
double N_j = radial_gto_norm_factor(L, alpha_j);
double factor = N_i * N_j * coef_i * coef_j;
for (int k = 0; k < pot->num_primitives; k++) {
double coef_k = pot->coeffs[k];
double zeta = pot->alpha[k];
int n_rpp = pot->powers[k];
double u_ijk = analytic_one_center_rpp_integral_primitive(
L, alpha_i, alpha_j, n_rpp, zeta);
sum += factor * coef_k * u_ijk;
}
}
}
return sum;
}
/**
* analytic formula for one-center RPP integral between two gaussian primitives.
* normalization factors are omitted here.
*/
static double analytic_one_center_rpp_integral_primitive(int L, double alpha1,
double alpha2, int n,
double zeta) {
double a = alpha1 + alpha2 + zeta;
if (n % 2 == 0) { // even n
int k = L + n / 2;
return libgrpp_double_factorial(2 * k - 1) / (pow(2.0, k + 1) * pow(a, k)) *
sqrt(M_PI / a);
} else { // odd n
int k = L + (n - 1) / 2;
return libgrpp_factorial(k) / (2.0 * pow(a, k + 1));
}
}
/**
* calculate normalization factor for the radial Gaussian-type orbital:
* G(r) = N * r^L * exp(-alpha * r^2)
*/
static double radial_gto_norm_factor(int L, double alpha) {
// pre-tabulated factors for calculation of normalization constants
// (for each value of L)
static const double factors[] = {
2.5264751109842587, 2.9173221708553032, 2.6093322745198853,
1.9724697960897537, 1.3149798640598356, 7.9296269381073192e-1,
4.3985656185609934e-1, 2.2714095183849672e-1, 1.1017954545099481e-1,
5.0553842554329785e-2, 2.2063505731056757e-2, 9.2011179391124215e-3,
3.6804471756449694e-3, 1.4166047783978804e-3, 5.2611380677564405e-4,
1.8898565833279173e-4, 6.5796360823633550e-5, 2.2243229718298637e-5,
7.3135288801774484e-6, 2.3422037547660024e-6};
return factors[L] * pow(alpha, 0.75 + L / 2.0);
}
/**
* Transforms matrix from the basis of unitary sphere polynomials
* to the basis of real spherical harmonics S_lm
* (separately for 'bra' and 'ket' vectors)
*/
static void transform_to_sph_basis_ket(int dim_bra, int dim_ket_cart,
int dim_ket_sph, double *A_in,
double *A_out, double *S_lm_coef) {
for (int i = 0; i < dim_bra; i++) {
for (int j = 0; j < dim_ket_sph; j++) {
double s = 0.0;
for (int icart = 0; icart < dim_ket_cart; icart++) {
double u_nljm = S_lm_coef[dim_ket_cart * j + icart];
s += u_nljm * A_in[i * dim_ket_cart + icart];
}
A_out[i * dim_ket_sph + j] = s;
}
}
}
static void transform_to_sph_basis_bra(int dim_bra_cart, int dim_bra_sph,
int dim_ket, double *A_in, double *A_out,
double *S_lm_coef) {
for (int j = 0; j < dim_ket; j++) {
for (int i = 0; i < dim_bra_sph; i++) {
double s = 0.0;
for (int icart = 0; icart < dim_bra_cart; icart++) {
double u_nljm = S_lm_coef[dim_bra_cart * i + icart];
s += u_nljm * A_in[icart * dim_ket + j];
}
A_out[i * dim_ket + j] = s;
}
}
}
static double ang_norm_factor(int lx, int ly, int lz) {
int L = lx + ly + lz;
return 1.0 / (2.0 * sqrt(M_PI)) *
sqrt(libgrpp_double_factorial(2 * L + 1)
/*(double_factorial(2 * lx - 1) * double_factorial(2 * ly - 1) *
double_factorial(2 * lz - 1))*/
);
}

145
src/grpp/grpp_overlap.c Normal file
View file

@ -0,0 +1,145 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/**
* Calculation of overlap integrals.
*
* The recursive Obara-Saika scheme is used to calculate 1- and 2-center overlap
* integrals. For details, see: T. Helgaker, P. Jorgensen, J. Olsen, Molecular
* Electronic-Structure Theory, John Wiley & Sons Ltd, 2000. Chapter 9.3.1,
* "Overlap integrals"
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_norm_gaussian.h"
#include "grpp_overlap.h"
#include "libgrpp.h"
#include "grpp_utils.h"
static void overlap_integrals_shell_pair_obara_saika(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double alpha_A,
double alpha_B,
double *overlap_matrix);
/**
* Calculates overlap integral between two shells represented by contracted
* Gaussian functions.
*/
void libgrpp_overlap_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *overlap_matrix) {
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double *buf = calloc(size_A * size_B, sizeof(double));
memset(overlap_matrix, 0, size_A * size_B * sizeof(double));
// loop over primitives in contractions
for (int i = 0; i < shell_A->num_primitives; i++) {
for (int j = 0; j < shell_B->num_primitives; j++) {
double alpha_i = shell_A->alpha[i];
double alpha_j = shell_B->alpha[j];
double coef_A_i = shell_A->coeffs[i];
double coef_B_j = shell_B->coeffs[j];
overlap_integrals_shell_pair_obara_saika(shell_A, shell_B, alpha_i,
alpha_j, buf);
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf, overlap_matrix);
}
}
free(buf);
}
static void overlap_integrals_shell_pair_obara_saika(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double alpha_A,
double alpha_B,
double *overlap_matrix) {
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
int L_A = shell_A->L;
int L_B = shell_B->L;
double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
double p = alpha_A + alpha_B;
double mu = alpha_A * alpha_B / (alpha_A + alpha_B);
double *A = shell_A->origin;
double *B = shell_B->origin;
double S[3][LIBGRPP_MAX_BASIS_L][LIBGRPP_MAX_BASIS_L];
for (int coord = 0; coord < 3; coord++) {
double P = (alpha_A * A[coord] + alpha_B * B[coord]) / p;
double X_AB = A[coord] - B[coord];
double X_PA = P - A[coord];
double X_PB = P - B[coord];
double pfac = 1.0 / (2.0 * p);
for (int i = 0; i <= L_A; i++) {
for (int j = 0; j <= L_B; j++) {
double S_ij = 0.0;
if (i + j == 0) {
S[coord][0][0] = sqrt(M_PI / p) * exp(-mu * X_AB * X_AB);
continue;
}
if (i == 0) { // upward by j
S_ij += X_PB * S[coord][i][j - 1];
if (j - 1 > 0) {
S_ij += (j - 1) * pfac * S[coord][i][j - 2];
}
} else { // upward by i
S_ij += X_PA * S[coord][i - 1][j];
if (i - 1 > 0) {
S_ij += (i - 1) * pfac * S[coord][i - 2][j];
}
if (j > 0) {
S_ij += j * pfac * S[coord][i - 1][j - 1];
}
}
S[coord][i][j] = S_ij;
}
}
}
// loop over cartesian functions inside the shells
for (int m = 0; m < size_A; m++) {
for (int n = 0; n < size_B; n++) {
int n_A = shell_A->cart_list[3 * m + 0];
int l_A = shell_A->cart_list[3 * m + 1];
int m_A = shell_A->cart_list[3 * m + 2];
int n_B = shell_B->cart_list[3 * n + 0];
int l_B = shell_B->cart_list[3 * n + 1];
int m_B = shell_B->cart_list[3 * n + 2];
overlap_matrix[m * size_B + n] =
N_A * N_B * S[0][n_A][n_B] * S[1][l_A][l_B] * S[2][m_A][m_B];
}
}
}

23
src/grpp/grpp_overlap.h Normal file
View file

@ -0,0 +1,23 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_OVERLAP_H
#define LIBGRPP_OVERLAP_H
#include "libgrpp_types.h"
void libgrpp_overlap_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *overlap_matrix);
#endif // LIBGRPP_OVERLAP_H

View file

@ -0,0 +1,225 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <stdlib.h>
#include <string.h>
#include "grpp_overlap_gradient.h"
#include "libgrpp.h"
#include "grpp_diff_gaussian.h"
#include "grpp_utils.h"
static void overlap_gradient_diff_bra_contribution(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double **grad,
double factor);
static void overlap_gradient_diff_bra_overlap_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double **overlap_down,
double **overlap_up, int *cart_size_down, int *cart_size_up);
int nlm_to_linear(int *nlm);
/**
* Analytic calculation of gradients of overlap integrals for a given shell pair
* with respect to the point 'point_3d'.
*/
void libgrpp_overlap_integrals_gradient(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *point_3d, double **grad) {
int cart_size_A = libgrpp_get_shell_size(shell_A);
int cart_size_B = libgrpp_get_shell_size(shell_B);
int buf_size = cart_size_A * cart_size_B;
/*
* initializations: set gradients to zero
*/
for (int icoord = 0; icoord < 3; icoord++) {
memset(grad[icoord], 0, sizeof(double) * buf_size);
}
/*
* integrals are zero:
* (1) for 1-center integrals <A|A> (due to the translational invariance)
* (2) d<A|B> / dC = 0 (integral is constant for the given 'point_3d')
*/
if (points_are_equal(shell_A->origin, shell_B->origin)) {
return;
}
/*
* construct gradients:
* d<A|B>/dA = + < df/dA | B >
* d<A|B>/dB = - < df/dA | B >
*
* note that due to the property of translational invariance,
* d<A|B>/dB = - d<A|B>/dA
*/
if (points_are_equal(shell_A->origin, point_3d)) {
overlap_gradient_diff_bra_contribution(shell_A, shell_B, grad, +1.0);
}
if (points_are_equal(shell_B->origin, point_3d)) {
overlap_gradient_diff_bra_contribution(shell_A, shell_B, grad, -1.0);
}
}
/**
* Calculates contribution to gradients arising from the < df/dA | g > term:
*
* grad += factor * < df/dA | g >
*
* (bra basis function is differentiated).
*/
static void overlap_gradient_diff_bra_contribution(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double **grad,
double factor) {
/*
* calculate overlap integrals < df/dA | B >
*/
double *overlap_down = NULL;
double *overlap_up = NULL;
int cart_size_down = 0;
int cart_size_up = 0;
overlap_gradient_diff_bra_overlap_integrals(shell_A, shell_B, &overlap_down,
&overlap_up, &cart_size_down,
&cart_size_up);
/*
* construct contributions to gradients:
* d<A|B>/dA += < df/dA | B >
*/
for (int icoord = 0; icoord < 3; icoord++) {
for (int i = 0; i < shell_A->cart_size; i++) {
for (int j = 0; j < shell_B->cart_size; j++) {
int *bra_nlm = shell_A->cart_list + 3 * i;
int *ket_nlm = shell_B->cart_list + 3 * j;
int index = i * shell_B->cart_size + j;
/*
* contribution from the L-1 gaussian
*/
if (shell_A->L > 0) {
bra_nlm[icoord] -= 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
bra_nlm[icoord] += 1;
grad[icoord][index] -=
factor * bra_nlm[icoord] *
overlap_down[shell_B->cart_size * bra_index + ket_index];
}
/*
* contribution from the L+1 gaussian
*/
bra_nlm[icoord] += 1;
int bra_index = libgrpp_nlm_to_linear(bra_nlm);
int ket_index = libgrpp_nlm_to_linear(ket_nlm);
bra_nlm[icoord] -= 1;
grad[icoord][index] +=
factor * overlap_up[shell_B->cart_size * bra_index + ket_index];
}
}
}
if (overlap_down) {
free(overlap_down);
}
free(overlap_up);
}
/**
* To assemble the contribution < df/dA | g > to gradients, one have to
* differentiate Gaussian function. Such a differentiation yields two Gaussians
* with angular momenta L-1 ("down") and L+1 ("up"): dG/dA -> G(L-1) and G(L+1)
*
* This function constructs overlap matrices with these "downgraded" and
* "upgraded" Gaussian functions: < G(L-1) | G' > and < G(L+1) | G' >
*
*/
static void overlap_gradient_diff_bra_overlap_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double **overlap_down,
double **overlap_up, int *cart_size_down, int *cart_size_up) {
/*
* differentiation of contracted Gaussian functions
*/
libgrpp_shell_t *shell_A_down = NULL;
libgrpp_shell_t *shell_A_up = NULL;
libgrpp_differentiate_shell(shell_A, &shell_A_down, &shell_A_up);
*cart_size_down = 0;
if (shell_A_down != NULL) {
*cart_size_down = shell_A_down->cart_size;
}
*cart_size_up = shell_A_up->cart_size;
/*
* overlap matrix:
* < L-1 | L>
*/
if (shell_A_down != NULL) {
*overlap_down = (double *)calloc(
shell_A_down->cart_size * shell_B->cart_size, sizeof(double));
libgrpp_overlap_integrals(shell_A_down, shell_B, *overlap_down);
} else {
*overlap_down = NULL;
}
/*
* overlap matrix:
* < L+1 | L>
*/
*overlap_up = (double *)calloc(shell_A_up->cart_size * shell_B->cart_size,
sizeof(double));
libgrpp_overlap_integrals(shell_A_up, shell_B, *overlap_up);
/*
* clean up
*/
if (shell_A_down) {
libgrpp_delete_shell(shell_A_down);
}
libgrpp_delete_shell(shell_A_up);
}
/**
* calculates sequential ("linear") index of the (n,l,m) primitive in the
* cartesian shell
*/
int libgrpp_nlm_to_linear(int *nlm) {
int n = nlm[0];
int l = nlm[1];
int m = nlm[2];
int L = n + l + m;
int cart_size = (L + 1) * (L + 2) / 2;
int *cart_list = libgrpp_generate_shell_cartesians(L);
int index = 0;
for (index = 0; index < cart_size; index++) {
if (cart_list[3 * index + 0] == n && cart_list[3 * index + 1] == l &&
cart_list[3 * index + 2] == m) {
break;
}
}
free(cart_list);
return index;
}

View file

@ -0,0 +1,25 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_OVERLAP_GRADIENT_H
#define LIBGRPP_OVERLAP_GRADIENT_H
#include "libgrpp_types.h"
extern void libgrpp_overlap_integrals_gradient(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *point_3d, double **grad);
extern int libgrpp_nlm_to_linear(int *nlm);
#endif // LIBGRPP_OVERLAP_GRADIENT_H

115
src/grpp/grpp_parameters.c Normal file
View file

@ -0,0 +1,115 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <assert.h>
#include "grpp_parameters.h"
static int cartesian_generator_dirac(int L, int *cart_list);
static int cartesian_generator_turbomole(int L, int *cart_list);
libgrpp_parameters_t libgrpp_params = {
// tolerance of radial integration
1e-16,
// tolerance of angular integral screening
1e-16,
// tolerance of modified Bessel functions evaluation
// is it really needed?
1e-16,
// subroutine to generate cartesian components with given ang momentum L
cartesian_generator_dirac};
void libgrpp_set_default_parameters() {
// #pragma omp critical
{
libgrpp_set_radial_tolerance(1e-16);
libgrpp_set_angular_screening_tolerance(1e-16);
libgrpp_set_modified_bessel_tolerance(1e-16);
libgrpp_set_cartesian_generator(cartesian_generator_dirac);
}
}
void libgrpp_set_radial_tolerance(double tolerance) {
// #pragma omp critical
{ libgrpp_params.radial_tolerance = tolerance; }
}
void libgrpp_set_angular_screening_tolerance(double tolerance) {
// #pragma omp critical
{ libgrpp_params.angular_screening_tolerance = tolerance; }
}
void libgrpp_set_modified_bessel_tolerance(double tolerance) {
// #pragma omp critical
{ libgrpp_params.modified_bessel_tolerance = tolerance; }
}
void libgrpp_set_cartesian_order(int order) {
// #pragma omp critical
{
assert(order == LIBGRPP_CART_ORDER_DIRAC ||
order == LIBGRPP_CART_ORDER_TURBOMOLE);
if (order == LIBGRPP_CART_ORDER_DIRAC) {
libgrpp_set_cartesian_generator(cartesian_generator_dirac);
} else if (order == LIBGRPP_CART_ORDER_TURBOMOLE) {
libgrpp_set_cartesian_generator(cartesian_generator_turbomole);
}
}
}
void libgrpp_set_cartesian_generator(
int (*cartesian_generator)(int L, int *cart_list)) {
// #pragma omp critical
{ libgrpp_params.cartesian_generator = cartesian_generator; }
}
static int cartesian_generator_dirac(int L, int *cart_list) {
int count = 0;
int n_cart = (L + 1) * (L + 2) / 2;
for (int r = L; r >= 0; r--) {
for (int s = L; s >= 0; s--) {
for (int t = L; t >= 0; t--) {
if (r + s + t == L) {
cart_list[3 * count + 0] = r;
cart_list[3 * count + 1] = s;
cart_list[3 * count + 2] = t;
count++;
}
}
}
}
return n_cart;
}
static int cartesian_generator_turbomole(int L, int *cart_list) {
int count = 0;
int n_cart = (L + 1) * (L + 2) / 2;
for (int r = L; r >= 0; r--) {
for (int s = L - r; s >= 0; s--) {
int t = L - r - s;
cart_list[3 * count + 0] = r;
cart_list[3 * count + 1] = s;
cart_list[3 * count + 2] = t;
count++;
}
}
return n_cart;
}

View file

@ -0,0 +1,42 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_PARAMETERS_H
#define LIBGRPP_PARAMETERS_H
enum { LIBGRPP_CART_ORDER_DIRAC, LIBGRPP_CART_ORDER_TURBOMOLE };
typedef struct {
double radial_tolerance;
double angular_screening_tolerance;
double modified_bessel_tolerance;
int (*cartesian_generator)(int L, int *cart_list);
} libgrpp_parameters_t;
extern libgrpp_parameters_t libgrpp_params;
void libgrpp_set_default_parameters();
void libgrpp_set_radial_tolerance(double tolerance);
void libgrpp_set_angular_screening_tolerance(double tolerance);
void libgrpp_set_modified_bessel_tolerance(double tolerance);
void libgrpp_set_cartesian_order(int order);
void libgrpp_set_cartesian_generator(
int (*cartesian_generator)(int L, int *cart_list));
#endif // LIBGRPP_PARAMETERS_H

122
src/grpp/grpp_potential.c Normal file
View file

@ -0,0 +1,122 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* representation of (generalized) effective core potentials
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433
#endif
#include "libgrpp.h"
/**
* constructor for the pseudopotential
*/
libgrpp_potential_t *libgrpp_new_potential(int L, int J, int num_primitives,
int *powers, double *coeffs,
double *alpha) {
libgrpp_potential_t *pot =
(libgrpp_potential_t *)calloc(1, sizeof(libgrpp_potential_t));
pot->L = L;
pot->J = J;
pot->powers = (int *)calloc(num_primitives, sizeof(int));
pot->coeffs = (double *)calloc(num_primitives, sizeof(double));
pot->alpha = (double *)calloc(num_primitives, sizeof(double));
pot->num_primitives = 0;
for (int i = 0; i < num_primitives; i++) {
if (fabs(coeffs[i]) < LIBGRPP_ZERO_THRESH) {
continue;
}
pot->coeffs[pot->num_primitives] = coeffs[i];
pot->powers[pot->num_primitives] = powers[i];
pot->alpha[pot->num_primitives] = alpha[i];
pot->num_primitives++;
}
return pot;
}
/*
* destructor for the pseudopotential
*/
void libgrpp_delete_potential(libgrpp_potential_t *potential) {
if (potential == NULL) {
return;
}
free(potential->powers);
free(potential->coeffs);
free(potential->alpha);
free(potential);
}
/*
* calculates value of the pseudopotential at the point 'r'
*
* TODO: remove the invocation of 'pow()'
*/
double libgrpp_potential_value(libgrpp_potential_t *potential, double r) {
double val = 0.0;
double r_2 = r * r;
for (int i = 0; i < potential->num_primitives; i++) {
int n = potential->powers[i];
val +=
potential->coeffs[i] * pow(r, n - 2) * exp(-potential->alpha[i] * r_2);
}
return val;
}
/*
* removes redundant (zero) primitives from the RPP.
* argument remains constant.
*/
libgrpp_potential_t *
libgrpp_shrink_potential(libgrpp_potential_t *src_potential) {
int n = src_potential->num_primitives;
int *new_powers = calloc(n, sizeof(int));
double *new_coeffs = calloc(n, sizeof(double));
double *new_alpha = calloc(n, sizeof(double));
int n_nonzero_primitives = 0;
for (int i = 0; i < n; i++) {
if (fabs(src_potential->coeffs[i]) > LIBGRPP_ZERO_THRESH) {
new_powers[n_nonzero_primitives] = src_potential->powers[i];
new_coeffs[n_nonzero_primitives] = src_potential->coeffs[i];
new_alpha[n_nonzero_primitives] = src_potential->alpha[i];
n_nonzero_primitives++;
}
}
libgrpp_potential_t *new_pot = libgrpp_new_potential(
src_potential->L, src_potential->J, n_nonzero_primitives, new_powers,
new_coeffs, new_alpha);
free(new_powers);
free(new_coeffs);
free(new_alpha);
return new_pot;
}

View file

@ -0,0 +1,330 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Evaluation of type 1 radial integrals.
*
* The procedure in general follows that described in:
* R. Flores-Moreno et al. Half-numerical evaluation of pseudopotential
integrals.
* J. Comp. Chem. 27, 1009 (2006)
* (see formulas (12) and (13) for radial integrals invoking contracted Gaussian
functions and RPPs).
* In contrast to type 2 integrals, the special case of type 1 integrals Bessel
functions
* cannot be factorized, and one cannot use contracted Gaussians directly
* (and we have to use primitive Gaussians instead).
* However, the RPP radial function still can be used as a whole in the
integrand.
*
* The Log3 integration scheme used here is detailed in:
* C.-K. Skylaris et al. An efficient method for calculating effective core
potential integrals
* which involve projection operators.
* Chem. Phys. Lett. 296, 445 (1998)
*/
#include <math.h>
#include <stdlib.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_radial_type1_integral.h"
#include "libgrpp.h"
#include "grpp_specfunc.h"
#include "grpp_utils.h"
#define MIN_GRID 2047
#define MAX_GRID 10000
typedef struct {
double k;
double alpha_A;
double alpha_B;
double CA_2;
double CB_2;
double prefactor;
double (*potential)(double r, void *params);
void *potential_params;
} radial_type1_params_t;
typedef struct {
int nr;
int n_max;
int lambda_max;
double *r;
double *w;
double *pot_values;
double *gto_values;
double **r_N;
double **mod_bessel;
radial_type1_params_t *params;
} radial_type1_grid_t;
static radial_type1_grid_t *
create_radial_type1_grid(int lambda_max, int n_max,
radial_type1_params_t *params);
static void expand_radial_type1_grid(radial_type1_grid_t *grid, int nr);
static void delete_radial_type1_grid(radial_type1_grid_t *grid);
static double calculate_radial_type1_integral(radial_type1_grid_t *grid, int n,
int lambda, double tolerance,
int *converged);
radial_type1_table_t *libgrpp_tabulate_radial_type1_integrals(
int lambda_max, int n_max, double CA_2, double CB_2, double alpha_A,
double alpha_B, double k, double prefactor,
double (*potential)(double r, void *params), void *potential_params) {
radial_type1_table_t *table;
double const tolerance = libgrpp_params.radial_tolerance;
table = (radial_type1_table_t *)calloc(1, sizeof(radial_type1_table_t));
table->lambda_max = lambda_max;
table->n_max = n_max;
table->radial_integrals =
(double *)calloc((lambda_max + 1) * (n_max + 1), sizeof(double));
radial_type1_params_t params;
params.CA_2 = CA_2;
params.CB_2 = CB_2;
params.alpha_A = alpha_A;
params.alpha_B = alpha_B;
params.k = k;
params.prefactor = prefactor;
params.potential = potential;
params.potential_params = potential_params;
radial_type1_grid_t *grid =
create_radial_type1_grid(lambda_max, n_max, &params);
for (int lambda = 0; lambda <= lambda_max; lambda++) {
for (int n = 0; n <= n_max; n++) {
int converged;
double Q = calculate_radial_type1_integral(grid, n, lambda, tolerance,
&converged);
table->radial_integrals[lambda * (lambda_max + 1) + n] = Q;
}
}
delete_radial_type1_grid(grid);
return table;
}
void libgrpp_delete_radial_type1_integrals(radial_type1_table_t *table) {
free(table->radial_integrals);
free(table);
}
double libgrpp_get_radial_type1_integral(radial_type1_table_t *table,
int lambda, int n) {
int lambda_max = table->lambda_max;
return table->radial_integrals[lambda * (lambda_max + 1) + n];
}
static double radial_type1_integrand_fun(double r,
radial_type1_params_t *params) {
double alpha_A = params->alpha_A;
double alpha_B = params->alpha_B;
double k = params->k;
double CA_2 = params->CA_2;
double CB_2 = params->CB_2;
double prefactor = params->prefactor;
double power = k * r - (alpha_A + alpha_B) * r * r - alpha_A * CA_2 -
alpha_B * CB_2; // + N * log(r);
return prefactor * exp(power);
}
static radial_type1_grid_t *
create_radial_type1_grid(int lambda_max, int n_max,
radial_type1_params_t *params) {
radial_type1_grid_t *grid =
(radial_type1_grid_t *)calloc(1, sizeof(radial_type1_grid_t));
grid->nr = MIN_GRID;
grid->n_max = n_max;
grid->lambda_max = lambda_max;
grid->params = params;
grid->r = (double *)calloc(MAX_GRID, sizeof(double));
grid->w = (double *)calloc(MAX_GRID, sizeof(double));
grid->pot_values = (double *)calloc(MAX_GRID, sizeof(double));
grid->gto_values = (double *)calloc(MAX_GRID, sizeof(double));
grid->r_N = alloc_zeros_2d(n_max + 1, MAX_GRID);
grid->mod_bessel = alloc_zeros_2d(lambda_max + 1, MAX_GRID);
// initial set of pre-calculated points
int nr = grid->nr;
const double R = 5.0;
const double R3 = R * R * R;
for (int i = 1; i <= nr; i++) {
double xi = i / (nr + 1.0);
double xi3 = xi * xi * xi;
double ln_xi = log(1 - xi3);
double wi = 3 * R3 * xi * xi * ln_xi * ln_xi / ((1 - xi3) * (nr + 1.0));
double ri = -R * ln_xi;
grid->r[i - 1] = ri;
grid->w[i - 1] = wi;
grid->pot_values[i - 1] = params->potential(ri, params->potential_params);
grid->gto_values[i - 1] = radial_type1_integrand_fun(ri, params);
for (int lambda = 0; lambda <= lambda_max; lambda++) {
grid->mod_bessel[lambda][i - 1] =
libgrpp_modified_bessel_scaled(lambda, ri * params->k);
}
for (int n = 0; n <= n_max; n++) {
grid->r_N[n][i - 1] = pow(ri, n);
}
}
return grid;
}
static void delete_radial_type1_grid(radial_type1_grid_t *grid) {
free(grid->r);
free(grid->w);
free(grid->pot_values);
free(grid->gto_values);
free_2d(grid->r_N, grid->n_max + 1);
free_2d(grid->mod_bessel, grid->lambda_max + 1);
free(grid);
}
static void expand_radial_type1_grid(radial_type1_grid_t *grid, int nr) {
const double R = 5.0;
const double R3 = R * R * R;
if (nr > MAX_GRID) {
return;
}
if (nr <= grid->nr) { // nothing to do
return;
}
int idx = grid->nr;
for (int i = 1; i <= nr; i += 2) {
double xi = i / (nr + 1.0);
double xi3 = xi * xi * xi;
double ln_xi = log(1 - xi3);
double wi = 3 * R3 * xi * xi * ln_xi * ln_xi / ((1 - xi3) * (nr + 1.0));
double ri = -R * ln_xi;
grid->r[idx] = ri;
grid->w[idx] = wi;
grid->pot_values[idx] =
grid->params->potential(ri, grid->params->potential_params);
grid->gto_values[idx] = radial_type1_integrand_fun(ri, grid->params);
for (int lambda = 0; lambda <= grid->lambda_max; lambda++) {
double kr = grid->params->k * ri;
double bessel = libgrpp_modified_bessel_scaled(lambda, kr);
grid->mod_bessel[lambda][idx] = bessel;
}
for (int n = 0; n <= grid->n_max; n++) {
grid->r_N[n][idx] = pow(ri, n);
}
idx++;
}
grid->nr = nr;
}
static double calculate_radial_type1_integral(radial_type1_grid_t *grid, int n,
int lambda, double tolerance,
int *converged) {
int nr = MIN_GRID;
*converged = 0;
double prev_sum = 0.0;
double sum = 0.0;
double *w = grid->w;
// double *r = grid->r;
double *pot_values = grid->pot_values;
double *gto_values = grid->gto_values;
double *r_N = grid->r_N[n];
double *mod_bessel = grid->mod_bessel[lambda];
/*
* first step: screening of an integral
*/
/*double screened = 0.0;
int screened_success = screening_radial_type1(
lambda,
n,
grid->params->CA_2,
grid->params->CB_2,
grid->params->alpha_A,
grid->params->alpha_B,
grid->params->k,
grid->params->prefactor,
grid->params->potential_params,
&screened
);
if (screened_success == EXIT_SUCCESS && fabs(screened) < tolerance) {
*converged = 1;
return screened;
}*/
/*
* second step: calculation on the smallest possible grid
*/
for (int i = 0; i < nr; i++) {
sum += w[i] * pot_values[i] * gto_values[i] * r_N[i] * mod_bessel[i];
}
/*
* third step: adaptive integration, refinement of the result
*/
do {
int idx = nr;
nr = 2 * nr + 1;
if (nr > MAX_GRID) {
break;
}
prev_sum = sum;
sum = 0.5 * sum;
expand_radial_type1_grid(grid, nr);
for (int i = idx; i < nr; i++) {
sum += w[i] * pot_values[i] * gto_values[i] * r_N[i] * mod_bessel[i];
}
/*if (screened_success == EXIT_SUCCESS && (fabs(sum) / fabs(screened) <
0.001)) { *converged = 0; continue;
}*/
*converged = fabs(sum - prev_sum) <= tolerance;
} while (!(*converged));
return sum;
}

View file

@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_RADIAL_TYPE1_INTEGRAL_H
#define LIBGRPP_RADIAL_TYPE1_INTEGRAL_H
#include "libgrpp.h"
typedef struct {
int lambda_max;
int n_max;
double *radial_integrals;
} radial_type1_table_t;
radial_type1_table_t *libgrpp_tabulate_radial_type1_integrals(
int lambda_max, int n_max, double CA_2, double CB_2, double alpha_A,
double alpha_B, double k, double prefactor,
double (*potential)(double r, void *params), void *potential_params);
void libgrpp_delete_radial_type1_integrals(radial_type1_table_t *table);
double libgrpp_get_radial_type1_integral(radial_type1_table_t *table,
int lambda, int n);
#endif // LIBGRPP_RADIAL_TYPE1_INTEGRAL_H

View file

@ -0,0 +1,470 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Evaluation of type 2 radial integrals.
*
* The procedure in general follows that described in:
* R. Flores-Moreno et al. Half-numerical evaluation of pseudopotential
* integrals. J. Comp. Chem. 27, 1009 (2006) (see formulas (12) and (13) for
* radial integrals invoking contracted Gaussian functions and RPPs)
*
* The Log3 integration scheme used here is detailed in:
* C.-K. Skylaris et al. An efficient method for calculating effective core
* potential integrals which involve projection operators. Chem. Phys. Lett.
* 296, 445 (1998)
*/
#include <math.h>
#include <stdlib.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_radial_type2_integral.h"
#include "grpp_norm_gaussian.h"
#include "grpp_screening.h"
#include "grpp_specfunc.h"
#include "grpp_utils.h"
#define MIN_GRID 31
#define MAX_GRID 10000
typedef struct {
double CA;
double CB;
libgrpp_potential_t *potential;
libgrpp_shell_t *bra;
libgrpp_shell_t *ket;
} radial_type2_params_t;
/**
* RPP and radial contracted Gaussians are pre-calculated on a grid,
* and then combined into radial integrals
*/
typedef struct {
int nr;
int n_max;
int lambda1_max;
int lambda2_max;
double *r;
double *w;
double *rpp_values;
double **r_N;
double **F1;
double **F2;
radial_type2_params_t *params;
} radial_type2_grid_t;
/**
* pre-definitions of the functions used below
*/
static double calculate_radial_type2_integral(radial_type2_grid_t *grid, int n,
int lambda1, int lambda2,
double tolerance, int *converged);
static radial_type2_grid_t *
create_radial_type2_grid(int lambda1_max, int lambda2_max, int n_max,
radial_type2_params_t *params);
static void delete_radial_type2_grid(radial_type2_grid_t *grid);
static double radial_type2_integrand_fun_contracted(double r, int lambda,
double *k, double CA,
libgrpp_shell_t *gauss_fun);
static void expand_radial_type2_grid(radial_type2_grid_t *grid, int nr);
static void calc_k_values(int nprim, const double *alpha, double CA, double *k);
double libgrpp_gaussian_integral(int n, double a);
/**
* Creates table with pre-calculated radial type 2 integrals.
*/
radial_type2_table_t *libgrpp_tabulate_radial_type2_integrals(
int lambda1_max, int lambda2_max, int n_max, double CA_2, double CB_2,
libgrpp_potential_t *potential, libgrpp_shell_t *bra,
libgrpp_shell_t *ket) {
/*
* create empty table containing pre-tabulated radial type 2 integrals
*/
radial_type2_table_t *table;
table = (radial_type2_table_t *)calloc(1, sizeof(radial_type2_table_t));
table->lambda1_max = lambda1_max;
table->lambda2_max = lambda2_max;
table->n_max = n_max;
table->radial_integrals = (double *)calloc(
(lambda1_max + 1) * (lambda2_max + 1) * (n_max + 1), sizeof(double));
/*
* the special case of one-center RPP integrals
*/
if (CA_2 < 1e-14 && CB_2 < 1e-14) {
for (int i = 0; i < bra->num_primitives; i++) {
double alpha_A = bra->alpha[i];
double coef_i =
bra->coeffs[i] * libgrpp_gaussian_norm_factor(bra->L, 0, 0, alpha_A);
for (int j = 0; j < ket->num_primitives; j++) {
double alpha_B = ket->alpha[j];
double coef_j = ket->coeffs[j] *
libgrpp_gaussian_norm_factor(ket->L, 0, 0, alpha_B);
for (int k = 0; k < potential->num_primitives; k++) {
double eta = potential->alpha[k];
int ni = potential->powers[k];
double coef_k = potential->coeffs[k];
double p = alpha_A + alpha_B + eta;
double factor = coef_i * coef_j * coef_k;
for (int n = 0; n <= n_max; n++) {
double val_ijk = libgrpp_gaussian_integral(ni + n, p);
table->radial_integrals[n] += factor * val_ijk;
;
}
}
}
}
return table;
}
/*
* for numerical integration on the grid
*/
radial_type2_params_t params;
params.CA = sqrt(CA_2);
params.CB = sqrt(CB_2);
params.potential = libgrpp_shrink_potential(potential);
params.bra = libgrpp_shell_deep_copy(bra);
libgrpp_shell_shrink(params.bra);
libgrpp_shell_mult_normcoef(params.bra);
params.ket = libgrpp_shell_deep_copy(ket);
libgrpp_shell_shrink(params.ket);
libgrpp_shell_mult_normcoef(params.ket);
/*
* create radial grid
*/
radial_type2_grid_t *grid =
create_radial_type2_grid(lambda1_max, lambda2_max, n_max, &params);
/*
* calculate radial integrals and store them into the table
*/
for (int lambda_1 = 0; lambda_1 <= lambda1_max; lambda_1++) {
for (int lambda_2 = 0; lambda_2 <= lambda2_max; lambda_2++) {
for (int n = 0; n <= n_max; n++) {
int converged;
double Q = calculate_radial_type2_integral(grid, n, lambda_1, lambda_2,
1e-16, &converged);
// int dim1 = lambda1_max + 1;
int dim2 = lambda2_max + 1;
int dimn = n_max + 1;
table->radial_integrals[dim2 * dimn * lambda_1 + dimn * lambda_2 + n] =
Q;
}
}
}
/*
* clean-up
*/
libgrpp_delete_potential(params.potential);
libgrpp_delete_shell(params.bra);
libgrpp_delete_shell(params.ket);
delete_radial_type2_grid(grid);
return table;
}
/**
* destructor for the table of radial type 2 integrals
*/
void libgrpp_delete_radial_type2_integrals(radial_type2_table_t *table) {
free(table->radial_integrals);
free(table);
}
/**
* Returns radial integral at complex index (lambda1,lambda2,N)
*/
double libgrpp_get_radial_type2_integral(radial_type2_table_t *table,
int lambda1, int lambda2, int n) {
// int lambda1_max = table->lambda1_max;
int lambda2_max = table->lambda2_max;
int n_max = table->n_max;
// int dim1 = lambda1_max + 1;
int dim2 = lambda2_max + 1;
int dimn = n_max + 1;
double Q =
table->radial_integrals[dim2 * dimn * lambda1 + dimn * lambda2 + n];
return Q;
}
/**
* calculates type 2 radial integral T^N_{lambda1,lambda2}
* for the two given contracted gaussian functions and the contracted potential
*/
static double calculate_radial_type2_integral(radial_type2_grid_t *grid, int n,
int lambda1, int lambda2,
double tolerance,
int *converged) {
int nr = MIN_GRID;
*converged = 0;
double prev_sum = 0.0;
double sum = 0.0;
double *w = grid->w;
// double *r = grid->r;
double *pot_values = grid->rpp_values;
double *F1 = grid->F1[lambda1];
double *F2 = grid->F2[lambda2];
double *r_N = grid->r_N[n];
/*
* first step: integral screening
*/
double CA = grid->params->CA;
double CB = grid->params->CB;
double screened = 0.0;
int screen_success = libgrpp_screening_radial_type2(
lambda1, lambda2, n, CA * CA, CB * CB, grid->params->bra,
grid->params->ket, grid->params->potential, &screened);
if (screen_success == EXIT_SUCCESS && fabs(screened) < tolerance) {
*converged = 1;
return screened;
}
/*
* second step: calculation on the smallest possible grid
*/
for (int i = 0; i < nr; i++) {
sum += w[i] * pot_values[i] * F1[i] * F2[i] * r_N[i];
}
/*
* third step: adaptive integration, refinement of the result
*/
do {
int idx = nr;
nr = 2 * nr + 1;
if (nr > MAX_GRID) {
break;
}
prev_sum = sum;
sum = 0.5 * sum;
expand_radial_type2_grid(grid, nr);
for (int i = idx; i < nr; i++) {
sum += w[i] * pot_values[i] * F1[i] * F2[i] * r_N[i];
}
if (screen_success == EXIT_SUCCESS &&
(fabs(sum) / fabs(screened) < 0.001)) {
*converged = 0;
continue;
}
*converged = fabs(sum - prev_sum) <= tolerance;
} while (!(*converged));
return sum;
}
/**
* Numerical integration on the Log3 grid
*/
static radial_type2_grid_t *
create_radial_type2_grid(int lambda1_max, int lambda2_max, int n_max,
radial_type2_params_t *params) {
radial_type2_grid_t *grid =
(radial_type2_grid_t *)calloc(1, sizeof(radial_type2_grid_t));
grid->nr = MIN_GRID;
grid->n_max = n_max;
grid->lambda1_max = lambda1_max;
grid->lambda2_max = lambda2_max;
grid->params = params;
grid->r = alloc_zeros_1d(MAX_GRID);
grid->w = alloc_zeros_1d(MAX_GRID);
grid->rpp_values = alloc_zeros_1d(MAX_GRID);
grid->F1 = alloc_zeros_2d(lambda1_max + 1, MAX_GRID);
grid->F2 = alloc_zeros_2d(lambda2_max + 1, MAX_GRID);
grid->r_N = alloc_zeros_2d(n_max + 1, MAX_GRID);
// vectors 'k': k = - 2 * alpha * |CA|
double *bra_k = alloc_zeros_1d(params->bra->num_primitives);
double *ket_k = alloc_zeros_1d(params->ket->num_primitives);
calc_k_values(params->bra->num_primitives, params->bra->alpha, params->CA,
bra_k);
calc_k_values(params->ket->num_primitives, params->ket->alpha, params->CB,
ket_k);
// initial set of pre-calculated points
int nr = grid->nr;
const double R = 5.0;
const double R3 = R * R * R;
for (int i = 1; i <= nr; i++) {
double xi = i / (nr + 1.0);
double xi3 = xi * xi * xi;
double ln_xi = log(1 - xi3);
double wi = 3 * R3 * xi * xi * ln_xi * ln_xi / ((1 - xi3) * (nr + 1.0));
double ri = -R * ln_xi;
grid->r[i - 1] = ri;
grid->w[i - 1] = wi;
grid->rpp_values[i - 1] =
libgrpp_potential_value(grid->params->potential, ri);
for (int n = 0; n <= n_max; n++) {
grid->r_N[n][i - 1] = pow(ri, n);
}
for (int lambda1 = 0; lambda1 <= lambda1_max; lambda1++) {
grid->F1[lambda1][i - 1] = radial_type2_integrand_fun_contracted(
ri, lambda1, bra_k, params->CA, params->bra);
}
for (int lambda2 = 0; lambda2 <= lambda2_max; lambda2++) {
grid->F2[lambda2][i - 1] = radial_type2_integrand_fun_contracted(
ri, lambda2, ket_k, params->CB, params->ket);
}
}
free(bra_k);
free(ket_k);
return grid;
}
/**
* constructs new radial grid points
*/
static void expand_radial_type2_grid(radial_type2_grid_t *grid, int nr) {
const double R = 5.0;
const double R3 = R * R * R;
if (nr > MAX_GRID) {
return;
}
if (nr <= grid->nr) { // nothing to do
return;
}
radial_type2_params_t *params = grid->params;
// vectors 'k': k = - 2 * alpha * |CA|
double *bra_k = alloc_zeros_1d(params->bra->num_primitives);
double *ket_k = alloc_zeros_1d(params->ket->num_primitives);
calc_k_values(params->bra->num_primitives, params->bra->alpha, params->CA,
bra_k);
calc_k_values(params->ket->num_primitives, params->ket->alpha, params->CB,
ket_k);
// additional set of grid points
int idx = grid->nr;
for (int i = 1; i <= nr; i += 2) {
double xi = i / (nr + 1.0);
double xi3 = xi * xi * xi;
double ln_xi = log(1 - xi3);
double wi = 3 * R3 * xi * xi * ln_xi * ln_xi / ((1 - xi3) * (nr + 1.0));
double ri = -R * ln_xi;
grid->r[idx] = ri;
grid->w[idx] = wi;
grid->rpp_values[idx] =
libgrpp_potential_value(grid->params->potential, ri);
for (int n = 0; n <= grid->n_max; n++) {
grid->r_N[n][idx] = pow(ri, n);
}
for (int lambda1 = 0; lambda1 <= grid->lambda1_max; lambda1++) {
grid->F1[lambda1][idx] = radial_type2_integrand_fun_contracted(
ri, lambda1, bra_k, grid->params->CA, params->bra);
}
for (int lambda2 = 0; lambda2 <= grid->lambda2_max; lambda2++) {
grid->F2[lambda2][idx] = radial_type2_integrand_fun_contracted(
ri, lambda2, ket_k, grid->params->CB, params->ket);
}
idx++;
}
grid->nr = nr;
free(bra_k);
free(ket_k);
}
/**
* deallocates memory used for the radial grid
*/
static void delete_radial_type2_grid(radial_type2_grid_t *grid) {
free(grid->r);
free(grid->w);
free(grid->rpp_values);
free_2d(grid->F1, grid->lambda1_max + 1);
free_2d(grid->F2, grid->lambda2_max + 1);
free_2d(grid->r_N, grid->n_max + 1);
free(grid);
}
/**
* Calculate the value of the integrand function
*/
static double
radial_type2_integrand_fun_contracted(double r, int lambda, double *k,
double CA, libgrpp_shell_t *gauss_fun) {
double F = 0.0;
double r_CA_2 = (r - CA) * (r - CA);
int nprim = gauss_fun->num_primitives;
double *alpha = gauss_fun->alpha;
double *coeffs = gauss_fun->coeffs;
for (int i = 0; i < nprim; i++) {
double power = -alpha[i] * r_CA_2;
F += coeffs[i] * exp(power) *
libgrpp_modified_bessel_scaled(lambda, k[i] * r);
}
return F;
}
static void calc_k_values(int nprim, const double *alpha, double CA,
double *k) {
for (int i = 0; i < nprim; i++) {
k[i] = 2.0 * alpha[i] * CA;
}
}

View file

@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_RADIAL_TYPE2_INTEGRAL_H
#define LIBGRPP_RADIAL_TYPE2_INTEGRAL_H
#include "libgrpp_types.h"
typedef struct {
int lambda1_max;
int lambda2_max;
int n_max;
double *radial_integrals;
} radial_type2_table_t;
radial_type2_table_t *libgrpp_tabulate_radial_type2_integrals(
int lambda1_max, int lambda2_max, int n_max, double CA_2, double CB_2,
libgrpp_potential_t *potential, libgrpp_shell_t *bra, libgrpp_shell_t *ket);
double libgrpp_get_radial_type2_integral(radial_type2_table_t *table,
int lambda1, int lambda2, int n);
void libgrpp_delete_radial_type2_integrals(radial_type2_table_t *table);
#endif // LIBGRPP_RADIAL_TYPE2_INTEGRAL_H

335
src/grpp/grpp_screening.c Normal file
View file

@ -0,0 +1,335 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Screening of radial integrals.
*
* The technique of screening is adopted from:
* R. A. Shaw, J. G. Hill. Prescreening and efficiency in the evaluation
* of integrals over ab initio effective core potentials.
* J. Chem. Phys. 147, 074108 (2017). doi: 10.1063/1.4986887
* (see also Supplementary Material for this article).
*
* Note that in this publication the transcendental equation (2) for
* type 2 integrals is not correct.
*/
#include <math.h>
#include <stdlib.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_factorial.h"
#include "grpp_screening.h"
#include "grpp_specfunc.h"
#include "libgrpp.h"
/*
* functions defined below in the file
*/
static int screening_radial_type1_integral_primitive(
int lambda, int n, double CA_2, double CB_2, double alpha_A, double alpha_B,
double k, double eta, double *screened_value);
static double screening_type1_equation_for_maximum(double r, int n, int lambda,
double p, double k);
static int screening_radial_type2_integral_primitive(
int lambda1, int lambda2, int n, double CA_2, double CB_2, double alpha_A,
double alpha_B, double k1, double k2, double eta, double *screened_value);
static double screening_type2_equation_for_maximum(double r, int n, int lambda1,
int lambda2, double p,
double k1, double k2);
// static double analytic_one_center_rpp_integral_primitive(int L, double
// alpha1,
// double alpha2, int
// n, double zeta);
/**
* screening for the type 1 radial integrals
* for the pair of contracted gaussian functions.
*/
int libgrpp_screening_radial_type1(int lambda, int n, double CA_2, double CB_2,
double alpha_A, double alpha_B, double k,
double prefactor,
libgrpp_potential_t *potential,
double *screened_value) {
*screened_value = 0.0;
if (lambda >= 1 && fabs(k) <= LIBGRPP_ZERO_THRESH) {
return EXIT_SUCCESS;
}
/*
* loop over RPP primitives
*/
for (int iprim = 0; iprim < potential->num_primitives; iprim++) {
double eta = potential->alpha[iprim];
int ni = n + potential->powers[iprim];
double coef = potential->coeffs[iprim];
double val_i = 0.0;
int err_code = screening_radial_type1_integral_primitive(
lambda, ni, CA_2, CB_2, alpha_A, alpha_B, k, eta, &val_i);
if (err_code == EXIT_FAILURE) {
return EXIT_FAILURE;
}
*screened_value += prefactor * coef * val_i;
}
return EXIT_SUCCESS;
}
/**
* screening for the type 1 radial integrals
* for the pair of primitive gaussian functions.
*/
static int screening_radial_type1_integral_primitive(
int lambda, int n, double CA_2, double CB_2, double alpha_A, double alpha_B,
double k, double eta, double *screened_value) {
double p = alpha_A + alpha_B + eta;
double CA = sqrt(CA_2);
double CB = sqrt(CB_2);
/*
* find position of the maximum of the integrand
*/
const double tol = 1e-2;
double r0 = (alpha_A * CA + alpha_B * CB) / p;
double r0_prev = 0.0;
int nsteps = 0;
do {
nsteps++;
if (nsteps == 10) {
*screened_value = 0.0;
return EXIT_FAILURE;
}
r0_prev = r0;
r0 = screening_type1_equation_for_maximum(r0, n, lambda, p, k);
} while (fabs(r0 - r0_prev) > tol);
/*
* envelope function for the integrand
*/
*screened_value =
sqrt(M_PI / p) * pow(r0, n) *
libgrpp_modified_bessel_scaled(lambda, k * r0) *
exp(-p * r0 * r0 - alpha_A * CA_2 - alpha_B * CB_2 + k * r0) * 0.5 *
(1 + erf(sqrt(p) * r0));
return EXIT_SUCCESS;
}
/**
* transcendental equation for finding maximum of the type 1 integrand
*/
static double screening_type1_equation_for_maximum(double r, int n, int lambda,
double p, double k) {
double K_ratio = 0.0;
if (lambda == 0) {
K_ratio = libgrpp_modified_bessel_scaled(1, k * r) /
libgrpp_modified_bessel_scaled(0, k * r);
} else {
K_ratio = libgrpp_modified_bessel_scaled(lambda - 1, k * r) /
libgrpp_modified_bessel_scaled(lambda, k * r);
}
double a = n + K_ratio * k * r;
if (lambda > 0) {
a = a - lambda - 1;
}
return sqrt(a / (2.0 * p));
}
/**
* screening for the type 2 radial integrals
* for the pair of contracted gaussian functions.
*/
int libgrpp_screening_radial_type2(int lambda1, int lambda2, int n, double CA_2,
double CB_2, libgrpp_shell_t *bra,
libgrpp_shell_t *ket,
libgrpp_potential_t *potential,
double *screened_value) {
*screened_value = 0.0;
double CA = sqrt(CA_2);
double CB = sqrt(CB_2);
/*
* loop over 'bra' contracted function
*/
for (int i = 0; i < bra->num_primitives; i++) {
double alpha_A = bra->alpha[i];
double coef_i = bra->coeffs[i];
double k1 = 2 * alpha_A * CA;
/*
* loop over 'ket' contracted function
*/
for (int j = 0; j < ket->num_primitives; j++) {
double alpha_B = ket->alpha[j];
double coef_j = ket->coeffs[j];
double k2 = 2 * alpha_B * CB;
/*
* loop over RPP primitives
*/
for (int k = 0; k < potential->num_primitives; k++) {
double eta = potential->alpha[k];
int ni = n + potential->powers[k];
double coef_k = potential->coeffs[k];
double val_ijk = 0.0;
int err_code = screening_radial_type2_integral_primitive(
lambda1, lambda2, ni, CA_2, CB_2, alpha_A, alpha_B, k1, k2, eta,
&val_ijk);
if (err_code == EXIT_FAILURE) {
return EXIT_FAILURE;
}
*screened_value += coef_i * coef_j * coef_k * val_ijk;
}
}
}
return EXIT_SUCCESS;
}
/**
* Analytically evaluates Gaussian integral:
* \int_0^\infty r^n e^(-a r^2) dr
*/
double libgrpp_gaussian_integral(int n, double a) {
if (n % 2 == 0) {
int k = n / 2;
return libgrpp_double_factorial(2 * k - 1) / (pow(2.0, k + 1) * pow(a, k)) *
sqrt(M_PI / a);
} else {
int k = (n - 1) / 2;
return libgrpp_factorial(k) / (2.0 * pow(a, k + 1));
}
}
/**
* screening for the type 2 radial integrals
* for the pair of primitive gaussian functions.
*/
static int screening_radial_type2_integral_primitive(
int lambda1, int lambda2, int n, double CA_2, double CB_2, double alpha_A,
double alpha_B, double k1, double k2, double eta, double *screened_value) {
*screened_value = 0.0;
if (lambda1 >= 1 && fabs(k1) <= LIBGRPP_ZERO_THRESH) {
return EXIT_SUCCESS;
}
if (lambda2 >= 1 && fabs(k2) <= LIBGRPP_ZERO_THRESH) {
return EXIT_SUCCESS;
}
double p = alpha_A + alpha_B + eta;
double CA = sqrt(CA_2);
double CB = sqrt(CB_2);
/*
* special case:
* lambda1 = lambda2 = 0,
* k1 = k2 = 0.
* => M_0(0) = 1
* => we have one-center integral which can be evaluated analytically
*/
if (lambda1 == 0 && lambda2 == 0) {
if (fabs(k1) <= LIBGRPP_ZERO_THRESH && fabs(k2) <= LIBGRPP_ZERO_THRESH) {
*screened_value = exp(-alpha_A * CA * CA - alpha_B * CB * CB) *
libgrpp_gaussian_integral(n, p);
return EXIT_SUCCESS;
}
}
/*
* find position of the maximum of the integrand
*/
const double tol = 1e-2;
double r0 = (alpha_A * CA + alpha_B * CB) / p;
double r0_prev = 0.0;
int nsteps = 0;
do {
nsteps++;
if (nsteps == 5) {
*screened_value = 0.0;
return EXIT_FAILURE;
}
r0_prev = r0;
r0 = screening_type2_equation_for_maximum(r0, n, lambda1, lambda2, p, k1,
k2);
} while (fabs(r0 - r0_prev) > tol);
/*
* envelope function for the integrand
*/
*screened_value = sqrt(M_PI / p) * pow(r0, n) *
libgrpp_modified_bessel_scaled(lambda1, k1 * r0) *
libgrpp_modified_bessel_scaled(lambda2, k2 * r0) *
exp(-eta * r0 * r0 - alpha_A * (r0 - CA) * (r0 - CA) -
alpha_B * (r0 - CB) * (r0 - CB)) *
0.5 * (1 + erf(sqrt(p) * r0));
return EXIT_SUCCESS;
}
/**
* transcendental equation for finding maximum of the type 2 integrand
*/
static double screening_type2_equation_for_maximum(double r, int n, int lambda1,
int lambda2, double p,
double k1, double k2) {
double K1_ratio = 0.0;
double k1_r = k1 * r;
if (lambda1 == 0) {
K1_ratio = libgrpp_modified_bessel_scaled(1, k1_r) /
libgrpp_modified_bessel_scaled(0, k1_r);
} else {
K1_ratio = libgrpp_modified_bessel_scaled(lambda1 - 1, k1_r) /
libgrpp_modified_bessel_scaled(lambda1, k1_r);
}
double K2_ratio = 0.0;
double k2_r = k2 * r;
if (lambda2 == 0) {
K2_ratio = libgrpp_modified_bessel_scaled(1, k2_r) /
libgrpp_modified_bessel_scaled(0, k2_r);
} else {
K2_ratio = libgrpp_modified_bessel_scaled(lambda2 - 1, k2_r) /
libgrpp_modified_bessel_scaled(lambda2, k2_r);
}
double a = K1_ratio * k1_r + K2_ratio * k2_r + n;
if (lambda1 > 0) {
a = a - lambda1 - 1;
}
if (lambda2 > 0) {
a = a - lambda2 - 1;
}
return sqrt(a / (2.0 * p));
}

32
src/grpp/grpp_screening.h Normal file
View file

@ -0,0 +1,32 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_SCREENING_H
#define LIBGRPP_SCREENING_H
#include "libgrpp.h"
int libgrpp_screening_radial_type1(int lambda, int n, double CA_2, double CB_2,
double alpha_A, double alpha_B, double k,
double prefactor,
libgrpp_potential_t *potential,
double *screened_value);
int libgrpp_screening_radial_type2(int lambda1, int lambda2, int n, double CA_2,
double CB_2, libgrpp_shell_t *bra,
libgrpp_shell_t *ket,
libgrpp_potential_t *potential,
double *screened_value);
#endif // LIBGRPP_SCREENING_H

117
src/grpp/grpp_shell.c Normal file
View file

@ -0,0 +1,117 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* representation of atom-centered shell of contracted Gaussian functions
*/
#include <math.h>
#include <stdlib.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433
#endif
#include "libgrpp.h"
#include "grpp_norm_gaussian.h"
/**
* constructs new object representing a shell; returns pointer to it.
*/
libgrpp_shell_t *libgrpp_new_shell(double *origin, int L, int num_primitives,
double *coeffs, double *alpha) {
libgrpp_shell_t *shell = (libgrpp_shell_t *)malloc(sizeof(libgrpp_shell_t));
shell->L = L;
shell->origin[0] = origin[0];
shell->origin[1] = origin[1];
shell->origin[2] = origin[2];
shell->cart_size = (L + 1) * (L + 2) / 2;
shell->cart_list = libgrpp_generate_shell_cartesians(L);
shell->num_primitives = num_primitives;
shell->coeffs = (double *)calloc(num_primitives, sizeof(double));
shell->alpha = (double *)calloc(num_primitives, sizeof(double));
for (int i = 0; i < num_primitives; i++) {
shell->coeffs[i] = coeffs[i];
shell->alpha[i] = alpha[i];
}
return shell;
}
/**
* creates deep copy of the 'libgrpp_shell_t' object
*/
libgrpp_shell_t *libgrpp_shell_deep_copy(libgrpp_shell_t *src_shell) {
libgrpp_shell_t *new_shell = libgrpp_new_shell(
src_shell->origin, src_shell->L, src_shell->num_primitives,
src_shell->coeffs, src_shell->alpha);
return new_shell;
}
/**
* removes primitive gaussians (from the contracted function)
* with zero coefficients
*/
void libgrpp_shell_shrink(libgrpp_shell_t *shell) {
int nprim = 0;
for (int i = 0; i < shell->num_primitives; i++) {
if (fabs(shell->coeffs[i]) > LIBGRPP_ZERO_THRESH) {
shell->coeffs[nprim] = shell->coeffs[i];
shell->alpha[nprim] = shell->alpha[i];
nprim++;
}
}
shell->num_primitives = nprim;
}
/**
* multiplies coefficients of the primitive gaussians by their normalization
* factors
*/
void libgrpp_shell_mult_normcoef(libgrpp_shell_t *shell) {
for (int i = 0; i < shell->num_primitives; i++) {
double norm_factor =
libgrpp_gaussian_norm_factor(shell->L, 0, 0, shell->alpha[i]);
shell->coeffs[i] *= norm_factor;
}
}
/**
* returns number of Cartesian primitives encapsulated inside the shell
*/
int libgrpp_get_shell_size(libgrpp_shell_t *shell) { return shell->cart_size; }
/**
* destructor for the shell object
*/
void libgrpp_delete_shell(libgrpp_shell_t *shell) {
free(shell->cart_list);
free(shell->coeffs);
free(shell->alpha);
free(shell);
}
int *libgrpp_generate_shell_cartesians(int L) {
int ncart = (L + 1) * (L + 2) / 2;
int *cart_list = (int *)calloc(3 * ncart, sizeof(int));
libgrpp_params.cartesian_generator(L, cart_list);
return cart_list;
}

35
src/grpp/grpp_specfunc.h Normal file
View file

@ -0,0 +1,35 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Interface to the MyMathLib library:
* http://www.mymathlib.com/
*/
#ifndef LIBGRPP_SPECFUNC_H
#define LIBGRPP_SPECFUNC_H
double libgrpp_modified_bessel_scaled(int n, double x);
void libgrpp_gfun_values(double x, int nmax, double *g);
double libgrpp_boys_function(int n, double x);
void libgrpp_boys_values(double x, int nmax, double *b);
double libgrpp_specfunc_fermi_sk(int k, double x);
double libgrpp_Dawsons_Integral(double x);
#endif // LIBGRPP_SPECFUNC_H

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,570 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <float.h> // required for LDBL_EPSILON
#include <math.h> // required for fabsl()
#ifndef M_PI
#define M_PI 3.1415926535897932384626433
#endif
#include "grpp_specfunc.h"
/*
* This code is taken from the website:
* http://www.mymathlib.com/functions/dawsons_integral.html
*/
////////////////////////////////////////////////////////////////////////////////
// File: specfunc_dawson.c //
// Routine(s): //
// Dawsons_Integral //
// xDawsons_Integral //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Description: //
// Dawson's integral, Daw(x), is the integral from 0 to x of the //
// integrand: //
// exp(-x^2) exp(t^2) dt. //
// I.e. //
// Daw(x) = exp(-x^2) * I[0,x] (exp(t^2)) dt, //
// where I[0,x] indicates the integral from 0 to x. //
////////////////////////////////////////////////////////////////////////////////
#define Asymptotic_Expansion_Cutoff 50
// Externally Defined Routines //
static long double xChebyshev_Tn_Series(long double x, long double a[],
int degree);
// Internally Defined Routines //
double libgrpp_Dawsons_Integral(double x);
long double xDawsons_Integral(long double x);
static long double Dawson_Power_Series(long double x);
static long double Dawson_Chebyshev_Expansion_1_175(long double x);
static long double Dawson_Chebyshev_Expansion_175_250(long double x);
static long double Dawson_Chebyshev_Expansion_250_325(long double x);
static long double Dawson_Chebyshev_Expansion_325_425(long double x);
static long double Dawson_Chebyshev_Expansion_425_550(long double x);
static long double Dawson_Chebyshev_Expansion_550_725(long double x);
static long double Dawson_Asymptotic_Expansion(long double x);
////////////////////////////////////////////////////////////////////////////////
// double libgrpp_Dawsons_Integral( double x ) //
// //
// Description: //
// Dawson's integral, Daw(x), is the integral with integrand //
// exp(-x^2) exp(t^2) dt //
// where the integral extends from 0 to x. //
// //
// Arguments: //
// double x The argument of Dawson's integral Daw(). //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x. //
// //
// Example: //
// double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawsons_Integral( x ); //
////////////////////////////////////////////////////////////////////////////////
double libgrpp_Dawsons_Integral(double x) {
return (double)xDawsons_Integral((long double)x);
}
////////////////////////////////////////////////////////////////////////////////
// long double xDawsons_Integral( long double x ) //
// //
// Description: //
// Dawson's integral, Daw(x), is the integral with integrand //
// exp(-x^2) exp(t^2) dt //
// where the integral extends from 0 to x. //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(). //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = xDawsons_Integral( x ); //
////////////////////////////////////////////////////////////////////////////////
long double xDawsons_Integral(long double x) {
long double abs_x = fabsl(x);
if (abs_x <= 1.0L)
return Dawson_Power_Series(x);
if (abs_x <= 1.75L)
return Dawson_Chebyshev_Expansion_1_175(x);
if (abs_x <= 2.50L)
return Dawson_Chebyshev_Expansion_175_250(x);
if (abs_x <= 3.25L)
return Dawson_Chebyshev_Expansion_250_325(x);
if (abs_x <= 4.25L)
return Dawson_Chebyshev_Expansion_325_425(x);
if (abs_x <= 5.50L)
return Dawson_Chebyshev_Expansion_425_550(x);
if (abs_x <= 7.25L)
return Dawson_Chebyshev_Expansion_550_725(x);
return Dawson_Asymptotic_Expansion(x);
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Power_Series( long double x ) //
// //
// Description: //
// Evaluate Dawsons integral for -1 <= x <= 1 using the power series //
// expansion: //
// Daw(x) = x Sum [(-2x^2)^j / (2j + 1)!!, //
// where the sum extends over j = 0, ... . //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// where |x| <= 1. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where |x| <= 1. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Power_Series(x); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Power_Series(long double x) {
long double two_x2 = -2.0L * x * x;
long double sum = 0.0;
long double term = 1.0L;
long double factorial = 1.0L;
long double xn = 1.0L;
const long double epsilon = LDBL_EPSILON / 2.0L;
int y = 0;
if (x == 0.0L)
return 0.0L;
do {
sum += term;
y += 1;
factorial *= (long double)(y + y + 1);
xn *= two_x2;
term = xn / factorial;
} while (fabsl(term) > epsilon * fabsl(sum));
return x * sum;
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Chebyshev_Expansion_1_175( long double x ) //
// //
// Description: //
// Evaluate Dawsons integral for 1 <= |x| <= 1.75 using the Chebyshev //
// expansion of Daw(x) for x in the interval [1,1.75]. //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// where 1 <= |x| <= 1.75. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where //
// 1 <= x <= 1.75. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Chebyshev_Expansion_1_175(x); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Chebyshev_Expansion_1_175(long double x) {
static long double c[] = {
+4.563960711239483142081e-1L, -9.268566100670767619861e-2L,
-7.334392170021420220239e-3L, +3.379523740404396755124e-3L,
-3.085898448678595090813e-4L, -1.519846724619319512311e-5L,
+4.903955822454009397182e-6L, -2.106910538629224721838e-7L,
-2.930676220603996192089e-8L, +3.326790071774057337673e-9L,
+3.335593457695769191326e-11L, -2.279104036721012221982e-11L,
+7.877561633156348806091e-13L, +9.173158167107974472228e-14L,
-7.341175636102869400671e-15L, -1.763370444125849029511e-16L,
+3.792946298506435014290e-17L, -4.251969162435936250171e-19L,
-1.358295820818448686821e-19L, +5.268740962820224108235e-21L,
+3.414939674304748094484e-22L};
static const int degree = sizeof(c) / sizeof(long double) - 1;
static const long double midpoint = (1.75L + 1.0L) / 2.0L;
static const long double half_length = (1.75L - 1.0L) / 2.0L;
long double daw =
xChebyshev_Tn_Series((fabsl(x) - midpoint) / half_length, c, degree);
return (x > 0.0L) ? daw : -daw;
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Chebyshev_Expansion_175_250( long double x ) //
// //
// Description: //
// Evaluate Dawsons integral for 1.75 <= |x| <= 2.50 using the Chebyshev //
// expansion of Daw(x) for x in the interval [1.75,2.50]. //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// where 1.75 <= |x| <= 2.50. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where //
// 1.75 <= x <= 2.50. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Chebyshev_Expansion_175_250(x); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Chebyshev_Expansion_175_250(long double x) {
static long double c[] = {
+2.843711194548592808550e-1L, -6.791774139166808940530e-2L,
+6.955211059379384327814e-3L, -2.726366582146839486784e-4L,
-6.516682485087925163874e-5L, +1.404387911504935155228e-5L,
-1.103288540946056915318e-6L, -1.422154597293404846081e-8L,
+1.102714664312839585330e-8L, -8.659211557383544255053e-10L,
-8.048589443963965285748e-12L, +6.092061709996351761426e-12L,
-3.580977611213519234324e-13L, -1.085173558590137965737e-14L,
+2.411707924175380740802e-15L, -7.760751294610276598631e-17L,
-6.701490147030045891595e-18L, +6.350145841254563572100e-19L,
-2.034625734538917052251e-21L, -2.260543651146274653910e-21L,
+9.782419961387425633151e-23L};
static const int degree = sizeof(c) / sizeof(long double) - 1;
static const long double midpoint = (2.50L + 1.75L) / 2.0L;
static const long double half_length = (2.50L - 1.75L) / 2.0;
long double daw =
xChebyshev_Tn_Series((fabsl(x) - midpoint) / half_length, c, degree);
return (x > 0.0L) ? daw : -daw;
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Chebyshev_Expansion_250_325( long double x ) //
// //
// Description: //
// Evaluate Dawsons integral for 2.5 <= |x| <= 3.25 using the Chebyshev //
// expansion of Daw(x) for x in the interval [2.50,3.25]. //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// where 2.50 <= |x| <= 3.25. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where //
// 2.50 <= x <= 3.25. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Chebyshev_Expansion_250_325(x); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Chebyshev_Expansion_250_325(long double x) {
static long double c[] = {
+1.901351274204578126827e-1L, -3.000575522193632460118e-2L,
+2.672138524890489432579e-3L, -2.498237548675235150519e-4L,
+2.013483163459701593271e-5L, -8.454663603108548182962e-7L,
-8.036589636334016432368e-8L, +2.055498509671357933537e-8L,
-2.052151324060186596995e-9L, +8.584315967075483822464e-11L,
+5.062689357469596748991e-12L, -1.038671167196342609090e-12L,
+6.367962851860231236238e-14L, +3.084688422647419767229e-16L,
-3.417946142546575188490e-16L, +2.311567730100119302160e-17L,
-6.170132546983726244716e-20L, -9.133176920944950460847e-20L,
+5.712092431423316128728e-21L, +1.269641078369737220790e-23L,
-2.072659711527711312699e-23L};
static const int degree = sizeof(c) / sizeof(long double) - 1;
static const long double midpoint = (3.25L + 2.50L) / 2.0L;
static const long double half_length = (3.25L - 2.50L) / 2.0L;
long double daw =
xChebyshev_Tn_Series((fabsl(x) - midpoint) / half_length, c, degree);
return (x > 0.0L) ? daw : -daw;
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Chebyshev_Expansion_325_425( long double x ) //
// //
// Description: //
// Evaluate Dawsons integral for 3.25 <= |x| <= 4.25 using the Chebyshev //
// expansion of Daw(x) for x in the interval [3.25,4.75]. //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// where 3.25 <= |x| <= 4.25. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where //
// 3.25 <= x <= 4.25. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Chebyshev_Expansion_325_425(x); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Chebyshev_Expansion_325_425(long double x) {
static long double c[] = {
+1.402884974484995678749e-1L, -2.053975371995937033959e-2L,
+1.595388628922920119352e-3L, -1.336894584910985998203e-4L,
+1.224903774178156286300e-5L, -1.206856028658387948773e-6L,
+1.187997233269528945503e-7L, -1.012936061496824448259e-8L,
+5.244408240062370605664e-10L, +2.901444759022254846562e-11L,
-1.168987502493903926906e-11L, +1.640096995420504465839e-12L,
-1.339190668554209618318e-13L, +3.643815972666851044790e-15L,
+6.922486581126169160232e-16L, -1.158761251467106749752e-16L,
+8.164320395639210093180e-18L, -5.397918405779863087588e-20L,
-5.052069908100339242896e-20L, +5.322512674746973445361e-21L,
-1.869294542789169825747e-22L};
static const int degree = sizeof(c) / sizeof(long double) - 1;
// static const long double lower_bound = 3.25L;
// static const long double upper_bound = 4.25L;
static const long double midpoint = (4.25L + 3.25L) / 2.0L;
static const long double half_length = (4.25L - 3.25L) / 2.0L;
long double daw =
xChebyshev_Tn_Series((fabsl(x) - midpoint) / half_length, c, degree);
return (x > 0.0L) ? daw : -daw;
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Chebyshev_Expansion_425_550( long double x ) //
// //
// Description: //
// Evaluate Dawsons integral for 4.25 <= |x| <= 5.50 using the Chebyshev //
// expansion of Daw(x) for x in the interval [4.25,5.50]. //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// where 4.25 <= |x| <= 5.50. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where //
// 4.25 <= x <= 5.50. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Chebyshev_Expansion_425_550(x); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Chebyshev_Expansion_425_550(long double x) {
static long double c[] = {
+1.058610209741581514157e-1L, -1.429297757627935191694e-2L,
+9.911301703835545472874e-4L, -7.079903107876049846509e-5L,
+5.229587914675267516134e-6L, -4.016071345964089296212e-7L,
+3.231734714422926453741e-8L, -2.752870944370338482109e-9L,
+2.503059741885009530630e-10L, -2.418699000594890423278e-11L,
+2.410158905786160001792e-12L, -2.327254341132174000949e-13L,
+1.958284411563056492727e-14L, -1.099893145048991004460e-15L,
-2.959085292526991317697e-17L, +1.966366179276295203082e-17L,
-3.314408783993662492621e-18L, +3.635520318133814622089e-19L,
-2.550826919215104648800e-20L, +3.830090587178262542288e-22L,
+1.836693763159216122739e-22L};
static const int degree = sizeof(c) / sizeof(long double) - 1;
static const long double midpoint = (5.50L + 4.25L) / 2.0L;
static const long double half_length = (5.50L - 4.25L) / 2.0L;
long double daw =
xChebyshev_Tn_Series((fabsl(x) - midpoint) / half_length, c, degree);
return (x > 0.0L) ? daw : -daw;
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Chebyshev_Expansion_550_725( long double x ) //
// //
// Description: //
// Evaluate Dawsons integral for 5.50 <= |x| <= 7.25 using the Chebyshev //
// expansion of Daw(x) for x in the interval [5.50,7.25]. //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// where 5.50 <= |x| <= 7.25. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where //
// 5.50 <= x <= 7.25. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Chebyshev_Expansion_550_725(x); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Chebyshev_Expansion_550_725(long double x) {
static long double c[] = {+8.024637207807814739314e-2L,
-1.136614891549306029413e-2L,
+8.164249750628661856014e-4L,
-5.951964778701328943018e-5L,
+4.407349502747483429390e-6L,
-3.317746826184531133862e-7L,
+2.541483569880571680365e-8L,
-1.983391157250772649001e-9L,
+1.579050614491277335581e-10L,
-1.284592098551537518322e-11L,
+1.070070857004674207604e-12L,
-9.151832297362522251950e-14L,
+8.065447314948125338081e-15L,
-7.360105847607056315915e-16L,
+6.995966000187407197283e-17L,
-6.964349343411584120055e-18L,
+7.268789359189778223225e-19L,
-7.885125241947769024019e-20L,
+8.689022564130615225208e-21L,
-9.353211304381231554634e-22L +
9.218280404899298404756e-23L};
static const int degree = sizeof(c) / sizeof(long double) - 1;
// static const long double lower_bound = 5.50L;
// static const long double upper_bound = 7.25L;
static const long double midpoint = (7.25L + 5.50L) / 2.0L;
static const long double half_length = (7.25L - 5.50L) / 2.0L;
long double daw =
xChebyshev_Tn_Series((fabsl(x) - midpoint) / half_length, c, degree);
return (x > 0.0L) ? daw : -daw;
}
////////////////////////////////////////////////////////////////////////////////
// static long double Dawson_Asymptotic_Expansion( long double x ) //
// //
// Description: //
// For a large magnitude of the argument x, Dawson's integral can be //
// expressed as the asymptotic series //
// Daw(x) ~ (1/2x) [ 1 + 1 / (2x^2) + ... + (2j - 1)!! / (2x^2)^j + ... ] //
// //
// Arguments: //
// long double x The argument of Dawson's integral Daw(), where //
// |x| > 7. //
// //
// Return Value: //
// The value of Dawson's integral, Daw(), evaluated at x where |x| > 7. //
// //
// Example: //
// long double y, x; //
// //
// ( code to initialize x ) //
// //
// y = Dawson_Asymptotic_Expansion( x ); //
////////////////////////////////////////////////////////////////////////////////
static long double Dawson_Asymptotic_Expansion(long double x) {
long double term[Asymptotic_Expansion_Cutoff + 1];
long double x2 = x * x;
long double two_x = x + x;
long double two_x2 = x2 + x2;
long double xn = two_x2;
long double Sn = 0.0L;
long double factorial = 1.0L;
int n;
term[0] = 1.0L;
term[1] = 1.0L / xn;
for (n = 2; n <= Asymptotic_Expansion_Cutoff; n++) {
xn *= two_x2;
factorial *= (long double)(n + n - 1);
term[n] = factorial / xn;
if (term[n] < LDBL_EPSILON / 2.0L)
break;
}
if (n > Asymptotic_Expansion_Cutoff)
n = Asymptotic_Expansion_Cutoff;
for (; n >= 0; n--)
Sn += term[n];
return Sn / two_x;
}
////////////////////////////////////////////////////////////////////////////////
// File: xchebyshev_Tn_series.c //
// Routine(s): //
// xChebyshev_Tn_Series //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// long double xChebyshev_Tn_Series(long double x, long double a[],int degree)//
// //
// Description: //
// This routine uses Clenshaw's recursion algorithm to evaluate a given //
// polynomial p(x) expressed as a linear combination of Chebyshev //
// polynomials of the first kind, Tn, at a point x, //
// p(x) = a[0] + a[1]*T[1](x) + a[2]*T[2](x) + ... + a[deg]*T[deg](x). //
// //
// Clenshaw's recursion formula applied to Chebyshev polynomials of the //
// first kind is: //
// Set y[degree + 2] = 0, y[degree + 1] = 0, then for k = degree, ..., 1 //
// set y[k] = 2 * x * y[k+1] - y[k+2] + a[k]. Finally //
// set y[0] = x * y[1] - y[2] + a[0]. Then p(x) = y[0]. //
// //
// Arguments: //
// long double x //
// The point at which to evaluate the polynomial. //
// long double a[] //
// The coefficients of the expansion in terms of Chebyshev polynomials,//
// i.e. a[k] is the coefficient of T[k](x). Note that in the calling //
// routine a must be defined double a[N] where N >= degree + 1. //
// int degree //
// The degree of the polynomial p(x). //
// //
// Return Value: //
// The value of the polynomial at x. //
// If degree is negative, then 0.0 is returned. //
// //
// Example: //
// long double x, a[N], p; //
// int deg = N - 1; //
// //
// ( code to initialize x, and a[i] i = 0, ... , a[deg] ) //
// //
// p = xChebyshev_Tn_Series(x, a, deg); //
////////////////////////////////////////////////////////////////////////////////
static long double xChebyshev_Tn_Series(long double x, long double a[],
int degree) {
long double yp2 = 0.0L;
long double yp1 = 0.0L;
long double y = 0.0L;
long double two_x = x + x;
int k;
// Check that degree >= 0. If not, then return 0. //
if (degree < 0)
return 0.0L;
// Apply Clenshaw's recursion save the last iteration. //
for (k = degree; k >= 1; k--, yp2 = yp1, yp1 = y)
y = two_x * yp1 - yp2 + a[k];
// Now apply the last iteration and return the result. //
return x * yp1 - yp2 + a[0];
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,94 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Implementation of the Gn(x) auxiliary function.
* This function is required to calculate integrals over the 1/r^2 operator.
*
* More on the Gn(x) function evaluation:
* (1) J. 0. Jensen, A. H. Cameri, C. P. Vlahacos, D. Zeroka, H. F. Hameka, C.
* N. Merrow, Evaluation of one-electron integrals for arbitrary operators V(r)
* over cartesian Gaussians: Application to inverse-square distance and Yukawa
* operators. J. Comput. Chem. 14(8), 986 (1993). doi: 10.1002/jcc.540140814 (2)
* B. Gao, A. J. Thorvaldsen, K. Ruud, GEN1INT: A unified procedure for the
* evaluation of one-electron integrals over Gaussian basis functions and their
* geometric derivatives. Int. J. Quantum Chem. 111(4), 858 (2011).
* doi: 10.1002/qua.22886
*/
#include <math.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433
#endif
#include "grpp_factorial.h"
#include "grpp_specfunc.h"
static double gfun_taylor(int n, double x);
/**
* Calculates values of the Gn(x) auxiliary function for n = 0, ..., nmax
* and stores them into the g[] array.
*/
void libgrpp_gfun_values(double x, int nmax, double *g) {
memset(g, 0, (nmax + 1) * sizeof(double));
if (x <= 12.0) {
/*
* downward recursion
*/
g[nmax] = gfun_taylor(nmax, x);
for (int n = nmax; n > 0; n--) {
g[n - 1] = (1.0 - 2.0 * x * g[n]) / (2.0 * n - 1.0);
}
} else {
/*
* upward recursion
*/
double sqrt_x = sqrt(x);
g[0] = libgrpp_Dawsons_Integral(sqrt_x) / sqrt_x;
for (int n = 0; n < nmax; n++) {
g[n + 1] = (1.0 - (2 * n + 1) * g[n]) / (2.0 * x);
}
}
}
/**
* Calculates value of the Gn(x) auxiliary function using the Taylor expansion.
* The Taylor series converges for x <= 30.
*/
static double gfun_taylor(int n, double x) {
const double thresh = 1e-15;
double sum = 0.0;
for (int k = 0; k < 100; k++) {
double y_exp = exp(-x);
double y_pow = pow(x, k);
double y_fac = libgrpp_factorial(k);
double y_nk1 = 2.0 * n + 2.0 * k + 1.0;
double contrib = y_exp * y_pow / y_fac / y_nk1;
sum += contrib;
if (fabs(contrib) < thresh) {
break;
}
}
return sum;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,304 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Constructs tables with the expansion coefficients of real spherical harmonics
* in the basis of (cartesian) unitary spherical polynomials.
*
* For more details about the algorithm used, see:
* R. Flores-Moreno et al, J. Comput. Chem. 27, 1009 (2006),
* doi: 10.1002/jcc.20410
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654752440
#endif
#include "grpp_binomial.h"
#include "grpp_factorial.h"
#include "grpp_spherical_harmonics.h"
#include "libgrpp.h"
/*
* Tables with pretabulated expansion coefficients
*/
static rsh_coef_table_t **rsh_coef_tables = NULL;
static int rsh_tables_lmax = -1;
/*
* Function pre-definitions
*/
rsh_coef_table_t *libgrpp_tabulate_real_spherical_harmonic_coeffs(int L);
static int *generate_cartesian_combinations(int L, int *num);
/**
* Constructs the set of tables with C_{l,m}^{lx,ly,lz} coefficients
* (up to maximum angular momentum Lmax).
* (pretabulation step)
*/
void libgrpp_create_real_spherical_harmonic_coeffs_tables(int Lmax) {
if (Lmax <= rsh_tables_lmax) {
// nothing to do
} else {
// expand tables: realloc memory and add tables for the highest L values
rsh_coef_tables = (rsh_coef_table_t **)realloc(
rsh_coef_tables, (Lmax + 1) * sizeof(rsh_coef_table_t *));
for (int L = rsh_tables_lmax + 1; L <= Lmax; L++) {
rsh_coef_tables[L] = libgrpp_tabulate_real_spherical_harmonic_coeffs(L);
}
rsh_tables_lmax = Lmax;
}
}
/**
* Calculates all C_{l,m}^{lx,ly,lz} coefficients for the given angular momentum
* L.
*/
rsh_coef_table_t *libgrpp_tabulate_real_spherical_harmonic_coeffs(int L) {
int ncart = (L + 1) * (L + 2) / 2;
rsh_coef_table_t *coef_table =
(rsh_coef_table_t *)calloc(1, sizeof(rsh_coef_table_t));
coef_table->n_cart_comb = ncart;
coef_table->cartesian_comb = generate_cartesian_combinations(L, &ncart);
coef_table->coeffs = (double *)calloc((2 * L + 1) * ncart, sizeof(double));
for (int m = -L; m <= L; m++) {
for (int icomb = 0; icomb < ncart; icomb++) {
int lx = coef_table->cartesian_comb[3 * icomb];
int ly = coef_table->cartesian_comb[3 * icomb + 1];
// int lz = coef_table->cartesian_comb[3 * icomb + 2];
double u_lm_lx_ly_lz = libgrpp_spherical_to_cartesian_coef(L, m, lx, ly);
int index = (m + L) * ncart + icomb;
coef_table->coeffs[index] = u_lm_lx_ly_lz;
}
}
return coef_table;
}
/**
* Access to the table for the angular momentum value L.
*/
rsh_coef_table_t *libgrpp_get_real_spherical_harmonic_table(int L) {
if (L > rsh_tables_lmax) {
printf("get_real_spherical_harmonic_table(): %d > Lmax\n", L);
return NULL;
}
return rsh_coef_tables[L];
}
/**
* For the given real spherical harmonic (RSH) S_lm calculates the coefficient
* C_{l,m}^{lx,ly,lz} before the unitary spherical polynomial (USP) in its
* expansion.
*
* The formula is taken from:
* R. Flores-Moreno et al, J. Comput. Chem. 27, 1009 (2006)
* doi: 10.1002/jcc.20410
* (formula 32)
*/
double libgrpp_spherical_to_cartesian_coef(int l, int m, int lx, int ly) {
int j = lx + ly - abs(m);
if (j % 2 != 0) {
return 0.0;
}
j /= 2;
if (!((m > 0 && (abs(m) - lx) % 2 == 0) || (m == 0 && lx % 2 == 0) ||
(m < 0 && (abs(m) - lx) % 2 != 0))) {
return 0.0;
}
double prefactor =
sqrt((2 * l + 1) / (2 * M_PI) * libgrpp_factorial(l - abs(m)) /
libgrpp_factorial(l + abs(m)));
prefactor /= pow(2, l) * libgrpp_factorial(l);
double u_lm_lx_ly_lz = 0.0;
for (int i = j; i <= (l - abs(m)) / 2; i++) {
// any term that implies the factorial of a negative number is neglected
if (2 * l - 2 * i < 0) {
u_lm_lx_ly_lz = 0.0;
break;
}
if (l - abs(m) - 2 * i < 0) {
u_lm_lx_ly_lz = 0.0;
break;
}
double factor_1 =
libgrpp_binomial(l, i) * libgrpp_binomial(i, j) * pow(-1, i) *
libgrpp_factorial_ratio(2 * l - 2 * i, l - abs(m) - 2 * i);
double sum = 0.0;
for (int k = 0; k <= j; k++) {
sum += libgrpp_binomial(j, k) * libgrpp_binomial(abs(m), lx - 2 * k) *
pow(-1, (abs(m) - lx + 2 * k) / 2);
}
u_lm_lx_ly_lz += factor_1 * sum;
}
u_lm_lx_ly_lz *= prefactor;
if (m == 0 && (lx % 2 == 0)) {
u_lm_lx_ly_lz *= M_SQRT1_2; // x 1/sqrt(2)
}
return u_lm_lx_ly_lz;
}
/**
* Calculates value of the real spherical harmonic S_lm at the point k/|k| of
* the unit sphere.
*/
double libgrpp_evaluate_real_spherical_harmonic(const int l, const int m,
const double *k) {
double unitary_kx;
double unitary_ky;
double unitary_kz;
double kx_powers[200];
double ky_powers[200];
double kz_powers[200];
rsh_coef_table_t *rsh_coef_l = libgrpp_get_real_spherical_harmonic_table(l);
double length_k = sqrt(k[0] * k[0] + k[1] * k[1] + k[2] * k[2]);
if (length_k > LIBGRPP_ZERO_THRESH) {
unitary_kx = k[0] / length_k;
unitary_ky = k[1] / length_k;
unitary_kz = k[2] / length_k;
} else {
unitary_kx = 0.0;
unitary_ky = 0.0;
unitary_kz = 0.0;
}
kx_powers[0] = 1.0;
ky_powers[0] = 1.0;
kz_powers[0] = 1.0;
for (int i = 1; i <= l; i++) {
kx_powers[i] = kx_powers[i - 1] * unitary_kx;
ky_powers[i] = ky_powers[i - 1] * unitary_ky;
kz_powers[i] = kz_powers[i - 1] * unitary_kz;
}
double value = 0.0;
int ncart = rsh_coef_l->n_cart_comb;
for (int icomb = 0; icomb < ncart; icomb++) {
int r = rsh_coef_l->cartesian_comb[3 * icomb];
int s = rsh_coef_l->cartesian_comb[3 * icomb + 1];
int t = rsh_coef_l->cartesian_comb[3 * icomb + 2];
double y_lm_rst = rsh_coef_l->coeffs[(m + l) * ncart + icomb];
value += y_lm_rst * kx_powers[r] * ky_powers[s] * kz_powers[t];
}
return value;
}
/**
* Calculates values of the real spherical harmonic S_lm at the point k/|k| of
* the unit sphere for all m = -l, ..., +l
*/
void libgrpp_evaluate_real_spherical_harmonics_array(const int l,
const double *k,
double *rsh_array) {
double unitary_kx;
double unitary_ky;
double unitary_kz;
double kx_powers[200];
double ky_powers[200];
double kz_powers[200];
rsh_coef_table_t *rsh_coef_l = libgrpp_get_real_spherical_harmonic_table(l);
double length_k = sqrt(k[0] * k[0] + k[1] * k[1] + k[2] * k[2]);
if (length_k > LIBGRPP_ZERO_THRESH) {
double inv_length = 1.0 / length_k;
unitary_kx = k[0] * inv_length;
unitary_ky = k[1] * inv_length;
unitary_kz = k[2] * inv_length;
} else {
unitary_kx = 0.0;
unitary_ky = 0.0;
unitary_kz = 0.0;
}
kx_powers[0] = 1.0;
ky_powers[0] = 1.0;
kz_powers[0] = 1.0;
for (int i = 1; i <= l; i++) {
kx_powers[i] = kx_powers[i - 1] * unitary_kx;
ky_powers[i] = ky_powers[i - 1] * unitary_ky;
kz_powers[i] = kz_powers[i - 1] * unitary_kz;
}
memset(rsh_array, 0, (2 * l + 1) * sizeof(double));
int ncart = rsh_coef_l->n_cart_comb;
int *rst_array = rsh_coef_l->cartesian_comb;
for (int icomb = 0; icomb < ncart; icomb++) {
int r = rst_array[3 * icomb];
int s = rst_array[3 * icomb + 1];
int t = rst_array[3 * icomb + 2];
double k_xyz = kx_powers[r] * ky_powers[s] * kz_powers[t];
for (int m = -l; m <= l; m++) {
double y_lm_rst = rsh_coef_l->coeffs[(m + l) * ncart + icomb];
rsh_array[m + l] += y_lm_rst * k_xyz;
}
}
}
static int *generate_cartesian_combinations(int L, int *num) {
*num = (L + 1) * (L + 2) / 2;
int *combinations = (int *)calloc(*num, 3 * sizeof(int));
int n = 0;
for (int i = 0; i <= L; i++) {
for (int j = 0; j <= L; j++) {
for (int k = 0; k <= L; k++) {
if (i + j + k == L) {
combinations[3 * n + 0] = i;
combinations[3 * n + 1] = j;
combinations[3 * n + 2] = k;
n++;
}
}
}
}
return combinations;
}

View file

@ -0,0 +1,39 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_SPHERICAL_HARMONICS_H
#define LIBGRPP_SPHERICAL_HARMONICS_H
/*
* Tables with pretabulated expansion coefficients
*/
typedef struct {
int L;
int n_cart_comb;
int *cartesian_comb;
double *coeffs;
} rsh_coef_table_t;
double libgrpp_spherical_to_cartesian_coef(int l, int m, int lx, int ly);
double libgrpp_evaluate_real_spherical_harmonic(int l, int m, const double *k);
void libgrpp_evaluate_real_spherical_harmonics_array(int l, const double *k,
double *rsh_array);
void libgrpp_create_real_spherical_harmonic_coeffs_tables(int Lmax);
rsh_coef_table_t *libgrpp_get_real_spherical_harmonic_table(int L);
#endif // LIBGRPP_SPHERICAL_HARMONICS_H

View file

@ -0,0 +1,302 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_angular_integrals.h"
#include "grpp_binomial.h"
#include "grpp_lmatrix.h"
#include "grpp_radial_type2_integral.h"
#include "grpp_spherical_harmonics.h"
#include "grpp_utils.h"
#include "libgrpp.h"
#define LMAX (2 * LIBGRPP_MAX_BASIS_L + LIBGRPP_MAX_RPP_L)
static void type3_angular_sum(int L, double *Lx_matrix, double *Ly_matrix,
double *Lz_matrix, int lambda_1, int a, int b,
int c, double *rsh_values_kA, int lambda_2, int d,
int e, int f, double *rsh_values_kB,
double *sum_angular_x, double *sum_angular_y,
double *sum_angular_z);
/**
* Evaluation of spin-orbit ("type 3") RPP integrals.
*
* The theoretical outline is given in the paper:
* R. M. Pitzer, N. W. Winter. Spin-orbit (core) and core potential integrals.
* Int. J. Quantum Chem. 40(6), 773 (1991). doi: 10.1002/qua.560400606
* However, the formula on page 776 of Pitzer & Winter is not reproduced in the
* code exactly.
*/
void libgrpp_spin_orbit_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B, double *rpp_origin,
libgrpp_potential_t *potential,
double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix) {
assert(libgrpp_is_initialized());
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
memset(so_x_matrix, 0, size_A * size_B * sizeof(double));
memset(so_y_matrix, 0, size_A * size_B * sizeof(double));
memset(so_z_matrix, 0, size_A * size_B * sizeof(double));
int L = potential->L;
int L_A =
shell_A->cart_list[0] + shell_A->cart_list[1] + shell_A->cart_list[2];
int L_B =
shell_B->cart_list[0] + shell_B->cart_list[1] + shell_B->cart_list[2];
double *A = shell_A->origin;
double *B = shell_B->origin;
double *C = rpp_origin;
double CA_x = C[0] - A[0];
double CA_y = C[1] - A[1];
double CA_z = C[2] - A[2];
double CB_x = C[0] - B[0];
double CB_y = C[1] - B[1];
double CB_z = C[2] - B[2];
double CA_2 = CA_x * CA_x + CA_y * CA_y + CA_z * CA_z;
double CB_2 = CB_x * CB_x + CB_y * CB_y + CB_z * CB_z;
double alpha_A = shell_A->alpha[0];
double alpha_B = shell_B->alpha[0];
double kA_x = -2.0 * (alpha_A * CA_x);
double kA_y = -2.0 * (alpha_A * CA_y);
double kA_z = -2.0 * (alpha_A * CA_z);
double kB_x = -2.0 * (alpha_B * CB_x);
double kB_y = -2.0 * (alpha_B * CB_y);
double kB_z = -2.0 * (alpha_B * CB_z);
double kA_vec[3];
kA_vec[0] = kA_x;
kA_vec[1] = kA_y;
kA_vec[2] = kA_z;
double kB_vec[3];
kB_vec[0] = kB_x;
kB_vec[1] = kB_y;
kB_vec[2] = kB_z;
int lambda1_max = L + L_A;
int lambda2_max = L + L_B;
int N_max = L_A + L_B; // + n_RPP;
/*
* pre-compute matrices of the Lx, Ly, Lz operators
*/
double *Lx_matrix = calloc((2 * L + 1) * (2 * L + 1), sizeof(double));
double *Ly_matrix = calloc((2 * L + 1) * (2 * L + 1), sizeof(double));
double *Lz_matrix = calloc((2 * L + 1) * (2 * L + 1), sizeof(double));
libgrpp_construct_angular_momentum_matrices_rsh(L, Lx_matrix, Ly_matrix,
Lz_matrix);
/*
* for further evaluation of angular integrals
*/
int lmax = int_max3(lambda1_max, lambda2_max, L);
// create_real_spherical_harmonic_coeffs_tables(lmax);
/*
* pre-calculate values of real spherical harmonics for different L
*/
double rsh_values_kA[LMAX][2 * LMAX + 1];
double rsh_values_kB[LMAX][2 * LMAX + 1];
for (int lambda = 0; lambda <= lmax; lambda++) {
libgrpp_evaluate_real_spherical_harmonics_array(lambda, kA_vec,
rsh_values_kA[lambda]);
libgrpp_evaluate_real_spherical_harmonics_array(lambda, kB_vec,
rsh_values_kB[lambda]);
}
/*
* pre-compute radial integrals
*/
radial_type2_table_t *radial_table = libgrpp_tabulate_radial_type2_integrals(
lambda1_max, lambda2_max, N_max, CA_2, CB_2, potential, shell_A, shell_B);
/*
* loop over shell pairs
*/
for (int icart = 0; icart < size_A; icart++) {
for (int jcart = 0; jcart < size_B; jcart++) {
double SO_x = 0.0;
double SO_y = 0.0;
double SO_z = 0.0;
int n_A = shell_A->cart_list[3 * icart + 0];
int l_A = shell_A->cart_list[3 * icart + 1];
int m_A = shell_A->cart_list[3 * icart + 2];
int n_B = shell_B->cart_list[3 * jcart + 0];
int l_B = shell_B->cart_list[3 * jcart + 1];
int m_B = shell_B->cart_list[3 * jcart + 2];
for (int a = 0; a <= n_A; a++) {
double C_nA_a = libgrpp_binomial(n_A, a);
double pow_CA_x = pow(CA_x, n_A - a);
for (int b = 0; b <= l_A; b++) {
double C_lA_b = libgrpp_binomial(l_A, b);
double pow_CA_y = pow(CA_y, l_A - b);
for (int c = 0; c <= m_A; c++) {
double C_mA_c = libgrpp_binomial(m_A, c);
double pow_CA_z = pow(CA_z, m_A - c);
for (int d = 0; d <= n_B; d++) {
double C_nB_d = libgrpp_binomial(n_B, d);
double pow_CB_x = pow(CB_x, n_B - d);
for (int e = 0; e <= l_B; e++) {
double C_lB_e = libgrpp_binomial(l_B, e);
double pow_CB_y = pow(CB_y, l_B - e);
for (int f = 0; f <= m_B; f++) {
double C_mB_f = libgrpp_binomial(m_B, f);
double pow_CB_z = pow(CB_z, m_B - f);
int N = a + b + c + d + e + f;
double factor = C_nA_a * C_lA_b * C_mA_c * C_nB_d * C_lB_e *
C_mB_f * pow_CA_x * pow_CA_y * pow_CA_z *
pow_CB_x * pow_CB_y * pow_CB_z;
if (fabs(factor) < LIBGRPP_ZERO_THRESH) {
continue;
}
/*
* contraction of radial integrals with angular integrals
*/
double sum_omega_Q_x = 0.0;
double sum_omega_Q_y = 0.0;
double sum_omega_Q_z = 0.0;
int lambda1_lower = int_max2(L - a - b - c, 0);
int lambda2_lower = int_max2(L - d - e - f, 0);
int lambda1_upper = L + a + b + c;
int lambda2_upper = L + d + e + f;
for (int lambda_1 = lambda1_lower; lambda_1 <= lambda1_upper;
lambda_1++) {
if ((L + a + b + c - lambda_1) % 2 != 0) {
continue;
}
for (int lambda_2 = lambda2_lower;
lambda_2 <= lambda2_upper; lambda_2++) {
if ((L + d + e + f - lambda_2) % 2 != 0) {
continue;
}
double QN = libgrpp_get_radial_type2_integral(
radial_table, lambda_1, lambda_2, N);
if (fabs(QN) < LIBGRPP_ZERO_THRESH) {
continue;
}
double sum_angular_x, sum_angular_y, sum_angular_z;
type3_angular_sum(
L, Lx_matrix, Ly_matrix, Lz_matrix, lambda_1, a, b, c,
rsh_values_kA[lambda_1], lambda_2, d, e, f,
rsh_values_kB[lambda_2], &sum_angular_x,
&sum_angular_y, &sum_angular_z);
sum_omega_Q_x += QN * sum_angular_x;
sum_omega_Q_y += QN * sum_angular_y;
sum_omega_Q_z += QN * sum_angular_z;
}
}
SO_x += factor * sum_omega_Q_x;
SO_y += factor * sum_omega_Q_y;
SO_z += factor * sum_omega_Q_z;
}
}
}
}
}
}
so_x_matrix[icart * size_B + jcart] = SO_x * (16.0 * M_PI * M_PI);
so_y_matrix[icart * size_B + jcart] = SO_y * (16.0 * M_PI * M_PI);
so_z_matrix[icart * size_B + jcart] = SO_z * (16.0 * M_PI * M_PI);
}
}
libgrpp_delete_radial_type2_integrals(radial_table);
free(Lx_matrix);
free(Ly_matrix);
free(Lz_matrix);
}
/*
* Double sum of products of type 2 angular integrals
* (Pitzer, Winter, 1991, formula on the top of the page 776)
*/
static void type3_angular_sum(int L, double *Lx_matrix, double *Ly_matrix,
double *Lz_matrix, int lambda_1, int a, int b,
int c, double *rsh_values_kA, int lambda_2, int d,
int e, int f, double *rsh_values_kB,
double *sum_angular_x, double *sum_angular_y,
double *sum_angular_z) {
*sum_angular_x = 0.0;
*sum_angular_y = 0.0;
*sum_angular_z = 0.0;
/*
* contract tensors with angular integrals
*/
for (int m1 = -L; m1 <= L; m1++) {
for (int m2 = -L; m2 <= L; m2++) {
double lx = Lx_matrix[(2 * L + 1) * (m1 + L) + (m2 + L)];
double ly = Ly_matrix[(2 * L + 1) * (m1 + L) + (m2 + L)];
double lz = Lz_matrix[(2 * L + 1) * (m1 + L) + (m2 + L)];
if (fabs(lx) < LIBGRPP_ZERO_THRESH && fabs(ly) < LIBGRPP_ZERO_THRESH &&
fabs(lz) < LIBGRPP_ZERO_THRESH) {
continue;
}
double omega_1 = libgrpp_angular_type2_integral(lambda_1, L, m1, a, b, c,
rsh_values_kA);
if (fabs(omega_1) < LIBGRPP_ZERO_THRESH) {
continue;
}
double omega_2 = libgrpp_angular_type2_integral(lambda_2, L, m2, d, e, f,
rsh_values_kB);
*sum_angular_x += omega_1 * omega_2 * lx;
*sum_angular_y += omega_1 * omega_2 * ly;
*sum_angular_z += omega_1 * omega_2 * lz;
}
}
}

View file

@ -0,0 +1,261 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_angular_integrals.h"
#include "grpp_binomial.h"
#include "grpp_norm_gaussian.h"
#include "grpp_radial_type1_integral.h"
#include "grpp_type1_mcmurchie_davidson.h"
#include "grpp_utils.h"
#include "libgrpp.h"
#include "libgrpp_types.h"
/* for the old (numerical) version:
void evaluate_type1_integral_primitive_gaussians(double *A, int n_cart_A, int
*cart_list_A, double alpha_A, double *B, int n_cart_B, int *cart_list_B, double
alpha_B, double *C, libgrpp_potential_t *potential, double *matrix);
*/
extern void libgrpp_delete_radial_type1_integrals(radial_type1_table_t *table);
void libgrpp_evaluate_radially_local_potential_integral_primitive_gaussians(
double *A, int n_cart_A, int *cart_list_A, double alpha_A, double *B,
int n_cart_B, int *cart_list_B, double alpha_B, double *C,
double (*potential)(double r, void *params), void *potential_params,
double *matrix);
static double evaluate_pseudopotential(double r, void *params);
/**
* Evaluation of type 1 RPP integrals (scalar-relativistic radially local RPP).
*/
void libgrpp_type1_integrals(libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
double *rpp_origin, libgrpp_potential_t *potential,
double *matrix) {
assert(libgrpp_is_initialized());
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
memset(matrix, 0, size_A * size_B * sizeof(double));
if (potential == NULL) {
return;
}
/*
* RPP terms with n = 1, 2 are evaluated in a completely analytic manner
* using the Obara-Saika-like recurrence relations
*/
double *buf = calloc(size_A * size_B, sizeof(double));
for (int k = 0; k < potential->num_primitives; k++) {
double pot_coef = potential->coeffs[k];
double pot_alpha = potential->alpha[k];
int pot_n = potential->powers[k];
libgrpp_type1_integrals_mcmurchie_davidson_1978(
shell_A, shell_B, rpp_origin, pot_alpha, pot_n, buf);
libgrpp_daxpy(size_A * size_B, pot_coef, buf, matrix);
}
free(buf);
/*
* old (numerical) version
*/
/*for (int i = 0; i < shell_A->num_primitives; i++) {
for (int j = 0; j < shell_B->num_primitives; j++) {
double coef_A_i = shell_A->coeffs[i];
double coef_B_j = shell_B->coeffs[j];
if (fabs(coef_A_i * coef_B_j) < 1e-15) {
continue;
}
evaluate_type1_integral_primitive_gaussians(
shell_A->origin, size_A, shell_A->cart_list,
shell_A->alpha[i], shell_B->origin, size_B, shell_B->cart_list,
shell_B->alpha[j], rpp_origin, potential, buf
);
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf, matrix);
}
}*/
}
/**
* Evaluation of type 1 RPP integrals (scalar-relativistic radially local RPP)
* for the pair of shells constructed from primitive Gaussians.
*/
void evaluate_type1_integral_primitive_gaussians(
double *A, int n_cart_A, int *cart_list_A, double alpha_A, double *B,
int n_cart_B, int *cart_list_B, double alpha_B, double *C,
libgrpp_potential_t *potential, double *matrix) {
libgrpp_potential_t *potential_shrinked = libgrpp_shrink_potential(potential);
libgrpp_evaluate_radially_local_potential_integral_primitive_gaussians(
A, n_cart_A, cart_list_A, alpha_A, B, n_cart_B, cart_list_B, alpha_B, C,
evaluate_pseudopotential, potential_shrinked, matrix);
libgrpp_delete_potential(potential_shrinked);
}
static double evaluate_pseudopotential(double r, void *params) {
libgrpp_potential_t *potential = (libgrpp_potential_t *)params;
double u = libgrpp_potential_value(potential, r);
return u;
}
/**
* Evaluation of AO integrals for an arbitrary radially-local operator
* for the pair of shells constructed from primitive Gaussians.
*/
void libgrpp_evaluate_radially_local_potential_integral_primitive_gaussians(
double *A, int n_cart_A, int *cart_list_A, double alpha_A, double *B,
int n_cart_B, int *cart_list_B, double alpha_B, double *C,
double (*potential)(double r, void *params), void *potential_params,
double *matrix) {
assert(n_cart_A > 0);
assert(n_cart_B > 0);
memset(matrix, 0, n_cart_A * n_cart_B * sizeof(double));
double CA_x = C[0] - A[0];
double CA_y = C[1] - A[1];
double CA_z = C[2] - A[2];
double CB_x = C[0] - B[0];
double CB_y = C[1] - B[1];
double CB_z = C[2] - B[2];
double CA_2 = CA_x * CA_x + CA_y * CA_y + CA_z * CA_z;
double CB_2 = CB_x * CB_x + CB_y * CB_y + CB_z * CB_z;
double kx = -2.0 * (alpha_A * CA_x + alpha_B * CB_x);
double ky = -2.0 * (alpha_A * CA_y + alpha_B * CB_y);
double kz = -2.0 * (alpha_A * CA_z + alpha_B * CB_z);
double k = sqrt(kx * kx + ky * ky + kz * kz);
double kvec[3];
kvec[0] = kx;
kvec[1] = ky;
kvec[2] = kz;
int L_A = cart_list_A[0] + cart_list_A[1] + cart_list_A[2];
int L_B = cart_list_B[0] + cart_list_B[1] + cart_list_B[2];
double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
double D_ABC = 4 * M_PI * N_A * N_B;
int lambda_max = L_A + L_B;
int n_max = lambda_max;
// create_real_spherical_harmonic_coeffs_tables(lambda_max);
/*
* pre-compute type 1 radial integrals
*/
radial_type1_table_t *radial_table = libgrpp_tabulate_radial_type1_integrals(
lambda_max, n_max, CA_2, CB_2, alpha_A, alpha_B, k, D_ABC, potential,
potential_params);
/*
* main loop
* over shell pairs
*/
for (int icart = 0; icart < n_cart_A; icart++) {
for (int jcart = 0; jcart < n_cart_B; jcart++) {
double chi_AB = 0.0;
int n_A = cart_list_A[3 * icart + 0];
int l_A = cart_list_A[3 * icart + 1];
int m_A = cart_list_A[3 * icart + 2];
int n_B = cart_list_B[3 * jcart + 0];
int l_B = cart_list_B[3 * jcart + 1];
int m_B = cart_list_B[3 * jcart + 2];
for (int a = 0; a <= n_A; a++) {
double C_nA_a = libgrpp_binomial(n_A, a);
double pow_CA_x = pow(CA_x, n_A - a);
for (int b = 0; b <= l_A; b++) {
double C_lA_b = libgrpp_binomial(l_A, b);
double pow_CA_y = pow(CA_y, l_A - b);
for (int c = 0; c <= m_A; c++) {
double C_mA_c = libgrpp_binomial(m_A, c);
double pow_CA_z = pow(CA_z, m_A - c);
for (int d = 0; d <= n_B; d++) {
double C_nB_d = libgrpp_binomial(n_B, d);
double pow_CB_x = pow(CB_x, n_B - d);
for (int e = 0; e <= l_B; e++) {
double C_lB_e = libgrpp_binomial(l_B, e);
double pow_CB_y = pow(CB_y, l_B - e);
for (int f = 0; f <= m_B; f++) {
double C_mB_f = libgrpp_binomial(m_B, f);
double pow_CB_z = pow(CB_z, m_B - f);
double factor = C_nA_a * C_lA_b * C_mA_c * C_nB_d * C_lB_e *
C_mB_f * pow_CA_x * pow_CA_y * pow_CA_z *
pow_CB_x * pow_CB_y * pow_CB_z;
if (fabs(factor) < 1e-13) {
continue;
}
int N = a + b + c + d + e + f;
double sum_omega_Q = 0.0;
for (int lambda = 0; lambda <= lambda_max; lambda++) {
double Q = libgrpp_get_radial_type1_integral(radial_table,
lambda, N);
if (fabs(Q) < 1e-16) {
continue;
}
double omega = libgrpp_angular_type1_integral(
lambda, a + d, b + e, c + f, kvec);
sum_omega_Q += omega * Q;
}
chi_AB += factor * sum_omega_Q;
}
}
}
}
}
}
matrix[icart * n_cart_B + jcart] = chi_AB;
}
}
libgrpp_delete_radial_type1_integrals(radial_table);
}

View file

@ -0,0 +1,577 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
/*
* Implementation of the McMurchie-Davidson (MMD) recurrence relations
* for radially local RPP integrals.
*
* Operators to be integrated are:
* n = 2: e^{-ar^2}
* n = 1: e^{-ar^2}/r^1
* n = 0: e^{-ar^2}/r^2
*
* General description of the MMD scheme is given in:
* (1) T. Helgaker, P. Jorgensen, J. Olsen, Molecular Electronic-Structure
* Theory, John Wiley & Sons Ltd, 2000. (2) L. E. McMurchie, E. R. Davidson,
* One- and two-electron integrals over cartesian gaussian functions.
* J. Comput. Phys. 26(2), 218 (1978).
* doi: 10.1016/0021-9991(78)90092-X
*
* More on the evaluation of integrals over the 1/r^2 operator:
* (1) J. 0. Jensen, A. H. Cameri, C. P. Vlahacos, D. Zeroka, H. F. Hameka, C.
* N. Merrow, Evaluation of one-electron integrals for arbitrary operators V(r)
* over cartesian Gaussians: Application to inverse-square distance and Yukawa
* operators. J. Comput. Chem. 14(8), 986 (1993). doi: 10.1002/jcc.540140814 (2)
* B. Gao, A. J. Thorvaldsen, K. Ruud, GEN1INT: A unified procedure for the
* evaluation of one-electron integrals over Gaussian basis functions and their
* geometric derivatives. Int. J. Quantum Chem. 111(4), 858 (2011).
* doi: 10.1002/qua.22886
*/
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "grpp_type1_mcmurchie_davidson.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_norm_gaussian.h"
#include "grpp_specfunc.h"
#include "grpp_utils.h"
#include "libgrpp.h"
#define LMAX LIBGRPP_MAX_BASIS_L
/*
* temporary data for the McMurchie-Davidson algorithm
*/
struct mmd_data {
double A[3];
double B[3];
double C[3];
double Q[3];
double q;
double K_abc[3];
double R_QC_2;
double boys[100];
double E[3][LMAX][LMAX][2 * LMAX];
double R[2 * LMAX][2 * LMAX][2 * LMAX][2 * LMAX];
};
/*
* functions used below in the file
*/
static void evaluate_rpp_type1_mmd_n2_primitive_shell_pair(
libgrpp_shell_t *shell_A, double alpha_A, libgrpp_shell_t *shell_B,
double alpha_B, double *rpp_origin, double rpp_alpha, double *rpp_matrix);
void libgrpp_evaluate_rpp_type1_mmd_n1_primitive_shell_pair(
libgrpp_shell_t *shell_A, double alpha_A, libgrpp_shell_t *shell_B,
double alpha_B, double *rpp_origin, double rpp_alpha, double *rpp_matrix);
static void evaluate_rpp_type1_mmd_n0_primitive_shell_pair(
libgrpp_shell_t *shell_A, double alpha_A, libgrpp_shell_t *shell_B,
double alpha_B, double *rpp_origin, double rpp_alpha, double *rpp_matrix);
static void setup_E_array(struct mmd_data *data, int L_A, int L_B);
static void setup_R_array(struct mmd_data *data, int L_A, int L_B);
static void setup_G_array(struct mmd_data *data, int L_A, int L_B);
/**
* General interface for the McMurchie-Davidson algorithm for integrals
* over the radially local RPP operator.
*/
void libgrpp_type1_integrals_mcmurchie_davidson_1978(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *origin_C,
double alpha_C, int ecp_power, double *rpp_matrix) {
assert((ecp_power == 0) || (ecp_power == 1) || (ecp_power == 2));
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double *buf = calloc(size_A * size_B, sizeof(double));
memset(rpp_matrix, 0, size_A * size_B * sizeof(double));
/*
* loop over primitives in contractions
*/
for (int i = 0; i < shell_A->num_primitives; i++) {
double coef_A_i = shell_A->coeffs[i];
if (fabs(coef_A_i) < LIBGRPP_ZERO_THRESH) {
continue;
}
for (int j = 0; j < shell_B->num_primitives; j++) {
double coef_B_j = shell_B->coeffs[j];
if (fabs(coef_B_j) < LIBGRPP_ZERO_THRESH) {
continue;
}
if (ecp_power == 2) {
evaluate_rpp_type1_mmd_n2_primitive_shell_pair(
shell_A, shell_A->alpha[i], shell_B, shell_B->alpha[j], origin_C,
alpha_C, buf);
} else if (ecp_power == 1) {
libgrpp_evaluate_rpp_type1_mmd_n1_primitive_shell_pair(
shell_A, shell_A->alpha[i], shell_B, shell_B->alpha[j], origin_C,
alpha_C, buf);
} else if (ecp_power == 0) {
evaluate_rpp_type1_mmd_n0_primitive_shell_pair(
shell_A, shell_A->alpha[i], shell_B, shell_B->alpha[j], origin_C,
alpha_C, buf);
}
libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf, rpp_matrix);
}
}
free(buf);
}
/**
* Integrals of the operator: e^{-ar^2}/r^2
*/
static void evaluate_rpp_type1_mmd_n0_primitive_shell_pair(
libgrpp_shell_t *shell_A, double alpha_A, libgrpp_shell_t *shell_B,
double alpha_B, double *rpp_origin, double rpp_alpha, double *rpp_matrix) {
double a = alpha_A;
double b = alpha_B;
double c = rpp_alpha;
double *A = shell_A->origin;
double *B = shell_B->origin;
double *C = rpp_origin;
double q = a + b + c;
double mu_AB = a * b / q;
double mu_AC = a * c / q;
double mu_BC = b * c / q;
struct mmd_data data;
for (int i = 0; i < 3; i++) {
data.A[i] = A[i];
data.B[i] = B[i];
data.C[i] = C[i];
data.Q[i] = (a * A[i] + b * B[i] + c * C[i]) / q;
double X_AB = A[i] - B[i];
double X_AC = A[i] - C[i];
double X_BC = B[i] - C[i];
double K_ab = exp(-mu_AB * X_AB * X_AB);
double K_ac = exp(-mu_AC * X_AC * X_AC);
double K_bc = exp(-mu_BC * X_BC * X_BC);
data.K_abc[i] = K_ab * K_ac * K_bc;
}
data.q = q;
data.R_QC_2 = distance_squared(data.Q, data.C);
int L_A = shell_A->L;
int L_B = shell_B->L;
int Nmax = L_A + L_B;
libgrpp_gfun_values(q * data.R_QC_2, Nmax, data.boys);
/*
* setup E array
*/
setup_E_array(&data, L_A, L_B);
/*
* setup R array
*/
setup_G_array(&data, L_A, L_B);
/*
* loop over cartesian functions inside the shells
*/
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
for (int m = 0; m < size_A; m++) {
for (int n = 0; n < size_B; n++) {
int n_A = shell_A->cart_list[3 * m + 0];
int l_A = shell_A->cart_list[3 * m + 1];
int m_A = shell_A->cart_list[3 * m + 2];
int n_B = shell_B->cart_list[3 * n + 0];
int l_B = shell_B->cart_list[3 * n + 1];
int m_B = shell_B->cart_list[3 * n + 2];
double s = 0.0;
for (int t = 0; t <= n_A + n_B; t++) {
double Ex = data.E[0][n_A][n_B][t];
for (int u = 0; u <= l_A + l_B; u++) {
double Ey = data.E[1][l_A][l_B][u];
for (int v = 0; v <= m_A + m_B; v++) {
double Ez = data.E[2][m_A][m_B][v];
double R_tuv = data.R[t][u][v][0];
s += Ex * Ey * Ez * R_tuv;
}
}
}
s *= N_A * N_B * 2.0 * pow(M_PI, 1.5) / sqrt(q);
rpp_matrix[m * size_B + n] = s;
}
}
}
/**
* Integrals of the operator: e^{-ar^2}/r
*/
void libgrpp_evaluate_rpp_type1_mmd_n1_primitive_shell_pair(
libgrpp_shell_t *shell_A, double alpha_A, libgrpp_shell_t *shell_B,
double alpha_B, double *rpp_origin, double rpp_alpha, double *rpp_matrix) {
double a = alpha_A;
double b = alpha_B;
double c = rpp_alpha;
double *A = shell_A->origin;
double *B = shell_B->origin;
double *C = rpp_origin;
double q = a + b + c;
double mu_AB = a * b / q;
double mu_AC = a * c / q;
double mu_BC = b * c / q;
struct mmd_data data;
for (int i = 0; i < 3; i++) {
data.A[i] = A[i];
data.B[i] = B[i];
data.C[i] = C[i];
data.Q[i] = (a * A[i] + b * B[i] + c * C[i]) / q;
double X_AB = A[i] - B[i];
double X_AC = A[i] - C[i];
double X_BC = B[i] - C[i];
double K_ab = exp(-mu_AB * X_AB * X_AB);
double K_ac = exp(-mu_AC * X_AC * X_AC);
double K_bc = exp(-mu_BC * X_BC * X_BC);
data.K_abc[i] = K_ab * K_ac * K_bc;
}
data.q = q;
data.R_QC_2 = distance_squared(data.Q, data.C);
int L_A = shell_A->L;
int L_B = shell_B->L;
int Nmax = L_A + L_B;
libgrpp_boys_values(q * data.R_QC_2, Nmax, data.boys);
/*
* setup E array
*/
setup_E_array(&data, L_A, L_B);
/*
* setup R array
*/
setup_R_array(&data, L_A, L_B);
/*
* loop over cartesian functions inside the shells
*/
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
for (int m = 0; m < size_A; m++) {
for (int n = 0; n < size_B; n++) {
int n_A = shell_A->cart_list[3 * m + 0];
int l_A = shell_A->cart_list[3 * m + 1];
int m_A = shell_A->cart_list[3 * m + 2];
int n_B = shell_B->cart_list[3 * n + 0];
int l_B = shell_B->cart_list[3 * n + 1];
int m_B = shell_B->cart_list[3 * n + 2];
double s = 0.0;
for (int t = 0; t <= n_A + n_B; t++) {
double Ex = data.E[0][n_A][n_B][t];
for (int u = 0; u <= l_A + l_B; u++) {
double Ey = data.E[1][l_A][l_B][u];
for (int v = 0; v <= m_A + m_B; v++) {
double Ez = data.E[2][m_A][m_B][v];
double R_tuv = data.R[t][u][v][0];
s += Ex * Ey * Ez * R_tuv;
}
}
}
s *= N_A * N_B * 2.0 * M_PI / q;
rpp_matrix[m * size_B + n] = s;
}
}
}
/**
* Integrals of the operator: e^{-ar^2}
*/
static void evaluate_rpp_type1_mmd_n2_primitive_shell_pair(
libgrpp_shell_t *shell_A, double alpha_A, libgrpp_shell_t *shell_B,
double alpha_B, double *rpp_origin, double rpp_alpha, double *rpp_matrix) {
double a = alpha_A;
double b = alpha_B;
double c = rpp_alpha;
double *A = shell_A->origin;
double *B = shell_B->origin;
double *C = rpp_origin;
double q = a + b + c;
double mu_AB = a * b / q;
double mu_AC = a * c / q;
double mu_BC = b * c / q;
struct mmd_data data;
for (int i = 0; i < 3; i++) {
data.A[i] = A[i];
data.B[i] = B[i];
data.C[i] = C[i];
data.Q[i] = (a * A[i] + b * B[i] + c * C[i]) / q;
double X_AB = A[i] - B[i];
double X_AC = A[i] - C[i];
double X_BC = B[i] - C[i];
double K_ab = exp(-mu_AB * X_AB * X_AB);
double K_ac = exp(-mu_AC * X_AC * X_AC);
double K_bc = exp(-mu_BC * X_BC * X_BC);
data.K_abc[i] = K_ab * K_ac * K_bc;
}
data.q = q;
data.R_QC_2 = distance_squared(data.Q, data.C);
int L_A = shell_A->L;
int L_B = shell_B->L;
/*
* setup E array
*/
setup_E_array(&data, L_A, L_B);
/*
* loop over cartesian functions inside the shells
*/
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
for (int m = 0; m < size_A; m++) {
for (int n = 0; n < size_B; n++) {
int n_A = shell_A->cart_list[3 * m + 0];
int l_A = shell_A->cart_list[3 * m + 1];
int m_A = shell_A->cart_list[3 * m + 2];
int n_B = shell_B->cart_list[3 * n + 0];
int l_B = shell_B->cart_list[3 * n + 1];
int m_B = shell_B->cart_list[3 * n + 2];
double E_ij_0 = data.E[0][n_A][n_B][0];
double E_kl_0 = data.E[1][l_A][l_B][0];
double E_mn_0 = data.E[2][m_A][m_B][0];
double s = N_A * N_B * E_ij_0 * E_kl_0 * E_mn_0 * pow(M_PI / q, 1.5);
rpp_matrix[m * size_B + n] = s;
}
}
}
/**
* Calculation of the R_{tuv}^N auxiliary integrals for the 1/r operator
*/
static void setup_R_array(struct mmd_data *data, int L_A, int L_B) {
double q = data->q;
double X_QC = data->Q[0] - data->C[0];
double Y_QC = data->Q[1] - data->C[1];
double Z_QC = data->Q[2] - data->C[2];
int nmax = L_A + L_B;
int L_SUM = L_A + L_B;
for (int t = 0; t <= L_SUM; t++) {
for (int u = 0; u <= L_SUM; u++) {
for (int v = 0; v <= L_SUM; v++) {
if (t + u + v > L_SUM) {
continue;
}
for (int n = 0; n <= nmax - (t + u + v); n++) {
double val = 0.0;
if (t + u + v == 0) {
val = pow(-2.0 * q, n) * data->boys[n];
} else if (t + u == 0) {
if (v > 1) {
val += (v - 1) * data->R[t][u][v - 2][n + 1];
}
val += Z_QC * data->R[t][u][v - 1][n + 1];
} else if (t == 0) {
if (u > 1) {
val += (u - 1) * data->R[t][u - 2][v][n + 1];
}
val += Y_QC * data->R[t][u - 1][v][n + 1];
} else {
if (t > 1) {
val += (t - 1) * data->R[t - 2][u][v][n + 1];
}
val += X_QC * data->R[t - 1][u][v][n + 1];
}
data->R[t][u][v][n] = val;
}
}
}
}
}
/**
* Calculation of the R_{tuv}^N auxiliary integrals for the 1/r^2 operator
*/
static void setup_G_array(struct mmd_data *data, int L_A, int L_B) {
double q = data->q;
double X_QC = data->Q[0] - data->C[0];
double Y_QC = data->Q[1] - data->C[1];
double Z_QC = data->Q[2] - data->C[2];
int nmax = L_A + L_B;
int L_SUM = L_A + L_B;
for (int t = 0; t <= L_SUM; t++) {
for (int u = 0; u <= L_SUM; u++) {
for (int v = 0; v <= L_SUM; v++) {
if (t + u + v > L_SUM) {
continue;
}
for (int n = 0; n <= nmax - (t + u + v); n++) {
double val = 0.0;
if (t + u + v == 0) {
val = pow(2.0 * q, n) * data->boys[n];
} else if (t + u == 0) {
if (v > 1) {
val += (v - 1) * (data->R[t][u][v - 2][n + 1] -
2.0 * q * data->R[t][u][v - 2][n]);
}
val += Z_QC * (data->R[t][u][v - 1][n + 1] -
2.0 * q * data->R[t][u][v - 1][n]);
} else if (t == 0) {
if (u > 1) {
val += (u - 1) * (data->R[t][u - 2][v][n + 1] -
2.0 * q * data->R[t][u - 2][v][n]);
}
val += Y_QC * (data->R[t][u - 1][v][n + 1] -
2.0 * q * data->R[t][u - 1][v][n]);
} else {
if (t > 1) {
val += (t - 1) * (data->R[t - 2][u][v][n + 1] -
2.0 * q * data->R[t - 2][u][v][n]);
}
val += X_QC * (data->R[t - 1][u][v][n + 1] -
2.0 * q * data->R[t - 1][u][v][n]);
}
data->R[t][u][v][n] = val;
}
}
}
}
}
/**
* Calculates E^{ij}_t coefficients in the MMD scheme.
*/
static void setup_E_array(struct mmd_data *data, int L_A, int L_B) {
double q = data->q;
for (int coord = 0; coord < 3; coord++) {
for (int i = 0; i <= L_A; i++) {
for (int j = 0; j <= L_B; j++) {
for (int t = 0; t <= i + j; t++) {
if (t == 0) {
if (i == 0 && j == 0) {
data->E[coord][0][0][0] = data->K_abc[coord];
continue;
} else if (i == 1 && j == 0) {
double X_QA = data->Q[coord] - data->A[coord];
data->E[coord][1][0][0] = X_QA * data->E[coord][0][0][0];
continue;
} else if (i == 0 && j == 1) {
double X_QB = data->Q[coord] - data->B[coord];
data->E[coord][0][1][0] = X_QB * data->E[coord][0][0][0];
continue;
} else if (i == 0) {
double X_QB = data->Q[coord] - data->B[coord];
data->E[coord][i][j][0] = X_QB * data->E[coord][i][j - 1][0] +
data->E[coord][i][j - 1][1];
continue;
} else {
double X_QA = data->Q[coord] - data->A[coord];
data->E[coord][i][j][0] = X_QA * data->E[coord][i - 1][j][0] +
data->E[coord][i - 1][j][1];
continue;
}
} else {
double E_ijt = 0.0;
double factor = 1.0 / (2.0 * q * t);
if (i > 0) {
E_ijt += factor * i * data->E[coord][i - 1][j][t - 1];
}
if (j > 0) {
E_ijt += factor * j * data->E[coord][i][j - 1][t - 1];
}
data->E[coord][i][j][t] = E_ijt;
}
}
}
}
}
}

View file

@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef __TYPE1_MCMURCHIE_DAVIDSON_H__
#define __TYPE1_MCMURCHIE_DAVIDSON_H__
#include "libgrpp_types.h"
void libgrpp_type1_integrals_mcmurchie_davidson_1978(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *origin_C,
double alpha_C, int ecp_power, double *rpp_matrix);
#endif // TYPE1_MCMURCHIE_DAVIDSON_H

View file

@ -0,0 +1,245 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <assert.h>
#include <math.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "grpp_angular_integrals.h"
#include "grpp_binomial.h"
#include "grpp_radial_type2_integral.h"
#include "grpp_spherical_harmonics.h"
#include "grpp_utils.h"
#include "libgrpp.h"
#define LMAX (2 * LIBGRPP_MAX_BASIS_L + LIBGRPP_MAX_RPP_L)
static double type2_angular_sum(int L, int lambda_1, int a, int b, int c,
int lambda_2, int d, int e, int f,
double *rsh_values_kA, double *rsh_values_kB);
/**
* Evaluation of type 2 RPP integrals (scalar-relativistic semilocal RPP with
* L-projectors).
*/
void libgrpp_type2_integrals(libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
double *rpp_origin, libgrpp_potential_t *potential,
double *matrix) {
assert(libgrpp_is_initialized());
int size_A = libgrpp_get_shell_size(shell_A);
int size_B = libgrpp_get_shell_size(shell_B);
memset(matrix, 0, size_A * size_B * sizeof(double));
int L = potential->L;
int L_A =
shell_A->cart_list[0] + shell_A->cart_list[1] + shell_A->cart_list[2];
int L_B =
shell_B->cart_list[0] + shell_B->cart_list[1] + shell_B->cart_list[2];
double *A = shell_A->origin;
double *B = shell_B->origin;
double *C = rpp_origin;
double CA_x = C[0] - A[0];
double CA_y = C[1] - A[1];
double CA_z = C[2] - A[2];
double CB_x = C[0] - B[0];
double CB_y = C[1] - B[1];
double CB_z = C[2] - B[2];
double CA_2 = CA_x * CA_x + CA_y * CA_y + CA_z * CA_z;
double CB_2 = CB_x * CB_x + CB_y * CB_y + CB_z * CB_z;
double alpha_A = shell_A->alpha[0];
double alpha_B = shell_B->alpha[0];
double kA_x = -2.0 * (alpha_A * CA_x);
double kA_y = -2.0 * (alpha_A * CA_y);
double kA_z = -2.0 * (alpha_A * CA_z);
double kB_x = -2.0 * (alpha_B * CB_x);
double kB_y = -2.0 * (alpha_B * CB_y);
double kB_z = -2.0 * (alpha_B * CB_z);
double kA_vec[3];
kA_vec[0] = kA_x;
kA_vec[1] = kA_y;
kA_vec[2] = kA_z;
double kB_vec[3];
kB_vec[0] = kB_x;
kB_vec[1] = kB_y;
kB_vec[2] = kB_z;
int lambda1_max = L + L_A;
int lambda2_max = L + L_B;
int N_max = L_A + L_B;
/*
* for further evaluation of angular integrals
*/
int lmax = int_max3(lambda1_max, lambda2_max, L);
// create_real_spherical_harmonic_coeffs_tables(lmax);
/*
* pre-compute type 2 radial integrals
*/
radial_type2_table_t *radial_table = libgrpp_tabulate_radial_type2_integrals(
lambda1_max, lambda2_max, N_max, CA_2, CB_2, potential, shell_A, shell_B);
/*
* pre-calculate values of real spherical harmonics for different L
*/
double rsh_values_kA[3 * LMAX][6 * LMAX];
double rsh_values_kB[3 * LMAX][6 * LMAX];
for (int lambda = 0; lambda <= lmax; lambda++) {
libgrpp_evaluate_real_spherical_harmonics_array(lambda, kA_vec,
rsh_values_kA[lambda]);
libgrpp_evaluate_real_spherical_harmonics_array(lambda, kB_vec,
rsh_values_kB[lambda]);
}
/*
* main loop
* over shell pairs
*/
for (int icart = 0; icart < size_A; icart++) {
for (int jcart = 0; jcart < size_B; jcart++) {
double gamma_AB = 0.0;
int n_A = shell_A->cart_list[3 * icart + 0];
int l_A = shell_A->cart_list[3 * icart + 1];
int m_A = shell_A->cart_list[3 * icart + 2];
int n_B = shell_B->cart_list[3 * jcart + 0];
int l_B = shell_B->cart_list[3 * jcart + 1];
int m_B = shell_B->cart_list[3 * jcart + 2];
for (int a = 0; a <= n_A; a++) {
double C_nA_a = libgrpp_binomial(n_A, a);
double pow_CA_x = pow(CA_x, n_A - a);
for (int b = 0; b <= l_A; b++) {
double C_lA_b = libgrpp_binomial(l_A, b);
double pow_CA_y = pow(CA_y, l_A - b);
for (int c = 0; c <= m_A; c++) {
double C_mA_c = libgrpp_binomial(m_A, c);
double pow_CA_z = pow(CA_z, m_A - c);
for (int d = 0; d <= n_B; d++) {
double C_nB_d = libgrpp_binomial(n_B, d);
double pow_CB_x = pow(CB_x, n_B - d);
for (int e = 0; e <= l_B; e++) {
double C_lB_e = libgrpp_binomial(l_B, e);
double pow_CB_y = pow(CB_y, l_B - e);
for (int f = 0; f <= m_B; f++) {
double C_mB_f = libgrpp_binomial(m_B, f);
double pow_CB_z = pow(CB_z, m_B - f);
double factor = C_nA_a * C_lA_b * C_mA_c * C_nB_d * C_lB_e *
C_mB_f * pow_CA_x * pow_CA_y * pow_CA_z *
pow_CB_x * pow_CB_y * pow_CB_z;
if (fabs(factor) < 1e-13) {
continue;
}
int N = a + b + c + d + e + f;
double sum_omega_Q = 0.0;
int lambda1_lower = int_max2(L - a - b - c, 0);
int lambda2_lower = int_max2(L - d - e - f, 0);
int lambda1_upper = L + a + b + c;
int lambda2_upper = L + d + e + f;
for (int lambda_1 = lambda1_lower; lambda_1 <= lambda1_upper;
lambda_1++) {
if ((L + a + b + c - lambda_1) % 2 != 0) {
continue;
}
for (int lambda_2 = lambda2_lower;
lambda_2 <= lambda2_upper; lambda_2++) {
if ((L + d + e + f - lambda_2) % 2 != 0) {
continue;
}
double QN = libgrpp_get_radial_type2_integral(
radial_table, lambda_1, lambda_2, N);
if (fabs(QN) < 1e-16) {
continue;
}
double sum_angular = type2_angular_sum(
L, lambda_1, a, b, c, lambda_2, d, e, f,
rsh_values_kA[lambda_1], rsh_values_kB[lambda_2]);
sum_omega_Q += QN * sum_angular;
} // loop over lambda_2
} // loop over lambda_1
gamma_AB += factor * sum_omega_Q;
}
}
}
}
}
}
gamma_AB *= 16 * M_PI * M_PI;
matrix[icart * size_B + jcart] = gamma_AB;
}
}
libgrpp_delete_radial_type2_integrals(radial_table);
}
/*
* Sum of products of type 2 angular integrals
* (McMurchie, Davidson, 1981, formulas (23) and (24))
*/
static double type2_angular_sum(int L, int lambda_1, int a, int b, int c,
int lambda_2, int d, int e, int f,
double *rsh_values_kA, double *rsh_values_kB) {
double sum_angular = 0.0;
/*
* contract tensors with angular integrals
*/
for (int m = -L; m <= L; m++) {
double omega_1 =
libgrpp_angular_type2_integral(lambda_1, L, m, a, b, c, rsh_values_kA);
if (fabs(omega_1) < 1e-16) {
continue;
}
double omega_2 =
libgrpp_angular_type2_integral(lambda_2, L, m, d, e, f, rsh_values_kB);
sum_angular += omega_1 * omega_2;
}
return sum_angular;
}

89
src/grpp/grpp_utils.c Normal file
View file

@ -0,0 +1,89 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#include <math.h>
#include <stdlib.h>
#include "grpp_utils.h"
inline int int_max2(int x, int y) { return (x > y) ? x : y; }
inline int int_max3(int x, int y, int z) { return int_max2(int_max2(x, y), z); }
double *alloc_zeros_1d(int n) { return (double *)calloc(n, sizeof(double)); }
double **alloc_zeros_2d(int n, int m) {
double **array = (double **)calloc(n, sizeof(double *));
for (int i = 0; i < n; i++) {
array[i] = (double *)calloc(m, sizeof(double));
}
return array;
}
void free_2d(double **array, int n) {
for (int i = 0; i < n; i++) {
free(array[i]);
}
free(array);
}
/*
* constant times a vector plus a vector:
* y = a * x + y
*/
void libgrpp_daxpy(int n, double a, double *x, double *y) {
for (int i = 0; i < n; i++) {
y[i] += a * x[i];
}
}
/*
* naive matrix multiplication
*/
void libgrpp_multiply_matrices(int M, int N, int K, double *A, double *B,
double *C) {
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
double sum = 0.0;
for (int k = 0; k < K; k++) {
sum += A[i * K + k] * B[k * N + j];
}
C[i * N + j] += sum;
}
}
}
double distance_squared(double *A, double *B) {
double dx = A[0] - B[0];
double dy = A[1] - B[1];
double dz = A[2] - B[2];
return dx * dx + dy * dy + dz * dz;
}
double distance(double *A, double *B) { return sqrt(distance_squared(A, B)); }
/**
* Checks if two 3d points coincide with each other.
*/
int points_are_equal(double *a, double *b) {
double const thresh = 1e-12;
if (fabs(a[0] - b[0]) < thresh && fabs(a[1] - b[1]) < thresh &&
fabs(a[2] - b[2]) < thresh) {
return 1;
}
return 0;
}

39
src/grpp/grpp_utils.h Normal file
View file

@ -0,0 +1,39 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_UTILS_H
#define LIBGRPP_UTILS_H
int int_max2(int x, int y);
int int_max3(int x, int y, int z);
double *alloc_zeros_1d(int n);
double **alloc_zeros_2d(int n, int m);
void free_2d(double **array, int n);
void libgrpp_daxpy(int n, double a, double *x, double *y);
void libgrpp_multiply_matrices(int M, int N, int K, double *A, double *B,
double *C);
double distance_squared(double *A, double *B);
double distance(double *A, double *B);
int points_are_equal(double *a, double *b);
#endif // LIBGRPP_UTILS_H

327
src/grpp/libgrpp.F Normal file
View file

@ -0,0 +1,327 @@
!--------------------------------------------------------------------------------------------------!
! CP2K: A general program to perform molecular dynamics simulations !
! Copyright 2000-2025 CP2K developers group <https://cp2k.org> !
! !
! SPDX-License-Identifier: MIT !
!--------------------------------------------------------------------------------------------------!
!
! libgrpp - a library for the evaluation of integrals over
! generalized relativistic pseudopotentials.
!
! Copyright (C) 2021-2023 Alexander Oleynichenko
!
MODULE libgrpp
INTEGER(4), PARAMETER :: LIBGRPP_CART_ORDER_DIRAC = 0
INTEGER(4), PARAMETER :: LIBGRPP_CART_ORDER_TURBOMOLE = 1
INTEGER(4), PARAMETER :: LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE = 0
INTEGER(4), PARAMETER :: LIBGRPP_NUCLEAR_MODEL_CHARGED_BALL = 1
INTEGER(4), PARAMETER :: LIBGRPP_NUCLEAR_MODEL_GAUSSIAN = 2
INTEGER(4), PARAMETER :: LIBGRPP_NUCLEAR_MODEL_FERMI = 3
INTEGER(4), PARAMETER :: LIBGRPP_NUCLEAR_MODEL_FERMI_BUBBLE = 4
INTEGER(4), PARAMETER :: LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE_NUMERICAL = 5
INTERFACE
SUBROUTINE libgrpp_init()
! no arguments
END SUBROUTINE libgrpp_init
SUBROUTINE libgrpp_finalize()
! no arguments
END SUBROUTINE libgrpp_finalize
SUBROUTINE libgrpp_set_default_parameters()
! no arguments
END SUBROUTINE libgrpp_set_default_parameters
SUBROUTINE libgrpp_set_radial_tolerance(tolerance)
REAL(8), INTENT(in) :: tolerance
END SUBROUTINE libgrpp_set_radial_tolerance
SUBROUTINE libgrpp_set_angular_screening_tolerance(tolerance)
REAL(8), INTENT(in) :: tolerance
END SUBROUTINE libgrpp_set_angular_screening_tolerance
SUBROUTINE libgrpp_set_modified_bessel_tolerance(tolerance)
REAL(8), INTENT(in) :: tolerance
END SUBROUTINE libgrpp_set_modified_bessel_tolerance
SUBROUTINE libgrpp_set_cartesian_order(order)
INTEGER(4), INTENT(in) :: order
END SUBROUTINE libgrpp_set_cartesian_order
SUBROUTINE libgrpp_type1_integrals( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, rpp_num_primitives, rpp_powers, rpp_coeffs, rpp_alpha, &
matrix &
)
! shell centered on atom A
REAL(8), DIMENSION(*), INTENT(in) :: origin_A
INTEGER(4), INTENT(in) :: L_A, num_primitives_A
REAL(8), INTENT(in) :: coeffs_A(*), alpha_A(*)
REAL(8), DIMENSION(*), INTENT(in) :: origin_B
INTEGER(4), INTENT(in) :: L_B, num_primitives_B
REAL(8), INTENT(in) :: coeffs_B(*), alpha_B(*)
REAL(8), DIMENSION(*), INTENT(in) :: rpp_origin
INTEGER(4), DIMENSION(*), INTENT(in) :: rpp_num_primitives, rpp_powers
REAL(8), DIMENSION(*), INTENT(in) :: rpp_coeffs, rpp_alpha
REAL(8), DIMENSION(*), INTENT(out) :: matrix
! shell centered on atom B
! pseudopotential expansion
! output: matrix with PP integrals
END SUBROUTINE libgrpp_type1_integrals
SUBROUTINE libgrpp_type2_integrals( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, rpp_ang_momentum, rpp_num_primitives, rpp_powers, rpp_coeffs, rpp_alpha, &
matrix &
)
! shell centered on atom A
REAL(8), DIMENSION(*), INTENT(in) :: origin_A
INTEGER(4), INTENT(in) :: L_A, num_primitives_A
REAL(8), INTENT(in) :: coeffs_A(*), alpha_A(*)
REAL(8), DIMENSION(*), INTENT(in) :: origin_B
INTEGER(4), INTENT(in) :: L_B, num_primitives_B
REAL(8), INTENT(in) :: coeffs_B(*), alpha_B(*)
REAL(8), DIMENSION(*), INTENT(in) :: rpp_origin
INTEGER(4), INTENT(in) :: rpp_ang_momentum
INTEGER(4), DIMENSION(*), INTENT(in) :: rpp_num_primitives, rpp_powers
REAL(8), DIMENSION(*), INTENT(in) :: rpp_coeffs, rpp_alpha
REAL(8), DIMENSION(*), INTENT(out) :: matrix
! shell centered on atom B
! pseudopotential expansion
! output: matrix with PP integrals
END SUBROUTINE libgrpp_type2_integrals
SUBROUTINE libgrpp_spin_orbit_integrals( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, rpp_ang_momentum, rpp_num_primitives, rpp_powers, rpp_coeffs, rpp_alpha, &
so_x_matrix, so_y_matrix, so_z_matrix &
)
! shell centered on atom A
REAL(8), DIMENSION(*), INTENT(in) :: origin_A
INTEGER(4), INTENT(in) :: L_A, num_primitives_A
REAL(8), INTENT(in) :: coeffs_A(*), alpha_A(*)
REAL(8), DIMENSION(*), INTENT(in) :: origin_B
INTEGER(4), INTENT(in) :: L_B, num_primitives_B
REAL(8), INTENT(in) :: coeffs_B(*), alpha_B(*)
REAL(8), DIMENSION(*), INTENT(in) :: rpp_origin
INTEGER(4), INTENT(in) :: rpp_ang_momentum
INTEGER(4), DIMENSION(*), INTENT(in) :: rpp_num_primitives, rpp_powers
REAL(8), DIMENSION(*), INTENT(in) :: rpp_coeffs, rpp_alpha
REAL(8), DIMENSION(*), INTENT(out) :: so_x_matrix, so_y_matrix, so_z_matrix
! shell centered on atom B
! pseudopotential expansion
! output: matrices with PP integrals
END SUBROUTINE libgrpp_spin_orbit_integrals
SUBROUTINE libgrpp_type1_integrals_gradient( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, rpp_num_primitives, rpp_powers, rpp_coeffs, rpp_alpha, &
point_3d, grad_arep_x, grad_arep_y, grad_arep_z &
)
! shell centered on atom A
REAL(8), DIMENSION(*), INTENT(in) :: origin_A
INTEGER(4), INTENT(in) :: L_A, num_primitives_A
REAL(8), INTENT(in) :: coeffs_A(*), alpha_A(*)
REAL(8), DIMENSION(*), INTENT(in) :: origin_B
INTEGER(4), INTENT(in) :: L_B, num_primitives_B
REAL(8), INTENT(in) :: coeffs_B(*), alpha_B(*)
REAL(8), DIMENSION(*), INTENT(in) :: rpp_origin
INTEGER(4), DIMENSION(*), INTENT(in) :: rpp_num_primitives, rpp_powers
REAL(8), DIMENSION(*), INTENT(in) :: rpp_coeffs, rpp_alpha, point_3d
REAL(8), DIMENSION(*), INTENT(out) :: grad_arep_x, grad_arep_y, grad_arep_z
! shell centered on atom B
! pseudopotential expansion
! differentiation wrt the 3d point (x,y,z)
! output: matrices d<Int>/dx, d<Int>/dy, d<Int>/dZ
END SUBROUTINE libgrpp_type1_integrals_gradient
SUBROUTINE libgrpp_type2_integrals_gradient( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, rpp_ang_momentum, rpp_num_primitives, rpp_powers, rpp_coeffs, rpp_alpha, &
point_3d, grad_arep_x, grad_arep_y, grad_arep_z &
)
! shell centered on atom A
REAL(8), DIMENSION(*), INTENT(in) :: origin_A
INTEGER(4), INTENT(in) :: L_A, num_primitives_A
REAL(8), INTENT(in) :: coeffs_A(*), alpha_A(*)
REAL(8), DIMENSION(*), INTENT(in) :: origin_B
INTEGER(4), INTENT(in) :: L_B, num_primitives_B
REAL(8), INTENT(in) :: coeffs_B(*), alpha_B(*)
REAL(8), DIMENSION(*), INTENT(in) :: rpp_origin
INTEGER(4), INTENT(in) :: rpp_ang_momentum
INTEGER(4), DIMENSION(*), INTENT(in) :: rpp_num_primitives, rpp_powers
REAL(8), DIMENSION(*), INTENT(in) :: rpp_coeffs, rpp_alpha, point_3d
REAL(8), DIMENSION(*), INTENT(out) :: grad_arep_x, grad_arep_y, grad_arep_z
! shell centered on atom B
! pseudopotential expansion
! differentiation wrt the 3d point (x,y,z)
! output: matrices d<Int>/dx, d<Int>/dy, d<Int>/dZ
END SUBROUTINE libgrpp_type2_integrals_gradient
SUBROUTINE libgrpp_spin_orbit_integrals_gradient( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, rpp_ang_momentum, rpp_num_primitives, rpp_powers, rpp_coeffs, rpp_alpha, &
point_3d, grad_sox_x, grad_sox_y, grad_sox_z, &
grad_soy_x, grad_soy_y, grad_soy_z, &
grad_soz_x, grad_soz_y, grad_soz_z &
)
! shell centered on atom A
REAL(8), DIMENSION(*), INTENT(in) :: origin_A
INTEGER(4), INTENT(in) :: L_A, num_primitives_A
REAL(8), INTENT(in) :: coeffs_A(*), alpha_A(*)
REAL(8), DIMENSION(*), INTENT(in) :: origin_B
INTEGER(4), INTENT(in) :: L_B, num_primitives_B
REAL(8), INTENT(in) :: coeffs_B(*), alpha_B(*)
REAL(8), DIMENSION(*), INTENT(in) :: rpp_origin
INTEGER(4), INTENT(in) :: rpp_ang_momentum
INTEGER(4), DIMENSION(*), INTENT(in) :: rpp_num_primitives, rpp_powers
REAL(8), DIMENSION(*), INTENT(in) :: rpp_coeffs, rpp_alpha, point_3d
REAL(8), DIMENSION(*), INTENT(out) :: grad_sox_x, grad_sox_y, grad_sox_z, &
grad_soy_x, grad_soy_y, grad_soy_z, &
grad_soz_x, grad_soz_y, grad_soz_z
! shell centered on atom B
! pseudopotential expansion
! differentiation wrt the 3d point (x,y,z)
! output: matrices d<SO_x>/dx, d<SO_x>/dy, d<SO_x>/dZ
! output: matrices d<SO_y>/dx, d<SO_y>/dy, d<SO_y>/dZ
! output: matrices d<SO_z>/dx, d<SO_z>/dy, d<SO_z>/dZ
END SUBROUTINE libgrpp_spin_orbit_integrals_gradient
END INTERFACE
CONTAINS
! **************************************************************************************************
!> \brief ...
!> \param origin_A ...
!> \param L_A ...
!> \param num_primitives_A ...
!> \param coeffs_A ...
!> \param alpha_A ...
!> \param origin_B ...
!> \param L_B ...
!> \param num_primitives_B ...
!> \param coeffs_B ...
!> \param alpha_B ...
!> \param rpp_origin ...
!> \param num_oc_shells ...
!> \param oc_shells_L ...
!> \param oc_shells_J ...
!> \param rpp_num_primitives ...
!> \param rpp_powers ...
!> \param rpp_coeffs ...
!> \param rpp_alpha ...
!> \param oc_shells_num_primitives ...
!> \param oc_shells_coeffs ...
!> \param oc_shells_alpha ...
!> \param arep_matrix ...
!> \param so_x_matrix ...
!> \param so_y_matrix ...
!> \param so_z_matrix ...
! **************************************************************************************************
SUBROUTINE libgrpp_outercore_potential_integrals( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, num_oc_shells, &
oc_shells_L, oc_shells_J, rpp_num_primitives, rpp_powers, rpp_coeffs, rpp_alpha, &
oc_shells_num_primitives, oc_shells_coeffs, oc_shells_alpha, &
arep_matrix, so_x_matrix, so_y_matrix, so_z_matrix &
)
! shell centered on atom A
REAL(8), INTENT(in) :: origin_A(*)
INTEGER(4), INTENT(in) :: L_A, num_primitives_A
REAL(8), INTENT(in) :: coeffs_A(*), alpha_A(*), origin_B(*)
INTEGER(4), INTENT(in) :: L_B, num_primitives_B
REAL(8), INTENT(in) :: coeffs_B(*), alpha_B(*), rpp_origin(*)
INTEGER(4) :: num_oc_shells
INTEGER(4), INTENT(in) :: oc_shells_L(:), oc_shells_J(:), &
rpp_num_primitives(:), rpp_powers(:, :)
REAL(8), INTENT(in) :: rpp_coeffs(:, :), rpp_alpha(:, :)
INTEGER(4) :: oc_shells_num_primitives(:)
REAL(8) :: oc_shells_coeffs(:, :), &
oc_shells_alpha(:, :)
REAL(8), INTENT(out) :: arep_matrix(*), so_x_matrix(*), &
so_y_matrix(*), so_z_matrix(*)
INTEGER :: i, j, ncart1, ncart2
! shell centered on atom B
! pseudopotential expansion
! outercore shells
! output: matrices with PP integrals
! local variables
ncart1 = (L_A + 1)*(L_A + 2)/2
ncart2 = (L_B + 1)*(L_B + 2)/2
arep_matrix(1:ncart1*ncart2) = 0.0d0
so_x_matrix(1:ncart1*ncart2) = 0.0d0
so_y_matrix(1:ncart1*ncart2) = 0.0d0
so_z_matrix(1:ncart1*ncart2) = 0.0d0
! the first non-local term:
! \sum_{nlj} U*|nlj><nlj| + |nlj><nlj|*U
DO i = 1, num_oc_shells
CALL libgrpp_outercore_potential_integrals_part_1( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, oc_shells_L(i), oc_shells_J(i), &
rpp_num_primitives(i), rpp_powers(i, :), rpp_coeffs(i, :), rpp_alpha(i, :), &
oc_shells_num_primitives(i), oc_shells_coeffs(i, :), oc_shells_alpha(i, :), &
arep_matrix, so_x_matrix, so_y_matrix, so_z_matrix &
)
END DO
! the second non-local term:
! \sum_{nlj,n'lj} |nlj><nlj| U |n'lj><n'lj|
DO i = 1, num_oc_shells
DO j = 1, num_oc_shells
CALL libgrpp_outercore_potential_integrals_part_2( &
origin_A, L_A, num_primitives_A, coeffs_A, alpha_A, &
origin_B, L_B, num_primitives_B, coeffs_B, alpha_B, &
rpp_origin, &
oc_shells_L(i), oc_shells_J(i), &
rpp_num_primitives(i), rpp_powers(i, :), rpp_coeffs(i, :), rpp_alpha(i, :), &
oc_shells_num_primitives(i), oc_shells_coeffs(i, :), oc_shells_alpha(i, :), &
oc_shells_L(j), oc_shells_J(j), &
rpp_num_primitives(j), rpp_powers(j, :), rpp_coeffs(j, :), rpp_alpha(j, :), &
oc_shells_num_primitives(j), oc_shells_coeffs(j, :), oc_shells_alpha(j, :), &
arep_matrix, so_x_matrix, so_y_matrix, so_z_matrix &
)
END DO
END DO
END SUBROUTINE libgrpp_outercore_potential_integrals
END MODULE libgrpp

197
src/grpp/libgrpp.h Normal file
View file

@ -0,0 +1,197 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
/*
* libgrpp - a library for the evaluation of integrals over
* generalized relativistic pseudopotentials.
*
* Copyright (C) 2021-2023 Alexander Oleynichenko
*/
#ifndef LIBGRPP_LIBGRPP_H
#define LIBGRPP_LIBGRPP_H
#include "grpp_parameters.h"
#include "libgrpp_types.h"
/*
* other integrals
*/
#include "grpp_kinetic.h"
#include "grpp_momentum.h"
#include "grpp_overlap.h"
#include "grpp_overlap_gradient.h"
/*
* models of nuclear charge density distribution
*/
#include "grpp_nuclear_models.h"
extern void libgrpp_init();
extern void libgrpp_finalize();
extern int libgrpp_is_initialized();
libgrpp_potential_t *libgrpp_new_potential(int L, int J, int num_primitives,
int *powers, double *coeffs,
double *alpha);
void libgrpp_delete_potential(libgrpp_potential_t *potential);
double libgrpp_potential_value(libgrpp_potential_t *potential, double r);
libgrpp_potential_t *
libgrpp_shrink_potential(libgrpp_potential_t *src_potential);
libgrpp_potential_t *
libgrpp_shrink_potential_n0(libgrpp_potential_t *src_potential);
libgrpp_shell_t *libgrpp_new_shell(double *origin, int L, int num_primitives,
double *coeffs, double *alpha);
int libgrpp_get_shell_size(libgrpp_shell_t *shell);
void libgrpp_delete_shell(libgrpp_shell_t *shell);
int *libgrpp_generate_shell_cartesians(int L);
libgrpp_shell_t *libgrpp_shell_deep_copy(libgrpp_shell_t *src_shell);
void libgrpp_shell_shrink(libgrpp_shell_t *shell);
void libgrpp_shell_mult_normcoef(libgrpp_shell_t *shell);
libgrpp_grpp_t *libgrpp_new_grpp();
void libgrpp_grpp_set_local_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot);
void libgrpp_grpp_add_averaged_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot);
void libgrpp_grpp_add_spin_orbit_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot);
void libgrpp_grpp_add_outercore_potential(libgrpp_grpp_t *grpp,
libgrpp_potential_t *pot,
libgrpp_shell_t *oc_shell);
void libgrpp_delete_grpp(libgrpp_grpp_t *);
/*
* integrators: pseudopotential
*/
void libgrpp_type1_integrals(libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
double *rpp_origin, libgrpp_potential_t *potential,
double *matrix);
void libgrpp_type2_integrals(libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
double *rpp_origin, libgrpp_potential_t *potential,
double *matrix);
void libgrpp_spin_orbit_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B, double *rpp_origin,
libgrpp_potential_t *potential,
double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix);
void libgrpp_outercore_potential_integrals(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *rpp_origin,
int num_oc_shells, libgrpp_potential_t **oc_potentials,
libgrpp_shell_t **oc_shells, double *arep, double *esop_x, double *esop_y,
double *esop_z);
void libgrpp_full_grpp_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator,
double *grpp_origin, double *arep_matrix,
double *so_x_matrix, double *so_y_matrix,
double *so_z_matrix);
/*
* gradients of pseudopotential matrix elements
* with respect to nuclear coordinates
*/
void libgrpp_type1_integrals_gradient(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *grpp_origin,
libgrpp_potential_t *potential,
double *point_3d, double **grad_arep);
void libgrpp_type2_integrals_gradient(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *grpp_origin,
libgrpp_potential_t *potential,
double *point_3d, double **grad_arep);
void libgrpp_spin_orbit_integrals_gradient(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *grpp_origin,
libgrpp_potential_t *potential, double *point_3d, double **grad_so_x,
double **grad_so_y, double **grad_so_z);
void libgrpp_outercore_potential_integrals_gradient(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *rpp_origin,
int num_oc_shells, libgrpp_potential_t **oc_potentials,
libgrpp_shell_t **oc_shells, double *point_3d, double **grad_arep,
double **grad_so_x, double **grad_so_y, double **grad_so_z);
void libgrpp_full_grpp_integrals_gradient(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
libgrpp_grpp_t *grpp_operator, double *grpp_origin, double *point_3d,
double **grad_arep, double **grad_so_x, double **grad_so_y,
double **grad_so_z);
/*
* integrator for nuclear attraction integrals
*/
enum {
LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE = 0,
LIBGRPP_NUCLEAR_MODEL_CHARGED_BALL,
LIBGRPP_NUCLEAR_MODEL_GAUSSIAN,
LIBGRPP_NUCLEAR_MODEL_FERMI,
LIBGRPP_NUCLEAR_MODEL_FERMI_BUBBLE,
LIBGRPP_NUCLEAR_MODEL_POINT_CHARGE_NUMERICAL
};
void libgrpp_nuclear_attraction_integrals(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *charge_origin, int charge,
int nuclear_model,
double *model_params,
double *coulomb_matrix);
void libgrpp_nuclear_attraction_integrals_point_charge(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *charge_origin,
int charge,
double *coulomb_matrix);
void libgrpp_nuclear_attraction_integrals_charged_ball(libgrpp_shell_t *shell_A,
libgrpp_shell_t *shell_B,
double *charge_origin,
int charge, double r_rms,
double *coulomb_matrix);
void libgrpp_nuclear_attraction_integrals_gaussian_model(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *charge_origin,
int charge, double r_rms, double *coulomb_matrix);
void libgrpp_nuclear_attraction_integrals_fermi_model(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *charge_origin,
int charge, double fermi_param_c, double fermi_param_a,
double *coulomb_matrix);
void libgrpp_nuclear_attraction_integrals_fermi_bubble_model(
libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *charge_origin,
int charge, double param_c, double param_a, double param_k,
double *coulomb_matrix);
#endif // LIBGRPP_LIBGRPP_H

62
src/grpp/libgrpp_types.h Normal file
View file

@ -0,0 +1,62 @@
/*----------------------------------------------------------------------------*/
/* CP2K: A general program to perform molecular dynamics simulations */
/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
/* */
/* SPDX-License-Identifier: MIT */
/*----------------------------------------------------------------------------*/
#ifndef __LIBGRPP_TYPES_H__
#define __LIBGRPP_TYPES_H__
typedef struct {
int L;
int J;
int num_primitives;
int *powers;
double *coeffs;
double *alpha;
} libgrpp_potential_t;
typedef struct {
int L;
int cart_size;
int *cart_list;
int num_primitives;
double *coeffs;
double *alpha;
double origin[3];
} libgrpp_shell_t;
/**
* Generalized relativistic pseudopotential: all-in-one
*/
typedef struct {
int n_arep;
int n_esop;
int n_oc_shells;
libgrpp_potential_t *U_L;
libgrpp_potential_t **U_arep;
libgrpp_potential_t **U_esop;
libgrpp_potential_t **U_oc;
libgrpp_shell_t **oc_shells;
} libgrpp_grpp_t;
/*
* maximum angular momentum of basis functions
*/
#define LIBGRPP_MAX_BASIS_L 10
/*
* maximum angular momentum occuring in the RPP operator
*/
#define LIBGRPP_MAX_RPP_L 10
/*
* threshold for zero
*/
#define LIBGRPP_ZERO_THRESH 1e-14
/*
* tolerance of radial integrals evaluation
*/
#define LIBGRPP_RADIAL_TOL 1e-14
#endif

View file

@ -33,10 +33,13 @@ FLAG_EXCEPTIONS = (
r"GRID_DO_COLLOCATE",
r"INTEL_MKL_VERSION",
r"LIBINT2_MAX_AM_eri",
r"LIBGRPP",
r"M_",
r"LIBINT_CONTRACTED_INTS",
r"XC_MAJOR_VERSION",
r"XC_MINOR_VERSION",
r"NDEBUG",
r"M_PI",
r"OMP_DEFAULT_NONE_WITH_OOP",
r"FTN_NO_DEFAULT_INIT",
r"_OPENMP",
@ -68,6 +71,8 @@ FLAG_EXCEPTIONS = (
r"LIBXSMM_VERSION2",
r"LIBXSMM_VERSION3",
r"LIBXSMM_VERSION4",
r"LIBGRPP_..*",
r"TEST_LIBGRPP_..*",
r"__LIBXSMM2",
r"CPVERSION",
r"_WIN32",
@ -118,6 +123,8 @@ C_EXTENSIONS = (".c", ".cu", ".cpp", ".cc", ".h", ".hpp")
BSD_PATHS = ("src/offload/", "src/grid/", "src/dbm/", "src/base/openmp_trace.c")
MIT_PATHS = "src/grpp/"
@lru_cache(maxsize=None)
def get_install_txt() -> str:
@ -148,7 +155,7 @@ def check_file(path: pathlib.Path) -> List[str]:
- undocumented preprocessor flags
- stray unicode characters
"""
warnings = []
warnings = [] # type: List[str]
fn_ext = path.suffix
abspath = path.resolve()
@ -192,8 +199,18 @@ def check_file(path: pathlib.Path) -> List[str]:
# check banner
year = datetime.now(timezone.utc).year
bsd_licensed = any(str(path).startswith(p) for p in BSD_PATHS)
spdx = "BSD-3-Clause " if bsd_licensed else "GPL-2.0-or-later"
# mit_licensed = any(str(path).startswith(p) for p in MIT_PATHS)
spdx = "GPL-2.0-or-later"
if bsd_licensed:
spdx = "BSD-3-Clause "
else:
if str(path).startswith(MIT_PATHS):
spdx = "MIT "
else:
spdx = "GPL-2.0-or-later"
if fn_ext == ".F" and not content.startswith(BANNER_F.format(year, spdx)):
print(BANNER_F.format(year, spdx))
warnings += [f"{path}: Copyright banner malformed"]
if fn_ext == ".fypp" and not content.startswith(BANNER_SHELL.format(year, spdx)):
warnings += [f"{path}: Copyright banner malformed"]
@ -211,7 +228,6 @@ def check_file(path: pathlib.Path) -> List[str]:
PY_SHEBANG = "#!/usr/bin/env python3"
if fn_ext == ".py" and is_executable and not content.startswith(f"{PY_SHEBANG}\n"):
warnings += [f"{path}: Wrong shebang, please use '{PY_SHEBANG}'"]
# find all flags
flags = set()
line_continuation = False

View file

@ -1,164 +0,0 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b5cc84f..efd61cf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,12 +6,29 @@
#
cmake_minimum_required(VERSION 3.19)
-project(test_libgrpp_c.x C)
-project(test_libgrpp_f90.x Fortran)
+
+include(CMakeDependentOption)
+include(CMakePackageConfigHelpers)
+
+set(VERSION_MAJOR 2023)
+set(VERSION_MINOR 12)
+set(VERSION_PATCH 25)
+
+project(libgrpp
+ DESCRIPTION "A library for the evaluation of molecular integrals of the generalized relativistic pseudopotential operator (GRPP) over Gaussian functions."
+ HOMEPAGE_URL https://github.com/aoleynichenko/libgrpp
+ VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}
+ LANGUAGES Fortran C)
set(CMAKE_C_STANDARD 11)
find_package(OpenMP)
+# imply -O3 -DNDEBUG, can be changed with cmake -DCMAKE_BUILD_TYPE=DEBUG,etc....
+
+set(CMAKE_BUILD_TYPE Release)
+
+set(libgrpp_APIVERSION ${libgrpp_VERSION_MAJOR}.${libgrpp_VERSION_MINOR})
+
add_subdirectory(libgrpp)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS} -O3")
@@ -43,10 +60,9 @@ add_executable(test_libgrpp_f90.x
test_libgrpp_f90/libgrpp.f90
)
-target_link_libraries(test_libgrpp_c.x libgrpp -lm ${OpenMP_C_LIBRARIES}) # -pg)
-target_link_libraries(test_libgrpp_f90.x libgrpp -lm ${OpenMP_C_LIBRARIES})
-
-
+target_link_libraries(test_libgrpp_c.x grpp OpenMP::OpenMP_C m)
+target_link_libraries(test_libgrpp_f90.x grpp OpenMP::OpenMP_Fortran m)
+set_target_properties(test_libgrpp_f90.x PROPERTIES Fortran_MODULE_DIRECTORY "fortran_modules")
# enable testing functionality
enable_testing()
@@ -63,3 +79,42 @@ add_test(NAME UO2 WORKING_DIRECTORY ../test/UO2 COMMAND bash run_test.sh)
foreach (t KCs)
set_property(TEST ${t} PROPERTY ENVIRONMENT "PATH=${CMAKE_BINARY_DIR}:$ENV{PATH}")
endforeach ()
+
+include(GNUInstallDirs)
+
+write_basic_package_version_file(
+ "${PROJECT_BINARY_DIR}/grppConfigVersion.cmake"
+ VERSION "${libgrpp_VERSION}"
+ COMPATIBILITY SameMajorVersion)
+
+if(NOT CMAKE_INSTALL_Fortran_MODULES)
+ set(CMAKE_INSTALL_Fortran_MODULES "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}"
+ )
+endif()
+
+configure_file("${PROJECT_SOURCE_DIR}/cmake/grppConfig.cmake.in"
+ "${PROJECT_BINARY_DIR}/grppConfig.cmake" @ONLY)
+
+configure_file(cmake/libgrpp.pc.in libgrpp.pc @ONLY)
+
+install(FILES "${PROJECT_BINARY_DIR}/grppConfig.cmake"
+ "${PROJECT_BINARY_DIR}/grppConfigVersion.cmake"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+install(FILES "${PROJECT_BINARY_DIR}/libgrpp.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+
+
+
+install(
+ DIRECTORY "${PROJECT_BINARY_DIR}/fortran_modules"
+ DESTINATION "${CMAKE_INSTALL_Fortran_MODULES}/${CMAKE_Fortran_COMPILER_ID}-${CMAKE_Fortran_COMPILER_VERSION}"
+ FILES_MATCHING
+ PATTERN "*.mod")
+
+install(
+ DIRECTORY "${PROJECT_SOURCE_DIR}/libgrpp"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+ FILES_MATCHING
+ PATTERN "*.h")
+
diff --git a/cmake/grppConfig.cmake.in b/cmake/grppConfig.cmake.in
new file mode 100644
index 0000000..ce54dc2
--- /dev/null
+++ b/cmake/grppConfig.cmake.in
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.19)
+include(CMakeFindDependencyMacro)
+
+if(NOT TARGET grpp::grpp)
+ include("${CMAKE_CURRENT_LIST_DIR}/grppTargets.cmake")
+endif()
diff --git a/cmake/libgrpp.pc.in b/cmake/libgrpp.pc.in
new file mode 100644
index 0000000..dfa3a9b
--- /dev/null
+++ b/cmake/libgrpp.pc.in
@@ -0,0 +1,11 @@
+prefix="@CMAKE_INSTALL_PREFIX@"
+exec_prefix="${prefix}"
+libdir="${prefix}/@CMAKE_INSTALL_LIBDIR@"
+includedir="${prefix}/@CMAKE_INSTALL_INCLUDEDIR@"
+
+Name: @PROJECT_NAME@
+Description: @CMAKE_PROJECT_DESCRIPTION@
+URL: @CMAKE_PROJECT_HOMEPAGE_URL@
+Version: @PROJECT_VERSION@
+Cflags: -I"${includedir}/libgrpp" -I"${includedir}/libgrpp/@CMAKE_Fortran_COMPILER_ID@-@CMAKE_Fortran_COMPILER_VERSION@/fortran_modules"
+Libs: -L"${libdir}" -lgrpp
diff --git a/libgrpp/CMakeLists.txt b/libgrpp/CMakeLists.txt
index f579f7b..ad3408c 100644
--- a/libgrpp/CMakeLists.txt
+++ b/libgrpp/CMakeLists.txt
@@ -5,16 +5,7 @@
# Copyright (C) 2021-2023 Alexander Oleynichenko
#
-cmake_minimum_required(VERSION 3.19)
-project(libgrpp C)
-
-set(CMAKE_C_STANDARD 11)
-
-find_package(OpenMP)
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS} -O3")
-#add_compile_options(-pg)
-
-add_library(libgrpp
+add_library(grpp
angular_integrals.c
binomial.c
diff_gaussian.c
@@ -53,4 +44,18 @@ add_library(libgrpp
utils.c
)
-target_link_libraries(libgrpp -lm)
+set_target_properties(grpp PROPERTIES POSITION_INDEPENDENT_CODE ON
+ VERSION ${grpp_VERSION}
+ SOVERSION ${grpp_APIVERSION})
+include(GNUInstallDirs)
+install(
+ TARGETS grpp
+ EXPORT libgrpp_targets
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+
+install(EXPORT libgrpp_targets
+ FILE grppTargets.cmake
+ NAMESPACE grpp::
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+

View file

@ -6,10 +6,6 @@
[ "${BASH_SOURCE[0]}" ] && SCRIPT_NAME="${BASH_SOURCE[0]}" || SCRIPT_NAME=$0
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_NAME")/.." && pwd -P)"
libgrpp_ver="20231225"
libgrpp_sha="64d157f1dc95815096b1fd437a5851abeb3425929cf7b2092bf8262db9c5e33d"
libgrpp_pkg="libgrpp-main-${libgrpp_ver}.zip"
source "${SCRIPT_DIR}"/common_vars.sh
source "${SCRIPT_DIR}"/tool_kit.sh
source "${SCRIPT_DIR}"/signal_trap.sh
@ -18,85 +14,11 @@ source "${INSTALLDIR}"/toolchain.env
[ -f "${BUILDDIR}/setup_libgrpp" ] && rm "${BUILDDIR}/setup_libgrpp"
LIBGRPP_CFLAGS=""
LIBGRPP_LDFLAGS=""
LIBGRPP_LIBS=""
! [ -d "${BUILDDIR}" ] && mkdir -p "${BUILDDIR}"
cd "${BUILDDIR}"
case "${with_libgrpp}" in
__INSTALL__)
echo "==================== Installing LIBGRPP ===================="
pkg_install_dir="${INSTALLDIR}/libgrpp-main-${libgrpp_ver}"
install_lock_file="$pkg_install_dir/install_successful"
if verify_checksums "${install_lock_file}"; then
echo "libgrpp-main-${libgrpp_ver} is already installed, skipping it."
else
if [ -f ${libgrpp_pkg} ]; then
echo "${libgrpp_pkg} is found"
else
download_pkg_from_cp2k_org "${libgrpp_sha}" "${libgrpp_pkg}"
fi
echo "Installing from scratch into ${pkg_install_dir}"
[ -d libgrpp-main ] && rm -rf libgrpp-main
unzip -qq ${libgrpp_pkg}
cd libgrpp-main
patch -Np1 -i ${SCRIPT_DIR}/stage3/grpp-cmake.patch
mkdir build
cd build
CC=${CC} FC=${FC} cmake -DCMAKE_INSTALL_PREFIX="${pkg_install_dir}" -DCMAKE_INSTALL_LIBDIR="lib" .. > cmake.log 2>&1 || tail -n ${LOG_LINES} cmake.log
make > make.log 2>&1 || tail -n ${LOG_LINES} make.log
make install > make.log 2>&1 || tail -n ${LOG_LINES} make.log
cd ..
write_checksums "${install_lock_file}" "${SCRIPT_DIR}/stage3/$(basename ${SCRIPT_NAME})"
fi
PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:${pkg_install_dir}/lib/pkgconfig:${pkg_install_dir}/lib64/pkgconfig
LIBGRPP_CFLAGS=$(pkg-config --cflags libgrpp)
LIBGRPP_LDFLAGS="-L'${pkg_install_dir}/lib' -Wl,-rpath,'${pkg_install_dir}/lib'"
;;
__SYSTEM__)
echo "==================== Finding libgrpp from system paths ===================="
check_lib -lgrpp "grpp"
add_include_from_paths -p LIBGRPP_CFLAGS "grpp.h" $INCLUDE_PATHS
add_lib_from_paths LIBGRPP_LDFLAGS "libgrpp.*" $LIB_PATHS
;;
__DONTUSE__) ;;
*)
echo "==================== Linking libgrpp to user paths ===================="
pkg_install_dir="$with_libgrpp"
check_dir "${pkg_install_dir}/include"
check_dir "${pkg_install_dir}/lib"
LIBGRPP_CFLAGS="-I'${pkg_install_dir}/include'"
LIBGRPP_LDFLAGS="-L'${pkg_install_dir}/lib'"
;;
esac
if [ "$with_libgrpp" != "__DONTUSE__" ]; then
LIBGRPP_LIBS="-lgrpp"
cat << EOF > "${BUILDDIR}/setup_libgrpp"
export LIBGRPP_VER="${libgrpp_ver}"
EOF
if [ "$with_libgrpp" != "__SYSTEM__" ]; then
cat << EOF >> "${BUILDDIR}/setup_libgrpp"
prepend_path LD_LIBRARY_PATH "$pkg_install_dir/lib"
prepend_path LD_RUN_PATH "$pkg_install_dir/lib"
prepend_path LIBRARY_PATH "$pkg_install_dir/lib"
prepend_path PKG_CONFIG_PATH "$pkg_install_dir/lib/pkgconfig"
prepend_path CMAKE_PREFIX_PATH "$pkg_install_dir"
export LIBGRPP_ROOT="${pkg_install_dir}"
EOF
cat "${BUILDDIR}/setup_libgrpp" >> $SETUPFILE
fi
echo "==================== Using libgrpp ===================="
cat << EOF >> "${BUILDDIR}/setup_libgrpp"
export LIBGRPP_CFLAGS="${LIBGRPP_CFLAGS}"
export LIBGRPP_LDFLAGS="${LIBGRPP_LDFLAGS}"
export LIBGRPP_LIBS="${LIBGRPP_LIBS}"
export CP_DFLAGS="\${CP_DFLAGS} -D__LIBGRPP"
export CP_CFLAGS="\${CP_CFLAGS} ${LIBGRPP_CFLAGS}"
export CP_LDFLAGS="\${CP_LDFLAGS} ${LIBGRPP_LDFLAGS}"
export CP_LIBS="${LIBGRPP_LIBS} \${CP_LIBS}"
EOF
fi