diff --git a/.gitignore b/.gitignore index 0664d48..2a0adea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ *.o *. *.so +*.mod +*.slurm +__pycache__ +int/ +.ninja_deps diff --git a/config/env.sh b/config/env.sh new file mode 100755 index 0000000..658fe91 --- /dev/null +++ b/config/env.sh @@ -0,0 +1,24 @@ +#!/bin/env + +CURRENT_HOSTNAME=$(hostname) +echo "Current machine: $CURRENT_HOSTNAME" + +export QUACK_ROOT=/users/p18005/ammar/tmpdir/QuAcK + +case $CURRENT_HOSTNAME in + *turpan*) + ;; + *olympe*) + module purge + module load python/3.9.5 + module load intel/18.2.199 + export PATH=$PATH:/users/p18005/ammar/qp2/bin + export LD_LIBRARY_PATH=/usr/local/miniconda/4.9.2/envs/python-3.9.5/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH=$QUACK_ROOT/lib:$LD_LIBRARY_PATH + export PYTHONPATH=$QUACK_ROOT/src/pyscf:$PYTHONPATH + ;; + *) + echo "Unknown hostname: $CURRENT_HOSTNAME. No modules loaded." + ;; +esac + diff --git a/src/QuAcK/QuAcK.f90 b/src/QuAcK/QuAcK.f90 index 6af981b..2dfab44 100644 --- a/src/QuAcK/QuAcK.f90 +++ b/src/QuAcK/QuAcK.f90 @@ -1,5 +1,7 @@ program QuAcK + use pyscf_module + implicit none include 'parameters.h' @@ -68,6 +70,27 @@ program QuAcK logical :: dotest,doRtest,doUtest,doGtest + ! test pyscf interface + character(len = 50) :: xyz, input_basis, unit_type + integer :: charge, multiplicity, cartesian + integer :: natoms, nalpha, nbeta + xyz = '/users/p18005/ammar/tmpdir/QuAcK/mol/H2O.xyz' // c_null_char + input_basis = 'sto-3g' // c_null_char + unit_type = 'Angstrom' // c_null_char + charge = 0 + multiplicity = 1 + cartesian = 0 + + call call_mol_prop(xyz, input_basis, charge, multiplicity, unit_type, cartesian, & + natoms, nalpha, nbeta, Enuc) + + print *, "Number of atoms:", natoms + print *, "Number of alpha electrons:", nalpha + print *, "Number of beta electrons:", nbeta + print *, "Nuclear energy:", Enuc + stop + + !-------------! ! Hello World ! !-------------! @@ -130,7 +153,8 @@ program QuAcK ! nBas = number of basis functions (see below) ! !------------------------------------------------! - call read_molecule(nNuc,nO,nC,nR) + call read_molecule(nNuc, nO, nC, nR) + allocate(ZNuc(nNuc),rNuc(nNuc,ncart)) ! Read geometry diff --git a/src/make_ninja.py b/src/make_ninja.py index d22ba52..acde8f9 100755 --- a/src/make_ninja.py +++ b/src/make_ninja.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 import os import sys +import subprocess + DEBUG=False try: @@ -46,7 +48,7 @@ FIX_ORDER_OF_LIBS=-Wl,--start-group compile_ifort_linux = """ FC = ifort -mkl=parallel -qopenmp AR = ar crs -FFLAGS = -I$IDIR -g -Ofast -traceback +FFLAGS = -I$IDIR -module $IDIR -g -Ofast -traceback CC = icc CXX = icpc LAPACK= @@ -79,7 +81,7 @@ FIX_ORDER_OF_LIBS=-Wl,--start-group compile_olympe = """ FC = ifort -mkl=parallel -qopenmp AR = ar crs -FFLAGS = -I$IDIR -Ofast -traceback -xCORE-AVX512 +FFLAGS = -I$IDIR -module $IDIR -Ofast -traceback -xCORE-AVX512 CC = icc CXX = icpc LAPACK= @@ -119,13 +121,25 @@ rule fc """ -rule_build_so = """ -rule build_so - command = $AR $out $in - description = Linking $out +rule_cctoso = """ +rule cctoso + command = $CC $CFLAGS $PYINC $in -o $out $LDFLAGS $PYLDF """ +def rule_cctoso_flags(): + + PYTHON_VERSION = "{}.{}".format(sys.version_info.major, sys.version_info.minor) + PYINC = subprocess.run(['python{}-config'.format(PYTHON_VERSION), '--includes'], stdout=subprocess.PIPE, text=True) + PYLDF = subprocess.run(['python{}-config'.format(PYTHON_VERSION), '--ldflags'], stdout=subprocess.PIPE, text=True) + + return """ +LDFLAGS = -shared -fPIC -L$LDIR -Wl,-rpath=$LDIR +PYINC = {} +PYLDF = {} -lpython{} + +""".format(PYINC.stdout.strip(), PYLDF.stdout.strip(), PYTHON_VERSION) + rule_build_lib = """ rule build_lib command = $AR $out $in @@ -137,7 +151,7 @@ rule_build_exe = """ LIBS = {0} $LAPACK $STDCXX rule build_exe - command = $FC $FIX_ORDER_OF_LIBS $in $LIBS -o $out + command = $FC $FFLAGS $FIX_ORDER_OF_LIBS $PYINC $in $LIBS -o $out -L$LDIR -lpyscf_wrapper $PYLDF pool = console description = Linking $out @@ -156,11 +170,12 @@ rule git_clone """ -build_in_so_dir = "\n".join([ +build_in_py_dir = "\n".join([ header, compiler, + rule_cctoso_flags(), + rule_cctoso, rule_fortran, - rule_build_so, ]) build_in_lib_dir = "\n".join([ @@ -174,6 +189,7 @@ build_in_lib_dir = "\n".join([ build_in_exe_dir = "\n".join([ header, compiler, + rule_cctoso_flags(), rule_fortran, rule_build_exe, ]) @@ -193,40 +209,14 @@ lib_dirs = list(filter(lambda x: os.path.isdir(x) and \ def create_makefile_in_pydir(directory): - PYTHON_VERSION = "{}.{}".format(sys.version_info.major, sys.version_info.minor) - CC = "gcc" - CCFLAGS = "$(shell python$(PYTHON_VERSION)-config --includes)" - LDFLAGS = "-shared -fPIC -L$(LDIR) -Wl,-rpath=$(LDIR) $(shell python$(PYTHON_VERSION)-config --ldflags)" - lib_pydir = "lib{}_wrapper.so".format(directory) c_pydir = "{}_wrapper.c".format(directory) + with open(os.path.join(directory, "build.ninja"), "w") as f: + f.write(build_in_py_dir) + f.write("build $LDIR/{}: cctoso {}\n\n".format(lib_pydir, c_pydir)) + f.write("build {}_module.o: fc {}_module.f90\n".format(directory, directory)) - with open(os.path.join(directory, "Makefile"), "w") as f: - f.write("# This file was automatically generated. Do not modify this file.\n\n") - f.write("IDIR = {}/include\n".format(QUACK_ROOT)) - f.write("LDIR = {}/lib\n".format(QUACK_ROOT)) - f.write("BDIR = {}/bin\n".format(QUACK_ROOT)) - f.write("SDIR = {}/src\n\n".format(QUACK_ROOT)) - - f.write("CC = gcc\n") - f.write("PYTHON_VERSION = {}\n".format(PYTHON_VERSION)) - f.write("CCFLAGS = {}\n".format(CCFLAGS)) - f.write("LDFLAGS = {}\n\n".format(LDFLAGS)) - - f.write("TARGETS = $(LDIR)/{} {}_module.o\n\n".format(lib_pydir, directory)) - - f.write("all: $(TARGETS)\n\n") - - f.write("$(LDIR)/{}: {}\n".format(lib_pydir, c_pydir)) - f.write("\t$(CC) $(CCFLAGS) {} -o $(LDIR)/{} $(LDFLAGS)\n\n".format(c_pydir, lib_pydir)) - - f.write("{}_module.o: {}_module.f90\n".format(directory, directory)) - f.write("\tgfortran -c {}_module.f90 -o {}_module.o -J.\n\n".format(directory, directory)) - - f.write(".PHONY: clean\n") - f.write("clean:\n") - f.write("\trm -f ../../lib/{} *.mod *.o\n\n".format(lib_pydir)) def create_ninja_in_libdir(directory): @@ -244,8 +234,8 @@ def create_ninja_in_libdir(directory): obj_file = write_rule(f, filename, suffix) objects.append(obj_file) objects = " ".join(objects) - f.write("build $LDIR/lib{}.a: build_lib {}\n".format(directory, objects)) - f.write("default $LDIR/lib{}.a\n".format(directory)) + f.write("build $LDIR/{}.a: build_lib {}\n".format(directory, objects)) + f.write("default $LDIR/{}.a\n".format(directory)) def create_ninja_in_exedir(directory): @@ -273,6 +263,8 @@ def create_ninja_in_exedir(directory): def create_main_ninja(): libs = " ".join([ "$LDIR/{0}.a".format(x) for x in lib_dirs]) + " "+LIBS + for py_dir in py_dirs: + libs += "$LDIR/lib{0}_wrapper.so".format(py_dir) with open("build.ninja","w") as f: f.write(build_main) f.write(""" @@ -290,6 +282,8 @@ rule build_lib sources = [ "$SDIR/{0}/{1}".format(exe_dir,x) for x in os.listdir(exe_dir) ] sources = filter(lambda x: x.endswith(".f") or x.endswith(".f90"), sources) sources = " ".join(sources) + for py_dir in py_dirs: + sources += " $SDIR/{0}/{0}_wrapper.c $SDIR/{0}/{0}_module.f90".format(py_dir) f.write("build $BDIR/{0}: build_exe {1} {2}\n".format(exe_dir,libs,sources)) f.write(" dir = {0} \n".format(exe_dir) ) @@ -298,6 +292,10 @@ rule build_lib sources = filter(lambda x: x.endswith(".f") or x.endswith(".f90"), sources) sources = " ".join(sources) f.write("build $LDIR/{0}.a: build_lib {1} \n dir = $SDIR/{0}\n".format(libname, sources)) + + for py_dir in py_dirs: + f.write("build $LDIR/lib{0}_wrapper.so: build_lib $SDIR/{0}/{0}_wrapper.c $SDIR/{0}/{0}_module.f90 \n dir = $SDIR/{0}\n".format(py_dir)) + f.write("build all: phony $BDIR/QuAcK\n") f.write("default all\n") @@ -318,6 +316,7 @@ debug: def main(): for py_dir in py_dirs: create_makefile_in_pydir(py_dir) + create_makefile(py_dir) for lib_dir in lib_dirs: create_ninja_in_libdir(lib_dir) diff --git a/src/pyscf/pyscf_module.py b/src/pyscf/pyscf_module.py index a89c8e6..fc9e41e 100644 --- a/src/pyscf/pyscf_module.py +++ b/src/pyscf/pyscf_module.py @@ -1,11 +1,14 @@ +import warnings +warnings.filterwarnings("ignore", category=UserWarning, module="numpy.core.getlimits") import numpy as np from pyscf import gto + def read_xyz(xyz): - f = open(xyz + '.xyz', 'r') + f = open(xyz, 'r') lines = f.read().splitlines() diff --git a/src/pyscf/pyscf_wrapper.c b/src/pyscf/pyscf_wrapper.c index 40d3443..6830a5b 100644 --- a/src/pyscf/pyscf_wrapper.c +++ b/src/pyscf/pyscf_wrapper.c @@ -1,5 +1,8 @@ #include #include +#include +#include +#include void call_mol_prop(const char *xyz, const char *input_basis, int charge, int multiplicity, @@ -13,12 +16,27 @@ void call_mol_prop(const char *xyz, const char *input_basis, int charge, int mul // Initialize the Python interpreter Py_Initialize(); - printf("Python version: %s\n", Py_GetVersion()); + //printf("Python version: %s\n", Py_GetVersion()); + //printf("xyz: %s\n", xyz); + //printf("input_basis: %s\n", input_basis); + //printf("charge: %d\n", charge); + //printf("multiplicity: %d\n", multiplicity); + //printf("unit: %s\n", unit); + //printf("cartesian: %d\n", cartesian); - PyRun_SimpleString("import sys"); - PyRun_SimpleString("sys.path.append('.')"); - - // Import the Python module + char exe_path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len != -1) { + exe_path[len] = '\0'; // Null-terminate the string + dirname(exe_path); // Get directory part + char sys_path_command[PATH_MAX + 30]; + snprintf(sys_path_command, sizeof(sys_path_command), "sys.path.append('%s')", exe_path); + PyRun_SimpleString("import sys"); + PyRun_SimpleString(sys_path_command); + } else { + PyRun_SimpleString("import sys"); + PyRun_SimpleString("sys.path.append('.')"); + } pName = PyUnicode_DecodeFSDefault("pyscf_module"); pModule = PyImport_Import(pName); Py_XDECREF(pName); diff --git a/src/pyscf/test_python_call.f90 b/src/pyscf/test_python_call.f90 index bc3f6fc..b3263be 100644 --- a/src/pyscf/test_python_call.f90 +++ b/src/pyscf/test_python_call.f90 @@ -8,16 +8,19 @@ program test_python_call integer :: natoms, nalpha, nbeta double precision :: Enuc - xyz = "../../mol/H2O" - input_basis = "sto-3g" + xyz = '/users/p18005/ammar/tmpdir/QuAcK/mol/H2O.xyz' // C_NULL_CHAR + input_basis = 'sto-3g' // C_NULL_CHAR charge = 0 multiplicity = 1 - unit_type = "Angstrom" + unit_type = 'Angstrom' // C_NULL_CHAR cartesian = 0 - call call_mol_prop(trim(adjustl(xyz)), trim(adjustl(input_basis)), charge, multiplicity, trim(adjustl(unit_type)), cartesian, & + call call_mol_prop(xyz, input_basis, charge, multiplicity, unit_type, cartesian, & natoms, nalpha, nbeta, Enuc) + !call call_mol_prop(trim(adjustl(xyz)), trim(adjustl(input_basis)), charge, multiplicity, trim(adjustl(unit_type)), cartesian, & + ! natoms, nalpha, nbeta, Enuc) + print *, "Number of atoms:", natoms print *, "Number of alpha electrons:", nalpha print *, "Number of beta electrons:", nbeta