From 1ce24dc25e137075a4d78a2a1d8c7434751634d2 Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Mon, 5 Oct 2020 03:25:39 +0200 Subject: [PATCH 1/9] First try with meson (to be tested) --- .gitignore | 1 + meson.build | 22 ++++++++++++++++++++++ psiesta/c_bindings/meson.build | 1 + psiesta/meson.build | 23 +++++++++++++++++++++++ pyproject.toml | 14 ++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 meson.build create mode 100644 psiesta/c_bindings/meson.build create mode 100644 psiesta/meson.build create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index c36c001..92d63a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.o *.mod _psiesta.c +builddir \ No newline at end of file diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..1e24b3b --- /dev/null +++ b/meson.build @@ -0,0 +1,22 @@ +project( + 'PSiesta', + version: '0.3.0', +) +add_languages('c', 'fortran', required: true) + +py_mod = import('python') +py3 = py_mod.find_installation('python3') +py3_dep = py3.dependency(required: true) + +py_version = py3.language_version() +if py_version.version_compare('< 3.6') + error('Old python detected') +endif + +cython = find_program('cython', required: true) + +libsiesta_dep = dependency('Siesta', required: true) + +#mpifort_dep = dependency('mpi', language: 'fortran', required: true) + +subdir('psiesta') diff --git a/psiesta/c_bindings/meson.build b/psiesta/c_bindings/meson.build new file mode 100644 index 0000000..e1cc650 --- /dev/null +++ b/psiesta/c_bindings/meson.build @@ -0,0 +1 @@ +fpsiesta_files = files('fpsiesta.f90') \ No newline at end of file diff --git a/psiesta/meson.build b/psiesta/meson.build new file mode 100644 index 0000000..5dad44e --- /dev/null +++ b/psiesta/meson.build @@ -0,0 +1,23 @@ +subdir('c_bindings') + +psiesta_c = custom_target( + '_psiesta_pyx', + output : '_psiesta.c', + input : '_psiesta.pyx', + command : [cython, '-3', '@INPUT@', '-o', '@OUTPUT@'], +) + +psiesta_ext = py3.extension_module( + '_psiesta', + [psiesta_c, fpsiesta_files], + subdir: 'psiesta', + install: true, + dependencies: [py3_dep, libsiesta_dep] # , mpifort_dep +) + +pyfiles = files( + 'ase.py', + 'psiesta.py', +) + +py3.install_sources(pyfiles, subdir: 'psiesta') diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b972f5c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[build-system] +# https://thiblahute.gitlab.io/mesonpep517/ +requires = ["mesonpep517", "ninja"] +build-backend = "mesonpep517.buildapi" + + +[tool.mesonpep517.metadata] +author="Jonas Lundholm Bertelsen" +author-email="" +summary="Run Siesta as a subroutine in Python, with ASE bindings" +home-page="https://github.com/jonaslb/psiesta" +requires-python=">=3.6" +description-file="README.md" +requires = ["numpy", "mpi4py", "sisl"] From 1b624fed6455c53972443be00f039da7d2d7c9b4 Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Fri, 9 Oct 2020 16:23:13 +0200 Subject: [PATCH 2/9] Add __init__.py to meson build --- psiesta/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/psiesta/meson.build b/psiesta/meson.build index 5dad44e..a3a7702 100644 --- a/psiesta/meson.build +++ b/psiesta/meson.build @@ -16,6 +16,7 @@ psiesta_ext = py3.extension_module( ) pyfiles = files( + '__init__.py', 'ase.py', 'psiesta.py', ) From b10244a6ad319a41a5801cd03b2dd5b89c7ea4c9 Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Sat, 10 Oct 2020 00:54:56 +0200 Subject: [PATCH 3/9] Meson-ninja based build system --- .gitignore | 3 +- README.md | 23 +++--- find_manual_siesta.py | 149 +++++++++++++++++++++++++++++++++++++++ meson.build | 2 - psiesta/meson.build | 2 +- pyproject.toml | 2 +- setup.py | 160 ------------------------------------------ 7 files changed, 168 insertions(+), 173 deletions(-) create mode 100644 find_manual_siesta.py delete mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 92d63a1..935974c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ *.o *.mod _psiesta.c -builddir \ No newline at end of file +builddir +Siesta.pc diff --git a/README.md b/README.md index 82fc27c..43c7011 100644 --- a/README.md +++ b/README.md @@ -65,15 +65,23 @@ You should use a Siesta version later than the git master as of 2020-06-10, as a ## Obtaining source, building and installing You can obtain the source by simply cloning this repository. -To build, you must have a properly set up `arch.make` for Siesta in your Obj-dir, and you must have at least compiled Siesta there (see the note above for a patched Siesta). -You can then run `OBJ=/my/custom/siesta/Obj/ python3 setup.py install [--user] [--prefix=]` (or use `build` instead of `install`) to build PSiesta. -The setup.py-file makes use of Siesta's own `Makefile` (which includes your `arch.make`) in combination with `--dry-run` to extract the compilation and link arguments. -It *should* work for both intel and gnu compilers, but be aware that LTO can complicate things, and ensure that any external libraries that are used (eg. flook) are compiled with `-fPIC`. -On some platforms it is necessary to link more libraries than Siesta is otherwise compiled with. It is currently a little unclear why, but in one case I needed to use `EXTRA_COMP_ARGS="-lmkl_avx512 -lmkl_def"` (which the setup.py-file will recognize). +To build and install, there are two situations listed below. +In all cases, Siesta must be built with `-fPIC`. +You must also use a Siesta version later than the git master as of 2020-06-10 as mentioned above. -As noted above, you should use a Siesta version later than the git master as of 2020-06-10. +### A: Installed Siesta *WITH* libsiesta and pkgconf +Siesta may include a "libsiesta" functionality in the near future (probably v. 4.2+). +If you have such a version installed, this is for you. +In that case you only need to execute `pip3 install .` to install PSiesta. +This uses `pkgconf` to find all dependencies and link flags. +You may use the flags `--user` to install for yourself only and `--no-build-isolation` which usually speeds things up. +### B: Custom built Siesta not installed +In this case you can use the script `find_manual_siesta.py` in the base directory to create an ad-hoc `pkgconf`-file that directs the build system towards your Siesta obj-dir. +The script takes the object dir as the first argument. +You can optionally pass `--create-libsiesta` if your version of Siesta hasn't created `libsiesta.a` in the object-directory. +The script will tell you how to proceed when it's done (similar to situation A, but you may need to set further options). ## Behaviour See also [the SiestaSubroutine readme](https://gitlab.com/siesta-project/siesta/tree/master/Util/SiestaSubroutine/README). @@ -88,5 +96,4 @@ It will also prepend some settings to your fdf-file: Notably `MD.TypeOfRun force * Only a few properties can currently be fetched directly via the bindings. Other properties must be obtained via the output-files. Feel free to create an issue if you'd like something in particular built-in, or send a PR if you've implemented it already. * You don't get an exception when eg. the fdf-file contains an error. Instead, the whole process dies. - This is because on error, Siesta calls `abort()` to "helpfully" crash and spit out a stacktrace. - TODO: Can we catch sigabrt and raise a Python exception with the stacktrace instead? + This is because on error, Siesta intentionally kills itself which unfortunately includes the Python process. \ No newline at end of file diff --git a/find_manual_siesta.py b/find_manual_siesta.py new file mode 100644 index 0000000..386671a --- /dev/null +++ b/find_manual_siesta.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Sets up an improvised .pc file that refers to a Siesta OBJ directory. +ONLY use this functionality if you don't have the ability to properly install +a libsiesta and pkgconf file. +You must have already built Siesta in the Obj directory (and not cleaned up). +""" +import subprocess as sp +from pathlib import Path +import argparse + + +PKGCONF_TEXT = """ +Name: {name} +Description: Improper pkgconf-file. Links to a custom Siesta Obj dir. +Version: {version} +Libs: {lpaths} {libs} +Cflags: {includes} +""" + + +def group_make_commands(lines): + if isinstance(lines, str): + lines = lines.splitlines() + commands = [] + cur_cmd = [] + for l in lines: + l = l.strip() + continues = l.endswith("\\") + cur_cmd.append(l[:-1] if continues else l) + if not continues: + commands.append(" ".join(cur_cmd)) + cur_cmd = [] + return commands + + +def abs_relative_dir(base, rel): + rel = Path(rel) + if not rel.root: + return str((Path(base) / rel).resolve()) + return rel + + +def get_strings_lstripped(strings, *lstrip): + ret = [] + for start in lstrip: + for s in strings: + if s.startswith(start): + ret.append(s[len(start) :]) + return ret + + +def get_include_args(objdir): + """Get the include args. + Use the stuff to do with siesta.F to obtain these args.""" + p = sp.run("make --dry-run siesta.o", shell=True, text=True, stdout=sp.PIPE, check=True, cwd=objdir) + commands = group_make_commands(p.stdout) + siesta_cmd = next(c for c in commands if "siesta.F" in c and "-c " in c) + args = siesta_cmd.split() + return [abs_relative_dir(objdir, inc) for inc in get_strings_lstripped(args, "-I")] + + +def get_link_args(objdir): + p = sp.run("make --dry-run siesta", shell=True, text=True, stdout=sp.PIPE, check=True, cwd=objdir) + commands = group_make_commands(p.stdout) + siesta_cmd = next(c for c in commands if "-o siesta" in c) + args = siesta_cmd.split() + lpaths = [abs_relative_dir(objdir, p) for p in get_strings_lstripped(args, "-L", "-Wl,rpath=")] + libs = get_strings_lstripped(args, "-l") + return lpaths, libs + + +def manual_create_libsiesta(objdir): + """For use with versions of Siesta that don't create the libsiesta.a file""" + libsiesta_tn = Path("libsiesta_thin_nested.a") + libsiesta = Path("libsiesta.a") + for l in (libsiesta_tn, libsiesta): + pl = objdir / l + if pl.exists(): + raise ValueError(pl, "already exists!") + + compiled_files = list(objdir.glob("*.o")) + list(objdir.glob("*.a")) + compiled_files = list(map(str, compiled_files)) + sp.run(["ar", "rcsT", str(libsiesta_tn)] + compiled_files, check=True, cwd=objdir) + sp.run( + ["ar", "-M"], + text=True, + input=f"CREATE {libsiesta!s}\nADDLIB {libsiesta_tn!s}\nSAVE\nEND", + check=True, + cwd=objdir, + ) + sp.run(["ranlib", str(libsiesta)], check=True, cwd=objdir) + + +def render_pkg_config(name, version, lpaths, libs, includes): + return PKGCONF_TEXT.format( + name=name, + version=version, + lpaths=" ".join(f"-L{p}" for p in lpaths), + libs=" ".join(f"-l{lib}" for lib in libs), + includes=" ".join(f"-I{p}" for p in includes), + ) + + +def get_parser(): + parser = argparse.ArgumentParser(usage=__doc__) + a = parser.add_argument + a("objdir", type=Path, help="The Siesta OBJ dir where you have already build Siesta.") + a( + "--pkgdir", + type=Path, + default=Path(), + help="Where to put the generated pkg-conf. Defaults to working directory.", + ) + a( + "--create-libsiesta", + action="store_true", + help=( + "Some versions of Siesta don't make the libsiesta.a. Use this option to run the archiver to make the lib." + ), + ) + return parser + + +if __name__ == "__main__": + args = get_parser().parse_args() + abs_obj = args.objdir.resolve() + + includes = get_include_args(abs_obj) + includes.append(str(abs_obj)) + + lpaths, libs = get_link_args(abs_obj) + lpaths.append(str(abs_obj)) + libs.append("siesta") + + if args.create_libsiesta: + print("Creating libsiesta.a in", abs_obj, "...") + manual_create_libsiesta(abs_obj) + + pcfile = args.pkgdir / "Siesta.pc" + pcfile.write_text(render_pkg_config("Siesta", "4.2", lpaths, libs, includes)) + + print( + f"Wrote a pkgconf file to {pcfile}. You can now use" + f"\n $PKG_CONFIG_PATH={pcfile.parent.resolve()!s}:$PKG_CONFIG_PATH" + f" pip3 install {Path(__file__).resolve()!s}\nto install PSiesta." + "\nIt might be advantageous to use --no-build-isolation (it's a bunch faster!)." + "\nIf you are compiling with MPI, you must use CC=mpicc FC=mpifort (or whichever appropriate for your" + " compiler suite), as this script can't auto-setup this for you." + ) diff --git a/meson.build b/meson.build index 1e24b3b..6268092 100644 --- a/meson.build +++ b/meson.build @@ -17,6 +17,4 @@ cython = find_program('cython', required: true) libsiesta_dep = dependency('Siesta', required: true) -#mpifort_dep = dependency('mpi', language: 'fortran', required: true) - subdir('psiesta') diff --git a/psiesta/meson.build b/psiesta/meson.build index a3a7702..6364e50 100644 --- a/psiesta/meson.build +++ b/psiesta/meson.build @@ -12,7 +12,7 @@ psiesta_ext = py3.extension_module( [psiesta_c, fpsiesta_files], subdir: 'psiesta', install: true, - dependencies: [py3_dep, libsiesta_dep] # , mpifort_dep + dependencies: [py3_dep, libsiesta_dep] ) pyfiles = files( diff --git a/pyproject.toml b/pyproject.toml index b972f5c..4695999 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,4 +11,4 @@ summary="Run Siesta as a subroutine in Python, with ASE bindings" home-page="https://github.com/jonaslb/psiesta" requires-python=">=3.6" description-file="README.md" -requires = ["numpy", "mpi4py", "sisl"] +requires = ["numpy", "mpi4py", "ase", "sisl"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 7ef3a1d..0000000 --- a/setup.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python3 -"""PSiesta: Siesta as a Python library -To build/install, please follow these instructions: -1. Compile siesta and the 'SiestaSubroutine' library in your Siesta Obj dir: `make siesta lib` -2. OBJ=/my/libraries/siesta/Obj ./setup.py install [--user] [--prefix=] -setup.py will use the Makefile in your Obj dir to decide compilation flags, includes, etc. -It is necessary that externally linked libraries, eg. Flook, if used, is compiled with -fPIC. -""" -import setuptools # noqa -from distutils.core import setup -from distutils.extension import Extension -from pathlib import Path -from Cython.Build import cythonize -import numpy as np -from contextlib import contextmanager -import os -import subprocess as sp -from itertools import chain -import sys -import re - - -# args = \ -# "-qopenmp -lmkl_intel_thread -lmkl_core -lmkl_intel_lp64 -lmkl_blas95_lp64 "\ -# "-lmkl_lapack95_lp64 -lmkl_scalapack_lp64 -lmkl_blacs_intelmpi_lp64 -lnetcdff -lnetcdf "\ -# "-lhdf5_fortran -lhdf5 -lz -lmkl_avx512 -lmkl_def " -# -# siestalib = "libSiestaForces.a libfdf.a libwxml.a libxmlparser.a MatrixSwitch.a libSiestaXC.a "\ -# "libmpi_f90.a libncdf.a libfdict.a" -# siestalib = [str(SIESTAOBJ/t) for t in siestalib.split(" ")] - - -BUILD_ANY_ARG = {"build", "install", "bdist", "develop"} -if any(x in cmd.lower() for cmd in sys.argv for x in BUILD_ANY_ARG): - BUILD = True - - -@contextmanager -def cd(where): - old = Path.cwd() - os.chdir(where) - yield - os.chdir(old) - - -def startswith(search, what): - match = re.search(r'\s+'.join(what.split()), search) - return match is not None - - -def get_build_args(objdir): - p = sp.run("make --dry-run siesta.o", shell=True, universal_newlines=True, stdout=sp.PIPE, - check=True, cwd=objdir) - line = next(l for l in p.stdout.split("\n") if "siesta.F" in l and "-c " in l) - args = line.split() - mpifort = args[0] - compile_args = list(filter( - # eg. -fopenmp -fexpensive-optimizations -qopenmp -DMPI -march=native -xHost -O3 - lambda a: any(a.startswith(x) for x in ("-f", "-q", "-D", "-m", "-x", "-O")), args - )) - includes = list(filter(lambda a: a.startswith("-I"), args)) - return mpifort, compile_args, [f"-I{SIESTAOBJ!s}/"] + includes - - -def get_link_args(objdir, compiler, extra=[]): - p = sp.run("make --dry-run siesta", shell=True, universal_newlines=True, stdout=sp.PIPE, - check=True, cwd=objdir) - output = p.stdout.split("\n") - linestart = next(i for i, l in enumerate(output) if startswith(l, f"{compiler} -o siesta\\s")) - for i, l in enumerate(output[linestart:]): - if not l.endswith("\\"): - break - lineend = linestart + i + 1 - args = "\n".join(output[linestart:lineend]+extra).replace("\\\n", " ").split() - libpath = list(filter(lambda a: a.startswith("-L"), args)) - runlibpath = list(filter(lambda a: a.startswith("-Wl,-rpath="), args)) - libs = list(filter(lambda a: a.startswith("-l"), args)) - siestalibs = ["libSiestaForces.a"] + list(filter(lambda a: a.endswith(".a"), args)) - return libpath, runlibpath, libs, siestalibs - - -def ensure_build_fsiesta(objdir): - libf = objdir / "libSiestaForces.a" - if not libf.is_file(): - sp.run("make lib", shell=True, check=True, cwd=objdir) - - -def build_fortran(compiler, c_args, includes): - # TODO: Always builds inplace, maybe monkey the Cython extension some more? (avoid building until setup()) - # -fp-model source? - cmd = f"{compiler} -c {' '.join(c_args)} {' '.join(includes)} -o fpsiesta.o fpsiesta.f90" - print(cmd) - sp.run(cmd, shell=True, check=True, cwd=Path(__file__).parent/"psiesta"/"c_bindings") - - -extargs = dict( - name='psiesta._psiesta', - sources=['psiesta/_psiesta.pyx'] -) -if BUILD: - SIESTAOBJ = os.environ.get("OBJ") - if SIESTAOBJ is None or SIESTAOBJ == "": - raise ValueError("You must specify the siesta obj dir (OBJ=/my/siesta/Obj python3 setup.py...)") - SIESTAOBJ = Path(SIESTAOBJ).resolve() - ensure_build_fsiesta(SIESTAOBJ) - - # eg "-lmkl_avx512 -lmkl_def" (on niflheim) - EXTRA_ARGS = os.environ.get("EXTRA_COMP_ARGS", "").split() - - fc, comp_args, includes = get_build_args(SIESTAOBJ) - if "-fp-model" in comp_args: - # intel argument. works fine without it. it needs a second argument that we aren't parsing - del comp_args[comp_args.index("-fp-model")] - print(f"DETECTED FC: {fc}") - print(f"DETECTED compiler args: {comp_args}") - print(f"DETECTED includes: {includes}") - build_fortran(fc, comp_args + EXTRA_ARGS, includes) - fortranlib = dict( - ifort="ifcore", mpiifort="ifcore" - ).get(fc, "gfortran") - lpaths, runlibpaths, libs, siestalibs = get_link_args(SIESTAOBJ, fc, extra=EXTRA_ARGS) - lpprint = '\n'.join(lpaths) - print(f"DETECTED lpaths=[multiline][\n{lpprint}\n]") - lpprint = '\n'.join(runlibpaths) - print(f"DETECTED runpaths=[multiline][\n{lpprint}\n]") - print(f"DETECTED libs={libs}") - print(f"DETECTED siestalibs={siestalibs}") - extargs.update(dict( - extra_compile_args=comp_args, - library_dirs=[l[2:] for l in lpaths], - runtime_library_dirs=[r[11:] for r in runlibpaths], - libraries=[fortranlib] + [l[2:] for l in libs], - extra_objects=["psiesta/c_bindings/fpsiesta.o"] + [str(SIESTAOBJ/l) for l in siestalibs], - include_dirs=[i[2:] for i in includes] - )) - -cythonmods = cythonize([Extension(**extargs)]) - -long_description = (Path(__file__).parent / "README.md").read_text() - -setup( - name="psiesta", - packages=['psiesta'], - version='0.3', - include_dirs=[np.get_include()], - ext_modules=cythonmods, - zip_safe=False, - install_requires=[ - "numpy", - "mpi4py", - "sisl", - ], - description='Siesta as a Python library', - long_description=long_description, - long_description_content_type="text/markdown", - author="Jonas Lundholm Bertelsen", - url="https://github.com/jonaslb/psiesta", - license="GPLv3+", - python_requires='>=3.6', -) From ab5c422141f836e28ef4d7d5b627186680a79382 Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Tue, 22 Dec 2020 13:46:13 +0100 Subject: [PATCH 4/9] Building with Meson is now a breeze --- README.md | 24 +++---- find_manual_siesta.py | 149 ---------------------------------------- meson.build | 13 +++- psiesta/meson.build | 2 +- subprojects/siesta.wrap | 4 ++ 5 files changed, 24 insertions(+), 168 deletions(-) delete mode 100644 find_manual_siesta.py create mode 100644 subprojects/siesta.wrap diff --git a/README.md b/README.md index 43c7011..da68c7b 100644 --- a/README.md +++ b/README.md @@ -66,22 +66,14 @@ You should use a Siesta version later than the git master as of 2020-06-10, as a ## Obtaining source, building and installing You can obtain the source by simply cloning this repository. -To build and install, there are two situations listed below. -In all cases, Siesta must be built with `-fPIC`. -You must also use a Siesta version later than the git master as of 2020-06-10 as mentioned above. - -### A: Installed Siesta *WITH* libsiesta and pkgconf -Siesta may include a "libsiesta" functionality in the near future (probably v. 4.2+). -If you have such a version installed, this is for you. -In that case you only need to execute `pip3 install .` to install PSiesta. -This uses `pkgconf` to find all dependencies and link flags. -You may use the flags `--user` to install for yourself only and `--no-build-isolation` which usually speeds things up. - -### B: Custom built Siesta not installed -In this case you can use the script `find_manual_siesta.py` in the base directory to create an ad-hoc `pkgconf`-file that directs the build system towards your Siesta obj-dir. -The script takes the object dir as the first argument. -You can optionally pass `--create-libsiesta` if your version of Siesta hasn't created `libsiesta.a` in the object-directory. -The script will tell you how to proceed when it's done (similar to situation A, but you may need to set further options). +You only need to execute `pip3 install .` to install PSiesta. +You may use the flags `--user` to install for yourself only. + +Note that for some reason, the default pip behavour of using build isolation is an extremely slow process. +You can speed up the process by using `--no-build-isolation`, but then you must install the build dependencies manually first: `mesonpep517` and `ninja`. + +Under the hood, `pkgconfig` is used via Meson to find all dependencies and link flags. +Siesta is currently built from a custom branch based on the upstream PSML branch with meson build instructions added. ## Behaviour See also [the SiestaSubroutine readme](https://gitlab.com/siesta-project/siesta/tree/master/Util/SiestaSubroutine/README). diff --git a/find_manual_siesta.py b/find_manual_siesta.py deleted file mode 100644 index 386671a..0000000 --- a/find_manual_siesta.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python3 -"""Sets up an improvised .pc file that refers to a Siesta OBJ directory. -ONLY use this functionality if you don't have the ability to properly install -a libsiesta and pkgconf file. -You must have already built Siesta in the Obj directory (and not cleaned up). -""" -import subprocess as sp -from pathlib import Path -import argparse - - -PKGCONF_TEXT = """ -Name: {name} -Description: Improper pkgconf-file. Links to a custom Siesta Obj dir. -Version: {version} -Libs: {lpaths} {libs} -Cflags: {includes} -""" - - -def group_make_commands(lines): - if isinstance(lines, str): - lines = lines.splitlines() - commands = [] - cur_cmd = [] - for l in lines: - l = l.strip() - continues = l.endswith("\\") - cur_cmd.append(l[:-1] if continues else l) - if not continues: - commands.append(" ".join(cur_cmd)) - cur_cmd = [] - return commands - - -def abs_relative_dir(base, rel): - rel = Path(rel) - if not rel.root: - return str((Path(base) / rel).resolve()) - return rel - - -def get_strings_lstripped(strings, *lstrip): - ret = [] - for start in lstrip: - for s in strings: - if s.startswith(start): - ret.append(s[len(start) :]) - return ret - - -def get_include_args(objdir): - """Get the include args. - Use the stuff to do with siesta.F to obtain these args.""" - p = sp.run("make --dry-run siesta.o", shell=True, text=True, stdout=sp.PIPE, check=True, cwd=objdir) - commands = group_make_commands(p.stdout) - siesta_cmd = next(c for c in commands if "siesta.F" in c and "-c " in c) - args = siesta_cmd.split() - return [abs_relative_dir(objdir, inc) for inc in get_strings_lstripped(args, "-I")] - - -def get_link_args(objdir): - p = sp.run("make --dry-run siesta", shell=True, text=True, stdout=sp.PIPE, check=True, cwd=objdir) - commands = group_make_commands(p.stdout) - siesta_cmd = next(c for c in commands if "-o siesta" in c) - args = siesta_cmd.split() - lpaths = [abs_relative_dir(objdir, p) for p in get_strings_lstripped(args, "-L", "-Wl,rpath=")] - libs = get_strings_lstripped(args, "-l") - return lpaths, libs - - -def manual_create_libsiesta(objdir): - """For use with versions of Siesta that don't create the libsiesta.a file""" - libsiesta_tn = Path("libsiesta_thin_nested.a") - libsiesta = Path("libsiesta.a") - for l in (libsiesta_tn, libsiesta): - pl = objdir / l - if pl.exists(): - raise ValueError(pl, "already exists!") - - compiled_files = list(objdir.glob("*.o")) + list(objdir.glob("*.a")) - compiled_files = list(map(str, compiled_files)) - sp.run(["ar", "rcsT", str(libsiesta_tn)] + compiled_files, check=True, cwd=objdir) - sp.run( - ["ar", "-M"], - text=True, - input=f"CREATE {libsiesta!s}\nADDLIB {libsiesta_tn!s}\nSAVE\nEND", - check=True, - cwd=objdir, - ) - sp.run(["ranlib", str(libsiesta)], check=True, cwd=objdir) - - -def render_pkg_config(name, version, lpaths, libs, includes): - return PKGCONF_TEXT.format( - name=name, - version=version, - lpaths=" ".join(f"-L{p}" for p in lpaths), - libs=" ".join(f"-l{lib}" for lib in libs), - includes=" ".join(f"-I{p}" for p in includes), - ) - - -def get_parser(): - parser = argparse.ArgumentParser(usage=__doc__) - a = parser.add_argument - a("objdir", type=Path, help="The Siesta OBJ dir where you have already build Siesta.") - a( - "--pkgdir", - type=Path, - default=Path(), - help="Where to put the generated pkg-conf. Defaults to working directory.", - ) - a( - "--create-libsiesta", - action="store_true", - help=( - "Some versions of Siesta don't make the libsiesta.a. Use this option to run the archiver to make the lib." - ), - ) - return parser - - -if __name__ == "__main__": - args = get_parser().parse_args() - abs_obj = args.objdir.resolve() - - includes = get_include_args(abs_obj) - includes.append(str(abs_obj)) - - lpaths, libs = get_link_args(abs_obj) - lpaths.append(str(abs_obj)) - libs.append("siesta") - - if args.create_libsiesta: - print("Creating libsiesta.a in", abs_obj, "...") - manual_create_libsiesta(abs_obj) - - pcfile = args.pkgdir / "Siesta.pc" - pcfile.write_text(render_pkg_config("Siesta", "4.2", lpaths, libs, includes)) - - print( - f"Wrote a pkgconf file to {pcfile}. You can now use" - f"\n $PKG_CONFIG_PATH={pcfile.parent.resolve()!s}:$PKG_CONFIG_PATH" - f" pip3 install {Path(__file__).resolve()!s}\nto install PSiesta." - "\nIt might be advantageous to use --no-build-isolation (it's a bunch faster!)." - "\nIf you are compiling with MPI, you must use CC=mpicc FC=mpifort (or whichever appropriate for your" - " compiler suite), as this script can't auto-setup this for you." - ) diff --git a/meson.build b/meson.build index 6268092..cb890b0 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,9 @@ project( 'PSiesta', version: '0.3.0', + default_options: [ + 'mpi=enabled' # Subprojects can switch this, but we require it + ] ) add_languages('c', 'fortran', required: true) @@ -10,11 +13,17 @@ py3_dep = py3.dependency(required: true) py_version = py3.language_version() if py_version.version_compare('< 3.6') - error('Old python detected') + error('Old python detected') endif cython = find_program('cython', required: true) -libsiesta_dep = dependency('Siesta', required: true) +mpi_dep = dependency('mpi', language: 'fortran', required: true) + +libsiesta_dep = dependency('siesta', required: false) +if not libsiesta_dep.found() + siestaproj = subproject('siesta') + libsiesta_dep = siestaproj.get_variable('siesta_dep') +endif subdir('psiesta') diff --git a/psiesta/meson.build b/psiesta/meson.build index 6364e50..25318ea 100644 --- a/psiesta/meson.build +++ b/psiesta/meson.build @@ -12,7 +12,7 @@ psiesta_ext = py3.extension_module( [psiesta_c, fpsiesta_files], subdir: 'psiesta', install: true, - dependencies: [py3_dep, libsiesta_dep] + dependencies: [py3_dep, mpi_dep, libsiesta_dep] ) pyfiles = files( diff --git a/subprojects/siesta.wrap b/subprojects/siesta.wrap new file mode 100644 index 0000000..c8914d0 --- /dev/null +++ b/subprojects/siesta.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url = https://gitlab.com/jonaslb/siesta.git +revision = psml-meson +depth=1 From 9e550ba8352fa7209ddf2dd6d0b1f085e5623c34 Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Wed, 23 Dec 2020 01:52:40 +0100 Subject: [PATCH 5/9] Big clean, direct pip install now possible --- meson.build | 11 +++++++++- meson_options.txt | 2 ++ psiesta/meson.build | 2 +- pyproject.toml | 2 +- requirements.txt | 4 ---- subprojects/.gitignore | 14 ++++++++++++ test/C.psf.zst | Bin 0 -> 23105 bytes test/run.py | 48 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 meson_options.txt delete mode 100644 requirements.txt create mode 100644 subprojects/.gitignore create mode 100644 test/C.psf.zst create mode 100755 test/run.py diff --git a/meson.build b/meson.build index cb890b0..9071017 100644 --- a/meson.build +++ b/meson.build @@ -2,11 +2,20 @@ project( 'PSiesta', version: '0.3.0', default_options: [ - 'mpi=enabled' # Subprojects can switch this, but we require it + 'mpi=enabled', + 'optimization=3', + # Consider using a native file (https://mesonbuild.com/Native-environments.html) if you want to + # set options (or switch toolchainetc). + # In practice further optimization than -O3 appears to be negligible. + # 'fortran_args=["-march=native", "-funroll-loops", "-fsplit-loops", "-funswitch-loops"]', ] ) add_languages('c', 'fortran', required: true) +if not get_option('mpi').enabled() + error('MPI is required.') +endif + py_mod = import('python') py3 = py_mod.find_installation('python3') py3_dep = py3.dependency(required: true) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..dc09a54 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,2 @@ +# Always on! +option('mpi', type: 'feature', value: 'enabled', yield: false) \ No newline at end of file diff --git a/psiesta/meson.build b/psiesta/meson.build index 25318ea..174eb76 100644 --- a/psiesta/meson.build +++ b/psiesta/meson.build @@ -21,4 +21,4 @@ pyfiles = files( 'psiesta.py', ) -py3.install_sources(pyfiles, subdir: 'psiesta') +py3.install_sources(pyfiles, subdir: 'psiesta', pure: false) diff --git a/pyproject.toml b/pyproject.toml index 4695999..914bad3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] # https://thiblahute.gitlab.io/mesonpep517/ -requires = ["mesonpep517", "ninja"] +requires = ["mesonpep517", "ninja", "Cython", "wheel<0.35"] build-backend = "mesonpep517.buildapi" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e1d8001..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -cython -numpy -mpi4py -sisl diff --git a/subprojects/.gitignore b/subprojects/.gitignore new file mode 100644 index 0000000..2f63ed5 --- /dev/null +++ b/subprojects/.gitignore @@ -0,0 +1,14 @@ +# Subproject folders +/fdict +/gridxc +/ncdf +/psml +/siesta +/xmlf90 + +# Wrapfiles promoted from siesta +/fdict.wrap +/gridxc.wrap +/ncdf.wrap +/psml.wrap +/xmlf90.wrap diff --git a/test/C.psf.zst b/test/C.psf.zst new file mode 100644 index 0000000000000000000000000000000000000000..0d2ccfd9a5165978e390869d95b5a5f20a8d0e84 GIT binary patch literal 23105 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 literal 0 HcmV?d00001 diff --git a/test/run.py b/test/run.py new file mode 100755 index 0000000..96616ed --- /dev/null +++ b/test/run.py @@ -0,0 +1,48 @@ +#!/usr/bin/env mpirun -n 4 python3 -m mpi4py +""" +A "slow" test to see if it works and also judge performance a bit. +On a AMD 1600X this file (see shebang) runs in about : +- 109s with O0 march=native +- 54 s with O1 march=native +- 49-50s with O2 march=native +- low 49s with O3 march=native +- 49-50s with O3 march=native funroll-loops +Also tried some other loop related options but basically I don't think they give anything. O3 is fine. +(march=native is also no big deal, so I've disabled it) +""" +from mpi4py import MPI +import sisl as si +from psiesta import FilePSiesta +import numpy as np +from datetime import datetime +import shutil +import sys +import subprocess as sp +from pathlib import Path + +rank = MPI.COMM_WORLD.rank + +if rank == 0: + sp.run(['zstd', '-d', '-f', 'C.psf.zst']) + +t0 = datetime.now() + +g = si.geom.graphene().tile(5, 0).tile(5, 1) +fdf = """ +TS.HS.Save True +SolutionMethod diagon +PAO.BasisSize SZP +""" +calc = FilePSiesta(fdf, '.', 'tenbyten', geometry=g) +calc.run() +g.xyz[25, 2] += 0.03 +calc.run() +del calc + +t1 = datetime.now() + +if rank == 0: + print(f"Time taken: {t1-t0}") + if "keep" not in sys.argv: + shutil.rmtree('tenbyten') + Path('C.psf').unlink() \ No newline at end of file From 3b5ad0b0df85ab461ffe2e3d1431c5408096fee6 Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Sun, 31 May 2026 00:55:31 +0200 Subject: [PATCH 6/9] Preserve dirty pre-migration snapshot --- .gitignore | 2 + BUILD-ENVIRONMENT.md | 89 ++++++++++++++++++++++++++++++++++ BUILD-SCIKIT-BUILD-CORE.md | 86 +++++++++++++++++++++++++++++++++ CMakeLists.txt | 97 ++++++++++++++++++++++++++++++++++++++ README.md | 11 +++-- pyproject.toml | 27 ++++++----- 6 files changed, 296 insertions(+), 16 deletions(-) create mode 100644 BUILD-ENVIRONMENT.md create mode 100644 BUILD-SCIKIT-BUILD-CORE.md create mode 100644 CMakeLists.txt diff --git a/.gitignore b/.gitignore index 935974c..5d2e6ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ *.so *.o *.mod +*.whl _psiesta.c +build/ builddir Siesta.pc diff --git a/BUILD-ENVIRONMENT.md b/BUILD-ENVIRONMENT.md new file mode 100644 index 0000000..7c99924 --- /dev/null +++ b/BUILD-ENVIRONMENT.md @@ -0,0 +1,89 @@ +# Build Environment Notes + +This project needs a substantial native scientific build stack to build upstream Siesta and the PSiesta extension. The goal is to avoid installing that stack globally with the system package manager. + +## Preferred Direction: Nix Dev Shell + +The preferred direction is to use Nix for the native build environment while keeping `pyproject.toml` as the source of truth for the Python package. + +Nix should provide: + +- C, C++, and Fortran compilers. +- CMake and Ninja. +- `pkg-config`. +- MPI, likely OpenMPI for local multi-process runs. +- BLAS, LAPACK, and ScaLAPACK. +- NetCDF C and Fortran. +- Siesta optional libraries such as libxc, libpsml, libfdf, xmlf90, libgridxc, and FFTW where available. +- Python plus build-time Python tools such as `pip`, `scikit-build-core`, `cython`, and `numpy`. + +The agent can work naturally with this setup by prefixing commands with `nix develop -c`, for example: + +```bash +nix develop -c python -m pip wheel . -v --no-build-isolation +nix develop -c python -m pip install -v --no-build-isolation -e . +``` + +This keeps the normal repository filesystem and avoids container bind-mount or shell-attach friction. + +## Nix Staging Plan + +Use Nix in two stages. + +Stage 1: development shell only. + +Nix supplies native dependencies and Python build tools. PSiesta itself is still built by pip/scikit-build-core from `pyproject.toml`. + +Stage 2: full Nix packaging if useful. + +Once the CMake build works, Siesta and PSiesta can optionally become proper Nix derivations for stronger reproducibility. This is more work and should not block the POC. + +## Python Dependency Duplication + +Avoid duplicating runtime Python dependencies in the environment definition where possible. Runtime dependencies belong in `pyproject.toml`. + +For the initial Nix shell, only duplicate Python packages required to build without isolation: + +- `scikit-build-core` +- `cython` +- `numpy` +- `pip` + +If this still feels too duplicated, an alternative is to let Nix provide native libraries plus `uv`, then let `uv` resolve the Python dependencies from `pyproject.toml`. + +## Siesta Source Strategy + +Initially, let PSiesta's CMake build fetch upstream Siesta with `FetchContent`. This is convenient for proving the integration. + +Later, if stricter reproducibility is desired, Siesta can be provided by Nix instead: + +- As a fixed source input. +- As a Nix-built CMake package. +- As a source path passed to PSiesta with `PSIESTA_SIESTA_SOURCE_DIR`. + +## Why Not Containers First + +Containers remain a reasonable fallback, but they are less convenient for this POC: + +- The agent must run commands through the container boundary. +- Build paths and caches are more awkward. +- MPI can work locally in containers, but host integration can become fiddly. +- Editing on the host while building in a container is workable but less direct than a Nix shell. + +## Why Not Conda/Pixi First + +Conda-forge likely has many of the required scientific packages, and pixi would be practical. However, this project prefers avoiding conda-style environment management and duplicated dependency metadata. Nix is a better match for a clean native toolchain shell while keeping Python packaging metadata in `pyproject.toml`. + +## Immediate Need + +The current host lacks a visible Fortran compiler. The first useful environment milestone is a shell where these commands work: + +```bash +cc --version +mpicc --version +mpifort --version +cmake --version +python -c "import numpy; print(numpy.get_include())" +``` + +Once that is available, rerun the scikit-build-core POC from inside the environment. diff --git a/BUILD-SCIKIT-BUILD-CORE.md b/BUILD-SCIKIT-BUILD-CORE.md new file mode 100644 index 0000000..4d4b8ea --- /dev/null +++ b/BUILD-SCIKIT-BUILD-CORE.md @@ -0,0 +1,86 @@ +# scikit-build-core Migration Notes + +This branch is a proof-of-concept for replacing the old Meson/mesonpep517 build with scikit-build-core and CMake. + +## Goal + +PSiesta should build its Python extension with CMake while using upstream Siesta's native CMake build directly. The intended integration story is simpler than the old Meson branch: stop maintaining a custom Siesta Meson fork and link against upstream Siesta as a CMake subproject or installed CMake package. + +## Current Shape + +The POC keeps the existing Python and binding design: + +- `psiesta/_psiesta.pyx` remains the Cython layer. +- `psiesta/c_bindings/fpsiesta.f90` remains the small Fortran `bind(c)` shim. +- The shim still calls the historical `fsiesta` API: `siesta_launch`, `siesta_units`, `siesta_forces`, `siesta_get`, and `siesta_quit`. +- The Python API is intentionally unchanged for now. + +The new top-level `CMakeLists.txt` currently: + +- Enables C and Fortran. +- Finds Python, NumPy, Cython, and MPI. +- Builds `_psiesta` as a Python extension module. +- Fetches upstream Siesta by default with CMake `FetchContent`. +- Allows pointing at an existing Siesta checkout via `PSIESTA_SIESTA_SOURCE_DIR`. +- Allows a future installed-package mode via `PSIESTA_USE_SYSTEM_SIESTA`. + +## Siesta Build Defaults + +For the subroutine binding POC, the CMake integration sets these Siesta defaults before adding/fetching Siesta: + +- `SIESTA_TESTS=OFF` +- `SIESTA_INSTALL=OFF` +- `SIESTA_WITH_MPI=ON` +- `SIESTA_WITH_PEXSI=OFF` +- `SIESTA_SHARED_LIBS=OFF` +- `CMAKE_POSITION_INDEPENDENT_CODE=ON` + +The MPI subroutine mode is the relevant one for PSiesta because Siesta is linked into the Python extension rather than contacted through pipes or sockets. + +## Build Commands + +With build isolation: + +```bash +python -m pip wheel . -v +``` + +Without build isolation, after installing Python build requirements in the active environment: + +```bash +python -m pip wheel . -v --no-build-isolation +``` + +Using an existing Siesta checkout: + +```bash +python -m pip wheel . -v -Ccmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta +``` + +## Current Verification State + +The POC reaches CMake when using build isolation. The current host environment does not have a visible Fortran compiler, so configuration stops at: + +```text +No CMAKE_Fortran_COMPILER could be found. +``` + +The next meaningful test needs an environment with a Fortran compiler and MPI compiler wrappers available. + +## Expected Next Issues + +After a Fortran-capable environment is available, the next integration points to check are: + +- The exact upstream Siesta CMake target name for `libsiesta`. +- Whether Siesta's Fortran module include directories propagate to `fpsiesta.f90`. +- Whether minor API updates are needed in `fpsiesta.f90` for current upstream Siesta modules. +- Whether all desired Siesta optional dependencies are discoverable through CMake. +- Whether static linking into the Python extension is clean with MPI and position-independent code. + +## Non-Goals For This POC + +- Refreshing the Python API. +- Designing a full Python abstraction for FDF input. +- Building redistributable wheels. +- Supporting cross-node MPI packaging. +- Removing Meson files before the CMake path is proven. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e5f5afa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,97 @@ +cmake_minimum_required(VERSION 3.20) + +project( + PSiesta + VERSION 0.3.0 + LANGUAGES C Fortran +) + +include(FetchContent) +include(GNUInstallDirs) + +option(PSIESTA_FETCH_SIESTA "Fetch/build Siesta as a CMake subproject" ON) +option(PSIESTA_USE_SYSTEM_SIESTA "Use an installed Siesta CMake package" OFF) +set(PSIESTA_SIESTA_SOURCE_DIR "" CACHE PATH "Existing Siesta source checkout") +set(PSIESTA_SIESTA_GIT_REPOSITORY "https://gitlab.com/siesta-project/siesta.git" CACHE STRING "Siesta git repository") +set(PSIESTA_SIESTA_GIT_TAG "master" CACHE STRING "Siesta git tag, branch, or commit") + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +find_package(Python REQUIRED COMPONENTS Interpreter Development.Module) +find_package(MPI REQUIRED COMPONENTS C Fortran) + +execute_process( + COMMAND "${Python_EXECUTABLE}" -c "import numpy; print(numpy.get_include())" + OUTPUT_VARIABLE NumPy_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY +) + +find_program(CYTHON_EXECUTABLE cython REQUIRED) + +if(PSIESTA_USE_SYSTEM_SIESTA) + find_package(SIESTA CONFIG REQUIRED) +elseif(PSIESTA_SIESTA_SOURCE_DIR) + set(SIESTA_TESTS OFF CACHE BOOL "" FORCE) + set(SIESTA_INSTALL OFF CACHE BOOL "" FORCE) + set(SIESTA_WITH_MPI ON CACHE BOOL "" FORCE) + set(SIESTA_WITH_PEXSI OFF CACHE BOOL "" FORCE) + set(SIESTA_SHARED_LIBS OFF CACHE BOOL "" FORCE) + add_subdirectory("${PSIESTA_SIESTA_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/_deps/siesta-build") +elseif(PSIESTA_FETCH_SIESTA) + set(SIESTA_TESTS OFF CACHE BOOL "" FORCE) + set(SIESTA_INSTALL OFF CACHE BOOL "" FORCE) + set(SIESTA_WITH_MPI ON CACHE BOOL "" FORCE) + set(SIESTA_WITH_PEXSI OFF CACHE BOOL "" FORCE) + set(SIESTA_SHARED_LIBS OFF CACHE BOOL "" FORCE) + FetchContent_Declare( + siesta + GIT_REPOSITORY "${PSIESTA_SIESTA_GIT_REPOSITORY}" + GIT_TAG "${PSIESTA_SIESTA_GIT_TAG}" + ) + FetchContent_MakeAvailable(siesta) +else() + message(FATAL_ERROR "Set one of PSIESTA_USE_SYSTEM_SIESTA, PSIESTA_SIESTA_SOURCE_DIR, or PSIESTA_FETCH_SIESTA") +endif() + +set(_siesta_target "") +foreach(candidate IN ITEMS SIESTA::libsiesta SIESTA.libsiesta SIESTA-libsiesta siesta.libsiesta siesta-libsiesta) + if(TARGET "${candidate}") + set(_siesta_target "${candidate}") + break() + endif() +endforeach() +if(NOT _siesta_target) + message(FATAL_ERROR "Could not find the upstream Siesta libsiesta CMake target") +endif() +message(STATUS "Using Siesta target: ${_siesta_target}") + +set(_psiesta_c "${CMAKE_CURRENT_BINARY_DIR}/psiesta/_psiesta.c") +add_custom_command( + OUTPUT "${_psiesta_c}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/psiesta" + COMMAND "${CYTHON_EXECUTABLE}" -3 "${CMAKE_CURRENT_SOURCE_DIR}/psiesta/_psiesta.pyx" -o "${_psiesta_c}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/psiesta/_psiesta.pyx" + VERBATIM +) + +Python_add_library(_psiesta MODULE + "${_psiesta_c}" + "${CMAKE_CURRENT_SOURCE_DIR}/psiesta/c_bindings/fpsiesta.f90" + WITH_SOABI +) + +target_include_directories(_psiesta PRIVATE "${NumPy_INCLUDE_DIR}") +target_link_libraries(_psiesta PRIVATE + "${_siesta_target}" + MPI::MPI_C + MPI::MPI_Fortran +) + +install(TARGETS _psiesta LIBRARY DESTINATION psiesta) +install(FILES + "psiesta/__init__.py" + "psiesta/ase.py" + "psiesta/psiesta.py" + DESTINATION psiesta +) diff --git a/README.md b/README.md index da68c7b..6038e4f 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,12 @@ You can obtain the source by simply cloning this repository. You only need to execute `pip3 install .` to install PSiesta. You may use the flags `--user` to install for yourself only. -Note that for some reason, the default pip behavour of using build isolation is an extremely slow process. -You can speed up the process by using `--no-build-isolation`, but then you must install the build dependencies manually first: `mesonpep517` and `ninja`. +The current build-system proof-of-concept uses scikit-build-core and CMake. +By default, CMake fetches upstream Siesta and builds it as a subproject, using Siesta's native CMake build. +You can also point at an existing Siesta checkout with `-Ccmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta` when invoking pip. -Under the hood, `pkgconfig` is used via Meson to find all dependencies and link flags. -Siesta is currently built from a custom branch based on the upstream PSML branch with meson build instructions added. +You need the native Siesta build requirements available in the build environment: C and Fortran compilers, CMake, BLAS/LAPACK, MPI, and ScaLAPACK for the MPI subroutine build. +If you use `--no-build-isolation`, install the Python build requirements manually first: `scikit-build-core`, `cython`, and `numpy`. ## Behaviour See also [the SiestaSubroutine readme](https://gitlab.com/siesta-project/siesta/tree/master/Util/SiestaSubroutine/README). @@ -88,4 +89,4 @@ It will also prepend some settings to your fdf-file: Notably `MD.TypeOfRun force * Only a few properties can currently be fetched directly via the bindings. Other properties must be obtained via the output-files. Feel free to create an issue if you'd like something in particular built-in, or send a PR if you've implemented it already. * You don't get an exception when eg. the fdf-file contains an error. Instead, the whole process dies. - This is because on error, Siesta intentionally kills itself which unfortunately includes the Python process. \ No newline at end of file + This is because on error, Siesta intentionally kills itself which unfortunately includes the Python process. diff --git a/pyproject.toml b/pyproject.toml index 914bad3..f09f0c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,19 @@ [build-system] -# https://thiblahute.gitlab.io/mesonpep517/ -requires = ["mesonpep517", "ninja", "Cython", "wheel<0.35"] -build-backend = "mesonpep517.buildapi" +requires = ["scikit-build-core", "cython", "numpy"] +build-backend = "scikit_build_core.build" +[project] +name = "psiesta" +version = "0.3.0" +description = "Run Siesta as a subroutine in Python, with ASE bindings" +readme = "README.md" +requires-python = ">=3.9" +authors = [{ name = "Jonas Lundholm Bertelsen" }] +dependencies = ["numpy", "mpi4py", "ase", "sisl"] -[tool.mesonpep517.metadata] -author="Jonas Lundholm Bertelsen" -author-email="" -summary="Run Siesta as a subroutine in Python, with ASE bindings" -home-page="https://github.com/jonaslb/psiesta" -requires-python=">=3.6" -description-file="README.md" -requires = ["numpy", "mpi4py", "ase", "sisl"] +[project.urls] +Homepage = "https://github.com/jonaslb/psiesta" + +[tool.scikit-build] +minimum-version = "0.10" +build-dir = "build/{wheel_tag}" From 5859ae8bf30c674f0b1074a9ede6263db62eae9c Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Tue, 9 Jun 2026 21:41:16 +0200 Subject: [PATCH 7/9] Add Nix uv build environment --- BUILD-ENVIRONMENT.md | 106 +++++++++++++++++++------------------------ README.md | 20 +++++--- flake.lock | 27 +++++++++++ flake.nix | 52 +++++++++++++++++++++ 4 files changed, 139 insertions(+), 66 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/BUILD-ENVIRONMENT.md b/BUILD-ENVIRONMENT.md index 7c99924..05410a0 100644 --- a/BUILD-ENVIRONMENT.md +++ b/BUILD-ENVIRONMENT.md @@ -1,89 +1,77 @@ -# Build Environment Notes +# Build Environment -This project needs a substantial native scientific build stack to build upstream Siesta and the PSiesta extension. The goal is to avoid installing that stack globally with the system package manager. +This repository uses a split build environment: -## Preferred Direction: Nix Dev Shell +- Nix provides the native scientific build stack. +- uv manages Python build isolation and Python dependencies from `pyproject.toml`. -The preferred direction is to use Nix for the native build environment while keeping `pyproject.toml` as the source of truth for the Python package. +This keeps compilers, MPI, BLAS/LAPACK, NetCDF, and other native libraries out of the host system package manager while avoiding duplicated Python dependency metadata in Nix. -Nix should provide: +## Requirements -- C, C++, and Fortran compilers. -- CMake and Ninja. -- `pkg-config`. -- MPI, likely OpenMPI for local multi-process runs. -- BLAS, LAPACK, and ScaLAPACK. -- NetCDF C and Fortran. -- Siesta optional libraries such as libxc, libpsml, libfdf, xmlf90, libgridxc, and FFTW where available. -- Python plus build-time Python tools such as `pip`, `scikit-build-core`, `cython`, and `numpy`. - -The agent can work naturally with this setup by prefixing commands with `nix develop -c`, for example: - -```bash -nix develop -c python -m pip wheel . -v --no-build-isolation -nix develop -c python -m pip install -v --no-build-isolation -e . -``` +Install Nix with flakes enabled. On Linux, daemon mode is recommended even for a single-user workstation because `/nix/store` remains root-owned and builds go through the normal Nix daemon path. -This keeps the normal repository filesystem and avoids container bind-mount or shell-attach friction. +The user does not need to be a trusted Nix user for the commands below. -## Nix Staging Plan +## Development Shell -Use Nix in two stages. +Enter the development shell with: -Stage 1: development shell only. - -Nix supplies native dependencies and Python build tools. PSiesta itself is still built by pip/scikit-build-core from `pyproject.toml`. +```bash +nix develop +``` -Stage 2: full Nix packaging if useful. +Or run one command inside it: -Once the CMake build works, Siesta and PSiesta can optionally become proper Nix derivations for stronger reproducibility. This is more work and should not block the POC. +```bash +nix develop -c +``` -## Python Dependency Duplication +The shell provides the native build environment from `flake.nix`, including: -Avoid duplicating runtime Python dependencies in the environment definition where possible. Runtime dependencies belong in `pyproject.toml`. +- C, C++, and Fortran compilers. +- OpenMPI compiler wrappers, with `CC=mpicc`, `CXX=mpicxx`, and `FC=mpifort`. +- CMake, Ninja, and `pkg-config`. +- BLAS, LAPACK, ScaLAPACK, NetCDF C/Fortran, HDF5, FFTW, libxc, readline, zlib, and curl. +- `uv` for Python dependency resolution and PEP 517 builds. -For the initial Nix shell, only duplicate Python packages required to build without isolation: +Check the shell with: -- `scikit-build-core` -- `cython` -- `numpy` -- `pip` +```bash +nix develop -c sh -c 'cc --version && mpicc --version && mpifort --version && cmake --version && uv --version' +``` -If this still feels too duplicated, an alternative is to let Nix provide native libraries plus `uv`, then let `uv` resolve the Python dependencies from `pyproject.toml`. +## Building A Wheel -## Siesta Source Strategy +Build the wheel with: -Initially, let PSiesta's CMake build fetch upstream Siesta with `FetchContent`. This is convenient for proving the integration. +```bash +nix develop -c uv build --wheel +``` -Later, if stricter reproducibility is desired, Siesta can be provided by Nix instead: +`uv` reads `pyproject.toml`, creates an isolated Python build environment, installs the declared Python build requirements, and calls the scikit-build-core backend. scikit-build-core then configures and builds the CMake project. -- As a fixed source input. -- As a Nix-built CMake package. -- As a source path passed to PSiesta with `PSIESTA_SIESTA_SOURCE_DIR`. +By default, the CMake project fetches upstream Siesta and builds it as a subproject. The build output is written under `build/`, and the wheel is written under `dist/`. -## Why Not Containers First +## Using An Existing Siesta Checkout -Containers remain a reasonable fallback, but they are less convenient for this POC: +To build against an existing Siesta source checkout instead of fetching it, pass the CMake definition through uv/scikit-build-core: -- The agent must run commands through the container boundary. -- Build paths and caches are more awkward. -- MPI can work locally in containers, but host integration can become fiddly. -- Editing on the host while building in a container is workable but less direct than a Nix shell. +```bash +nix develop -c uv build --wheel --config-setting=cmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta +``` -## Why Not Conda/Pixi First +## Build Artifacts -Conda-forge likely has many of the required scientific packages, and pixi would be practical. However, this project prefers avoiding conda-style environment management and duplicated dependency metadata. Nix is a better match for a clean native toolchain shell while keeping Python packaging metadata in `pyproject.toml`. +Expected local artifacts include: -## Immediate Need +- `build/` for scikit-build-core, CMake, and fetched Siesta build trees. +- `dist/` for generated wheels. +- `~/.cache/uv/` for uv-managed Python build environments and downloads. +- `/nix/store` and `/nix/var/nix` for Nix-managed native dependencies and build metadata. -The current host lacks a visible Fortran compiler. The first useful environment milestone is a shell where these commands work: +The Nix-managed system state is garbage-collectable through normal Nix commands. The repository build artifacts can be removed with: ```bash -cc --version -mpicc --version -mpifort --version -cmake --version -python -c "import numpy; print(numpy.get_include())" +rm -rf build dist ``` - -Once that is available, rerun the scikit-build-core POC from inside the environment. diff --git a/README.md b/README.md index 6038e4f..11a0cb0 100644 --- a/README.md +++ b/README.md @@ -66,15 +66,21 @@ You should use a Siesta version later than the git master as of 2020-06-10, as a ## Obtaining source, building and installing You can obtain the source by simply cloning this repository. -You only need to execute `pip3 install .` to install PSiesta. -You may use the flags `--user` to install for yourself only. +The current build uses scikit-build-core and CMake. By default, CMake fetches upstream Siesta and builds it as a subproject, using Siesta's native CMake build. -The current build-system proof-of-concept uses scikit-build-core and CMake. -By default, CMake fetches upstream Siesta and builds it as a subproject, using Siesta's native CMake build. -You can also point at an existing Siesta checkout with `-Ccmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta` when invoking pip. +For development, the recommended environment is Nix for native dependencies and uv for Python dependencies: -You need the native Siesta build requirements available in the build environment: C and Fortran compilers, CMake, BLAS/LAPACK, MPI, and ScaLAPACK for the MPI subroutine build. -If you use `--no-build-isolation`, install the Python build requirements manually first: `scikit-build-core`, `cython`, and `numpy`. +```bash +nix develop -c uv build --wheel +``` + +This produces a wheel in `dist/`. See `BUILD-ENVIRONMENT.md` for details. + +You can point at an existing Siesta checkout with: + +```bash +nix develop -c uv build --wheel --config-setting=cmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta +``` ## Behaviour See also [the SiestaSubroutine readme](https://gitlab.com/siesta-project/siesta/tree/master/Util/SiestaSubroutine/README). diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..14e3cb3 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1780749050, + "narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a799d3e3886da994fa307f817a6bc705ae538eeb", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..69489b8 --- /dev/null +++ b/flake.nix @@ -0,0 +1,52 @@ +{ + description = "PSiesta development shell"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { nixpkgs, ... }: + let + systems = [ "x86_64-linux" "aarch64-linux" ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + in + { + devShells = forAllSystems (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + default = pkgs.mkShell { + packages = with pkgs; [ + stdenv.cc + gfortran + cmake + ninja + pkg-config + git + + openmpi + blas + lapack + scalapack + curl + hdf5 + netcdf + netcdffortran + fftw + libxc + readline + zlib + + uv + ]; + + shellHook = '' + export CC=mpicc + export CXX=mpicxx + export FC=mpifort + export F77=mpifort + export F90=mpifort + ''; + }; + }); + }; +} From 1bcceb06cefc57c867f62583d1195fd515e97593 Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Tue, 9 Jun 2026 23:47:05 +0200 Subject: [PATCH 8/9] Add smoke tests and dev test command --- .gitignore | 5 + README.md | 52 ++- flake.nix | 1 + pyproject.toml | 9 + tests/fixtures/H.psml | 683 +++++++++++++++++++++++++++++++++++++ tests/test_calculations.py | 107 ++++++ tests/test_import.py | 6 + tests/test_mpi_import.py | 8 + 8 files changed, 869 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/H.psml create mode 100644 tests/test_calculations.py create mode 100644 tests/test_import.py create mode 100644 tests/test_mpi_import.py diff --git a/.gitignore b/.gitignore index 5d2e6ac..7a31223 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,11 @@ *.o *.mod *.whl +__pycache__/ +*.py[cod] +.pytest_cache/ +.venv/ +uv.lock _psiesta.c build/ builddir diff --git a/README.md b/README.md index 11a0cb0..6da4b60 100644 --- a/README.md +++ b/README.md @@ -68,13 +68,33 @@ You can obtain the source by simply cloning this repository. The current build uses scikit-build-core and CMake. By default, CMake fetches upstream Siesta and builds it as a subproject, using Siesta's native CMake build. -For development, the recommended environment is Nix for native dependencies and uv for Python dependencies: +For development, the recommended environment is Nix for native dependencies and uv for Python dependencies. Nix provides the native scientific build stack, including C, C++, and Fortran compilers, OpenMPI compiler wrappers, CMake, Ninja, BLAS/LAPACK, ScaLAPACK, NetCDF C/Fortran, HDF5, FFTW, libxc, readline, zlib, curl, and `uv`. uv manages Python build isolation and dependencies from `pyproject.toml`. + +Install Nix with flakes enabled, then run commands inside the development shell: + +```bash +nix develop +``` + +Or run a single command inside it: + +```bash +nix develop -c +``` + +Check the shell with: + +```bash +nix develop -c sh -c 'cc --version && mpicc --version && mpifort --version && cmake --version && uv --version' +``` + +Build the wheel with: ```bash nix develop -c uv build --wheel ``` -This produces a wheel in `dist/`. See `BUILD-ENVIRONMENT.md` for details. +This produces a wheel in `dist/`. Build output is written under `build/`. You can point at an existing Siesta checkout with: @@ -82,6 +102,34 @@ You can point at an existing Siesta checkout with: nix develop -c uv build --wheel --config-setting=cmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta ``` +Local build artifacts can be removed with: + +```bash +rm -rf build dist +``` + +## Smoke tests + +The test suite contains lightweight technical smoke tests. They are not intended +to validate scientific accuracy; they only check that the Python extension +imports, MPI starts, Siesta launches as a library, and simple H/H2 force calls +return finite energy, force, and stress arrays. + +The calculation tests use `tests/fixtures/H.psml`. + +Run the tests from the Nix development shell with: + +```bash +nix develop -c mpirun -n 1 uv run python -m mpi4py -m pytest -q +``` + +The Nix development shell exports OpenMPI's library directory so uv's isolated +Python environment and `mpi4py` can locate `libmpi.so`. + +The smoke calculations deliberately use very loose settings, including a tiny +basis, low mesh cutoff, one SCF step, and `SCF.MustConverge false`, so warnings +about unconverged SCF are expected. + ## Behaviour See also [the SiestaSubroutine readme](https://gitlab.com/siesta-project/siesta/tree/master/Util/SiestaSubroutine/README). In summary, the fsiesta module that this is based on copies all fdf and psf-files from your working directory `` into `/` where `` is the label you give. diff --git a/flake.nix b/flake.nix index 69489b8..d10428d 100644 --- a/flake.nix +++ b/flake.nix @@ -45,6 +45,7 @@ export FC=mpifort export F77=mpifort export F90=mpifort + export LD_LIBRARY_PATH=${pkgs.openmpi}/lib:$LD_LIBRARY_PATH ''; }; }); diff --git a/pyproject.toml b/pyproject.toml index f09f0c8..943cab6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,15 @@ dependencies = ["numpy", "mpi4py", "ase", "sisl"] [project.urls] Homepage = "https://github.com/jonaslb/psiesta" +[dependency-groups] +dev = ["pytest"] + +[tool.uv] +default-groups = ["dev"] + [tool.scikit-build] minimum-version = "0.10" build-dir = "build/{wheel_tag}" + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/tests/fixtures/H.psml b/tests/fixtures/H.psml new file mode 100644 index 0000000..090e805 --- /dev/null +++ b/tests/fixtures/H.psml @@ -0,0 +1,683 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 0.000000000000E+00 5.058123513090E-03 1.010320477012E-02 1.515641821668E-02 + 2.018035865727E-02 2.531387423390E-02 3.063704102628E-02 3.577613994437E-02 + 4.079238691286E-02 4.596044693552E-02 5.116922694735E-02 5.629281359158E-02 + 6.192942616304E-02 6.732256330795E-02 7.318536293912E-02 7.861534161388E-02 + 8.444819686430E-02 8.963816212109E-02 9.514708906524E-02 1.009945802476E-01 + 1.072014429406E-01 1.124404774478E-01 1.179355484579E-01 1.236991687138E-01 + 1.297444624675E-01 1.360851953657E-01 1.427358057947E-01 1.479362033189E-01 + 1.533260707120E-01 1.589123110676E-01 1.647020789849E-01 1.707027897317E-01 + 1.769221287416E-01 1.833680614574E-01 1.900488435324E-01 1.969730314044E-01 + 2.041494932539E-01 2.115874203624E-01 2.166959870397E-01 2.219278949507E-01 + 2.272861220464E-01 2.327737181771E-01 2.383938068288E-01 2.441495869009E-01 + 2.500443345270E-01 2.560814049398E-01 2.622642343807E-01 2.685963420556E-01 + 2.750813321382E-01 2.817228958213E-01 2.885248134180E-01 2.954909565132E-01 + 3.026252901672E-01 3.099318751730E-01 3.174148703672E-01 3.250785349974E-01 + 3.329272311463E-01 3.409654262151E-01 3.491976954657E-01 3.576287246250E-01 + 3.662633125523E-01 3.751063739706E-01 3.841629422638E-01 3.934381723418E-01 + 4.029373435748E-01 4.126658627981E-01 4.226292673895E-01 4.277008185981E-01 + 4.328332284213E-01 4.380272271624E-01 4.432835538883E-01 4.486029565350E-01 + 4.539861920134E-01 4.594340263175E-01 4.649472346334E-01 4.705266014490E-01 + 4.761729206663E-01 4.818869957143E-01 4.876696396629E-01 4.935216753389E-01 + 4.994439354429E-01 5.054372626683E-01 5.115025098203E-01 5.176405399381E-01 + 5.238522264174E-01 5.301384531344E-01 5.365001145720E-01 5.429381159469E-01 + 5.494533733382E-01 5.560468138183E-01 5.627193755841E-01 5.694720080911E-01 + 5.763056721882E-01 5.832213402545E-01 5.902199963375E-01 5.973026362936E-01 + 6.044702679291E-01 6.117239111442E-01 6.190645980780E-01 6.264933732549E-01 + 6.340112937340E-01 6.416194292588E-01 6.493188624099E-01 6.571106887588E-01 + 6.649960170239E-01 6.729759692282E-01 6.810516808589E-01 6.892243010292E-01 + 6.974949926416E-01 7.058649325533E-01 7.143353117439E-01 7.229073354848E-01 + 7.315822235107E-01 7.403612101928E-01 7.492455447151E-01 7.582364912517E-01 + 7.673353291467E-01 7.765433530965E-01 7.858618733336E-01 7.952922158136E-01 + 8.048357224034E-01 8.144937510722E-01 8.242676760851E-01 8.341588881981E-01 + 8.441687948565E-01 8.542988203948E-01 8.645504062395E-01 8.749250111144E-01 + 8.854241112478E-01 8.960492005827E-01 9.068017909897E-01 9.176834124816E-01 + 9.286956134314E-01 9.398399607926E-01 9.511180403221E-01 9.625314568059E-01 + 9.740818342876E-01 9.857708162991E-01 9.976000660946E-01 1.009571266888E+00 + 1.021686122090E+00 1.033946355556E+00 1.046353711822E+00 1.058909956364E+00 + 1.071616875840E+00 1.084476278350E+00 1.097489993691E+00 1.110659873615E+00 + 1.123987792098E+00 1.137475645604E+00 1.151125353351E+00 1.164938857591E+00 + 1.178918123882E+00 1.193065141369E+00 1.207381923065E+00 1.221870506142E+00 + 1.236532952216E+00 1.251371347642E+00 1.266387803814E+00 1.281584457460E+00 + 1.296963470949E+00 1.312527032601E+00 1.328277356992E+00 1.344216685276E+00 + 1.360347285499E+00 1.376671452925E+00 1.393191510360E+00 1.409909808484E+00 + 1.426828726186E+00 1.443950670900E+00 1.461278078951E+00 1.478813415899E+00 + 1.496559176889E+00 1.514517887012E+00 1.532692101656E+00 1.551084406876E+00 + 1.569697419759E+00 1.588533788796E+00 1.607596194261E+00 1.626887348592E+00 + 1.646409996776E+00 1.666166916737E+00 1.686160919738E+00 1.706394850775E+00 + 1.726871588984E+00 1.747594048052E+00 1.768565176628E+00 1.789787958748E+00 + 1.811265414253E+00 1.833000599224E+00 1.854996606414E+00 1.877256565691E+00 + 1.899783644480E+00 1.922581048214E+00 1.945652020792E+00 1.968999845042E+00 + 1.992627843182E+00 2.016539377300E+00 2.040737849828E+00 2.065226704026E+00 + 2.090009424474E+00 2.115089537568E+00 2.140470612019E+00 2.166156259363E+00 + 2.192150134475E+00 2.218455936089E+00 2.245077407322E+00 2.272018336210E+00 + 2.299282556244E+00 2.326873946919E+00 2.354796434282E+00 2.383053991494E+00 + 2.411650639392E+00 2.440590447064E+00 2.469877532429E+00 2.499516062818E+00 + 2.529510255572E+00 2.559864378639E+00 2.590582751183E+00 2.621669744197E+00 + 2.653129781127E+00 2.684967338501E+00 2.717186946563E+00 2.749793189921E+00 + 2.782790708200E+00 2.816184196699E+00 2.849978407059E+00 2.884178147944E+00 + 2.918788285719E+00 2.953813745148E+00 2.989259510090E+00 3.025130624211E+00 + 3.061432191701E+00 3.098169378002E+00 3.135347410538E+00 3.172971579464E+00 + 3.211047238418E+00 3.249579805279E+00 3.288574762942E+00 3.328037660097E+00 + 3.367974112019E+00 3.408389801363E+00 3.449290478979E+00 3.490681964727E+00 + 3.532570148304E+00 3.574960990083E+00 3.617860521964E+00 3.661274848228E+00 + 3.705210146407E+00 3.749672668164E+00 3.794668740181E+00 3.840204765064E+00 + 3.886287222244E+00 3.932922668911E+00 3.980117740938E+00 4.027879153830E+00 + 4.076213703676E+00 4.125128268120E+00 4.174629807337E+00 4.224725365025E+00 + 4.275422069405E+00 4.326727134238E+00 4.378647859849E+00 4.431191634167E+00 + 4.484365933777E+00 4.538178324983E+00 4.592636464882E+00 4.647748102461E+00 + 4.703521079691E+00 4.759963332647E+00 4.817082892639E+00 4.874887887350E+00 + 4.933386541998E+00 4.992587180502E+00 5.052498226668E+00 5.113128205389E+00 + 5.174485743853E+00 5.236579572779E+00 5.299418527653E+00 5.363011549985E+00 + 5.427367688584E+00 5.492496100847E+00 5.558406054058E+00 5.625106926706E+00 + 5.692608209827E+00 5.760919508345E+00 5.830050542445E+00 5.900011148954E+00 + 5.970811282742E+00 6.042461018134E+00 6.114970550352E+00 6.188350196956E+00 + 6.262610399320E+00 6.337761724112E+00 6.413814864801E+00 6.490780643179E+00 + 6.568670010897E+00 6.647494051028E+00 6.727263979640E+00 6.807991147396E+00 + 6.889687041164E+00 6.972363285658E+00 7.056031645086E+00 7.140704024827E+00 + 7.226392473125E+00 7.313109182803E+00 7.400866492996E+00 7.489676890912E+00 + 7.579553013603E+00 7.670507649766E+00 7.762553741564E+00 7.855704386462E+00 + 7.949972839100E+00 8.045372513169E+00 8.141916983327E+00 8.239619987127E+00 + 8.338495426973E+00 8.438557372096E+00 8.539820060561E+00 8.642297901288E+00 + 8.746005476104E+00 8.850957541817E+00 8.957169032319E+00 9.064655060706E+00 + 9.173430921435E+00 9.283512092492E+00 9.394914237602E+00 9.507653208453E+00 + 9.621745046955E+00 9.737205987518E+00 9.854052459368E+00 9.972301088881E+00 + 1.009196870195E+01 1.021307232637E+01 1.033562919429E+01 1.045965674462E+01 + 1.058517262555E+01 1.071219469706E+01 1.084074103343E+01 1.097082992583E+01 + 1.110247988494E+01 1.123570964356E+01 1.137053815928E+01 1.150698461719E+01 + 1.164506843260E+01 1.178480925379E+01 1.192622696483E+01 1.206934168841E+01 + 1.221417378867E+01 1.236074387414E+01 1.250907280063E+01 1.265918167423E+01 + 1.281109185432E+01 1.296482495658E+01 1.312040285605E+01 1.327784769033E+01 + 1.343718186261E+01 1.359842804496E+01 1.376160918150E+01 1.392674849168E+01 + 1.409386947358E+01 1.426299590726E+01 1.443415185815E+01 1.460736168045E+01 + 1.478265002061E+01 1.496004182086E+01 1.513956232271E+01 1.532123707058E+01 + 1.550509191543E+01 1.569115301842E+01 1.587944685464E+01 1.607000021689E+01 + 1.626284021949E+01 1.645799430213E+01 1.665549023375E+01 1.685535611656E+01 + 1.705762038996E+01 1.726231183464E+01 1.746945957665E+01 1.767909309157E+01 + 1.789124220867E+01 1.810593711518E+01 1.832320836056E+01 1.854308686088E+01 + 1.876560390322E+01 1.899079115005E+01 1.921868064385E+01 1.944930481158E+01 + 1.968269646932E+01 1.991888882695E+01 2.015791549288E+01 2.039981047879E+01 + 2.064460820454E+01 2.089234350299E+01 2.114305162503E+01 2.139676824453E+01 + 2.165352946346E+01 2.191337181702E+01 2.217633227883E+01 2.244244826617E+01 + 2.271175764537E+01 2.298429873711E+01 2.326011032196E+01 2.353923164582E+01 + 2.382170242557E+01 2.410756285468E+01 2.439685360893E+01 2.468961585224E+01 + 2.498589124247E+01 2.528572193738E+01 2.558915060062E+01 2.589622040783E+01 + 2.620697505273E+01 2.652145875336E+01 2.683971625840E+01 2.716179285350E+01 + 2.748773436774E+01 2.781758718015E+01 2.815139822632E+01 2.848921500503E+01 + 2.883108558509E+01 2.917705861211E+01 2.952718331546E+01 2.988150951524E+01 + 3.024008762943E+01 3.060296868098E+01 3.097020430515E+01 3.134184675681E+01 + 3.171794891790E+01 3.209856430491E+01 3.248374707657E+01 3.287355204149E+01 + 3.326803466599E+01 3.366725108198E+01 3.407125809496E+01 3.448011319210E+01 + 3.489387455041E+01 3.531260104501E+01 3.573635225755E+01 3.616518848464E+01 + 3.659917074646E+01 3.703836079541E+01 3.748282112496E+01 3.793261497846E+01 + 3.838780635820E+01 3.884846003450E+01 3.931464155491E+01 3.978641725357E+01 + 4.026385426062E+01 4.074702051174E+01 4.123598475788E+01 4.173081657498E+01 + 4.223158637388E+01 4.273836541036E+01 4.325122579529E+01 4.377024050483E+01 + 4.429548339089E+01 + + + + + + 2.412019630324E+00 2.411853005229E+00 2.411353884902E+00 2.410521218807E+00 + 2.409363894978E+00 2.407842834133E+00 2.405905422383E+00 2.403688517939E+00 + 2.401197883302E+00 2.398296126938E+00 2.395028848964E+00 2.391481733965E+00 + 2.387200650115E+00 2.382736259077E+00 2.377478705569E+00 2.372237613382E+00 + 2.366214253866E+00 2.360516451327E+00 2.354124554554E+00 2.346957842354E+00 + 2.338927199918E+00 2.331815035439E+00 2.324032705198E+00 2.315521460881E+00 + 2.306218280979E+00 2.296055736137E+00 2.284961889330E+00 2.275984489835E+00 + 2.266406336050E+00 2.256192037573E+00 2.245304801293E+00 2.233706475646E+00 + 2.221357613080E+00 2.208217552989E+00 2.194244527431E+00 2.179395792050E+00 + 2.163627784629E+00 2.146896313761E+00 2.135184727301E+00 2.123011852400E+00 + 2.110364432438E+00 2.097229259092E+00 2.083593217066E+00 2.069443332477E+00 + 2.054766825032E+00 2.039551164113E+00 2.023784128888E+00 2.007453872564E+00 + 1.990548990911E+00 1.973058595172E+00 1.954972389496E+00 1.936280753036E+00 + 1.916974826862E+00 1.897046605855E+00 1.876489035769E+00 1.855296115649E+00 + 1.833463005815E+00 1.810986141636E+00 1.787863353277E+00 1.764093991637E+00 + 1.739679060591E+00 1.714621355629E+00 1.688925608815E+00 1.662598639868E+00 + 1.635649512876E+00 1.608089697874E+00 1.579933236083E+00 1.565636394717E+00 + 1.551196907075E+00 1.536617325833E+00 1.521900395485E+00 1.507049057303E+00 + 1.492066454130E+00 1.476955934990E+00 1.461721059449E+00 1.446365601679E+00 + 1.430893554185E+00 1.415309131106E+00 1.399616771053E+00 1.383821139398E+00 + 1.367927129938E+00 1.351939865859E+00 1.335864699922E+00 1.319707213765E+00 + 1.303473216248E+00 1.287168740742E+00 1.270800041257E+00 1.254373587333E+00 + 1.237896057573E+00 1.221374331751E+00 1.204815481387E+00 1.188226758706E+00 + 1.171615583909E+00 1.154989530693E+00 1.138356309938E+00 1.121723751551E+00 + 1.105099784408E+00 1.088492414416E+00 1.071909700684E+00 1.055359729848E+00 + 1.038850588633E+00 1.022390334716E+00 1.005986966044E+00 9.896483887582E-01 + 9.733823839245E-01 9.571965733077E-01 9.410983844725E-01 9.250950155238E-01 + 9.091933998499E-01 8.934001712670E-01 8.777216300041E-01 8.621637100037E-01 + 8.467319480448E-01 8.314314552235E-01 8.162668913473E-01 8.012424428199E-01 + 7.863618045936E-01 7.716281667671E-01 7.570442063890E-01 7.426120849977E-01 + 7.283334523842E-01 7.142094570043E-01 7.002407633857E-01 6.864275767788E-01 + 6.727696751814E-01 6.592664487279E-01 6.459169462772E-01 6.327199288513E-01 + 6.196739293858E-01 6.067773180382E-01 5.940283720803E-01 5.814253491724E-01 + 5.689665625877E-01 5.566504567351E-01 5.444756811238E-01 5.324411607344E-01 + 5.205461606186E-01 5.087903424554E-01 4.971738107570E-01 4.856971464501E-01 + 4.743613855180E-01 4.631678174116E-01 4.521176883544E-01 4.412121506946E-01 + 4.304523121819E-01 4.198392116152E-01 4.093738216642E-01 3.990570476551E-01 + 3.888897267827E-01 3.788726273950E-01 3.690064483399E-01 3.592918183769E-01 + 3.497292956556E-01 3.403193672611E-01 3.310624488286E-01 3.219588842273E-01 + 3.130089453154E-01 3.042128317657E-01 2.955706709648E-01 2.870825179833E-01 + 2.787483556209E-01 2.705680945236E-01 2.625415733763E-01 2.546685591684E-01 + 2.469487475336E-01 2.393817631642E-01 2.319671602984E-01 2.247044232821E-01 + 2.175929672028E-01 2.106321385965E-01 2.038212162265E-01 1.971594119332E-01 + 1.906458715543E-01 1.842796759142E-01 1.780598418816E-01 1.719853234950E-01 + 1.660550131530E-01 1.602677428698E-01 1.546222855942E-01 1.491173565893E-01 + 1.437516148736E-01 1.385236647196E-01 1.334320572094E-01 1.284752918458E-01 + 1.236518182157E-01 1.189600377057E-01 1.143983052659E-01 1.099649312218E-01 + 1.056581831306E-01 1.014762876808E-01 9.741743263208E-02 9.347976879390E-02 + 8.966141203943E-02 8.596044535329E-02 8.237492090991E-02 7.890286218010E-02 + 7.554226606297E-02 7.229110504039E-02 6.914732935106E-02 6.610886918102E-02 + 6.317363686659E-02 6.033952911113E-02 5.760442920243E-02 5.496620923738E-02 + 5.242273234479E-02 4.997185490400E-02 4.761142875521E-02 4.533930339758E-02 + 4.315332817099E-02 4.105135441745E-02 3.903123761774E-02 3.709083949913E-02 + 3.522803010985E-02 3.344068985597E-02 3.172671149641E-02 3.008400209172E-02 + 2.851048490262E-02 2.700410123392E-02 2.556281222006E-02 2.418460054822E-02 + 2.286747211548E-02 2.160945761638E-02 2.040861405785E-02 1.926302619821E-02 + 1.817080790786E-02 1.713010344891E-02 1.613908867191E-02 1.519597212771E-02 + 1.429899609312E-02 1.344643750931E-02 1.263660883213E-02 1.186785879419E-02 + 1.113857307856E-02 1.044717490452E-02 9.792125526249E-03 9.171924645302E-03 + 8.585110738562E-03 8.030261303249E-03 7.505993021126E-03 7.010961844229E-03 + 6.543863004720E-03 6.103430951708E-03 5.688439218088E-03 5.297700220629E-03 + 4.930064996715E-03 4.584422881232E-03 4.259701127250E-03 3.954864474150E-03 + 3.668914666957E-03 3.400889930590E-03 3.149864402775E-03 2.914947529305E-03 + 2.695283425261E-03 2.490050205752E-03 2.298459289598E-03 2.119754679272E-03 + 1.953212220288E-03 1.798138843051E-03 1.653871790052E-03 1.519777831104E-03 + 1.395252469163E-03 1.279719139097E-03 1.172628401597E-03 1.073457134274E-03 + 9.817077217942E-04 8.969072467913E-04 8.186066831149E-04 7.463800928651E-04 + 6.798238285267E-04 6.185557414097E-04 5.622143974997E-04 5.104583017327E-04 + 4.629651316296E-04 4.194309811546E-04 3.795696156042E-04 3.431117382743E-04 + 3.098042696150E-04 2.794096395326E-04 2.517050934684E-04 2.264820128445E-04 + 2.035452504382E-04 1.827124812120E-04 1.638135690947E-04 1.466899501785E-04 + 1.311940327575E-04 1.171886146009E-04 1.045463178134E-04 9.314904159121E-05 + 8.288743314362E-05 7.366037699677E-05 6.537450285345E-05 5.794371212890E-05 + 5.128872323184E-05 4.533663560749E-05 4.002051250571E-05 3.527898238457E-05 + 3.105585880761E-05 2.729977864122E-05 2.396385830976E-05 2.100536781824E-05 + 1.838542220767E-05 1.606869006643E-05 1.402311868230E-05 1.221967538467E-05 + 1.063210459524E-05 9.236700077642E-06 8.012091853346E-06 6.939047231169E-06 + 6.000285382355E-06 5.180304881362E-06 4.465223624611E-06 3.842630535109E-06 + 3.301448460066E-06 2.831807671053E-06 2.424929381689E-06 2.073018706110E-06 + 1.769166492217E-06 1.507259476768E-06 1.281898224359E-06 1.088322329137E-06 + 9.223423762559E-07 7.802781795400E-07 6.589028321896E-07 5.553921284563E-07 + 4.672789358408E-07 3.924121192640E-07 3.289196406817E-07 2.751754795647E-07 + 2.297700413999E-07 1.914837427422E-07 1.592634822459E-07 1.322017274152E-07 + 1.095179664579E-07 9.054229352454E-08 7.470091371085E-08 6.150337145346E-08 + 5.053132232032E-08 4.142868367207E-08 3.389301423743E-08 2.766798630469E-08 + 2.253682699032E-08 1.831661692041E-08 1.485334567220E-08 1.201763349782E-08 + 9.701038222515E-09 7.812874807910E-09 6.277482935877E-09 5.031885137995E-09 + 4.023784510050E-09 3.209856951181E-09 2.554298193825E-09 2.027590684118E-09 + 1.605459672518E-09 1.267991719851E-09 9.988922520227E-10 7.848618430607E-10 + 6.150736050527E-10 4.807364471033E-10 3.747310639776E-10 2.913073569813E-10 + 2.258336012076E-10 1.745890789753E-10 1.345931215718E-10 1.034645609042E-10 + 7.930650816007E-11 6.061216516454E-11 4.618805123577E-11 3.509160790553E-11 + 2.658063834585E-11 2.007245887364E-11 1.511099639018E-11 1.134036682597E-11 + 8.483723339809E-12 6.326375961755E-12 4.702362522184E-12 3.483799257861E-12 + 2.572462933034E-12 1.893158551888E-12 1.388511122039E-12 1.014889334523E-12 + 7.392259134522E-13 5.365458518528E-13 3.880515646573E-13 2.796446550998E-13 + 2.007887684823E-13 1.436379547192E-13 1.023709606281E-13 7.268465812559E-14 + 5.140999304785E-14 3.622190795610E-14 2.542108894492E-14 1.777033251585E-14 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 + + + + + + + + -4.598429577865E+00 -4.597947604277E+00 -4.596507788635E+00 -4.594108055643E+00 + -4.590776454068E+00 -4.586404294564E+00 -4.580846031231E+00 -4.574500496613E+00 + -4.567389893783E+00 -4.559130022492E+00 -4.549860979312E+00 -4.539835293656E+00 + -4.527786277240E+00 -4.515280415365E+00 -4.500629220395E+00 -4.486105341399E+00 + -4.469512879738E+00 -4.453913661284E+00 -4.436524308471E+00 -4.417163632942E+00 + -4.395638028280E+00 -4.376721136174E+00 -4.356177260502E+00 -4.333892318955E+00 + -4.309748985370E+00 -4.283627524149E+00 -4.255406812514E+00 -4.232790649361E+00 + -4.208874627224E+00 -4.183608735484E+00 -4.156944141585E+00 -4.128833627536E+00 + -4.099232046932E+00 -4.068096792931E+00 -4.035388264237E+00 -4.001070311841E+00 + -3.965110644579E+00 -3.927481165883E+00 -3.901455299754E+00 -3.874670787398E+00 + -3.847122621794E+00 -3.818806656403E+00 -3.789719553835E+00 -3.759858707343E+00 + -3.729222131022E+00 -3.697808314607E+00 -3.665616038555E+00 -3.632644145145E+00 + -3.598891261481E+00 -3.564355470636E+00 -3.529033927784E+00 -3.492922419041E+00 + -3.456014861974E+00 -3.418302748482E+00 -3.379774532949E+00 -3.340414971315E+00 + -3.300204420353E+00 -3.259118110537E+00 -3.217125411158E+00 -3.174189112251E+00 + -3.130264754900E+00 -3.085300049439E+00 -3.039234429786E+00 -2.991998801807E+00 + -2.943515553681E+00 -2.893698906559E+00 -2.842455693890E+00 -2.816268299641E+00 + -2.789686666791E+00 -2.762697756160E+00 -2.735288429968E+00 -2.707445554479E+00 + -2.679156116667E+00 -2.650407355777E+00 -2.621186910417E+00 -2.591482981850E+00 + -2.561284514043E+00 -2.530581390800E+00 -2.499364650305E+00 -2.467626717027E+00 + -2.435361650907E+00 -2.402565413262E+00 -2.369236148743E+00 -2.335374482191E+00 + -2.300983828859E+00 -2.266070716104E+00 -2.230645114040E+00 -2.194720772199E+00 + -2.158315558601E+00 -2.121451797018E+00 -2.084156597584E+00 -2.046462175101E+00 + -2.008406148805E+00 -1.970031816487E+00 -1.931388395209E+00 -1.892531220145E+00 + -1.853521892330E+00 -1.814428365649E+00 -1.775324962694E+00 -1.736292308975E+00 + -1.697417174559E+00 -1.658792212340E+00 -1.620515582351E+00 -1.582690452011E+00 + -1.545424363153E+00 -1.508828457791E+00 -1.473016556328E+00 -1.438104083901E+00 + -1.404206843118E+00 -1.371439634497E+00 -1.339914729293E+00 -1.309740203331E+00 + -1.281018145021E+00 -1.253842755994E+00 -1.228298367426E+00 -1.204457400468E+00 + -1.182378305092E+00 -1.162103517081E+00 -1.143657478492E+00 -1.127044771998E+00 + -1.112248424331E+00 -1.099228437994E+00 -1.087920613605E+00 -1.078235727269E+00 + -1.070059128010E+00 -1.063250819496E+00 -1.057646087583E+00 -1.053056730542E+00 + -1.049272942079E+00 -1.046065888001E+00 -1.043191005891E+00 -1.040392042884E+00 + -1.037405830018E+00 -1.033967772203E+00 -1.029818010975E+00 -1.024708192792E+00 + -1.018408748735E+00 -1.010716562500E+00 -1.001463433638E+00 -9.905180485599E-01 + -9.787821588035E-01 -9.671787950733E-01 -9.557068326934E-01 -9.443750303939E-01 + -9.331767244306E-01 -9.221113184143E-01 -9.111771206536E-01 -9.003725785542E-01 + -8.896961549708E-01 -8.791463306795E-01 -8.687216045025E-01 -8.584204930582E-01 + -8.482415305557E-01 -8.381832685837E-01 -8.282442759052E-01 -8.184231382544E-01 + -8.087184581345E-01 -7.991288546193E-01 -7.896529631567E-01 -7.802894353741E-01 + -7.710369388872E-01 -7.618941571097E-01 -7.528597890661E-01 -7.439325492069E-01 + -7.351111672250E-01 -7.263943878755E-01 -7.177809707966E-01 -7.092696903332E-01 + -7.008593353627E-01 -6.925487091220E-01 -6.843366290379E-01 -6.762219265580E-01 + -6.682034469852E-01 -6.602800493124E-01 -6.524506060611E-01 -6.447140031200E-01 + -6.370691395870E-01 -6.295149276126E-01 -6.220502922444E-01 -6.146741712747E-01 + -6.073855150895E-01 -6.001832865184E-01 -5.930664606879E-01 -5.860340248750E-01 + -5.790849783633E-01 -5.722183323010E-01 -5.654331095597E-01 -5.587283445961E-01 + -5.521030833143E-01 -5.455563829303E-01 -5.390873118385E-01 -5.326949494785E-01 + -5.263783862053E-01 -5.201367231594E-01 -5.139690721397E-01 -5.078745554772E-01 + -5.018523059106E-01 -4.959014664633E-01 -4.900211903217E-01 -4.842106407145E-01 + -4.784689907961E-01 -4.727954235289E-01 -4.671891315644E-01 -4.616493171323E-01 + -4.561751919259E-01 -4.507659769908E-01 -4.454209026141E-01 -4.401392082158E-01 + -4.349201422403E-01 -4.297629620500E-01 -4.246669338200E-01 -4.196313324337E-01 + -4.146554413798E-01 -4.097385526502E-01 -4.048799666397E-01 -4.000789920459E-01 + -3.953349457707E-01 -3.906471528232E-01 -3.860149462229E-01 -3.814376669045E-01 + -3.769146636240E-01 -3.724452928647E-01 -3.680289187456E-01 -3.636649129301E-01 + -3.593526545356E-01 -3.550915300446E-01 -3.508809332162E-01 -3.467202649993E-01 + -3.426089334463E-01 -3.385463536277E-01 -3.345319475484E-01 -3.305651440641E-01 + + + + + + + -5.843046406645E+00 -5.840662133424E+00 -5.833545048113E+00 -5.821704807273E+00 + -5.805312177163E+00 -5.783879947652E+00 -5.756765318399E+00 -5.725991027313E+00 + -5.691736001818E+00 -5.652250005541E+00 -5.608331964073E+00 -5.561297390243E+00 + -5.505417056709E+00 -5.448167132186E+00 -5.382070611001E+00 -5.317590080492E+00 + -5.245199712548E+00 -5.178385899467E+00 -5.105329203033E+00 -5.025762145058E+00 + -4.939494326063E+00 -4.865593897698E+00 -4.787365880783E+00 -4.704896321576E+00 + -4.618351661159E+00 -4.527991457735E+00 -4.434180758935E+00 -4.361843450426E+00 + -4.288082779573E+00 -4.213188966554E+00 -4.137493615357E+00 -4.061368820565E+00 + -3.985224711971E+00 -3.909505085560E+00 -3.834680751754E+00 -3.761240231613E+00 + -3.689677457203E+00 -3.620476192432E+00 -3.575880297820E+00 -3.532659986833E+00 + -3.490924713966E+00 -3.450766851421E+00 -3.412257871394E+00 -3.375444363393E+00 + -3.340343962548E+00 -3.306941285960E+00 -3.275183996653E+00 -3.244979138749E+00 + -3.216189912029E+00 -3.188633078239E+00 -3.162077214055E+00 -3.136242044779E+00 + -3.110799106777E+00 -3.085373992944E+00 -3.059550431353E+00 -3.032876429619E+00 + -3.004872683342E+00 -2.975043392500E+00 -2.942889552021E+00 -2.907924678771E+00 + -2.869692804951E+00 -2.827788406705E+00 -2.781877747564E+00 -2.731720903443E+00 + -2.677193506283E+00 -2.618307009366E+00 -2.555226055656E+00 -2.522209282594E+00 + -2.488281344407E+00 -2.453510619048E+00 -2.417976269656E+00 -2.381767989076E+00 + -2.344985581753E+00 -2.307738373096E+00 -2.270144438094E+00 -2.232329643545E+00 + -2.194426501128E+00 -2.156572831776E+00 -2.118910245941E+00 -2.081582448378E+00 + -2.044733381119E+00 -2.008505223219E+00 -1.973036271469E+00 -1.938458731809E+00 + -1.904896456892E+00 -1.872462670930E+00 -1.841257728191E+00 -1.811366956544E+00 + -1.782858641533E+00 -1.755782209808E+00 -1.730166672848E+00 -1.706019392494E+00 + -1.683325228958E+00 -1.662046128882E+00 -1.642121206097E+00 -1.623467360353E+00 + -1.605980469492E+00 -1.589537178424E+00 -1.573997293424E+00 -1.559206773490E+00 + -1.545001291183E+00 -1.531210314678E+00 -1.517661640483E+00 -1.504186283432E+00 + -1.490623607746E+00 -1.476826560884E+00 -1.462666851919E+00 -1.448039898893E+00 + -1.432869356643E+00 -1.417111028886E+00 -1.401068248317E+00 -1.385233562562E+00 + -1.369536981381E+00 -1.353971045957E+00 -1.338528682151E+00 -1.323203298273E+00 + -1.307988890963E+00 -1.292880157810E+00 -1.277872614724E+00 -1.262962715339E+00 + -1.248147969024E+00 -1.233427053300E+00 -1.218799915795E+00 -1.204267860270E+00 + -1.189833610785E+00 -1.175501347803E+00 -1.161276710058E+00 -1.147166756208E+00 + -1.133179880989E+00 -1.119325681489E+00 -1.105614770605E+00 -1.092058536508E+00 + -1.078668849149E+00 -1.065457717426E+00 -1.052436903451E+00 -1.039617503338E+00 + -1.027009506811E+00 -1.014621350451E+00 -1.002460042319E+00 -9.905247744534E-01 + -9.787821588035E-01 -9.671787950733E-01 -9.557068326934E-01 -9.443750303939E-01 + -9.331767244306E-01 -9.221113184143E-01 -9.111771206536E-01 -9.003725785542E-01 + -8.896961549708E-01 -8.791463306795E-01 -8.687216045025E-01 -8.584204930582E-01 + -8.482415305557E-01 -8.381832685837E-01 -8.282442759052E-01 -8.184231382544E-01 + -8.087184581345E-01 -7.991288546193E-01 -7.896529631567E-01 -7.802894353741E-01 + -7.710369388872E-01 -7.618941571097E-01 -7.528597890661E-01 -7.439325492069E-01 + -7.351111672250E-01 -7.263943878755E-01 -7.177809707966E-01 -7.092696903332E-01 + -7.008593353627E-01 -6.925487091220E-01 -6.843366290379E-01 -6.762219265580E-01 + -6.682034469852E-01 -6.602800493124E-01 -6.524506060611E-01 -6.447140031200E-01 + -6.370691395870E-01 -6.295149276126E-01 -6.220502922444E-01 -6.146741712747E-01 + -6.073855150895E-01 -6.001832865184E-01 -5.930664606879E-01 -5.860340248750E-01 + -5.790849783633E-01 -5.722183323010E-01 -5.654331095597E-01 -5.587283445961E-01 + -5.521030833143E-01 -5.455563829303E-01 -5.390873118385E-01 -5.326949494785E-01 + -5.263783862053E-01 -5.201367231594E-01 -5.139690721397E-01 -5.078745554772E-01 + -5.018523059106E-01 -4.959014664633E-01 -4.900211903217E-01 -4.842106407145E-01 + -4.784689907961E-01 -4.727954235289E-01 -4.671891315644E-01 -4.616493171323E-01 + -4.561751919259E-01 -4.507659769908E-01 -4.454209026141E-01 -4.401392082158E-01 + -4.349201422403E-01 -4.297629620500E-01 -4.246669338200E-01 -4.196313324337E-01 + -4.146554413798E-01 -4.097385526502E-01 -4.048799666397E-01 -4.000789920459E-01 + -3.953349457707E-01 -3.906471528232E-01 -3.860149462229E-01 -3.814376669045E-01 + -3.769146636240E-01 -3.724452928647E-01 -3.680289187456E-01 -3.636649129301E-01 + -3.593526545356E-01 -3.550915300446E-01 -3.508809332162E-01 -3.467202649993E-01 + -3.426089334463E-01 -3.385463536277E-01 -3.345319475484E-01 -3.305651440641E-01 + + + + + + + + -3.152334495694E+00 -3.152115378862E+00 -3.151460909451E+00 -3.150370000183E+00 + -3.148855198895E+00 -3.146866789237E+00 -3.144338121192E+00 -3.141450112718E+00 + -3.138212345597E+00 -3.134449106271E+00 -3.130223182263E+00 -3.125648656057E+00 + -3.120145640606E+00 -3.114427513734E+00 -3.107719558659E+00 -3.101059805928E+00 + -3.093438426837E+00 -3.086259723000E+00 -3.078240750389E+00 -3.069291024949E+00 + -3.059311933717E+00 -3.050515781934E+00 -3.040933379492E+00 -3.030501856206E+00 + -3.019154350346E+00 -3.006819861016E+00 -2.993423097959E+00 -2.982630615469E+00 + -2.971160520418E+00 -2.958975992068E+00 -2.946038679902E+00 -2.932308668187E+00 + -2.917744443667E+00 -2.902302868903E+00 -2.885939164618E+00 -2.868606905450E+00 + -2.850258034941E+00 -2.830842907068E+00 -2.817281918768E+00 -2.803208822329E+00 + -2.788607857216E+00 -2.773463038573E+00 -2.757758192754E+00 -2.741477000954E+00 + -2.724603051919E+00 -2.707119904976E+00 -2.689011164584E+00 -2.670260567659E+00 + -2.650852084971E+00 -2.630770037872E+00 -2.609999231649E+00 -2.588525106654E+00 + -2.566333908276E+00 -2.543412876669E+00 -2.519750456944E+00 -2.495336530154E+00 + -2.470162665149E+00 -2.444222390851E+00 -2.417511487966E+00 -2.390028298553E+00 + -2.361774051061E+00 -2.332753197728E+00 -2.302973760203E+00 -2.272447678399E+00 + -2.241191156456E+00 -2.209224998711E+00 -2.176574927508E+00 -2.160002879581E+00 + -2.143271873741E+00 -2.126386631768E+00 -2.109352230078E+00 -2.092174104853E+00 + -2.074858056100E+00 -2.057410250641E+00 -2.039837223855E+00 -2.022145880103E+00 + -2.004343491774E+00 -1.986437696740E+00 -1.968436494252E+00 -1.950348239004E+00 + -1.932181633391E+00 -1.913945717701E+00 -1.895649858178E+00 -1.877303732766E+00 + -1.858917314332E+00 -1.840500851216E+00 -1.822064844824E+00 -1.803620024050E+00 + -1.785177316184E+00 -1.766747813992E+00 -1.748342738583E+00 -1.729973397578E+00 + -1.711651138137E+00 -1.693387294229E+00 -1.675193127530E+00 -1.657079761271E+00 + -1.639058106208E+00 -1.621138777949E+00 -1.603332004622E+00 -1.585647524012E+00 + -1.568094469027E+00 -1.550681240437E+00 -1.533415365698E+00 -1.516303342620E+00 + -1.499350466645E+00 -1.482560640305E+00 -1.465936163520E+00 -1.449477503167E+00 + -1.433183040287E+00 -1.417048793163E+00 -1.401068248317E+00 -1.385233562562E+00 + -1.369536981381E+00 -1.353971045957E+00 -1.338528682151E+00 -1.323203298273E+00 + -1.307988890963E+00 -1.292880157810E+00 -1.277872614724E+00 -1.262962715339E+00 + -1.248147969024E+00 -1.233427053300E+00 -1.218799915795E+00 -1.204267860270E+00 + -1.189833610785E+00 -1.175501347803E+00 -1.161276710058E+00 -1.147166756208E+00 + -1.133179880989E+00 -1.119325681489E+00 -1.105614770605E+00 -1.092058536508E+00 + -1.078668849149E+00 -1.065457717426E+00 -1.052436903451E+00 -1.039617503338E+00 + -1.027009506811E+00 -1.014621350451E+00 -1.002460042319E+00 -9.905247744534E-01 + -9.787821588035E-01 -9.671787950733E-01 -9.557068326934E-01 -9.443750303939E-01 + -9.331767244306E-01 -9.221113184143E-01 -9.111771206536E-01 -9.003725785542E-01 + -8.896961549708E-01 -8.791463306795E-01 -8.687216045025E-01 -8.584204930582E-01 + -8.482415305557E-01 -8.381832685837E-01 -8.282442759052E-01 -8.184231382544E-01 + -8.087184581345E-01 -7.991288546193E-01 -7.896529631567E-01 -7.802894353741E-01 + -7.710369388872E-01 -7.618941571097E-01 -7.528597890661E-01 -7.439325492069E-01 + -7.351111672250E-01 -7.263943878755E-01 -7.177809707966E-01 -7.092696903332E-01 + -7.008593353627E-01 -6.925487091220E-01 -6.843366290379E-01 -6.762219265580E-01 + -6.682034469852E-01 -6.602800493124E-01 -6.524506060611E-01 -6.447140031200E-01 + -6.370691395870E-01 -6.295149276126E-01 -6.220502922444E-01 -6.146741712747E-01 + -6.073855150895E-01 -6.001832865184E-01 -5.930664606879E-01 -5.860340248750E-01 + -5.790849783633E-01 -5.722183323010E-01 -5.654331095597E-01 -5.587283445961E-01 + -5.521030833143E-01 -5.455563829303E-01 -5.390873118385E-01 -5.326949494785E-01 + -5.263783862053E-01 -5.201367231594E-01 -5.139690721397E-01 -5.078745554772E-01 + -5.018523059106E-01 -4.959014664633E-01 -4.900211903217E-01 -4.842106407145E-01 + -4.784689907961E-01 -4.727954235289E-01 -4.671891315644E-01 -4.616493171323E-01 + -4.561751919259E-01 -4.507659769908E-01 -4.454209026141E-01 -4.401392082158E-01 + -4.349201422403E-01 -4.297629620500E-01 -4.246669338200E-01 -4.196313324337E-01 + -4.146554413798E-01 -4.097385526502E-01 -4.048799666397E-01 -4.000789920459E-01 + -3.953349457707E-01 -3.906471528232E-01 -3.860149462229E-01 -3.814376669045E-01 + -3.769146636240E-01 -3.724452928647E-01 -3.680289187456E-01 -3.636649129301E-01 + -3.593526545356E-01 -3.550915300446E-01 -3.508809332162E-01 -3.467202649993E-01 + -3.426089334463E-01 -3.385463536277E-01 -3.345319475484E-01 -3.305651440641E-01 + + + + + + + + 3.865700205494E+00 3.869390231455E+00 3.880407598397E+00 3.898743807042E+00 + 3.924144972474E+00 3.957381406274E+00 3.999472332429E+00 4.047301504436E+00 + 4.100611711321E+00 4.162155491301E+00 4.230723075097E+00 4.304290590252E+00 + 4.391872190599E+00 4.481798014983E+00 4.585863154575E+00 4.687628371101E+00 + 4.802153754648E+00 4.908103815590E+00 5.024207463235E+00 5.150932931461E+00 + 5.288613279899E+00 5.406746725468E+00 5.531936976725E+00 5.663997472538E+00 + 5.802569573608E+00 5.947083527160E+00 6.096713980259E+00 6.211627212208E+00 + 6.328182124702E+00 6.445642984309E+00 6.563131151299E+00 6.679609876309E+00 + 6.793868969396E+00 6.904509763839E+00 7.009930924389E+00 7.108315798552E+00 + 7.197622180532E+00 7.275575549967E+00 7.320007861276E+00 7.357483527853E+00 + 7.387157769456E+00 7.408143794892E+00 7.419515931010E+00 7.420313811775E+00 + 7.409547762244E+00 7.386205516521E+00 7.349260410516E+00 7.297681188686E+00 + 7.230443558090E+00 7.146543611964E+00 7.045013227560E+00 6.924937517933E+00 + 6.785474383334E+00 6.625876163697E+00 6.445513337745E+00 6.243900145464E+00 + 6.020721927735E+00 5.775863878923E+00 5.509440794762E+00 5.221827268874E+00 + 4.913687647590E+00 4.586004896290E+00 4.240107364170E+00 3.877692262982E+00 + 3.500844505230E+00 3.112049387221E+00 2.714197463235E+00 2.512888380395E+00 + 2.310579852494E+00 2.107743226056E+00 1.904872166786E+00 1.702481409405E+00 + 1.501105262339E+00 1.301295856122E+00 1.103621125708E+00 9.086625186060E-01 + 7.170124226680E-01 5.292713097449E-01 3.460445940539E-01 1.679392071693E-01 + -4.440105046796E-03 -1.704947544557E-01 -3.296365383124E-01 -4.812921047770E-01 + -6.249076335709E-01 -7.599537038942E-01 -8.859303162497E-01 -1.002372029140E+00 + -1.108853165762E+00 -1.204993039926E+00 -1.290461144530E+00 -1.364982240222E+00 + -1.428341276406E+00 -1.480388071750E+00 -1.521041676951E+00 -1.550294338899E+00 + -1.568214982811E+00 -1.574952127502E+00 -1.570736149136E+00 -1.555880810576E+00 + -1.530783977342E+00 -1.495927447207E+00 -1.451875829073E+00 -1.399274418071E+00 + -1.338846028155E+00 -1.271386760927E+00 -1.197760710236E+00 -1.118893626375E+00 + -1.035765591395E+00 -9.494027882868E-01 -8.608678847129E-01 -7.712432596694E-01 + -6.816113533823E-01 -5.930390152566E-01 -5.065617516299E-01 -4.231675861350E-01 + -3.437808143136E-01 -2.692459688170E-01 -2.003123438775E-01 -1.376194545739E-01 + -8.168382814804E-02 -3.288753780121E-02 8.531106306610E-03 4.248430115166E-02 + 6.903854238774E-02 8.841510957545E-02 1.009869415022E-01 1.072716644606E-01 + 1.079206201966E-01 1.037038419561E-01 9.549104495019E-02 8.422883208574E-02 + 7.091446286421E-02 5.656669182596E-02 4.219434708216E-02 2.876348503720E-02 + 1.716411818583E-02 8.177660541928E-03 2.446363158613E-03 4.461095900985E-04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + + + + + + + 1.023583980538E+01 1.022845125259E+01 1.020638509392E+01 1.016964020231E+01 + 1.011869549423E+01 1.005196184836E+01 9.967326734501E+00 9.870983349432E+00 + 9.763381572662E+00 9.638868229350E+00 9.499764092036E+00 9.350055737827E+00 + 9.171183131482E+00 8.986760704648E+00 8.772328882074E+00 8.561528886516E+00 + 8.322907313631E+00 8.100758422367E+00 7.855673267579E+00 7.586050778592E+00 + 7.290397897035E+00 7.034248495327E+00 6.760060586023E+00 6.467455180723E+00 + 6.156261520436E+00 5.826573333484E+00 5.478813419715E+00 5.206612779292E+00 + 4.925223284391E+00 4.635340839216E+00 4.337856729433E+00 4.033880212138E+00 + 3.724761372752E+00 3.412113623547E+00 3.097835028680E+00 2.784127419218E+00 + 2.473512008450E+00 2.168839934226E+00 1.970586109180E+00 1.777407517585E+00 + 1.590392325304E+00 1.410688597713E+00 1.239499796102E+00 1.078078724078E+00 + 9.277197306009E-01 7.897489719569E-01 6.655125349607E-01 5.563622291283E-01 + 4.636388678847E-01 3.886528796595E-01 3.326621206452E-01 2.968468039748E-01 + 2.822815170351E-01 2.899043716416E-01 3.204834228009E-01 3.745806026738E-01 + 4.525135486354E-01 5.543158591489E-01 6.796964888677E-01 8.279991944037E-01 + 9.981631626290E-01 1.188686190642E+00 1.397592034949E+00 1.622403798767E+00 + 1.860125469379E+00 2.107233937542E+00 2.359684009505E+00 2.486509980092E+00 + 2.612929036766E+00 2.738300870295E+00 2.861959812603E+00 2.983217228737E+00 + 3.101364287219E+00 3.215675122619E+00 3.325410401522E+00 3.429821300069E+00 + 3.528153897620E+00 3.619653986966E+00 3.703572296808E+00 3.779170116938E+00 + 3.845725310660E+00 3.902538692524E+00 3.948940742377E+00 3.984298619158E+00 + 4.008023429701E+00 4.019577699283E+00 4.018482981670E+00 4.004327537206E+00 + 3.976773998110E+00 3.935566930730E+00 3.880540195322E+00 3.811623995029E+00 + 3.728851497515E+00 3.632364905316E+00 3.522420844759E+00 3.399394938565E+00 + 3.263785424380E+00 3.116215680766E+00 2.957435524174E+00 2.788321145344E+00 + 2.609873562024E+00 2.423215477174E+00 2.229586448393E+00 2.030336295541E+00 + 1.826916699790E+00 1.620870978862E+00 1.413822060321E+00 1.207458717474E+00 + 1.003520180816E+00 8.037792918180E-01 6.100229227434E-01 4.240155603566E-01 + 2.474585326746E-01 8.196146854345E-02 -7.098543397590E-02 -2.100392578594E-01 + -3.340318003003E-01 -4.419959950599E-01 -5.331906765532E-01 -6.071229954342E-01 + -6.635677618046E-01 -7.025829746600E-01 -7.245207946884E-01 -7.300332366817E-01 + -7.200719005602E-01 -6.958811275404E-01 -6.589840648690E-01 -6.111612485511E-01 + -5.544214706552E-01 -4.909648869658E-01 -4.231385388412E-01 -3.533847111819E-01 + -2.841828190163E-01 -2.179858066731E-01 -1.571523456897E-01 -1.038764231267E-01 + -6.011621002803E-02 -2.752437583051E-02 -7.382256943224E-03 -5.404776570338E-04 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 -0.000000000000E+00 + + + + + + + 0.000000000000E+00 -4.740765824874E-01 -9.445738063457E-01 -1.411131060398E+00 + -1.868048614423E+00 -2.325490798520E+00 -2.787348723089E+00 -3.218943995936E+00 + -3.624699303698E+00 -4.024809596987E+00 -4.407877727916E+00 -4.763310609630E+00 + -5.128197312954E+00 -5.450288965271E+00 -5.769114504947E+00 -6.034376595966E+00 + -6.286458784220E+00 -6.481835608635E+00 -6.659386439716E+00 -6.814511066046E+00 + -6.942311613291E+00 -7.021485603378E+00 -7.077371610020E+00 -7.107521707752E+00 + -7.109728507434E+00 -7.082172664281E+00 -7.023598131867E+00 -6.958980782887E+00 + -6.876765586180E+00 -6.777467635457E+00 -6.662026999436E+00 -6.531863226354E+00 + -6.388924381075E+00 -6.235726277151E+00 -6.075376549813E+00 -5.911577171164E+00 + -5.748598016727E+00 -5.591213271523E+00 -5.491939022965E+00 -5.399040755638E+00 + -5.314134094527E+00 -5.238819039662E+00 -5.174639724267E+00 -5.123037950831E+00 + -5.085300612057E+00 -5.062501368574E+00 -5.055437281510E+00 -5.064561488663E+00 + -5.089913468228E+00 -5.131048950295E+00 -5.186972104750E+00 -5.256073239291E+00 + -5.336075859369E+00 -5.423997539604E+00 -5.516129589021E+00 -5.608040903744E+00 + -5.694611621192E+00 -5.770102138286E+00 -5.828262641230E+00 -5.862487418610E+00 + -5.866016795202E+00 -5.832187442287E+00 -5.754729023957E+00 -5.628101597586E+00 + -5.447863926002E+00 -5.211057982845E+00 -4.916589646421E+00 -4.747987407872E+00 + -4.565580209726E+00 -4.369902378948E+00 -4.161658355812E+00 -3.941726118461E+00 + -3.711158279005E+00 -3.471180567962E+00 -3.223187438735E+00 -2.968734547229E+00 + -2.709527894298E+00 -2.447409461438E+00 -2.184339223480E+00 -1.922373486493E+00 + -1.663639574816E+00 -1.410306978020E+00 -1.164555166116E+00 -9.285383885867E-01 + -7.043478884494E-01 -4.939720845353E-01 -2.992554010922E-01 -1.218565504249E-01 + 3.679280212031E-02 1.755279453869E-01 2.934884527581E-01 3.901517008908E-01 + 4.653612804929E-01 5.193489715250E-01 5.527489606739E-01 5.666030274517E-01 + 5.623555227038E-01 5.418371148041E-01 5.072364883881E-01 4.610594505785E-01 + 4.060752307725E-01 3.452501501760E-01 2.816692814906E-01 2.184472092606E-01 + 1.586295254631E-01 1.050872371725E-01 6.040680370662E-02 2.677903583909E-02 + 5.890552048745E-03 -1.178133963900E-03 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + + + + + diff --git a/tests/test_calculations.py b/tests/test_calculations.py new file mode 100644 index 0000000..7e20851 --- /dev/null +++ b/tests/test_calculations.py @@ -0,0 +1,107 @@ +from pathlib import Path +from shutil import copyfile + +import numpy as np +import pytest +import sisl as si + +from psiesta import FilePSiesta + + +FIXTURES = Path(__file__).resolve().parent / "fixtures" +H_PSML = FIXTURES / "H.psml" + + +@pytest.fixture +def h_psml(): + if not H_PSML.exists(): + pytest.skip("H.psml is required for PSiesta smoke calculations") + return H_PSML + + +@pytest.fixture +def psiesta_workdir(tmp_path, h_psml): + copyfile(h_psml, tmp_path / "H.psml") + return tmp_path + + +@pytest.fixture +def h_atom_geom(): + return si.Geometry( + [[0.0, 0.0, 0.0]], + atoms=[si.Atom("H")], + lattice=si.Lattice([8.0, 8.0, 8.0]), + ) + + +@pytest.fixture +def h2_geom(): + return si.Geometry( + [[0.0, 0.0, 0.0], [0.9, 0.0, 0.0]], + atoms=[si.Atom("H"), si.Atom("H")], + lattice=si.Lattice([10.0, 10.0, 10.0]), + ) + + +def _fdf(spin_polarized=False): + spin = "SpinPolarized true\n" if spin_polarized else "" + return """ +NumberOfSpecies 1 +%block ChemicalSpeciesLabel + 1 1 H +%endblock ChemicalSpeciesLabel + +PAO.BasisSize SZ +MeshCutoff 20 Ry +XC.functional GGA +XC.authors PBE +MaxSCFIterations 1 +SCF.MustConverge false +DM.Tolerance 1.d-2 +DM.MixingWeight 0.3 +SolutionMethod diagon +WriteMullikenPop 0 +WriteForces true +""" + spin + + +def _calculator(psiesta_workdir, h_psml, label, geom, spin_polarized=False): + calc = FilePSiesta(_fdf(spin_polarized=spin_polarized), psiesta_workdir, label, geometry=geom) + copyfile(h_psml, calc.label_dir / "H.psml") + return calc + + +def _assert_result_is_technical_smoke_ok(result, natoms): + assert np.isfinite(result.energy) + assert result.forces.shape == (natoms, 3) + assert result.stress.shape == (3, 3) + assert np.all(np.isfinite(result.forces)) + assert np.all(np.isfinite(result.stress)) + + +def test_h_atom_in_box_runs(psiesta_workdir, h_psml, h_atom_geom): + calc = _calculator(psiesta_workdir, h_psml, "h_atom", h_atom_geom, spin_polarized=True) + + result = calc.run(h_atom_geom) + + _assert_result_is_technical_smoke_ok(result, len(h_atom_geom)) + + +def test_h2_in_box_runs(psiesta_workdir, h_psml, h2_geom): + calc = _calculator(psiesta_workdir, h_psml, "h2", h2_geom) + + result = calc.run(h2_geom) + + _assert_result_is_technical_smoke_ok(result, len(h2_geom)) + + +def test_h2_can_rerun_with_perturbed_geometry(psiesta_workdir, h_psml, h2_geom): + calc = _calculator(psiesta_workdir, h_psml, "h2_rerun", h2_geom) + + first = calc.run(h2_geom) + moved = h2_geom.copy() + moved.xyz[1, 0] += 0.02 + second = calc.run(moved) + + _assert_result_is_technical_smoke_ok(first, len(h2_geom)) + _assert_result_is_technical_smoke_ok(second, len(h2_geom)) diff --git a/tests/test_import.py b/tests/test_import.py new file mode 100644 index 0000000..c1d4056 --- /dev/null +++ b/tests/test_import.py @@ -0,0 +1,6 @@ +def test_imports_compiled_extension(): + import psiesta + import psiesta._psiesta + + assert psiesta.FilePSiesta is not None + assert psiesta._psiesta.FSiesta is not None diff --git a/tests/test_mpi_import.py b/tests/test_mpi_import.py new file mode 100644 index 0000000..d019255 --- /dev/null +++ b/tests/test_mpi_import.py @@ -0,0 +1,8 @@ +from mpi4py import MPI + + +def test_imports_under_mpi(): + import psiesta + + assert psiesta.FilePSiesta is not None + assert MPI.COMM_WORLD.Get_size() >= 1 From 946c2a7341dcc36e71e440c3987e06a3e87681da Mon Sep 17 00:00:00 2001 From: Jonas Lundholm Bertelsen Date: Tue, 9 Jun 2026 23:47:10 +0200 Subject: [PATCH 9/9] Remove obsolete build documentation and Meson files --- BUILD-ENVIRONMENT.md | 77 ------------------------------ BUILD-SCIKIT-BUILD-CORE.md | 86 ---------------------------------- meson.build | 38 --------------- meson_options.txt | 2 - psiesta/c_bindings/meson.build | 1 - psiesta/meson.build | 24 ---------- subprojects/.gitignore | 14 ------ subprojects/siesta.wrap | 4 -- 8 files changed, 246 deletions(-) delete mode 100644 BUILD-ENVIRONMENT.md delete mode 100644 BUILD-SCIKIT-BUILD-CORE.md delete mode 100644 meson.build delete mode 100644 meson_options.txt delete mode 100644 psiesta/c_bindings/meson.build delete mode 100644 psiesta/meson.build delete mode 100644 subprojects/.gitignore delete mode 100644 subprojects/siesta.wrap diff --git a/BUILD-ENVIRONMENT.md b/BUILD-ENVIRONMENT.md deleted file mode 100644 index 05410a0..0000000 --- a/BUILD-ENVIRONMENT.md +++ /dev/null @@ -1,77 +0,0 @@ -# Build Environment - -This repository uses a split build environment: - -- Nix provides the native scientific build stack. -- uv manages Python build isolation and Python dependencies from `pyproject.toml`. - -This keeps compilers, MPI, BLAS/LAPACK, NetCDF, and other native libraries out of the host system package manager while avoiding duplicated Python dependency metadata in Nix. - -## Requirements - -Install Nix with flakes enabled. On Linux, daemon mode is recommended even for a single-user workstation because `/nix/store` remains root-owned and builds go through the normal Nix daemon path. - -The user does not need to be a trusted Nix user for the commands below. - -## Development Shell - -Enter the development shell with: - -```bash -nix develop -``` - -Or run one command inside it: - -```bash -nix develop -c -``` - -The shell provides the native build environment from `flake.nix`, including: - -- C, C++, and Fortran compilers. -- OpenMPI compiler wrappers, with `CC=mpicc`, `CXX=mpicxx`, and `FC=mpifort`. -- CMake, Ninja, and `pkg-config`. -- BLAS, LAPACK, ScaLAPACK, NetCDF C/Fortran, HDF5, FFTW, libxc, readline, zlib, and curl. -- `uv` for Python dependency resolution and PEP 517 builds. - -Check the shell with: - -```bash -nix develop -c sh -c 'cc --version && mpicc --version && mpifort --version && cmake --version && uv --version' -``` - -## Building A Wheel - -Build the wheel with: - -```bash -nix develop -c uv build --wheel -``` - -`uv` reads `pyproject.toml`, creates an isolated Python build environment, installs the declared Python build requirements, and calls the scikit-build-core backend. scikit-build-core then configures and builds the CMake project. - -By default, the CMake project fetches upstream Siesta and builds it as a subproject. The build output is written under `build/`, and the wheel is written under `dist/`. - -## Using An Existing Siesta Checkout - -To build against an existing Siesta source checkout instead of fetching it, pass the CMake definition through uv/scikit-build-core: - -```bash -nix develop -c uv build --wheel --config-setting=cmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta -``` - -## Build Artifacts - -Expected local artifacts include: - -- `build/` for scikit-build-core, CMake, and fetched Siesta build trees. -- `dist/` for generated wheels. -- `~/.cache/uv/` for uv-managed Python build environments and downloads. -- `/nix/store` and `/nix/var/nix` for Nix-managed native dependencies and build metadata. - -The Nix-managed system state is garbage-collectable through normal Nix commands. The repository build artifacts can be removed with: - -```bash -rm -rf build dist -``` diff --git a/BUILD-SCIKIT-BUILD-CORE.md b/BUILD-SCIKIT-BUILD-CORE.md deleted file mode 100644 index 4d4b8ea..0000000 --- a/BUILD-SCIKIT-BUILD-CORE.md +++ /dev/null @@ -1,86 +0,0 @@ -# scikit-build-core Migration Notes - -This branch is a proof-of-concept for replacing the old Meson/mesonpep517 build with scikit-build-core and CMake. - -## Goal - -PSiesta should build its Python extension with CMake while using upstream Siesta's native CMake build directly. The intended integration story is simpler than the old Meson branch: stop maintaining a custom Siesta Meson fork and link against upstream Siesta as a CMake subproject or installed CMake package. - -## Current Shape - -The POC keeps the existing Python and binding design: - -- `psiesta/_psiesta.pyx` remains the Cython layer. -- `psiesta/c_bindings/fpsiesta.f90` remains the small Fortran `bind(c)` shim. -- The shim still calls the historical `fsiesta` API: `siesta_launch`, `siesta_units`, `siesta_forces`, `siesta_get`, and `siesta_quit`. -- The Python API is intentionally unchanged for now. - -The new top-level `CMakeLists.txt` currently: - -- Enables C and Fortran. -- Finds Python, NumPy, Cython, and MPI. -- Builds `_psiesta` as a Python extension module. -- Fetches upstream Siesta by default with CMake `FetchContent`. -- Allows pointing at an existing Siesta checkout via `PSIESTA_SIESTA_SOURCE_DIR`. -- Allows a future installed-package mode via `PSIESTA_USE_SYSTEM_SIESTA`. - -## Siesta Build Defaults - -For the subroutine binding POC, the CMake integration sets these Siesta defaults before adding/fetching Siesta: - -- `SIESTA_TESTS=OFF` -- `SIESTA_INSTALL=OFF` -- `SIESTA_WITH_MPI=ON` -- `SIESTA_WITH_PEXSI=OFF` -- `SIESTA_SHARED_LIBS=OFF` -- `CMAKE_POSITION_INDEPENDENT_CODE=ON` - -The MPI subroutine mode is the relevant one for PSiesta because Siesta is linked into the Python extension rather than contacted through pipes or sockets. - -## Build Commands - -With build isolation: - -```bash -python -m pip wheel . -v -``` - -Without build isolation, after installing Python build requirements in the active environment: - -```bash -python -m pip wheel . -v --no-build-isolation -``` - -Using an existing Siesta checkout: - -```bash -python -m pip wheel . -v -Ccmake.define.PSIESTA_SIESTA_SOURCE_DIR=/path/to/siesta -``` - -## Current Verification State - -The POC reaches CMake when using build isolation. The current host environment does not have a visible Fortran compiler, so configuration stops at: - -```text -No CMAKE_Fortran_COMPILER could be found. -``` - -The next meaningful test needs an environment with a Fortran compiler and MPI compiler wrappers available. - -## Expected Next Issues - -After a Fortran-capable environment is available, the next integration points to check are: - -- The exact upstream Siesta CMake target name for `libsiesta`. -- Whether Siesta's Fortran module include directories propagate to `fpsiesta.f90`. -- Whether minor API updates are needed in `fpsiesta.f90` for current upstream Siesta modules. -- Whether all desired Siesta optional dependencies are discoverable through CMake. -- Whether static linking into the Python extension is clean with MPI and position-independent code. - -## Non-Goals For This POC - -- Refreshing the Python API. -- Designing a full Python abstraction for FDF input. -- Building redistributable wheels. -- Supporting cross-node MPI packaging. -- Removing Meson files before the CMake path is proven. diff --git a/meson.build b/meson.build deleted file mode 100644 index 9071017..0000000 --- a/meson.build +++ /dev/null @@ -1,38 +0,0 @@ -project( - 'PSiesta', - version: '0.3.0', - default_options: [ - 'mpi=enabled', - 'optimization=3', - # Consider using a native file (https://mesonbuild.com/Native-environments.html) if you want to - # set options (or switch toolchainetc). - # In practice further optimization than -O3 appears to be negligible. - # 'fortran_args=["-march=native", "-funroll-loops", "-fsplit-loops", "-funswitch-loops"]', - ] -) -add_languages('c', 'fortran', required: true) - -if not get_option('mpi').enabled() - error('MPI is required.') -endif - -py_mod = import('python') -py3 = py_mod.find_installation('python3') -py3_dep = py3.dependency(required: true) - -py_version = py3.language_version() -if py_version.version_compare('< 3.6') - error('Old python detected') -endif - -cython = find_program('cython', required: true) - -mpi_dep = dependency('mpi', language: 'fortran', required: true) - -libsiesta_dep = dependency('siesta', required: false) -if not libsiesta_dep.found() - siestaproj = subproject('siesta') - libsiesta_dep = siestaproj.get_variable('siesta_dep') -endif - -subdir('psiesta') diff --git a/meson_options.txt b/meson_options.txt deleted file mode 100644 index dc09a54..0000000 --- a/meson_options.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Always on! -option('mpi', type: 'feature', value: 'enabled', yield: false) \ No newline at end of file diff --git a/psiesta/c_bindings/meson.build b/psiesta/c_bindings/meson.build deleted file mode 100644 index e1cc650..0000000 --- a/psiesta/c_bindings/meson.build +++ /dev/null @@ -1 +0,0 @@ -fpsiesta_files = files('fpsiesta.f90') \ No newline at end of file diff --git a/psiesta/meson.build b/psiesta/meson.build deleted file mode 100644 index 174eb76..0000000 --- a/psiesta/meson.build +++ /dev/null @@ -1,24 +0,0 @@ -subdir('c_bindings') - -psiesta_c = custom_target( - '_psiesta_pyx', - output : '_psiesta.c', - input : '_psiesta.pyx', - command : [cython, '-3', '@INPUT@', '-o', '@OUTPUT@'], -) - -psiesta_ext = py3.extension_module( - '_psiesta', - [psiesta_c, fpsiesta_files], - subdir: 'psiesta', - install: true, - dependencies: [py3_dep, mpi_dep, libsiesta_dep] -) - -pyfiles = files( - '__init__.py', - 'ase.py', - 'psiesta.py', -) - -py3.install_sources(pyfiles, subdir: 'psiesta', pure: false) diff --git a/subprojects/.gitignore b/subprojects/.gitignore deleted file mode 100644 index 2f63ed5..0000000 --- a/subprojects/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Subproject folders -/fdict -/gridxc -/ncdf -/psml -/siesta -/xmlf90 - -# Wrapfiles promoted from siesta -/fdict.wrap -/gridxc.wrap -/ncdf.wrap -/psml.wrap -/xmlf90.wrap diff --git a/subprojects/siesta.wrap b/subprojects/siesta.wrap deleted file mode 100644 index c8914d0..0000000 --- a/subprojects/siesta.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -url = https://gitlab.com/jonaslb/siesta.git -revision = psml-meson -depth=1