import os
import glob
import subprocess
import re
dir_libcint_src = "/home/a/Software/source/libcint-6.1.2/"
dir_qcint_src = "/home/a/Git-Others/qcint/"
with open(f"{dir_qcint_src}/CMakeLists.txt", "r") as f:
raw_cmakelists = f.read()
lines = raw_cmakelists.split("\n")
for n, l in enumerate(lines):
if l.startswith("set(cintSrc"):
break
files = []
for l in lines[n+1:]:
if l.strip() == "":
break
files += l.strip().replace(")", "").split()
qcint_src_paths = [f"{dir_qcint_src}/{f}" for f in files]
actual_intor = {}
valid_suffix = ("_sph", "_cart", "_spinor")
for src_path in list(qcint_src_paths):
with open(src_path, "r") as f:
lines = f.readlines()
for n, line in enumerate(lines):
if line.startswith("CACHE_SIZE_T int"):
intor = line.split("(")[0].split()[-1].strip()
is_intor_valid = False
for suffix in valid_suffix:
if intor.endswith(suffix):
intor = intor.replace(suffix, "")
is_intor_valid = True
if "#" in intor or intor in actual_intor or not is_intor_valid:
continue
ng = None
for l in lines[n:n+5]:
if l.strip().startswith("FINT ng[]") or l.strip().startswith("int ng[]"):
ng = [int(i) for i in l.strip().replace(";", "").replace("}", "").split("{")[-1].split(",")]
if ng is None:
raise ValueError(f"{intor}")
actual_intor[intor] = ng
actual_intor_qcint = actual_intor
with open(f"{dir_libcint_src}/include/cint.h.in", "r") as f:
raw_cint_h_in = f.read()
with open(f"{dir_libcint_src}/include/cint_funcs.h", "r") as f:
raw_cint_funcs_h = f.read()
with open(f"{dir_libcint_src}/CMakeLists.txt", "r") as f:
raw_cmakelists = f.read()
known_intor = []
for line in raw_cint_funcs_h.split("\n"):
if line.startswith("extern CINTOptimizerFunction"):
known_intor.append(line.strip().replace(";", "").split()[-1].replace("_optimizer", ""))
known_intor = sorted(set(known_intor))
actual_intor = {}
valid_suffix = ("_sph", "_cart", "_spinor")
for src_path in list(glob.iglob(f"{dir_libcint_src}/src/**/*.c", recursive=True)):
with open(src_path, "r") as f:
lines = f.readlines()
for n, line in enumerate(lines):
if line.startswith("CACHE_SIZE_T int"):
intor = line.split("(")[0].split()[-1].strip()
is_intor_valid = False
for suffix in valid_suffix:
if intor.endswith(suffix):
intor = intor.replace(suffix, "")
is_intor_valid = True
if "#" in intor or intor in actual_intor or not is_intor_valid:
continue
ng = None
for l in lines[n:n+5]:
if l.strip().startswith("FINT ng[]") or l.strip().startswith("int ng[]"):
ng = [int(i) for i in l.strip().replace(";", "").replace("}", "").split("{")[-1].split(",")]
if ng is None:
raise ValueError(f"{intor}")
actual_intor[intor] = ng
assert len(set(known_intor) - set(actual_intor)) == 0
def rule_qcint_exclusion(intor):
return (intor in actual_intor) and (intor not in actual_intor_qcint)
def rule_with_f12(intor):
return "_stg" in intor or "_yp" in intor
def rule_with_4c1e(intor):
return "int4c1e" in intor
ver = {
"cint_VERSION_MAJOR": None,
"cint_VERSION_MINOR": None,
"cint_VERSION_PATCH": None,
}
for line in raw_cmakelists.split("\n"):
for key in ver:
if line.startswith("set(" + key):
ver[key] = int(line.strip().split()[1].replace(")", "").replace("\"", ""))
break
cint_h = raw_cint_h_in \
.replace("@cint_VERSION@", f"{ver['cint_VERSION_MAJOR']}.{ver['cint_VERSION_MINOR']}.{ver['cint_VERSION_PATCH']}") \
.replace("@cint_SOVERSION@", f"{ver['cint_VERSION_MAJOR']}") \
.replace("#cmakedefine I8", "/* #undef I8 */") \
.replace("#cmakedefine CACHE_SIZE_I8", "/* #undef CACHE_SIZE_I8 */")
cint_funcs = """
#include "cint.h"
#if !defined HAVE_DEFINED_CINTINTEGRALFUNCTION
#define HAVE_DEFINED_CINTINTEGRALFUNCTION
typedef void CINTOptimizerFunction(
CINTOpt **opt,
const FINT *atm, FINT natm, const FINT *bas, FINT nbas, const double *env);
typedef CACHE_SIZE_T CINTIntegralFunctionReal(
double *out, const FINT *dims, const FINT *shls,
const FINT *atm, FINT natm, const FINT *bas, FINT nbas, const double *env,
const CINTOpt *opt, double *cache);
typedef CACHE_SIZE_T CINTIntegralFunctionComplex(
double complex *out, const FINT *dims, const FINT *shls,
const FINT *atm, FINT natm, const FINT *bas, FINT nbas, const double *env,
const CINTOpt *opt, double *cache);
#endif
"""
token = """
extern CINTOptimizerFunction {0:}_optimizer;
extern CINTIntegralFunctionReal {0:}_cart;
extern CINTIntegralFunctionReal {0:}_sph;
extern CINTIntegralFunctionComplex {0:}_spinor;
"""
cint_funcs = cint_funcs + "".join([
token.format(intor)
for intor in sorted(set(actual_intor))
if not rule_with_f12(intor)])
cint_funcs = cint_funcs.replace("#include <cint.h>", "#include \"cint.h\"")
token = """
extern CINTOptimizerFunction {0:}_optimizer;
extern CINTIntegralFunctionReal {0:}_sph;
"""
cint_funcs = cint_funcs + "".join([
token.format(intor)
for intor in sorted(set(actual_intor))
if rule_with_f12(intor)])
cint_funcs = cint_funcs.replace("#include <cint.h>", "#include \"cint.h\"")
cint_funcs = cint_funcs.replace('#include "cint.h"', cint_h)
with open("cint_funcs.h", "w") as f:
f.write(cint_funcs)
subprocess.run([
"bindgen", "cint_funcs.h",
"-o", "cint_ffi.rs",
"--allowlist-file", "cint_funcs.h",
"--no-layout-tests",
"--merge-extern-blocks",
])
with open("cint_ffi.rs", "r") as f:
token = f.read()
res = re.findall(r"(?:\#.*?\})", token.replace("\n", "NEWLINE"))
for r in res:
if "pub fn c" in r and "pub fn cint" not in r:
token = token.replace(r.replace("NEWLINE", "\n") + "\n", "")
token = """
use core::ffi::c_int;
""" + token
token = token.replace("::std::os::raw::c_int", "c_int")
token = token.replace("::std::option::Option", "Option")
with open("cint_ffi.rs", "w") as f:
f.write(token)
subprocess.run(["mv", "cint_ffi.rs", "../src/ffi/cint_ffi.rs"])
subprocess.run(["rm", "cint_funcs.h"])
token_wrapper = """
/* Generated by python scripts for libcint low-level wrapper. */
/* Should not modify manually. */
use crate::cint::CIntKind;
use crate::ffi::cint_ffi::*;
use crate::ffi::wrapper_traits::Integrator;
use crate::impl_integrator;
use core::any::Any;
use core::ffi::{c_int, c_void};
#[allow(unused_imports)]
use crate::ffi::wrapper_traits::{panic_cart, panic_spinor};
"""
def gen_impl_integrator(intor):
token = f"""impl_integrator!(
{0:},
{0:}_optimizer,
{0:}_sph,
{0:}_cart,
{0:}_spinor,
{5:}, {6:}, {7:}
{2:}, {3:}, {4:}, vec!{5:},
"{1:}", "{0:}",
CIntKind::Int);\n"""
integrator_category = intor.split("_")[0]
is_sph_available = "true"
is_cart_available = "true"
is_spinor_available = "true"
func_integral_sph = f"{intor}_sph"
func_integral_cart = f"{intor}_cart"
func_integral_spinor = f"{intor}_spinor"
if rule_with_f12(intor):
is_cart_available = "false"
is_spinor_available = "false"
func_integral_cart = "panic_cart"
func_integral_spinor = "panic_spinor"
intor_center = intor[:5]
if intor_center == "int2e":
intor_center = "int4c"
if intor_center == "int1e":
intor_center = "int2c"
n_center = int(intor_center[3])
ng = actual_intor[intor]
assert len(ng) == 8
comp_1e, comp_2e, comp_tensor = ng[-3:]
comp_all = max(comp_1e, 1) * max(comp_2e, 1) * comp_tensor
token = f"""impl_integrator!(
{intor},
{intor}_optimizer,
{func_integral_sph},
{func_integral_cart},
{func_integral_spinor},
{is_sph_available},
{is_cart_available},
{is_spinor_available},
{comp_all}, {comp_tensor}, {n_center},
vec!{ng},
"{integrator_category}", "{intor}",
CIntKind::Int);\n"""
if rule_with_f12(intor):
token = '#[cfg(feature = "with_f12")]\n' + token
if rule_with_4c1e(intor):
token = '#[cfg(feature = "with_4c1e")]\n' + token
if rule_qcint_exclusion(intor):
token = '#[cfg(not(feature = "qcint"))]\n' + token
return token
for intor in actual_intor:
token_wrapper += gen_impl_integrator(intor)
token_wrapper += """
pub fn get_cint_integrator(name: &str) -> Option<Box<dyn Integrator>> {
match name {
"""
for intor in actual_intor:
if rule_with_f12(intor):
token_wrapper += '#[cfg(feature = "with_f12")]\n'
if rule_with_4c1e(intor):
token_wrapper += '#[cfg(feature = "with_4c1e")]\n'
if rule_qcint_exclusion(intor):
token_wrapper += '#[cfg(not(feature = "qcint"))]\n'
token_wrapper += f"""
"{intor}" => Some(Box::new({intor})),
""".strip()
token_wrapper += """
_ => None,
}
}
"""
with open("../src/ffi/cint_wrapper.rs", "w") as f:
f.write(token_wrapper)
os.chdir("..")
subprocess.run(["cargo", "fmt"])