diff --git a/.github/workflows/package_and_unittest.yml b/.github/workflows/package_and_unittest.yml index 0047a13..4575f5c 100644 --- a/.github/workflows/package_and_unittest.yml +++ b/.github/workflows/package_and_unittest.yml @@ -31,7 +31,7 @@ jobs: fail-fast: false matrix: python-version: ["3.9"] - vasp-version: ["vasp.5.4.4.pl2", "vasp.6.1.2_pgi_mkl_beef", "vasp.6.2.0_pgi_mkl", "vasp.6.3.0_pgi_mkl"] + vasp-version: ["vasp.5.4.4.pl2", "vasp.6.1.2_pgi_mkl_beef", "vasp.6.2.0_pgi_mkl", "vasp.6.3.2_pgi_mkl"] steps: @@ -59,9 +59,9 @@ jobs: python -m pip install --upgrade . - name: Run ex00_vasp_test.py script run: | - source activate actions - export VASP_COMMAND="${VASP_PREFIX} /opt/${{ matrix.vasp-version }}/bin/vasp_std" - python examples/ex00_vasp_test.py + source activate actions + export VASP_COMMAND="${VASP_PREFIX} /opt/${{ matrix.vasp-version }}/bin/vasp_std" + python examples/ex00_vasp_test.py - name: Test with pytest vasp6+ if: ${{ matrix.vasp-version != 'vasp.5.4.4.pl2' }} run: | diff --git a/examples/ex00_vasp_test.py b/examples/ex00_vasp_test.py index 65b4c77..457b157 100644 --- a/examples/ex00_vasp_test.py +++ b/examples/ex00_vasp_test.py @@ -5,7 +5,7 @@ import sys import subprocess import tempfile -from ase.build import molecule +from ase.build import molecule, bulk from ase.io import read from vasp_interactive import VaspInteractive from vasp_interactive.utils import time_limit @@ -86,7 +86,6 @@ def demo_test(): istart=0, xc="pbe", directory=tmpdir, - # directory="test1" ) # Low level calculator interfacing @@ -94,7 +93,7 @@ def demo_test(): try: # In some cases the vasp binary spin up can take longer with time_limit(30): - calc._run(atoms, out=out) + calc._run(atoms, out=out, require_cell_stdin=False) except Exception as e: print("Running VASP encountered problem ", e) print(traceback.format_exc(limit=2)) @@ -151,17 +150,17 @@ def demo_test(): print("Single point calculation finished. Checking output file parsing.") status = None if vasprun_ok: - cprint(f"vasprunxml: OK", color="OKGREEN") + cprint("vasprunxml: OK", color="OKGREEN") else: - cprint(f"vasprunxml: FAIL", color="FAIL") + cprint("vasprunxml: FAIL", color="FAIL") if outcar_ok: - cprint(f"OUTCAR: OK", color="OKGREEN") + cprint("OUTCAR: OK", color="OKGREEN") else: - cprint(f"OUTCAR: FAIL", color="FAIL") + cprint("OUTCAR: FAIL", color="FAIL") if vaspout_ok: - cprint(f"VASP raw output: OK", color="OKGREEN") + cprint("VASP raw output: OK", color="OKGREEN") else: - cprint(f"VASP raw output: FAIL", color="FAIL") + cprint("VASP raw output: FAIL", color="FAIL") if not vaspout_ok: cprint( @@ -174,7 +173,7 @@ def demo_test(): ( "VaspInteractive can read from raw output but OUTCAR is incomplete. " "Only the energy and forces are read in this case. " - "Please refer to the README for details" + "Please refer to https://github.com/ulissigroup/vasp-interactive#compatibility-test-fails for details" ), color="WARNING", ) @@ -190,16 +189,102 @@ def demo_test(): ) status = "partial pass" else: - cprint("All test pass! Enjoy coding.", color="OKGREEN") + cprint("All basic test pass!", color="OKGREEN") status = "all pass" # Output the total status print("#" * 80) - print(f"Test result: {status}") + print(f"Basic Tests: {status}") print("#" * 80) + # Should we do extra test? + if status in ("minimal support", "partial pass", "all pass"): + return True + else: + return False + + +def demo_test_extra(): + """Use low-level commands to execute VASP on a simple fcc Al structure + to check whether cell input / stress is supported. Only check if stdin cell input is supported + """ + cprint( + "Running extra test example for lattice change compatibility", color="HEADER" + ) + atoms = bulk("Al") + with tempfile.TemporaryDirectory() as tmpdir: + calc = VaspInteractive( + nsw=2, + istart=0, + xc="pbe", + directory=tmpdir, + ) + # Low level calculator interfacing + + with calc._txt_outstream() as out: + try: + # In some cases the vasp binary spin up can take longer + with time_limit(60): + calc._run(atoms, out=out, require_cell_stdin=True) + calc.steps += 1 + atoms.rattle(0.005) + calc._run(atoms, out=out, require_cell_stdin=True) + except Exception as e: + print("Running VASP encountered problem ", e) + print(traceback.format_exc(limit=2)) + pass + + pid = calc.process.pid + + # Check vaspout + try: + vaspout_lines = calc._txt_to_handler().readlines() + except Exception: + vaspout_lines = [] + + vaspout_ok = False + for line in vaspout_lines: + if "LATTICE: reading from stdin" in line: + vaspout_ok = True + break + # Low level kill to prevent any issue with STOPCAR etc. + subprocess.run(["kill", "-9", str(pid)]) + + print( + "Lattice-aware single point calculations finished. Checking output file parsing." + ) + status = None + if vaspout_ok: + cprint("LATTICE support: OK", color="OKGREEN") + else: + cprint("LATTICE support: NEED PATCH", color="WARNING") + + if not vaspout_ok: + cprint( + ( + "Your VASP binary does not support inputting LATTICE via stdin. " + "A patch is needed if you want to use VaspInteractive for relaxation / MD that require lattice change. \n" + "Check the instructions at: https://github.com/ulissigroup/vasp-interactive/blob/main/vasp-build/README.md \n" + "In other cases, VaspInteractive should work normally without problem." + ), + color="WARNING", + ) + + status = "need_patch" + else: + cprint("Your VASP is properly patched!", color="OKGREEN") + status = "all pass" + + # Output the total status + print("#" * 80) + print(f"Advanced Test -- lattice change: {status}") + print("#" * 80) + + + if __name__ == "__main__": if not vasp_env_test(): sys.exit(1) - demo_test() + if demo_test(): + demo_test_extra() diff --git a/vasp_interactive/vasp_interactive.py b/vasp_interactive/vasp_interactive.py index a50bc37..456fb2d 100644 --- a/vasp_interactive/vasp_interactive.py +++ b/vasp_interactive/vasp_interactive.py @@ -239,11 +239,6 @@ def __init__( cell_tolerance=cell_tolerance, kill_timeout=kill_timeout, parse_vaspout=parse_vaspout, - # host=host, - # unixsocket=unixsocket, - # port=port, - # timeout=timeout, - # log=log, ) input_params.update(**kwargs) @@ -596,7 +591,7 @@ def close(self): with open(stopcar, "w") as fd: fd.write("LABORT = .TRUE.") - # Following two calls to _run_vasp: 1 is to let vasp see STOPCAR and do 1 SCF + # Following two calls to _run: 1 is to let vasp see STOPCAR and do 1 SCF # second is to exit the program # Program may have ended before we can write, if so then cancel the stdin for i in range(2): @@ -738,7 +733,6 @@ def calculate( # VaspInteractive supports positions change by default. # Change of cell is supported by a patch provided by this package, but needs to check on the fly - # TODO: send flag to _run to warn cell change if "numbers" in system_changes: if self.process is not None: raise NotImplementedError( @@ -756,16 +750,6 @@ def calculate( require_cell_stdin = True else: require_cell_stdin = False - # elif "cell" in system_changes: - - # if self.process is not None: - # raise NotImplementedError( - # ( - # "VaspInteractive does not support change of lattice parameters. " - # "Set VaspInteractive.cell_tolerance to a higher value if you think it's caused by round-off error. " - # "Otherwise, please create a new calculator instance or use standard Vasp calculator" - # ) - # ) self.clear_results() if atoms is not None: @@ -896,14 +880,7 @@ def read_results(self): except Exception: pass - # print(self.results) - # print(all_properties) - # Construct a fake _xml_calc object - # results = self.results.copy() - # nbands = results.pop("nbands", None) - # print(self.results["forces"]) results = {k: v for k, v in self.results.items() if k in all_properties} - # print(list(results.keys())) calc_xml = SinglePointDFTCalculator( atoms=self.atoms, efermi=self.results.get("fermi", None), **results ) @@ -1051,7 +1028,6 @@ def _force_kill_process(self): ) if self.process is not None: if self.process.poll() is None: - # self.process.kill() self._send_mpi_signal(signal.SIGKILL) # Reset process tracker self._reset_process()