From 474b1a8a1dfc484d9c68da607a0e98dcf6440cf1 Mon Sep 17 00:00:00 2001 From: Max Hutchinson Date: Mon, 7 Dec 2020 22:16:08 -0500 Subject: [PATCH 01/44] Adding pypi deployment --- .travis.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51b66ff..2b60153 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,23 @@ language: python - python: - '2.7' - '3.8' - install: - pip install -r requirements.txt - pip install -r test_requirements.txt - pip install -e . - script: -- pytest --cov=src/ --cov-report term-missing --cov-report term:skip-covered --cov-config=tox.ini --cov-fail-under=100 -svv . -- flake8 src \ No newline at end of file +- pytest --cov=src/ --cov-report term-missing --cov-report term:skip-covered --cov-config=tox.ini + --cov-fail-under=100 -svv . +- flake8 src +deploy: +- provider: pypi + user: CitrineInformatics + password: "$PYPI_PASSWORD" + distributions: sdist bdist_wheel + skip_existing: true + on: + tags: true +env: + global: + secure: M9HI5bpY5t4Rd17rjdrSo6QF4jV2JMi3hOgMzMGgly4jP1NoafOBSpJHy4TisBeE8eyrkJPJurMnF0Fw7kACtpCMT6h8tLE4r6Ss97542MJnFLV33hILdcwJZjAYfaQ0q5lrWmtNr420y5kvD1EjL3jQnYYVJC2wjAb4b5C8Ji5i/43n5Vuk3aO2BG8IzNXF0buqtAWIwyclOpr/QjkSWxf+ptj4Fv64Cy84aVTFMOGL523DRVkZgCl5vdq6gSpFTk9S/a2G16LM6GPU3ohKRzIGRzRv8HsGL4oyi2c+NjzysIdOq7yx/8crM4fADAl+xG3bh8biYJY6PVfxrR4ttNdL6UFxg0xUwPVpmngL+YjUS6DOi7cVs9VoaqsPq5mrrcZ5HvL+RaJt43o0okxBqvmZW/SRJs0fAdDLu1UKIxzH9MTpdQmuUhCyyh6muePnnzvNkSzXnZRSO2z/DUmLsRiA3qMJkY/CSIWvxqj9+tVvAT9jRjb+loWBrra3FR5+sMikmoT/Qd27xCYSrwTMBy7jgw4mOh6xNK6FmT1nS/JDpH0KrZDXwjsB2uHSw9G1EnrBF7fhXCTx7SodD9ypwKrB0aIUrRj2iUcsAKGN4mVz92KvF/OhhuLmH2+hiivSM98k2R36bZfhEuAqFTSN5DM/UoqLXckdr63vahxHlEA= From e20632ffd24fbab4270df7c72972e453679e594b Mon Sep 17 00:00:00 2001 From: Max Hutchinson Date: Mon, 7 Dec 2020 22:18:32 -0500 Subject: [PATCH 02/44] Bump version to test pypi --- src/dftinputgen/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dftinputgen/VERSION.txt b/src/dftinputgen/VERSION.txt index 6e8bf73..c4cef53 100644 --- a/src/dftinputgen/VERSION.txt +++ b/src/dftinputgen/VERSION.txt @@ -1 +1 @@ -0.1.0 +0.1.0a0 From 388f70952ab87c241e8f8490b9594620fb059eef Mon Sep 17 00:00:00 2001 From: Max Hutchinson Date: Mon, 7 Dec 2020 22:23:01 -0500 Subject: [PATCH 03/44] Working, so switching to a normal version bump. --- src/dftinputgen/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dftinputgen/VERSION.txt b/src/dftinputgen/VERSION.txt index c4cef53..17e51c3 100644 --- a/src/dftinputgen/VERSION.txt +++ b/src/dftinputgen/VERSION.txt @@ -1 +1 @@ -0.1.0a0 +0.1.1 From 87ec5427de434391aa862eb895c0ed9809ed909d Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Mon, 7 Dec 2020 19:48:55 -0800 Subject: [PATCH 04/44] read release version from package version --- docs/src/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/conf.py b/docs/src/conf.py index 81cbe9e..4f535a3 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -23,7 +23,9 @@ author = "Vinay Hegde " # The full version, including alpha/beta/rc tags -release = "0.1.0" +version_file = os.path.join("..", "..", "src", "dftinputgen", "VERSION.txt") +with open(version_file, "r") as fr: + release = fr.read().strip() # -- General configuration --------------------------------------------------- From 7174eeada3afcabb510311c77e996503163d2ead Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Mon, 7 Dec 2020 20:26:04 -0800 Subject: [PATCH 05/44] fix missing refactor to dftinputgen in MANIFEST --- MANIFEST.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 11dad2a..a84d772 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include src/dftinpgen/*.txt -include src/dftinpgen/data/*.json -include src/dftinpgen/qe/settings/*.json -include src/dftinpgen/qe/settings/calculation_presets/*.json +include src/dftinputgen/*.txt +include src/dftinputgen/data/*.json +include src/dftinputgen/qe/settings/*.json +include src/dftinputgen/qe/settings/calculation_presets/*.json From d825a27b6fb8c572fa41e5ceaa81619a429e379f Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Mon, 7 Dec 2020 21:11:34 -0800 Subject: [PATCH 06/44] ask setuptools to include data files --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 3996cd5..794b1bb 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ author_email="vhegde@citrine.io", packages=find_packages(where="src"), package_dir={"": "src"}, + include_package_data=True, install_requires=["six", "numpy", "ase <= 3.17"], entry_points={"console_scripts": ["dftinputgen = dftinputgen.cli:driver"]}, classifiers=[ From 7ac84dac1a2b4f40f6283e9f14159cf322a0d849 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Mon, 7 Dec 2020 21:14:21 -0800 Subject: [PATCH 07/44] version bump --- src/dftinputgen/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dftinputgen/VERSION.txt b/src/dftinputgen/VERSION.txt index 17e51c3..d917d3e 100644 --- a/src/dftinputgen/VERSION.txt +++ b/src/dftinputgen/VERSION.txt @@ -1 +1 @@ -0.1.1 +0.1.2 From 2a12777e5c54714ee8b8bba7e0191c467c66b117 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 8 Dec 2020 13:33:52 -0500 Subject: [PATCH 08/44] adds gpaw --- src/dftinputgen/gpaw/__init__.py | 1 + src/dftinputgen/gpaw/gpaw.py | 157 ++++++++++++++++++ src/dftinputgen/gpaw/settings/__init__.py | 10 ++ .../gpaw/settings/base_recipes/__init__.py | 15 ++ .../gpaw/settings/base_recipes/bulk_opt.json | 12 ++ .../settings/base_recipes/bulk_opt_hcp.json | 12 ++ .../gpaw/settings/base_recipes/molecule.json | 9 + .../gpaw/settings/base_recipes/relax.json | 15 ++ .../gpaw/settings/tags_and_groups.json | 28 ++++ 9 files changed, 259 insertions(+) create mode 100644 src/dftinputgen/gpaw/__init__.py create mode 100644 src/dftinputgen/gpaw/gpaw.py create mode 100644 src/dftinputgen/gpaw/settings/__init__.py create mode 100644 src/dftinputgen/gpaw/settings/base_recipes/__init__.py create mode 100644 src/dftinputgen/gpaw/settings/base_recipes/bulk_opt.json create mode 100644 src/dftinputgen/gpaw/settings/base_recipes/bulk_opt_hcp.json create mode 100644 src/dftinputgen/gpaw/settings/base_recipes/molecule.json create mode 100644 src/dftinputgen/gpaw/settings/base_recipes/relax.json create mode 100644 src/dftinputgen/gpaw/settings/tags_and_groups.json diff --git a/src/dftinputgen/gpaw/__init__.py b/src/dftinputgen/gpaw/__init__.py new file mode 100644 index 0000000..6fb5a4b --- /dev/null +++ b/src/dftinputgen/gpaw/__init__.py @@ -0,0 +1 @@ +from dftinpgen.gpaw.gpaw import GPAWInputGenerator diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py new file mode 100644 index 0000000..eec4e75 --- /dev/null +++ b/src/dftinputgen/gpaw/gpaw.py @@ -0,0 +1,157 @@ +import os +import json +import six +import itertools + +from dftinpgen.data import STANDARD_ATOMIC_WEIGHTS +from dftinpgen.utils import get_elem_symbol +from dftinpgen.gpaw.settings import GPAW_TAGS +from dftinpgen.gpaw.settings.base_recipes import GPAW_BASE_RECIPES + +from dftinpgen.base import DftInputGenerator +from dftinpgen.base import DftInputGeneratorError + +class GPAWInputGeneratorError(DftInputGeneratorError): + pass + +class GPAWInputGenerator(DftInputGenerator): + """Base class to generate input python scripts for GPAW """ + + def __init__(self, crystal_structure=None, base_recipe=None, + custom_sett_file=None, custom_sett_dict=None, + write_location=None, overwrite_files=None, **kwargs): + """ + """ + super(GPAWInputGenerator, self).__init__( + crystal_structure=crystal_structure, + base_recipe=base_recipe, + custom_sett_file=custom_sett_file, + custom_sett_dict=custom_sett_dict, + write_location=write_location, + overwrite_files=overwrite_files, + **kwargs) + + @property + def dft_package(self): + return 'GPAW' + + + @property + def calculation_settings(self): + calc_sett = {} + if self.base_recipe is not None: + calc_sett.update(GPAW_BASE_RECIPES[self.base_recipe]) + if self.custom_sett_file is not None: + with open(self.custom_sett_file, 'r') as fr: + calc_sett_update(json.load(fr)) + if self.custom_sett_dict is not None: + calc_sett.update(self.custom_sett_dict) + return calc_sett + + + def _get_default_input_filename(self): + return '{}_in.py'.format(self.base_recipe) \ + if self.base_recipe is not None else 'gpaw_in.py' + + @property + def calc_obj_as_str(self): + top = "slab.calc = GPAW(" + + calc_sett = self.calculation_settings + + params = [] + for p in GPAW_TAGS['parameters']: + if p in calc_sett: + if type(calc_sett[p]) is str: + params.append(f"{p}='{str(calc_sett[p])}'") + else: + params.append(f"{p}={str(calc_sett[p])}") + + return '\n'.join([top,',\n'.join(params),')']) + + + @property + def gpaw_input_as_str(self): + header = """from gpaw import GPAW +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.eos import EquationOfState +from fractions import Fraction +import numpy as np +import glob +""" + + read_init_traj = """ +a = glob.glob('input.traj') +slab = read(a[-1]) +""" + + calc_sett = self.calculation_settings +# +# gpaw_skel=f"""slab.calc=GPAW( +# xc='{calc_sett['xc']}', +# h={calc_sett['h']}, +# occupations={str(calc_sett['occupations'])}, +# poissonsolver={str(calc_sett['poissonsolver'])} +# ) +# """ + + calc_type = calc_sett['calculation'] + if calc_type == 'relax': + # This definition will become unnecessary when defined in DFT FLOW + define_relax_fn=""" +def relax(atoms, fmax=0.05, step=0.04): + name = atoms.get_chemical_formula(mode='hill') + atoms.calc.set(txt='output.txt') + atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') + dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn.run(fmax=fmax) +""" + return '\n'.join([header,read_init_traj,define_relax_fn,self.calc_obj_as_str,'relax(slab)']) + + elif calc_type == 'bulk_opt' or calc_type == 'bulk_opt_hcp': + define_bulk_opt_fn=""" +def bulk_opt(atoms, step=0.05): + cell = atoms.get_cell() + name = atoms.get_chemical_formula(mode='hill') + vol=atoms.get_volume() + volumes =[] + energies=[] + for x in np.linspace(1-2*step,1+2*step,5): + atoms.set_cell(cell*x, scale_atoms=True) + atoms.calc.set(txt=name+'_'+str(x)+'.txt') + energies.append(atoms.get_potential_energy()) + volumes.append(atoms.get_volume()) + eos = EquationOfState(volumes, energies) + v0,e0,B= eos.fit() + atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) + x0=(v0/vol)**Fraction('1/3') + atoms.calc.set(txt='output.txt') + dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') + dyn.run(fmax=0.05) + atoms.calc.write('output.gpw') +""" + return '\n'.join([header,read_init_traj,define_bulk_opt_fn,self.calc_obj_as_str,'bulk_opt(slab)']) + # if not a relax or bulk_opt calculation, defaults to getting total energy of static structure + return '\n'.join([header,read_init_traj,define_relax_fn,calc_obj_as_str,'slab.get_total_energy()']) + + def write_gpaw_input(self, write_location=None, filename=None): + if not self.gpaw_input_as_str.strip(): + msg = 'Nothing to write (probably no input settings found.)' + raise GPAWInputGeneratorError(msg) + if write_location is None: + msg = 'Location to write files not specified' + raise GPAWInputGeneratorError(msg) + if filename is None: + msg = 'Name of the input file to write into not specified' + raise GPAWInputGeneratorError(msg) + with open(os.path.join(write_location, filename), 'w') as fw: + fw.write(self.gpaw_input_as_str) + + def write_input_files(self): + self.write_gpaw_input( + write_location = self.write_location, + filename = self._get_default_input_filename(), + ) + diff --git a/src/dftinputgen/gpaw/settings/__init__.py b/src/dftinputgen/gpaw/settings/__init__.py new file mode 100644 index 0000000..1ed1dd2 --- /dev/null +++ b/src/dftinputgen/gpaw/settings/__init__.py @@ -0,0 +1,10 @@ +import json +import pkg_resources + +__all__ = ['GPAW_TAGS'] + +tags_file = pkg_resources.resource_filename('dftinpgen.gpaw.settings', + 'tags_and_groups.json') + +with open(tags_file, 'r') as fr: + GPAW_TAGS = json.load(fr) diff --git a/src/dftinputgen/gpaw/settings/base_recipes/__init__.py b/src/dftinputgen/gpaw/settings/base_recipes/__init__.py new file mode 100644 index 0000000..1dd0c50 --- /dev/null +++ b/src/dftinputgen/gpaw/settings/base_recipes/__init__.py @@ -0,0 +1,15 @@ +import os +import glob +import json + + +__all__ = ['GPAW_BASE_RECIPES'] + + +recipe_jsons = glob.glob(os.path.join(os.path.dirname(__file__), '*.json')) + +GPAW_BASE_RECIPES = {} +for recipe_json in recipe_jsons: + recipe_name = os.path.splitext(os.path.basename(recipe_json))[0] + with open(recipe_json, 'r') as fr: + GPAW_BASE_RECIPES[recipe_name] = json.load(fr) diff --git a/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt.json b/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt.json new file mode 100644 index 0000000..c9344ed --- /dev/null +++ b/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt.json @@ -0,0 +1,12 @@ +{ + "kpts": { + "size": [12,12,12] + }, + "xc": "BEEF-vdW", + "h": 0.16, + "occupations": { + "name": "fermi-dirac", + "width": 0.05 + }, + "calculation": "bulk_opt" +} diff --git a/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt_hcp.json b/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt_hcp.json new file mode 100644 index 0000000..039f8de --- /dev/null +++ b/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt_hcp.json @@ -0,0 +1,12 @@ +{ + "kpts": { + "size": [12,12,6] + }, + "xc": "BEEF-vdW", + "h": 0.16, + "occupations": { + "name": "fermi-dirac", + "width": 0.05 + }, + "calculation": "bulk_opt_hcp" +} diff --git a/src/dftinputgen/gpaw/settings/base_recipes/molecule.json b/src/dftinputgen/gpaw/settings/base_recipes/molecule.json new file mode 100644 index 0000000..aff579f --- /dev/null +++ b/src/dftinputgen/gpaw/settings/base_recipes/molecule.json @@ -0,0 +1,9 @@ +{ + "xc": "BEEF-vdW", + "h": 0.16, + "occupations": { + "name": "fermi-dirac", + "width": 0.05 + }, + "calculation": "relax" +} diff --git a/src/dftinputgen/gpaw/settings/base_recipes/relax.json b/src/dftinputgen/gpaw/settings/base_recipes/relax.json new file mode 100644 index 0000000..4194010 --- /dev/null +++ b/src/dftinputgen/gpaw/settings/base_recipes/relax.json @@ -0,0 +1,15 @@ +{ + "kpts": { + "size": [4,4,1] + }, + "xc": "BEEF-vdW", + "h": 0.16, + "occupations": { + "name": "fermi-dirac", + "width": 0.05 + }, + "poissonsolver": { + "dipolelayer": "xy" + }, + "calculation": "relax" +} diff --git a/src/dftinputgen/gpaw/settings/tags_and_groups.json b/src/dftinputgen/gpaw/settings/tags_and_groups.json new file mode 100644 index 0000000..e6105cb --- /dev/null +++ b/src/dftinputgen/gpaw/settings/tags_and_groups.json @@ -0,0 +1,28 @@ +{ + "parameters" : [ + "basis", + "charge", + "communicator", + "convergence", + "eigensolver", + "external", + "fixdensity", + "gpts", + "h", + "hund", + "idiotproof", + "kpts", + "maxiter", + "mode", + "nbands", + "occupations", + "parallel", + "poissonsolver", + "random", + "setups", + "spinpol", + "symmetry", + "txt", + "xc" + ] +} From e62b7a075248b80875fa36c41178b1097ef2a392 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 14 Dec 2020 21:51:04 -0500 Subject: [PATCH 09/44] Fix typos base_recipe -> calculation_presets and dftinpgen -> dftinputgen --- src/dftinputgen/gpaw/__init__.py | 2 +- src/dftinputgen/gpaw/gpaw.py | 24 +++++++++---------- src/dftinputgen/gpaw/settings/__init__.py | 2 +- .../__init__.py | 0 .../bulk_opt.json | 0 .../bulk_opt_hcp.json | 0 .../molecule.json | 0 .../relax.json | 0 8 files changed, 14 insertions(+), 14 deletions(-) rename src/dftinputgen/gpaw/settings/{base_recipes => calculation_presets}/__init__.py (100%) rename src/dftinputgen/gpaw/settings/{base_recipes => calculation_presets}/bulk_opt.json (100%) rename src/dftinputgen/gpaw/settings/{base_recipes => calculation_presets}/bulk_opt_hcp.json (100%) rename src/dftinputgen/gpaw/settings/{base_recipes => calculation_presets}/molecule.json (100%) rename src/dftinputgen/gpaw/settings/{base_recipes => calculation_presets}/relax.json (100%) diff --git a/src/dftinputgen/gpaw/__init__.py b/src/dftinputgen/gpaw/__init__.py index 6fb5a4b..6c1eb45 100644 --- a/src/dftinputgen/gpaw/__init__.py +++ b/src/dftinputgen/gpaw/__init__.py @@ -1 +1 @@ -from dftinpgen.gpaw.gpaw import GPAWInputGenerator +from dftinputgen.gpaw.gpaw import GPAWInputGenerator diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index eec4e75..4a0ea3a 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -3,13 +3,13 @@ import six import itertools -from dftinpgen.data import STANDARD_ATOMIC_WEIGHTS -from dftinpgen.utils import get_elem_symbol -from dftinpgen.gpaw.settings import GPAW_TAGS -from dftinpgen.gpaw.settings.base_recipes import GPAW_BASE_RECIPES +from dftinputgen.data import STANDARD_ATOMIC_WEIGHTS +from dftinputgen.utils import get_elem_symbol +from dftinputgen.gpaw.settings import GPAW_TAGS +from dftinputgen.gpaw.settings.calculation_presetss import GPAW_BASE_RECIPES -from dftinpgen.base import DftInputGenerator -from dftinpgen.base import DftInputGeneratorError +from dftinputgen.base import DftInputGenerator +from dftinputgen.base import DftInputGeneratorError class GPAWInputGeneratorError(DftInputGeneratorError): pass @@ -17,14 +17,14 @@ class GPAWInputGeneratorError(DftInputGeneratorError): class GPAWInputGenerator(DftInputGenerator): """Base class to generate input python scripts for GPAW """ - def __init__(self, crystal_structure=None, base_recipe=None, + def __init__(self, crystal_structure=None, calculation_presets=None, custom_sett_file=None, custom_sett_dict=None, write_location=None, overwrite_files=None, **kwargs): """ """ super(GPAWInputGenerator, self).__init__( crystal_structure=crystal_structure, - base_recipe=base_recipe, + calculation_presets=calculation_presets, custom_sett_file=custom_sett_file, custom_sett_dict=custom_sett_dict, write_location=write_location, @@ -39,8 +39,8 @@ def dft_package(self): @property def calculation_settings(self): calc_sett = {} - if self.base_recipe is not None: - calc_sett.update(GPAW_BASE_RECIPES[self.base_recipe]) + if self.calculation_presets is not None: + calc_sett.update(GPAW_BASE_RECIPES[self.calculation_presets]) if self.custom_sett_file is not None: with open(self.custom_sett_file, 'r') as fr: calc_sett_update(json.load(fr)) @@ -50,8 +50,8 @@ def calculation_settings(self): def _get_default_input_filename(self): - return '{}_in.py'.format(self.base_recipe) \ - if self.base_recipe is not None else 'gpaw_in.py' + return '{}_in.py'.format(self.calculation_presets) \ + if self.calculation_presets is not None else 'gpaw_in.py' @property def calc_obj_as_str(self): diff --git a/src/dftinputgen/gpaw/settings/__init__.py b/src/dftinputgen/gpaw/settings/__init__.py index 1ed1dd2..282f609 100644 --- a/src/dftinputgen/gpaw/settings/__init__.py +++ b/src/dftinputgen/gpaw/settings/__init__.py @@ -3,7 +3,7 @@ __all__ = ['GPAW_TAGS'] -tags_file = pkg_resources.resource_filename('dftinpgen.gpaw.settings', +tags_file = pkg_resources.resource_filename('dftinputgen.gpaw.settings', 'tags_and_groups.json') with open(tags_file, 'r') as fr: diff --git a/src/dftinputgen/gpaw/settings/base_recipes/__init__.py b/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py similarity index 100% rename from src/dftinputgen/gpaw/settings/base_recipes/__init__.py rename to src/dftinputgen/gpaw/settings/calculation_presets/__init__.py diff --git a/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt.json b/src/dftinputgen/gpaw/settings/calculation_presets/bulk_opt.json similarity index 100% rename from src/dftinputgen/gpaw/settings/base_recipes/bulk_opt.json rename to src/dftinputgen/gpaw/settings/calculation_presets/bulk_opt.json diff --git a/src/dftinputgen/gpaw/settings/base_recipes/bulk_opt_hcp.json b/src/dftinputgen/gpaw/settings/calculation_presets/bulk_opt_hcp.json similarity index 100% rename from src/dftinputgen/gpaw/settings/base_recipes/bulk_opt_hcp.json rename to src/dftinputgen/gpaw/settings/calculation_presets/bulk_opt_hcp.json diff --git a/src/dftinputgen/gpaw/settings/base_recipes/molecule.json b/src/dftinputgen/gpaw/settings/calculation_presets/molecule.json similarity index 100% rename from src/dftinputgen/gpaw/settings/base_recipes/molecule.json rename to src/dftinputgen/gpaw/settings/calculation_presets/molecule.json diff --git a/src/dftinputgen/gpaw/settings/base_recipes/relax.json b/src/dftinputgen/gpaw/settings/calculation_presets/relax.json similarity index 100% rename from src/dftinputgen/gpaw/settings/base_recipes/relax.json rename to src/dftinputgen/gpaw/settings/calculation_presets/relax.json From ca2140608d1b4608c020c427525b93325cf2f2e3 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 14 Dec 2020 22:01:01 -0500 Subject: [PATCH 10/44] fix import typo --- src/dftinputgen/gpaw/gpaw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 4a0ea3a..29484fc 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -6,7 +6,7 @@ from dftinputgen.data import STANDARD_ATOMIC_WEIGHTS from dftinputgen.utils import get_elem_symbol from dftinputgen.gpaw.settings import GPAW_TAGS -from dftinputgen.gpaw.settings.calculation_presetss import GPAW_BASE_RECIPES +from dftinputgen.gpaw.settings.calculation_presets import GPAW_BASE_RECIPES from dftinputgen.base import DftInputGenerator from dftinputgen.base import DftInputGeneratorError From 03d77597c8406e853aad4273fbf63a269cb60f42 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 15 Dec 2020 12:37:01 -0500 Subject: [PATCH 11/44] Start updating gpaw syntax to match qe --- src/dftinputgen/gpaw/gpaw.py | 31 ++++++++----------- .../settings/calculation_presets/__init__.py | 24 +++++++++----- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 29484fc..8010047 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -6,7 +6,7 @@ from dftinputgen.data import STANDARD_ATOMIC_WEIGHTS from dftinputgen.utils import get_elem_symbol from dftinputgen.gpaw.settings import GPAW_TAGS -from dftinputgen.gpaw.settings.calculation_presets import GPAW_BASE_RECIPES +from dftinputgen.gpaw.settings.calculation_presets import GPAW_PRESETS from dftinputgen.base import DftInputGenerator from dftinputgen.base import DftInputGeneratorError @@ -31,6 +31,8 @@ def __init__(self, crystal_structure=None, calculation_presets=None, overwrite_files=overwrite_files, **kwargs) + self._calculation_settings = self._get_calculation_settings() + @property def dft_package(self): return 'GPAW' @@ -38,17 +40,20 @@ def dft_package(self): @property def calculation_settings(self): + """Dictionary of all calculation settings to use as input for gpaw.""" + return self._get_calculation_settings() + + def _get_calculation_settings(self): + """Load all calculation settings: user-input and auto-determined.""" calc_sett = {} if self.calculation_presets is not None: - calc_sett.update(GPAW_BASE_RECIPES[self.calculation_presets]) - if self.custom_sett_file is not None: - with open(self.custom_sett_file, 'r') as fr: - calc_sett_update(json.load(fr)) + calc_sett.update(GPAW_PRESETS[self.calculation_presets]) + if self.custom_sett_from_file is not None: + calc_sett.update(self.custom_sett_from_file) if self.custom_sett_dict is not None: calc_sett.update(self.custom_sett_dict) return calc_sett - def _get_default_input_filename(self): return '{}_in.py'.format(self.calculation_presets) \ if self.calculation_presets is not None else 'gpaw_in.py' @@ -57,7 +62,7 @@ def _get_default_input_filename(self): def calc_obj_as_str(self): top = "slab.calc = GPAW(" - calc_sett = self.calculation_settings + calc_sett = self._calculation_settings params = [] for p in GPAW_TAGS['parameters']: @@ -87,19 +92,9 @@ def gpaw_input_as_str(self): slab = read(a[-1]) """ - calc_sett = self.calculation_settings -# -# gpaw_skel=f"""slab.calc=GPAW( -# xc='{calc_sett['xc']}', -# h={calc_sett['h']}, -# occupations={str(calc_sett['occupations'])}, -# poissonsolver={str(calc_sett['poissonsolver'])} -# ) -# """ - + calc_sett = self._calculation_settings calc_type = calc_sett['calculation'] if calc_type == 'relax': - # This definition will become unnecessary when defined in DFT FLOW define_relax_fn=""" def relax(atoms, fmax=0.05, step=0.04): name = atoms.get_chemical_formula(mode='hill') diff --git a/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py b/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py index 1dd0c50..268ebeb 100644 --- a/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py +++ b/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py @@ -1,15 +1,23 @@ import os -import glob import json +import pkg_resources -__all__ = ['GPAW_BASE_RECIPES'] +__all__ = ['GPAW_PRESETS'] -recipe_jsons = glob.glob(os.path.join(os.path.dirname(__file__), '*.json')) +GPAW_PRESETS = {} -GPAW_BASE_RECIPES = {} -for recipe_json in recipe_jsons: - recipe_name = os.path.splitext(os.path.basename(recipe_json))[0] - with open(recipe_json, 'r') as fr: - GPAW_BASE_RECIPES[recipe_name] = json.load(fr) + +preset_listdir = pkg_resources.resource_listdir( + "dftinputgen.gpaw.settings", "calculation_presets" +) +for filename in preset_listdir: + root, ext = os.path.splitext(filename) + if not ext == ".json": + continue + resource = pkg_resources.resource_filename( + "dftinputgen.gpaw.settings.calculation_presets", filename + ) + with open(resource, "r") as fr: + GPAW_PRESETS[root] = json.load(fr) From 60ed5dfc70379c6770ab429eba092eedb7bb3854 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 15 Dec 2020 13:17:10 -0500 Subject: [PATCH 12/44] Reformat with black --- src/dftinputgen/gpaw/gpaw.py | 93 +++++++++++++------ src/dftinputgen/gpaw/settings/__init__.py | 9 +- .../settings/calculation_presets/__init__.py | 2 +- 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 8010047..56fe1dc 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -11,17 +11,25 @@ from dftinputgen.base import DftInputGenerator from dftinputgen.base import DftInputGeneratorError + class GPAWInputGeneratorError(DftInputGeneratorError): pass + class GPAWInputGenerator(DftInputGenerator): """Base class to generate input python scripts for GPAW """ - def __init__(self, crystal_structure=None, calculation_presets=None, - custom_sett_file=None, custom_sett_dict=None, - write_location=None, overwrite_files=None, **kwargs): - """ - """ + def __init__( + self, + crystal_structure=None, + calculation_presets=None, + custom_sett_file=None, + custom_sett_dict=None, + write_location=None, + overwrite_files=None, + **kwargs, + ): + """""" super(GPAWInputGenerator, self).__init__( crystal_structure=crystal_structure, calculation_presets=calculation_presets, @@ -29,14 +37,14 @@ def __init__(self, crystal_structure=None, calculation_presets=None, custom_sett_dict=custom_sett_dict, write_location=write_location, overwrite_files=overwrite_files, - **kwargs) + **kwargs, + ) self._calculation_settings = self._get_calculation_settings() @property def dft_package(self): - return 'GPAW' - + return "GPAW" @property def calculation_settings(self): @@ -55,25 +63,27 @@ def _get_calculation_settings(self): return calc_sett def _get_default_input_filename(self): - return '{}_in.py'.format(self.calculation_presets) \ - if self.calculation_presets is not None else 'gpaw_in.py' + return ( + "{}_in.py".format(self.calculation_presets) + if self.calculation_presets is not None + else "gpaw_in.py" + ) @property def calc_obj_as_str(self): top = "slab.calc = GPAW(" - + calc_sett = self._calculation_settings params = [] - for p in GPAW_TAGS['parameters']: + for p in GPAW_TAGS["parameters"]: if p in calc_sett: if type(calc_sett[p]) is str: params.append(f"{p}='{str(calc_sett[p])}'") else: params.append(f"{p}={str(calc_sett[p])}") - return '\n'.join([top,',\n'.join(params),')']) - + return "\n".join([top, ",\n".join(params), ")"]) @property def gpaw_input_as_str(self): @@ -93,9 +103,9 @@ def gpaw_input_as_str(self): """ calc_sett = self._calculation_settings - calc_type = calc_sett['calculation'] - if calc_type == 'relax': - define_relax_fn=""" + calc_type = calc_sett["calculation"] + if calc_type == "relax": + define_relax_fn = """ def relax(atoms, fmax=0.05, step=0.04): name = atoms.get_chemical_formula(mode='hill') atoms.calc.set(txt='output.txt') @@ -103,10 +113,18 @@ def relax(atoms, fmax=0.05, step=0.04): dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) dyn.run(fmax=fmax) """ - return '\n'.join([header,read_init_traj,define_relax_fn,self.calc_obj_as_str,'relax(slab)']) - - elif calc_type == 'bulk_opt' or calc_type == 'bulk_opt_hcp': - define_bulk_opt_fn=""" + return "\n".join( + [ + header, + read_init_traj, + define_relax_fn, + self.calc_obj_as_str, + "relax(slab)", + ] + ) + + elif calc_type == "bulk_opt" or calc_type == "bulk_opt_hcp": + define_bulk_opt_fn = """ def bulk_opt(atoms, step=0.05): cell = atoms.get_cell() name = atoms.get_chemical_formula(mode='hill') @@ -127,26 +145,41 @@ def bulk_opt(atoms, step=0.05): dyn.run(fmax=0.05) atoms.calc.write('output.gpw') """ - return '\n'.join([header,read_init_traj,define_bulk_opt_fn,self.calc_obj_as_str,'bulk_opt(slab)']) + return "\n".join( + [ + header, + read_init_traj, + define_bulk_opt_fn, + self.calc_obj_as_str, + "bulk_opt(slab)", + ] + ) # if not a relax or bulk_opt calculation, defaults to getting total energy of static structure - return '\n'.join([header,read_init_traj,define_relax_fn,calc_obj_as_str,'slab.get_total_energy()']) + return "\n".join( + [ + header, + read_init_traj, + define_relax_fn, + calc_obj_as_str, + "slab.get_total_energy()", + ] + ) def write_gpaw_input(self, write_location=None, filename=None): if not self.gpaw_input_as_str.strip(): - msg = 'Nothing to write (probably no input settings found.)' + msg = "Nothing to write (probably no input settings found.)" raise GPAWInputGeneratorError(msg) if write_location is None: - msg = 'Location to write files not specified' + msg = "Location to write files not specified" raise GPAWInputGeneratorError(msg) if filename is None: - msg = 'Name of the input file to write into not specified' + msg = "Name of the input file to write into not specified" raise GPAWInputGeneratorError(msg) - with open(os.path.join(write_location, filename), 'w') as fw: + with open(os.path.join(write_location, filename), "w") as fw: fw.write(self.gpaw_input_as_str) def write_input_files(self): self.write_gpaw_input( - write_location = self.write_location, - filename = self._get_default_input_filename(), + write_location=self.write_location, + filename=self._get_default_input_filename(), ) - diff --git a/src/dftinputgen/gpaw/settings/__init__.py b/src/dftinputgen/gpaw/settings/__init__.py index 282f609..bbbdc13 100644 --- a/src/dftinputgen/gpaw/settings/__init__.py +++ b/src/dftinputgen/gpaw/settings/__init__.py @@ -1,10 +1,11 @@ import json import pkg_resources -__all__ = ['GPAW_TAGS'] +__all__ = ["GPAW_TAGS"] -tags_file = pkg_resources.resource_filename('dftinputgen.gpaw.settings', - 'tags_and_groups.json') +tags_file = pkg_resources.resource_filename( + "dftinputgen.gpaw.settings", "tags_and_groups.json" +) -with open(tags_file, 'r') as fr: +with open(tags_file, "r") as fr: GPAW_TAGS = json.load(fr) diff --git a/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py b/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py index 268ebeb..3191b35 100644 --- a/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py +++ b/src/dftinputgen/gpaw/settings/calculation_presets/__init__.py @@ -3,7 +3,7 @@ import pkg_resources -__all__ = ['GPAW_PRESETS'] +__all__ = ["GPAW_PRESETS"] GPAW_PRESETS = {} From 41a98641b4b5ba0c6459ef241cd1993a8ae61967 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 17 Dec 2020 16:08:09 -0500 Subject: [PATCH 13/44] Adds allowing gpaw input file name to write to --- src/dftinputgen/gpaw/gpaw.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 8010047..164231f 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -19,7 +19,7 @@ class GPAWInputGenerator(DftInputGenerator): def __init__(self, crystal_structure=None, calculation_presets=None, custom_sett_file=None, custom_sett_dict=None, - write_location=None, overwrite_files=None, **kwargs): + write_location=None, gpaw_input_file=None, overwrite_files=None, **kwargs): """ """ super(GPAWInputGenerator, self).__init__( @@ -33,10 +33,22 @@ def __init__(self, crystal_structure=None, calculation_presets=None, self._calculation_settings = self._get_calculation_settings() + self._gpaw_input_file= self._get_default_input_filename() + self.gpaw_input_file = gpaw_input_file + @property def dft_package(self): return 'GPAW' + @property + def gpaw_input_file(self): + """Name of the gpaw input file to write to.""" + return self._gpaw_input_file + + @gpaw_input_file.setter + def gpaw_input_file(self, gpaw_input_file): + if gpaw_input_file is not None: + self._gpaw_input_file = gpaw_input_file @property def calculation_settings(self): @@ -55,8 +67,9 @@ def _get_calculation_settings(self): return calc_sett def _get_default_input_filename(self): - return '{}_in.py'.format(self.calculation_presets) \ - if self.calculation_presets is not None else 'gpaw_in.py' + if self.calculation_presets is None: + return "gpaw_in.py" + return '{}_in.py'.format(self.calculation_presets) @property def calc_obj_as_str(self): @@ -147,6 +160,6 @@ def write_gpaw_input(self, write_location=None, filename=None): def write_input_files(self): self.write_gpaw_input( write_location = self.write_location, - filename = self._get_default_input_filename(), + filename = self.gpaw_input_file, ) From c6799737e414c7cddd18b93e86ed58c79ac05315 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 17 Dec 2020 16:53:26 -0500 Subject: [PATCH 14/44] Start adding gpaw tests --- tests/gpaw/files/TEST_cu_bulk_opt.py | 39 ++++++++++++++++++++++++++ tests/gpaw/files/cu_bulk.traj | Bin 0 -> 332 bytes tests/gpaw/test_gpaw.py | 40 +++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 tests/gpaw/files/TEST_cu_bulk_opt.py create mode 100644 tests/gpaw/files/cu_bulk.traj create mode 100644 tests/gpaw/test_gpaw.py diff --git a/tests/gpaw/files/TEST_cu_bulk_opt.py b/tests/gpaw/files/TEST_cu_bulk_opt.py new file mode 100644 index 0000000..20ab9e0 --- /dev/null +++ b/tests/gpaw/files/TEST_cu_bulk_opt.py @@ -0,0 +1,39 @@ +from gpaw import GPAW +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +import numpy as np +import glob + + +a = glob.glob('input.traj') +slab = read(a[-1]) + + +def bulk_opt(atoms, step=0.05): + cell = atoms.get_cell() + name = atoms.get_chemical_formula(mode='hill') + vol=atoms.get_volume() + volumes =[] + energies=[] + for x in np.linspace(1-2*step,1+2*step,5): + atoms.set_cell(cell*x, scale_atoms=True) + atoms.calc.set(txt=name+'_'+str(x)+'.txt') + energies.append(atoms.get_potential_energy()) + volumes.append(atoms.get_volume()) + eos = EquationOfState(volumes, energies) + v0,e0,B= eos.fit() + atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) + x0=(v0/vol)**Fraction('1/3') + atoms.calc.set(txt=name+'_'+str(x0)+'.txt') + dyn=BFGS(atoms=atoms,trajectory=name+'_'+str(x0)+'.traj',logfile = 'qn.log') + dyn.run(fmax=0.05) + atoms.calc.write(name+str(x0)+'.gpw') + +slab.calc = GPAW( +h=0.16, +kpts={'size': [12, 12, 12]}, +occupations={'name': 'fermi-dirac', 'width': 0.05}, +xc='BEEF-vdW' +) +bulk_opt(slab) \ No newline at end of file diff --git a/tests/gpaw/files/cu_bulk.traj b/tests/gpaw/files/cu_bulk.traj new file mode 100644 index 0000000000000000000000000000000000000000..c6c344cb1d7f90338d33ff55e43c63b1ef4643fe GIT binary patch literal 332 zcmZ{d(F%ev6o!kQBRK3@$4ryxu8W?afadURb{{O?!XgK*d=q~_C3 e9F04 Date: Thu, 17 Dec 2020 16:56:08 -0500 Subject: [PATCH 15/44] Updates MANIFEST.in to include gpaw --- MANIFEST.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index a84d772..d5c2f9c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,5 @@ include src/dftinputgen/*.txt include src/dftinputgen/data/*.json include src/dftinputgen/qe/settings/*.json include src/dftinputgen/qe/settings/calculation_presets/*.json +include src/dftinputgen/gpaw/settings/*.json +include src/dftinputgen/gpaw/settings/calculation_presets/*.json From d25d7a6aec38af9064a1da4a3b2df37a46375e3e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 17 Dec 2020 17:22:57 -0500 Subject: [PATCH 16/44] reformatting --- src/dftinputgen/gpaw/gpaw.py | 11 +++--- tests/gpaw/files/TEST_cu_bulk_opt.py | 53 +++++++++++++++------------- tests/gpaw/test_gpaw.py | 15 +++++--- 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index f0f9cc8..ce647e1 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -18,6 +18,7 @@ class GPAWInputGeneratorError(DftInputGeneratorError): class GPAWInputGenerator(DftInputGenerator): """Base class to generate input python scripts for GPAW """ + def __init__( self, crystal_structure=None, @@ -42,12 +43,12 @@ def __init__( self._calculation_settings = self._get_calculation_settings() - self._gpaw_input_file= self._get_default_input_filename() + self._gpaw_input_file = self._get_default_input_filename() self.gpaw_input_file = gpaw_input_file @property def dft_package(self): - return 'GPAW' + return "GPAW" @property def gpaw_input_file(self): @@ -78,7 +79,7 @@ def _get_calculation_settings(self): def _get_default_input_filename(self): if self.calculation_presets is None: return "gpaw_in.py" - return '{}_in.py'.format(self.calculation_presets) + return "{}_in.py".format(self.calculation_presets) @property def calc_obj_as_str(self): @@ -191,6 +192,6 @@ def write_gpaw_input(self, write_location=None, filename=None): def write_input_files(self): self.write_gpaw_input( - write_location = self.write_location, - filename = self.gpaw_input_file, + write_location=self.write_location, + filename=self.gpaw_input_file, ) diff --git a/tests/gpaw/files/TEST_cu_bulk_opt.py b/tests/gpaw/files/TEST_cu_bulk_opt.py index 20ab9e0..8bcd941 100644 --- a/tests/gpaw/files/TEST_cu_bulk_opt.py +++ b/tests/gpaw/files/TEST_cu_bulk_opt.py @@ -6,34 +6,39 @@ import glob -a = glob.glob('input.traj') +a = glob.glob("input.traj") slab = read(a[-1]) def bulk_opt(atoms, step=0.05): - cell = atoms.get_cell() - name = atoms.get_chemical_formula(mode='hill') - vol=atoms.get_volume() - volumes =[] - energies=[] - for x in np.linspace(1-2*step,1+2*step,5): - atoms.set_cell(cell*x, scale_atoms=True) - atoms.calc.set(txt=name+'_'+str(x)+'.txt') - energies.append(atoms.get_potential_energy()) - volumes.append(atoms.get_volume()) - eos = EquationOfState(volumes, energies) - v0,e0,B= eos.fit() - atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) - x0=(v0/vol)**Fraction('1/3') - atoms.calc.set(txt=name+'_'+str(x0)+'.txt') - dyn=BFGS(atoms=atoms,trajectory=name+'_'+str(x0)+'.traj',logfile = 'qn.log') - dyn.run(fmax=0.05) - atoms.calc.write(name+str(x0)+'.gpw') + cell = atoms.get_cell() + name = atoms.get_chemical_formula(mode="hill") + vol = atoms.get_volume() + volumes = [] + energies = [] + for x in np.linspace(1 - 2 * step, 1 + 2 * step, 5): + atoms.set_cell(cell * x, scale_atoms=True) + atoms.calc.set(txt=name + "_" + str(x) + ".txt") + energies.append(atoms.get_potential_energy()) + volumes.append(atoms.get_volume()) + eos = EquationOfState(volumes, energies) + v0, e0, B = eos.fit() + atoms.set_cell((v0 / vol) ** Fraction("1/3") * cell, scale_atoms=True) + x0 = (v0 / vol) ** Fraction("1/3") + atoms.calc.set(txt=name + "_" + str(x0) + ".txt") + dyn = BFGS( + atoms=atoms, + trajectory=name + "_" + str(x0) + ".traj", + logfile="qn.log", + ) + dyn.run(fmax=0.05) + atoms.calc.write(name + str(x0) + ".gpw") + slab.calc = GPAW( -h=0.16, -kpts={'size': [12, 12, 12]}, -occupations={'name': 'fermi-dirac', 'width': 0.05}, -xc='BEEF-vdW' + h=0.16, + kpts={"size": [12, 12, 12]}, + occupations={"name": "fermi-dirac", "width": 0.05}, + xc="BEEF-vdW", ) -bulk_opt(slab) \ No newline at end of file +bulk_opt(slab) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 18156e2..5f128f6 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -20,21 +20,26 @@ def test_dft_package_name(): def test_gpaw_input_file(): # default "[calculation_presets]_in.py" - gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt") + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" + ) assert gig.gpaw_input_file == "bulk_opt_in.py" # otherwise use any user-input file name - gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt") + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" + ) gig.gpaw_input_file = "test.py" assert gig.gpaw_input_file == "test.py" def test_bulk_opt_calculation_presets_settings(): - gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt") + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" + ) cs = gig.calculation_settings assert cs["calculation"] == "bulk_opt" - assert cs["kpts"]["size"] == [12,12,12] + assert cs["kpts"]["size"] == [12, 12, 12] assert cs["xc"] == "BEEF-vdW" assert cs["h"] == 0.16 assert cs["occupations"]["name"] == "fermi-dirac" assert cs["occupations"]["width"] == 0.05 - From 503eed81c8e24005d0a98b40b8ab1a64d9239e1a Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 18 Dec 2020 11:38:27 -0500 Subject: [PATCH 17/44] Adds bulk_opt_hcp settings test --- tests/gpaw/test_gpaw.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 5f128f6..bf03546 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -33,6 +33,7 @@ def test_gpaw_input_file(): def test_bulk_opt_calculation_presets_settings(): + # Tests bulk_opt settings (fcc/bcc) gig = GPAWInputGenerator( crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" ) @@ -43,3 +44,17 @@ def test_bulk_opt_calculation_presets_settings(): assert cs["h"] == 0.16 assert cs["occupations"]["name"] == "fermi-dirac" assert cs["occupations"]["width"] == 0.05 + + +def test_bulk_opt_hcp_calculation_presets_settings(): + # Tests bulk_opt_hcp settings (hcp) + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt_hcp" + ) + cs = gig.calculation_settings + assert cs["calculation"] == "bulk_opt_hcp" + assert cs["kpts"]["size"] == [12, 12, 6] + assert cs["xc"] == "BEEF-vdW" + assert cs["h"] == 0.16 + assert cs["occupations"]["name"] == "fermi-dirac" + assert cs["occupations"]["width"] == 0.05 From 8eb89d6ac22829ec8d9e165b4f6cd5108bbde74d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 13:12:03 -0500 Subject: [PATCH 18/44] fixes indentation of bulk_opt function definition in script --- src/dftinputgen/gpaw/gpaw.py | 39 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index ce647e1..a3a8177 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -138,24 +138,24 @@ def relax(atoms, fmax=0.05, step=0.04): elif calc_type == "bulk_opt" or calc_type == "bulk_opt_hcp": define_bulk_opt_fn = """ def bulk_opt(atoms, step=0.05): - cell = atoms.get_cell() - name = atoms.get_chemical_formula(mode='hill') - vol=atoms.get_volume() - volumes =[] - energies=[] - for x in np.linspace(1-2*step,1+2*step,5): - atoms.set_cell(cell*x, scale_atoms=True) - atoms.calc.set(txt=name+'_'+str(x)+'.txt') - energies.append(atoms.get_potential_energy()) - volumes.append(atoms.get_volume()) - eos = EquationOfState(volumes, energies) - v0,e0,B= eos.fit() - atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) - x0=(v0/vol)**Fraction('1/3') - atoms.calc.set(txt='output.txt') - dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') - dyn.run(fmax=0.05) - atoms.calc.write('output.gpw') + cell = atoms.get_cell() + name = atoms.get_chemical_formula(mode='hill') + vol=atoms.get_volume() + volumes =[] + energies=[] + for x in np.linspace(1-2*step,1+2*step,5): + atoms.set_cell(cell*x, scale_atoms=True) + atoms.calc.set(txt=name+'_'+str(x)+'.txt') + energies.append(atoms.get_potential_energy()) + volumes.append(atoms.get_volume()) + eos = EquationOfState(volumes, energies) + v0,e0,B= eos.fit() + atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) + x0=(v0/vol)**Fraction('1/3') + atoms.calc.set(txt='output.txt') + dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') + dyn.run(fmax=0.05) + atoms.calc.write('output.gpw') """ return "\n".join( [ @@ -192,6 +192,5 @@ def write_gpaw_input(self, write_location=None, filename=None): def write_input_files(self): self.write_gpaw_input( - write_location=self.write_location, - filename=self.gpaw_input_file, + write_location=self.write_location, filename=self.gpaw_input_file, ) From 51f348a7e298d7e3bacd7d10b5fb641b066f3eee Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 13:47:00 -0500 Subject: [PATCH 19/44] Adds line to exclude from black formatting --- tests/gpaw/files/TEST_cu_bulk_opt.py | 44 +++++++++++++--------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/tests/gpaw/files/TEST_cu_bulk_opt.py b/tests/gpaw/files/TEST_cu_bulk_opt.py index 8bcd941..8fe6bab 100644 --- a/tests/gpaw/files/TEST_cu_bulk_opt.py +++ b/tests/gpaw/files/TEST_cu_bulk_opt.py @@ -1,44 +1,42 @@ +# fmt: off from gpaw import GPAW from ase.io import read from ase.io import write from ase.optimize import BFGS +from ase.eos import EquationOfState +from fractions import Fraction import numpy as np import glob -a = glob.glob("input.traj") +a = glob.glob('input.traj') slab = read(a[-1]) def bulk_opt(atoms, step=0.05): cell = atoms.get_cell() - name = atoms.get_chemical_formula(mode="hill") - vol = atoms.get_volume() - volumes = [] - energies = [] - for x in np.linspace(1 - 2 * step, 1 + 2 * step, 5): - atoms.set_cell(cell * x, scale_atoms=True) - atoms.calc.set(txt=name + "_" + str(x) + ".txt") + name = atoms.get_chemical_formula(mode='hill') + vol=atoms.get_volume() + volumes =[] + energies=[] + for x in np.linspace(1-2*step,1+2*step,5): + atoms.set_cell(cell*x, scale_atoms=True) + atoms.calc.set(txt=name+'_'+str(x)+'.txt') energies.append(atoms.get_potential_energy()) volumes.append(atoms.get_volume()) eos = EquationOfState(volumes, energies) - v0, e0, B = eos.fit() - atoms.set_cell((v0 / vol) ** Fraction("1/3") * cell, scale_atoms=True) - x0 = (v0 / vol) ** Fraction("1/3") - atoms.calc.set(txt=name + "_" + str(x0) + ".txt") - dyn = BFGS( - atoms=atoms, - trajectory=name + "_" + str(x0) + ".traj", - logfile="qn.log", - ) + v0,e0,B= eos.fit() + atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) + x0=(v0/vol)**Fraction('1/3') + atoms.calc.set(txt='output.txt') + dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') dyn.run(fmax=0.05) - atoms.calc.write(name + str(x0) + ".gpw") - + atoms.calc.write('output.gpw') slab.calc = GPAW( - h=0.16, - kpts={"size": [12, 12, 12]}, - occupations={"name": "fermi-dirac", "width": 0.05}, - xc="BEEF-vdW", +h=0.16, +kpts={'size': [12, 12, 12]}, +occupations={'name': 'fermi-dirac', 'width': 0.05}, +xc='BEEF-vdW' ) bulk_opt(slab) From f71ee59ebb6a0425478655ef72ffb9d6f37cadda Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 13:51:28 -0500 Subject: [PATCH 20/44] rename test bulk_opt script --- tests/gpaw/files/{TEST_cu_bulk_opt.py => TEST_bulk_opt.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/gpaw/files/{TEST_cu_bulk_opt.py => TEST_bulk_opt.py} (100%) diff --git a/tests/gpaw/files/TEST_cu_bulk_opt.py b/tests/gpaw/files/TEST_bulk_opt.py similarity index 100% rename from tests/gpaw/files/TEST_cu_bulk_opt.py rename to tests/gpaw/files/TEST_bulk_opt.py From 8adf43d6eb86caa320094ebc39b2ca4359e4d177 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 14:08:30 -0500 Subject: [PATCH 21/44] adds tests for more calculation presets and scripts as strings --- tests/gpaw/files/TEST_relax.py | 30 +++++++++++++++++++ tests/gpaw/test_gpaw.py | 54 ++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 tests/gpaw/files/TEST_relax.py diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py new file mode 100644 index 0000000..7f2b0b3 --- /dev/null +++ b/tests/gpaw/files/TEST_relax.py @@ -0,0 +1,30 @@ +# fmt: off +from gpaw import GPAW +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.eos import EquationOfState +from fractions import Fraction +import numpy as np +import glob + + +a = glob.glob('input.traj') +slab = read(a[-1]) + + +def relax(atoms, fmax=0.05, step=0.04): + name = atoms.get_chemical_formula(mode='hill') + atoms.calc.set(txt='output.txt') + atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') + dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn.run(fmax=fmax) + +slab.calc = GPAW( +h=0.16, +kpts={'size': [4, 4, 1]}, +occupations={'name': 'fermi-dirac', 'width': 0.05}, +poissonsolver={'dipolelayer': 'xy'}, +xc='BEEF-vdW' +) +relax(slab) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index bf03546..70f279b 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -9,8 +9,10 @@ test_data_dir = os.path.join(os.path.dirname(__file__), "files") cu_bulk_struct = ase_io.read(os.path.join(test_data_dir, "cu_bulk.traj")) -with open(os.path.join(test_data_dir, "TEST_cu_bulk_opt.py"), "r") as fr: - cu_bulk_opt_in = fr.read() +with open(os.path.join(test_data_dir, "TEST_bulk_opt.py"), "r") as fr: + bulk_opt_in = fr.read() +with open(os.path.join(test_data_dir, "TEST_relax.py"), "r") as fr: + relax_in = fr.read() def test_dft_package_name(): @@ -58,3 +60,51 @@ def test_bulk_opt_hcp_calculation_presets_settings(): assert cs["h"] == 0.16 assert cs["occupations"]["name"] == "fermi-dirac" assert cs["occupations"]["width"] == 0.05 + + +def test_molecule_calculation_presets_settings(): + # Tests molecule settings + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="molecule" + ) + cs = gig.calculation_settings + assert cs["xc"] == "BEEF-vdW" + assert cs["h"] == 0.16 + assert cs["occupations"]["name"] == "fermi-dirac" + assert cs["occupations"]["width"] == 0.05 + assert cs["calculation"] == "relax" + + +def test_relax_calculation_presets_settings(): + # Tests relax presets + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="relax" + ) + cs = gig.calculation_settings + assert cs["xc"] == "BEEF-vdW" + assert cs["h"] == 0.16 + assert cs["occupations"]["name"] == "fermi-dirac" + assert cs["occupations"]["width"] == 0.05 + assert cs["calculation"] == "relax" + assert cs["poissonsolver"]["dipolelayer"] == "xy" + + +def test_calc_obj_as_str(): + # Tests generation of calculator object as string + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" + ) + calc_obj = "\n".join(bulk_opt_in.splitlines()[35:-1]) + assert gig.calc_obj_as_str == calc_obj + + +def test_gpaw_input_as_str(): + # Test generation of full bulk opt script + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" + ) + assert gig.gpaw_input_as_str == "\n".join(bulk_opt_in.splitlines()[1:]) + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="relax" + ) + assert gig.gpaw_input_as_str == "\n".join(relax_in.splitlines()[1:]) From 3810d76c0f93638cabf40efd37f9b7f5d3dbebbf Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 14:42:43 -0500 Subject: [PATCH 22/44] if no settings found will return empty calc object that will use gpaw defaults (ie. slab.calc = GPAW()) --- src/dftinputgen/gpaw/gpaw.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index a3a8177..d38ba60 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -115,7 +115,11 @@ def gpaw_input_as_str(self): """ calc_sett = self._calculation_settings - calc_type = calc_sett["calculation"] + try: + calc_type = calc_sett["calculation"] + except KeyError: + # if no input settings found will return calc object without any settings defined + calc_type = "defaults" if calc_type == "relax": define_relax_fn = """ def relax(atoms, fmax=0.05, step=0.04): @@ -171,8 +175,7 @@ def bulk_opt(atoms, step=0.05): [ header, read_init_traj, - define_relax_fn, - calc_obj_as_str, + self.calc_obj_as_str, "slab.get_total_energy()", ] ) From dc1ee552450dfffb1552169d7b7f33d2330be44f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 18:18:40 -0500 Subject: [PATCH 23/44] Fixes typo that prevented updating settings. Adds additional tests --- src/dftinputgen/gpaw/gpaw.py | 9 ++-- tests/gpaw/files/TEST_defaults.py | 18 ++++++++ tests/gpaw/files/TEST_relax_custom.py | 30 ++++++++++++++ tests/gpaw/test_gpaw.py | 60 +++++++++++++++++++++++++-- 4 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 tests/gpaw/files/TEST_defaults.py create mode 100644 tests/gpaw/files/TEST_relax_custom.py diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index d38ba60..ecbfc3c 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -85,7 +85,7 @@ def _get_default_input_filename(self): def calc_obj_as_str(self): top = "slab.calc = GPAW(" - calc_sett = self._calculation_settings + calc_sett = self.calculation_settings params = [] for p in GPAW_TAGS["parameters"]: @@ -114,12 +114,12 @@ def gpaw_input_as_str(self): slab = read(a[-1]) """ - calc_sett = self._calculation_settings + calc_sett = self.calculation_settings try: calc_type = calc_sett["calculation"] except KeyError: # if no input settings found will return calc object without any settings defined - calc_type = "defaults" + calc_type = None if calc_type == "relax": define_relax_fn = """ def relax(atoms, fmax=0.05, step=0.04): @@ -181,9 +181,6 @@ def bulk_opt(atoms, step=0.05): ) def write_gpaw_input(self, write_location=None, filename=None): - if not self.gpaw_input_as_str.strip(): - msg = "Nothing to write (probably no input settings found.)" - raise GPAWInputGeneratorError(msg) if write_location is None: msg = "Location to write files not specified" raise GPAWInputGeneratorError(msg) diff --git a/tests/gpaw/files/TEST_defaults.py b/tests/gpaw/files/TEST_defaults.py new file mode 100644 index 0000000..138cd89 --- /dev/null +++ b/tests/gpaw/files/TEST_defaults.py @@ -0,0 +1,18 @@ +# fmt: off +from gpaw import GPAW +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.eos import EquationOfState +from fractions import Fraction +import numpy as np +import glob + + +a = glob.glob('input.traj') +slab = read(a[-1]) + +slab.calc = GPAW( + +) +slab.get_total_energy() diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py new file mode 100644 index 0000000..6d28ff5 --- /dev/null +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -0,0 +1,30 @@ +# fmt: off +from gpaw import GPAW +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.eos import EquationOfState +from fractions import Fraction +import numpy as np +import glob + + +a = glob.glob('input.traj') +slab = read(a[-1]) + + +def relax(atoms, fmax=0.05, step=0.04): + name = atoms.get_chemical_formula(mode='hill') + atoms.calc.set(txt='output.txt') + atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') + dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn.run(fmax=fmax) + +slab.calc = GPAW( +h=0.18, +kpts={'size': [6, 6, 1]}, +occupations={'name': 'fermi-dirac', 'width': 0.05}, +poissonsolver={'dipolelayer': 'xy'}, +xc='PBE' +) +relax(slab) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 70f279b..350d980 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -5,7 +5,7 @@ from ase import io as ase_io -from dftinputgen.gpaw.gpaw import GPAWInputGenerator +from dftinputgen.gpaw.gpaw import GPAWInputGenerator, GPAWInputGeneratorError test_data_dir = os.path.join(os.path.dirname(__file__), "files") cu_bulk_struct = ase_io.read(os.path.join(test_data_dir, "cu_bulk.traj")) @@ -13,6 +13,10 @@ bulk_opt_in = fr.read() with open(os.path.join(test_data_dir, "TEST_relax.py"), "r") as fr: relax_in = fr.read() +with open(os.path.join(test_data_dir, "TEST_relax_custom.py"), "r") as fr: + relax_custom_in = fr.read() +with open(os.path.join(test_data_dir, "TEST_defaults.py"), "r") as fr: + defaults_in = fr.read() def test_dft_package_name(): @@ -91,14 +95,17 @@ def test_relax_calculation_presets_settings(): def test_calc_obj_as_str(): # Tests generation of calculator object as string - gig = GPAWInputGenerator( - crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" - ) + gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) + assert gig.calc_obj_as_str == "slab.calc = GPAW(\n\n)" + gig.calculation_presets = "bulk_opt" calc_obj = "\n".join(bulk_opt_in.splitlines()[35:-1]) assert gig.calc_obj_as_str == calc_obj def test_gpaw_input_as_str(): + # Test defaults + gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) + assert gig.gpaw_input_as_str == "\n".join(defaults_in.splitlines()[1:]) # Test generation of full bulk opt script gig = GPAWInputGenerator( crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" @@ -108,3 +115,48 @@ def test_gpaw_input_as_str(): crystal_structure=cu_bulk_struct, calculation_presets="relax" ) assert gig.gpaw_input_as_str == "\n".join(relax_in.splitlines()[1:]) + + +def test_write_gpaw_input(): + gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) + gig.calculation_presets = "relax" + # no `write_location` input: error + with pytest.raises(GPAWInputGeneratorError, match="Location to write"): + gig.write_gpaw_input() + # no input filename: error + with pytest.raises(GPAWInputGeneratorError, match="file to write"): + gig.write_gpaw_input(write_location="/path/to/write_location") + + import tempfile + + _tmp_file = tempfile.NamedTemporaryFile(mode="w", delete=True) + filename = _tmp_file.name + write_location = os.path.dirname(filename) + gig.custom_sett_dict = { + "kpts": {"size": [6, 6, 1]}, + "xc": "PBE", + "h": 0.18, + } + gig.write_gpaw_input(write_location=write_location, filename=filename) + with open(filename, "r") as fr: + assert fr.read() == "\n".join(relax_custom_in.splitlines()[1:]) + + +def test_write_input_files(): + import tempfile + + _tmp_file = tempfile.NamedTemporaryFile(mode="w", delete=True) + filename = _tmp_file.name + write_location = os.path.dirname(filename) + gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) + gig.calculation_presets = "relax" + gig.custom_sett_dict = { + "kpts": {"size": [6, 6, 1]}, + "xc": "PBE", + "h": 0.18, + } + gig.write_location = write_location + gig.gpaw_input_file = filename + gig.write_input_files() + with open(filename, "r") as fr: + assert fr.read() == "\n".join(relax_custom_in.splitlines()[1:]) From 02dcc2d01974ad0e66840e959abf214f0748d80e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 18:30:35 -0500 Subject: [PATCH 24/44] rename preset relax -> surface_relax --- .../{relax.json => surface_relax.json} | 0 tests/gpaw/test_gpaw.py | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/dftinputgen/gpaw/settings/calculation_presets/{relax.json => surface_relax.json} (100%) diff --git a/src/dftinputgen/gpaw/settings/calculation_presets/relax.json b/src/dftinputgen/gpaw/settings/calculation_presets/surface_relax.json similarity index 100% rename from src/dftinputgen/gpaw/settings/calculation_presets/relax.json rename to src/dftinputgen/gpaw/settings/calculation_presets/surface_relax.json diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 350d980..99b3eaa 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -79,10 +79,10 @@ def test_molecule_calculation_presets_settings(): assert cs["calculation"] == "relax" -def test_relax_calculation_presets_settings(): +def test_surface_relax_calculation_presets_settings(): # Tests relax presets gig = GPAWInputGenerator( - crystal_structure=cu_bulk_struct, calculation_presets="relax" + crystal_structure=cu_bulk_struct, calculation_presets="surface_relax" ) cs = gig.calculation_settings assert cs["xc"] == "BEEF-vdW" @@ -112,14 +112,14 @@ def test_gpaw_input_as_str(): ) assert gig.gpaw_input_as_str == "\n".join(bulk_opt_in.splitlines()[1:]) gig = GPAWInputGenerator( - crystal_structure=cu_bulk_struct, calculation_presets="relax" + crystal_structure=cu_bulk_struct, calculation_presets="surface_relax" ) assert gig.gpaw_input_as_str == "\n".join(relax_in.splitlines()[1:]) def test_write_gpaw_input(): gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) - gig.calculation_presets = "relax" + gig.calculation_presets = "surface_relax" # no `write_location` input: error with pytest.raises(GPAWInputGeneratorError, match="Location to write"): gig.write_gpaw_input() @@ -149,7 +149,7 @@ def test_write_input_files(): filename = _tmp_file.name write_location = os.path.dirname(filename) gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) - gig.calculation_presets = "relax" + gig.calculation_presets = "surface_relax" gig.custom_sett_dict = { "kpts": {"size": [6, 6, 1]}, "xc": "PBE", From ad9314b0e87b71cd1fbac4ca5a66dbf4fe8978d8 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Dec 2020 20:24:27 -0500 Subject: [PATCH 25/44] structure filename for the written gpaw script can now be specified --- src/dftinputgen/gpaw/gpaw.py | 20 ++++++++++++++++++-- tests/gpaw/files/TEST_relax_custom.py | 2 +- tests/gpaw/test_gpaw.py | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index ecbfc3c..ec29ffd 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -27,6 +27,7 @@ def __init__( custom_sett_dict=None, write_location=None, gpaw_input_file=None, + struct_filename=None, overwrite_files=None, **kwargs, ): @@ -46,6 +47,9 @@ def __init__( self._gpaw_input_file = self._get_default_input_filename() self.gpaw_input_file = gpaw_input_file + self._struct_filename = "input.traj" + self.struct_filename = struct_filename + @property def dft_package(self): return "GPAW" @@ -60,6 +64,16 @@ def gpaw_input_file(self, gpaw_input_file): if gpaw_input_file is not None: self._gpaw_input_file = gpaw_input_file + @property + def struct_filename(self): + """Name of the structure file that the gpaw script will read from.""" + return self._struct_filename + + @struct_filename.setter + def struct_filename(self, struct_filename): + if struct_filename is not None: + self._struct_filename = struct_filename + @property def calculation_settings(self): """Dictionary of all calculation settings to use as input for gpaw.""" @@ -110,9 +124,11 @@ def gpaw_input_as_str(self): """ read_init_traj = """ -a = glob.glob('input.traj') +a = glob.glob('{}') slab = read(a[-1]) -""" +""".format( + self.struct_filename + ) calc_sett = self.calculation_settings try: diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index 6d28ff5..c497e2c 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -9,7 +9,7 @@ import glob -a = glob.glob('input.traj') +a = glob.glob('CH3.traj') slab = read(a[-1]) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 99b3eaa..7b1c4da 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -132,6 +132,7 @@ def test_write_gpaw_input(): _tmp_file = tempfile.NamedTemporaryFile(mode="w", delete=True) filename = _tmp_file.name write_location = os.path.dirname(filename) + gig.struct_filename = "CH3.traj" gig.custom_sett_dict = { "kpts": {"size": [6, 6, 1]}, "xc": "PBE", @@ -150,6 +151,7 @@ def test_write_input_files(): write_location = os.path.dirname(filename) gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) gig.calculation_presets = "surface_relax" + gig.struct_filename = "CH3.traj" gig.custom_sett_dict = { "kpts": {"size": [6, 6, 1]}, "xc": "PBE", From c9cf8b70087bc376a8ab80c24d0ce8c21eb86b8b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 2 Jan 2021 13:48:05 -0500 Subject: [PATCH 26/44] Adds docstring for gpaw class --- src/dftinputgen/gpaw/gpaw.py | 57 +++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index ec29ffd..3607cc8 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -17,7 +17,62 @@ class GPAWInputGeneratorError(DftInputGeneratorError): class GPAWInputGenerator(DftInputGenerator): - """Base class to generate input python scripts for GPAW """ + """ + Constructor for the GPAW class. + + Parameters + ---------- + + crystal_structure: :class:`ase.Atoms` object + :class:`ase.Atoms` object from `ase.io.read([crystal structure + file])`. + + calculation_presets: str, optional + The "base" calculation settings to use--must be one of the + pre-defined groups of tags and values provided for pw.x. + + Pre-defined settings for some common calculation types are in + INSTALL_PATH/qe/settings/calculation_presets/ + + custom_sett_file: str, optional + Location of a JSON file with custom calculation settings as a + dictionary of tags and values. + + NB: Custom settings specified here always OVERRIDE those in + `calculation_presets` in case of overlap. + + custom_sett_dict: dict, optional + Dictionary with custom calculation settings as tags and values/ + + NB: Custom settings specified here always OVERRIDE those in + `calculation_presets` and `custom_sett_file`. + + write_location: str, optional + Path to the directory in which to write the input file(s). + + Default: Current working directory. + + gpaw_input_file: str, optional + Name of the file in which to write the GPAW python script + + Default: "[`calculation_presets`]_in.py" if `calculation_presets` is + specified by the user, else "gpaw_in.py". + + struct_filename: str, optional + Name of the structure file readable by `ase.io.read` that the written + gpaw script will call from. + + Default: "input.traj" + + overwrite_files: bool, optional + To overwrite files or not, that is the question. + + Default: True + + **kwargs: + Arbitrary keyword arguments. + + """ def __init__( self, From c090043e07be154da45ba24bab5c9467a3a0d3f2 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 4 Jan 2021 14:56:40 -0500 Subject: [PATCH 27/44] Updates function for optimizing bulk hcp structures --- src/dftinputgen/gpaw/gpaw.py | 65 +++++++++++++++++++++++- tests/gpaw/files/TEST_bulk_opt_hcp.py | 73 +++++++++++++++++++++++++++ tests/gpaw/test_gpaw.py | 6 +++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 tests/gpaw/files/TEST_bulk_opt_hcp.py diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 3607cc8..7774428 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -210,7 +210,7 @@ def relax(atoms, fmax=0.05, step=0.04): ] ) - elif calc_type == "bulk_opt" or calc_type == "bulk_opt_hcp": + elif calc_type == "bulk_opt": define_bulk_opt_fn = """ def bulk_opt(atoms, step=0.05): cell = atoms.get_cell() @@ -241,6 +241,69 @@ def bulk_opt(atoms, step=0.05): "bulk_opt(slab)", ] ) + elif calc_type == "bulk_opt_hcp": + define_bulk_opt_hcp_fn = """ +def optimize_bulk_hcp(atoms, step=0.05, nstep=5): + # Get initial cell + cell = atoms.get_cell() + a_in = cell[0] + b_in = cell[1] + c_in = cell[2] + name = atoms.get_chemical_formula(mode='hill') + vol=atoms.get_volume() + + # Optimize in ab first + volumes_ab =[] + energies_ab=[] + scale_factors = np.linspace(1-2*step,1+2*step,nstep) + for x in scale_factors: + atoms.set_cell([a_in*x,b_in*x,c_in], scale_atoms=True) + atoms.calc.set(txt=name+'_'+'ab'+'_'+str(x)+'.txt') + energies_ab.append(atoms.get_potential_energy()) + volumes_ab.append(atoms.get_volume()) + + # Fit in ab + eos = EquationOfState(volumes_ab, energies_ab) + v0,e0,B= eos.fit() + a_fit = a_in*np.sqrt(v0/vol) + b_fit = b_in*np.sqrt(v0/vol) + + # Generate new cell with fit ab parameters + atoms.set_cell([a_fit,b_fit,c_in], scale_atoms=True) + vol_ab = atoms.get_volume() + + # Optimize c + energies_c = [] + volumes_c = [] + for x in scale_factors: + atoms.set_cell([a_fit,b_fit,c_in*x], scale_atoms=True) + atoms.calc.set(txt=name+'_'+'c'+'_'+str(x)+'.txt') + energies_c.append(atoms.get_potential_energy()) + volumes_c.append(atoms.get_volume()) + + # Fit in c + eos_c = EquationOfState(volumes_c,energies_c,eos='birchmurnaghan') + v0, e0, B = eos_c.fit() + c_scaling = v0/vol_ab + c_fit = c_in*c_scaling + + # Update cell with optimized parameters and relax atoms + atoms.set_cell([a_fit,b_fit,c_fit], scale_atoms=True) + atoms.calc.set(txt='output.txt') + dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') + dyn.run(fmax=0.05) + atoms.calc.write("output.gpw") +""" + return "\n".join( + [ + header, + read_init_traj, + define_bulk_opt_hcp_fn, + self.calc_obj_as_str, + "bulk_opt_hcp(slab)", + ] + ) + # if not a relax or bulk_opt calculation, defaults to getting total energy of static structure return "\n".join( [ diff --git a/tests/gpaw/files/TEST_bulk_opt_hcp.py b/tests/gpaw/files/TEST_bulk_opt_hcp.py new file mode 100644 index 0000000..e6d705a --- /dev/null +++ b/tests/gpaw/files/TEST_bulk_opt_hcp.py @@ -0,0 +1,73 @@ +# fmt: off +from gpaw import GPAW +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.eos import EquationOfState +from fractions import Fraction +import numpy as np +import glob + + +a = glob.glob('input.traj') +slab = read(a[-1]) + + +def optimize_bulk_hcp(atoms, step=0.05, nstep=5): + # Get initial cell + cell = atoms.get_cell() + a_in = cell[0] + b_in = cell[1] + c_in = cell[2] + name = atoms.get_chemical_formula(mode='hill') + vol=atoms.get_volume() + + # Optimize in ab first + volumes_ab =[] + energies_ab=[] + scale_factors = np.linspace(1-2*step,1+2*step,nstep) + for x in scale_factors: + atoms.set_cell([a_in*x,b_in*x,c_in], scale_atoms=True) + atoms.calc.set(txt=name+'_'+'ab'+'_'+str(x)+'.txt') + energies_ab.append(atoms.get_potential_energy()) + volumes_ab.append(atoms.get_volume()) + + # Fit in ab + eos = EquationOfState(volumes_ab, energies_ab) + v0,e0,B= eos.fit() + a_fit = a_in*np.sqrt(v0/vol) + b_fit = b_in*np.sqrt(v0/vol) + + # Generate new cell with fit ab parameters + atoms.set_cell([a_fit,b_fit,c_in], scale_atoms=True) + vol_ab = atoms.get_volume() + + # Optimize c + energies_c = [] + volumes_c = [] + for x in scale_factors: + atoms.set_cell([a_fit,b_fit,c_in*x], scale_atoms=True) + atoms.calc.set(txt=name+'_'+'c'+'_'+str(x)+'.txt') + energies_c.append(atoms.get_potential_energy()) + volumes_c.append(atoms.get_volume()) + + # Fit in c + eos_c = EquationOfState(volumes_c,energies_c,eos='birchmurnaghan') + v0, e0, B = eos_c.fit() + c_scaling = v0/vol_ab + c_fit = c_in*c_scaling + + # Update cell with optimized parameters and relax atoms + atoms.set_cell([a_fit,b_fit,c_fit], scale_atoms=True) + atoms.calc.set(txt='output.txt') + dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') + dyn.run(fmax=0.05) + atoms.calc.write("output.gpw") + +slab.calc = GPAW( +h=0.16, +kpts={'size': [12, 12, 6]}, +occupations={'name': 'fermi-dirac', 'width': 0.05}, +xc='BEEF-vdW' +) +bulk_opt_hcp(slab) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 7b1c4da..8dc53ab 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -11,6 +11,8 @@ cu_bulk_struct = ase_io.read(os.path.join(test_data_dir, "cu_bulk.traj")) with open(os.path.join(test_data_dir, "TEST_bulk_opt.py"), "r") as fr: bulk_opt_in = fr.read() +with open(os.path.join(test_data_dir, "TEST_bulk_opt_hcp.py"), "r") as fr: + bulk_opt_hcp_in = fr.read() with open(os.path.join(test_data_dir, "TEST_relax.py"), "r") as fr: relax_in = fr.read() with open(os.path.join(test_data_dir, "TEST_relax_custom.py"), "r") as fr: @@ -111,6 +113,10 @@ def test_gpaw_input_as_str(): crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt" ) assert gig.gpaw_input_as_str == "\n".join(bulk_opt_in.splitlines()[1:]) + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, calculation_presets="bulk_opt_hcp" + ) + assert gig.gpaw_input_as_str == "\n".join(bulk_opt_hcp_in.splitlines()[1:]) gig = GPAWInputGenerator( crystal_structure=cu_bulk_struct, calculation_presets="surface_relax" ) From f86785459db988686dcbed42da9664636ed76c45 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 4 Jan 2021 15:05:01 -0500 Subject: [PATCH 28/44] calc_obj_as_str -> calculator_object_as_str --- src/dftinputgen/gpaw/gpaw.py | 10 +++++----- tests/gpaw/test_gpaw.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 7774428..36e77fe 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -151,7 +151,7 @@ def _get_default_input_filename(self): return "{}_in.py".format(self.calculation_presets) @property - def calc_obj_as_str(self): + def calculator_object_as_str(self): top = "slab.calc = GPAW(" calc_sett = self.calculation_settings @@ -205,7 +205,7 @@ def relax(atoms, fmax=0.05, step=0.04): header, read_init_traj, define_relax_fn, - self.calc_obj_as_str, + self.calculator_object_as_str, "relax(slab)", ] ) @@ -237,7 +237,7 @@ def bulk_opt(atoms, step=0.05): header, read_init_traj, define_bulk_opt_fn, - self.calc_obj_as_str, + self.calculator_object_as_str, "bulk_opt(slab)", ] ) @@ -299,7 +299,7 @@ def optimize_bulk_hcp(atoms, step=0.05, nstep=5): header, read_init_traj, define_bulk_opt_hcp_fn, - self.calc_obj_as_str, + self.calculator_object_as_str, "bulk_opt_hcp(slab)", ] ) @@ -309,7 +309,7 @@ def optimize_bulk_hcp(atoms, step=0.05, nstep=5): [ header, read_init_traj, - self.calc_obj_as_str, + self.calculator_object_as_str, "slab.get_total_energy()", ] ) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 8dc53ab..fd9f0f1 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -95,13 +95,13 @@ def test_surface_relax_calculation_presets_settings(): assert cs["poissonsolver"]["dipolelayer"] == "xy" -def test_calc_obj_as_str(): +def test_calculator_object_as_str(): # Tests generation of calculator object as string gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) - assert gig.calc_obj_as_str == "slab.calc = GPAW(\n\n)" + assert gig.calculator_object_as_str == "slab.calc = GPAW(\n\n)" gig.calculation_presets = "bulk_opt" calc_obj = "\n".join(bulk_opt_in.splitlines()[35:-1]) - assert gig.calc_obj_as_str == calc_obj + assert gig.calculator_object_as_str == calc_obj def test_gpaw_input_as_str(): From e46e7886356607e5791e8c068c57b1fbe89a9a17 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 4 Jan 2021 16:14:09 -0500 Subject: [PATCH 29/44] Adds ability to write gpaw restart files --- src/dftinputgen/gpaw/gpaw.py | 55 ++++++++++++++++++++++++++++++-- tests/gpaw/files/TEST_restart.py | 31 ++++++++++++++++++ tests/gpaw/test_gpaw.py | 9 ++++++ 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 tests/gpaw/files/TEST_restart.py diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 36e77fe..490dd2e 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -58,6 +58,17 @@ class GPAWInputGenerator(DftInputGenerator): Default: "[`calculation_presets`]_in.py" if `calculation_presets` is specified by the user, else "gpaw_in.py". + gpaw_restart_file: str, optional + Name of the gpaw restart file that the written gpaw script will read + from. + + Default: "output.gpw" + + restart: bool, optional + Bool specifying if the gpaw script written should be a restart job + + Default: False + struct_filename: str, optional Name of the structure file readable by `ase.io.read` that the written gpaw script will call from. @@ -82,6 +93,8 @@ def __init__( custom_sett_dict=None, write_location=None, gpaw_input_file=None, + gpaw_restart_file=None, + restart=None, struct_filename=None, overwrite_files=None, **kwargs, @@ -102,9 +115,15 @@ def __init__( self._gpaw_input_file = self._get_default_input_filename() self.gpaw_input_file = gpaw_input_file + self._gpaw_restart_file = "output.gpw" + self.gpaw_restart_file = gpaw_restart_file + self._struct_filename = "input.traj" self.struct_filename = struct_filename + self._restart = False + self.restart = restart + @property def dft_package(self): return "GPAW" @@ -119,6 +138,16 @@ def gpaw_input_file(self, gpaw_input_file): if gpaw_input_file is not None: self._gpaw_input_file = gpaw_input_file + @property + def gpaw_restart_file(self): + """Name of gpaw output file to read from if restarting a calculation""" + return self._gpaw_restart_file + + @gpaw_restart_file.setter + def gpaw_restart_file(self, gpaw_restart_file): + if gpaw_restart_file is not None: + self._gpaw_restart_file = gpaw_restart_file + @property def struct_filename(self): """Name of the structure file that the gpaw script will read from.""" @@ -129,6 +158,16 @@ def struct_filename(self, struct_filename): if struct_filename is not None: self._struct_filename = struct_filename + @property + def restart(self): + """Whether script should be a restart script""" + return self._restart + + @restart.setter + def restart(self, restart): + if restart is not None: + self._restart = restart + @property def calculation_settings(self): """Dictionary of all calculation settings to use as input for gpaw.""" @@ -178,12 +217,22 @@ def gpaw_input_as_str(self): import glob """ - read_init_traj = """ + if self.restart: + read_init_traj = """from gpaw import restart + +a = glob.glob('{}') +slab,calc = restart(a[-1]) +""".format( + self.gpaw_restart_file + ) + + else: + read_init_traj = """ a = glob.glob('{}') slab = read(a[-1]) """.format( - self.struct_filename - ) + self.struct_filename + ) calc_sett = self.calculation_settings try: diff --git a/tests/gpaw/files/TEST_restart.py b/tests/gpaw/files/TEST_restart.py new file mode 100644 index 0000000..dfb5778 --- /dev/null +++ b/tests/gpaw/files/TEST_restart.py @@ -0,0 +1,31 @@ +# fmt: off +from gpaw import GPAW +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.eos import EquationOfState +from fractions import Fraction +import numpy as np +import glob + +from gpaw import restart + +a = glob.glob('C3N4.gpw') +slab,calc = restart(a[-1]) + + +def relax(atoms, fmax=0.05, step=0.04): + name = atoms.get_chemical_formula(mode='hill') + atoms.calc.set(txt='output.txt') + atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') + dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn.run(fmax=fmax) + +slab.calc = GPAW( +h=0.16, +kpts={'size': [4, 4, 1]}, +occupations={'name': 'fermi-dirac', 'width': 0.05}, +poissonsolver={'dipolelayer': 'xy'}, +xc='BEEF-vdW' +) +relax(slab) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index fd9f0f1..253a69b 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -15,6 +15,8 @@ bulk_opt_hcp_in = fr.read() with open(os.path.join(test_data_dir, "TEST_relax.py"), "r") as fr: relax_in = fr.read() +with open(os.path.join(test_data_dir, "TEST_restart.py"), "r") as fr: + restart_in = fr.read() with open(os.path.join(test_data_dir, "TEST_relax_custom.py"), "r") as fr: relax_custom_in = fr.read() with open(os.path.join(test_data_dir, "TEST_defaults.py"), "r") as fr: @@ -121,6 +123,13 @@ def test_gpaw_input_as_str(): crystal_structure=cu_bulk_struct, calculation_presets="surface_relax" ) assert gig.gpaw_input_as_str == "\n".join(relax_in.splitlines()[1:]) + gig = GPAWInputGenerator( + crystal_structure=cu_bulk_struct, + calculation_presets="surface_relax", + restart=True, + gpaw_restart_file="C3N4.gpw", + ) + assert gig.gpaw_input_as_str == "\n".join(restart_in.splitlines()[1:]) def test_write_gpaw_input(): From 24a0ae4f87624ebc9b83b2ac41dc80b1eb332a85 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 4 Jan 2021 16:34:48 -0500 Subject: [PATCH 30/44] Removes Fraction dependence in written bulk_opt function --- src/dftinputgen/gpaw/gpaw.py | 5 ++--- tests/gpaw/files/TEST_bulk_opt.py | 5 ++--- tests/gpaw/files/TEST_bulk_opt_hcp.py | 1 - tests/gpaw/files/TEST_defaults.py | 1 - tests/gpaw/files/TEST_relax.py | 1 - tests/gpaw/files/TEST_relax_custom.py | 1 - tests/gpaw/files/TEST_restart.py | 1 - tests/gpaw/test_gpaw.py | 2 +- 8 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 490dd2e..8e4f588 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -212,7 +212,6 @@ def gpaw_input_as_str(self): from ase.io import write from ase.optimize import BFGS from ase.eos import EquationOfState -from fractions import Fraction import numpy as np import glob """ @@ -274,8 +273,8 @@ def bulk_opt(atoms, step=0.05): volumes.append(atoms.get_volume()) eos = EquationOfState(volumes, energies) v0,e0,B= eos.fit() - atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) - x0=(v0/vol)**Fraction('1/3') + atoms.set_cell(np.cbrt(v0/vol)*cell,scale_atoms=True) + x0=np.cbrt(v0/vol) atoms.calc.set(txt='output.txt') dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') dyn.run(fmax=0.05) diff --git a/tests/gpaw/files/TEST_bulk_opt.py b/tests/gpaw/files/TEST_bulk_opt.py index 8fe6bab..e227a97 100644 --- a/tests/gpaw/files/TEST_bulk_opt.py +++ b/tests/gpaw/files/TEST_bulk_opt.py @@ -4,7 +4,6 @@ from ase.io import write from ase.optimize import BFGS from ase.eos import EquationOfState -from fractions import Fraction import numpy as np import glob @@ -26,8 +25,8 @@ def bulk_opt(atoms, step=0.05): volumes.append(atoms.get_volume()) eos = EquationOfState(volumes, energies) v0,e0,B= eos.fit() - atoms.set_cell((v0/vol)**Fraction('1/3')*cell,scale_atoms=True) - x0=(v0/vol)**Fraction('1/3') + atoms.set_cell(np.cbrt(v0/vol)*cell,scale_atoms=True) + x0=np.cbrt(v0/vol) atoms.calc.set(txt='output.txt') dyn=BFGS(atoms=atoms,trajectory='output.traj',logfile = 'qn.log') dyn.run(fmax=0.05) diff --git a/tests/gpaw/files/TEST_bulk_opt_hcp.py b/tests/gpaw/files/TEST_bulk_opt_hcp.py index e6d705a..b6e4006 100644 --- a/tests/gpaw/files/TEST_bulk_opt_hcp.py +++ b/tests/gpaw/files/TEST_bulk_opt_hcp.py @@ -4,7 +4,6 @@ from ase.io import write from ase.optimize import BFGS from ase.eos import EquationOfState -from fractions import Fraction import numpy as np import glob diff --git a/tests/gpaw/files/TEST_defaults.py b/tests/gpaw/files/TEST_defaults.py index 138cd89..4d3fe08 100644 --- a/tests/gpaw/files/TEST_defaults.py +++ b/tests/gpaw/files/TEST_defaults.py @@ -4,7 +4,6 @@ from ase.io import write from ase.optimize import BFGS from ase.eos import EquationOfState -from fractions import Fraction import numpy as np import glob diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py index 7f2b0b3..d69eef1 100644 --- a/tests/gpaw/files/TEST_relax.py +++ b/tests/gpaw/files/TEST_relax.py @@ -4,7 +4,6 @@ from ase.io import write from ase.optimize import BFGS from ase.eos import EquationOfState -from fractions import Fraction import numpy as np import glob diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index c497e2c..97fc8de 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -4,7 +4,6 @@ from ase.io import write from ase.optimize import BFGS from ase.eos import EquationOfState -from fractions import Fraction import numpy as np import glob diff --git a/tests/gpaw/files/TEST_restart.py b/tests/gpaw/files/TEST_restart.py index dfb5778..03745bf 100644 --- a/tests/gpaw/files/TEST_restart.py +++ b/tests/gpaw/files/TEST_restart.py @@ -4,7 +4,6 @@ from ase.io import write from ase.optimize import BFGS from ase.eos import EquationOfState -from fractions import Fraction import numpy as np import glob diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 253a69b..9cbc97f 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -102,7 +102,7 @@ def test_calculator_object_as_str(): gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) assert gig.calculator_object_as_str == "slab.calc = GPAW(\n\n)" gig.calculation_presets = "bulk_opt" - calc_obj = "\n".join(bulk_opt_in.splitlines()[35:-1]) + calc_obj = "\n".join(bulk_opt_in.splitlines()[34:-1]) assert gig.calculator_object_as_str == calc_obj From e3ad07ebbd15fc30f9401613825c8e124237ea7e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 4 Jan 2021 16:58:41 -0500 Subject: [PATCH 31/44] Removes unused imports --- src/dftinputgen/gpaw/gpaw.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 8e4f588..b41c281 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -1,10 +1,5 @@ import os -import json -import six -import itertools -from dftinputgen.data import STANDARD_ATOMIC_WEIGHTS -from dftinputgen.utils import get_elem_symbol from dftinputgen.gpaw.settings import GPAW_TAGS from dftinputgen.gpaw.settings.calculation_presets import GPAW_PRESETS From e3af7c3306a5f3259b54991d222ec2dfa7f0fd58 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 6 Jan 2021 13:21:41 -0500 Subject: [PATCH 32/44] Fixes docstring for gpaw class --- src/dftinputgen/gpaw/gpaw.py | 137 ++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index b41c281..44d2e64 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -12,73 +12,7 @@ class GPAWInputGeneratorError(DftInputGeneratorError): class GPAWInputGenerator(DftInputGenerator): - """ - Constructor for the GPAW class. - - Parameters - ---------- - - crystal_structure: :class:`ase.Atoms` object - :class:`ase.Atoms` object from `ase.io.read([crystal structure - file])`. - - calculation_presets: str, optional - The "base" calculation settings to use--must be one of the - pre-defined groups of tags and values provided for pw.x. - - Pre-defined settings for some common calculation types are in - INSTALL_PATH/qe/settings/calculation_presets/ - - custom_sett_file: str, optional - Location of a JSON file with custom calculation settings as a - dictionary of tags and values. - - NB: Custom settings specified here always OVERRIDE those in - `calculation_presets` in case of overlap. - - custom_sett_dict: dict, optional - Dictionary with custom calculation settings as tags and values/ - - NB: Custom settings specified here always OVERRIDE those in - `calculation_presets` and `custom_sett_file`. - - write_location: str, optional - Path to the directory in which to write the input file(s). - - Default: Current working directory. - - gpaw_input_file: str, optional - Name of the file in which to write the GPAW python script - - Default: "[`calculation_presets`]_in.py" if `calculation_presets` is - specified by the user, else "gpaw_in.py". - - gpaw_restart_file: str, optional - Name of the gpaw restart file that the written gpaw script will read - from. - - Default: "output.gpw" - - restart: bool, optional - Bool specifying if the gpaw script written should be a restart job - - Default: False - - struct_filename: str, optional - Name of the structure file readable by `ase.io.read` that the written - gpaw script will call from. - - Default: "input.traj" - - overwrite_files: bool, optional - To overwrite files or not, that is the question. - - Default: True - - **kwargs: - Arbitrary keyword arguments. - - """ + """Base class to generate python scripts for GPAW""" def __init__( self, @@ -94,7 +28,74 @@ def __init__( overwrite_files=None, **kwargs, ): - """""" + """ + Constructor. + + Parameters + ---------- + + crystal_structure: :class:`ase.Atoms` object + :class:`ase.Atoms` object from `ase.io.read([crystal structure + file])`. + + calculation_presets: str, optional + The "base" calculation settings to use--must be one of the + pre-defined groups of tags and values provided for pw.x. + + Pre-defined settings for some common calculation types are in + INSTALL_PATH/qe/settings/calculation_presets/ + + custom_sett_file: str, optional + Location of a JSON file with custom calculation settings as a + dictionary of tags and values. + + NB: Custom settings specified here always OVERRIDE those in + `calculation_presets` in case of overlap. + + custom_sett_dict: dict, optional + Dictionary with custom calculation settings as tags and values/ + + NB: Custom settings specified here always OVERRIDE those in + `calculation_presets` and `custom_sett_file`. + + write_location: str, optional + Path to the directory in which to write the input file(s). + + Default: Current working directory. + + gpaw_input_file: str, optional + Name of the file in which to write the GPAW python script + + Default: "[`calculation_presets`]_in.py" if `calculation_presets` is + specified by the user, else "gpaw_in.py". + + gpaw_restart_file: str, optional + Name of the gpaw restart file that the written gpaw script will read + from. + + Default: "output.gpw" + + restart: bool, optional + Bool specifying if the gpaw script written should be a restart job + + Default: False + + struct_filename: str, optional + Name of the structure file readable by `ase.io.read` that the written + gpaw script will call from. + + Default: "input.traj" + + overwrite_files: bool, optional + To overwrite files or not, that is the question. + + Default: True + + **kwargs: + Arbitrary keyword arguments. + + """ + super(GPAWInputGenerator, self).__init__( crystal_structure=crystal_structure, calculation_presets=calculation_presets, From b3e52dd90bc408c0de144f2bacf706615b2a0095 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 6 Jan 2021 14:37:55 -0500 Subject: [PATCH 33/44] Adds gpaw documentation --- docs/src/developer_notes/index.rst | 14 +++++- docs/src/module_reference/gpaw/gpaw.rst | 49 +++++++++++++++++++++ docs/src/module_reference/gpaw/index.rst | 21 +++++++++ docs/src/module_reference/gpaw/settings.rst | 33 ++++++++++++++ docs/src/module_reference/index.rst | 1 + 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 docs/src/module_reference/gpaw/gpaw.rst create mode 100644 docs/src/module_reference/gpaw/index.rst create mode 100644 docs/src/module_reference/gpaw/settings.rst diff --git a/docs/src/developer_notes/index.rst b/docs/src/developer_notes/index.rst index 2a179a9..8b1a0cd 100644 --- a/docs/src/developer_notes/index.rst +++ b/docs/src/developer_notes/index.rst @@ -20,10 +20,20 @@ Code Organization settings/ tags_and_groups.json ... - base_recipes/ + calculation_presets/ scf.json vc-relax.json ... + gpaw/ + gpaw.py + ... + settings/ + tags_and_groups.json + ... + calculation_presets/ + bulk_opt.json + bulk_opt_hcp.json + ... vasp/ ... @@ -33,6 +43,8 @@ Code Organization - ``dftinputgen.qe``: derived classes that can generate input files for various DFT-based and postprocessing codes in the Quantum Espresso suite (more :ref:`here `) +- ``dftinputgen.gpaw``: derived classes that can generate input python scripts + for the GPAW package (more :ref:`here `). - ``dftinputgen.vasp``: [under development] derived classes that can generate input files for the VASP package. - ``dftinputgen.utils``: general-purpose helper functions, e.g. chemical formula diff --git a/docs/src/module_reference/gpaw/gpaw.rst b/docs/src/module_reference/gpaw/gpaw.rst new file mode 100644 index 0000000..cd33043 --- /dev/null +++ b/docs/src/module_reference/gpaw/gpaw.rst @@ -0,0 +1,49 @@ +.. _sssec-gpaw: + +Input GPAW +++++++++++ + +The :class:`GPAWInputGenerator ` class +(derived from :class:`DftInputGenerator `) +implements functionality to generate python scripts for the `GPAW`_ +package. + +Python scripts are written that use `ASE`_ with GPAW specified via a +calculator object. This calculator object is where all settings for +the DFT calculation is given. An example of the calculator object +that is automatically written is given below. + +.. code-block:: python + + slab.calc = GPAW( + h=0.16, + kpts={'size': [4, 4, 1]}, + occupations={'name': 'fermi-dirac', 'width': 0.05}, + poissonsolver={'dipolelayer': 'xy'}, + xc='BEEF-vdW' + ) + +For each calculator setting, a list of valid parameters is looked up from +a ``GPAW_TAGS`` dictionary which is then defined within the written +calculator object (more information about the valid calculator parameters +and defaults provided as ``calculation_presets`` is :ref:`here +`). + +Currently supported calculations include relaxations, bulk optimizations, +and total energy. + +**Note:** The scripts are written assuming that the crystal structure +is stored in an `ASE readable format`_ (e.g. traj, cif, etc..) with +the desired initial magnetic moments defined. + +.. _`GPAW`: https://wiki.fysik.dtu.dk/gpaw/index.html +.. _`ASE`: https://wiki.fysik.dtu.dk/ase/ +.. _`ASE readable format`: https://wiki.fysik.dtu.dk/ase/ase/io/io.html + +Interfaces +========== + +.. automodule:: dftinputgen.gpaw.gpaw + :members: + :inherited-members: + :undoc-members: diff --git a/docs/src/module_reference/gpaw/index.rst b/docs/src/module_reference/gpaw/index.rst new file mode 100644 index 0000000..b72945e --- /dev/null +++ b/docs/src/module_reference/gpaw/index.rst @@ -0,0 +1,21 @@ +.. _ssec-gpaw: + +Input generators for GPAW ++++++++++++++++++++++++++ + +``dftinputgen`` provides an input generator class for the `GPAW`_ package. +More information :ref:`here ` + +.. _`GPAW`: https://wiki.fysik.dtu.dk/gpaw/index.html + + +.. automodule:: dftinputgen.gpaw + :members: + :undoc-members: + +.. toctree:: + :maxdepth: 2 + :hidden: + + gpaw + settings diff --git a/docs/src/module_reference/gpaw/settings.rst b/docs/src/module_reference/gpaw/settings.rst new file mode 100644 index 0000000..a46288f --- /dev/null +++ b/docs/src/module_reference/gpaw/settings.rst @@ -0,0 +1,33 @@ +.. _sssec-gpaw-input-settings: + +Input settings +++++++++++++++ + +All input settings have been parsed from the `GPAW manual`_ +(last updated January 2021). +The calculator parameter names are stored in `tags_and_groups.json`_. +These parameter names are then made available to the user via +a module level variable ``GPAW_TAGS``. + +The settings module also makes available a few sets of default settings +to be used for common DFT calculations such as ``surface_relax``, +and ``bulk_opt``. +The defaults are stored in JSON files in the `calculation_presets`_ module. +These can be accessed by the user via a module level variable ``GPAW_PRESETS``. +Note that these presets are only reasonable defaults and are not meant to be +prescriptive. + +.. _`GPAW manual`: https://wiki.fysik.dtu.dk/gpaw/documentation/manual.html#parameters +.. _`tags_and_groups.json`: https://github.com/CitrineInformatics/dft-input-gen/blob/master/src/dftinputgen/qe/settings/tags_and_groups.json +.. _`calculation_presets`: https://github.com/CitrineInformatics/dft-input-gen/tree/master/src/dftinputgen/qe/settings/calculation_presets + +.. automodule:: dftinputgen.gpaw.settings + :members: + :undoc-members: + :special-members: + +.. automodule:: dftinputgen.gpaw.settings.calculation_presets + :members: + :undoc-members: + :special-members: + diff --git a/docs/src/module_reference/index.rst b/docs/src/module_reference/index.rst index 2ac7eb8..630831a 100644 --- a/docs/src/module_reference/index.rst +++ b/docs/src/module_reference/index.rst @@ -9,5 +9,6 @@ Module reference base qe/index + gpaw/index utils data From e070478d202fcb2152e8cdd5b9bd1b5b47322122 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 8 Jan 2021 13:56:03 -0500 Subject: [PATCH 34/44] Reformats according to PEP8 --- src/dftinputgen/gpaw/__init__.py | 2 +- src/dftinputgen/gpaw/gpaw.py | 66 ++++++++++++++------------- tests/gpaw/files/TEST_relax.py | 4 +- tests/gpaw/files/TEST_relax_custom.py | 4 +- tests/gpaw/files/TEST_restart.py | 4 +- 5 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/dftinputgen/gpaw/__init__.py b/src/dftinputgen/gpaw/__init__.py index 6c1eb45..0dad217 100644 --- a/src/dftinputgen/gpaw/__init__.py +++ b/src/dftinputgen/gpaw/__init__.py @@ -1 +1 @@ -from dftinputgen.gpaw.gpaw import GPAWInputGenerator +from dftinputgen.gpaw.gpaw import GPAWInputGenerator # noqa: F401 diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 44d2e64..08b742d 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -30,70 +30,70 @@ def __init__( ): """ Constructor. - + Parameters ---------- - + crystal_structure: :class:`ase.Atoms` object :class:`ase.Atoms` object from `ase.io.read([crystal structure file])`. - + calculation_presets: str, optional The "base" calculation settings to use--must be one of the pre-defined groups of tags and values provided for pw.x. - + Pre-defined settings for some common calculation types are in INSTALL_PATH/qe/settings/calculation_presets/ - + custom_sett_file: str, optional Location of a JSON file with custom calculation settings as a dictionary of tags and values. - + NB: Custom settings specified here always OVERRIDE those in `calculation_presets` in case of overlap. - + custom_sett_dict: dict, optional Dictionary with custom calculation settings as tags and values/ - + NB: Custom settings specified here always OVERRIDE those in `calculation_presets` and `custom_sett_file`. - + write_location: str, optional Path to the directory in which to write the input file(s). - + Default: Current working directory. - + gpaw_input_file: str, optional Name of the file in which to write the GPAW python script - - Default: "[`calculation_presets`]_in.py" if `calculation_presets` is - specified by the user, else "gpaw_in.py". - + + Default: "[`calculation_presets`]_in.py" if `calculation_presets` + is specified by the user, else "gpaw_in.py". + gpaw_restart_file: str, optional - Name of the gpaw restart file that the written gpaw script will read - from. - + Name of the gpaw restart file that the written gpaw script will + read from. + Default: "output.gpw" - + restart: bool, optional Bool specifying if the gpaw script written should be a restart job - + Default: False - + struct_filename: str, optional - Name of the structure file readable by `ase.io.read` that the written - gpaw script will call from. - + Name of the structure file readable by `ase.io.read` that the + written gpaw script will call from. + Default: "input.traj" - + overwrite_files: bool, optional To overwrite files or not, that is the question. - + Default: True - + **kwargs: Arbitrary keyword arguments. - + """ super(GPAWInputGenerator, self).__init__( @@ -233,15 +233,16 @@ def gpaw_input_as_str(self): try: calc_type = calc_sett["calculation"] except KeyError: - # if no input settings found will return calc object without any settings defined + # if no input settings found will return calc object + # without any settings defined calc_type = None if calc_type == "relax": define_relax_fn = """ def relax(atoms, fmax=0.05, step=0.04): - name = atoms.get_chemical_formula(mode='hill') atoms.calc.set(txt='output.txt') atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn = BFGS(atoms=atoms, trajectory='output.traj', + logfile='qn.log', maxstep=step) dyn.run(fmax=fmax) """ return "\n".join( @@ -348,7 +349,8 @@ def optimize_bulk_hcp(atoms, step=0.05, nstep=5): ] ) - # if not a relax or bulk_opt calculation, defaults to getting total energy of static structure + # if not a relax or bulk_opt calculation, + # defaults to getting total energy of static structure return "\n".join( [ header, diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py index d69eef1..b682603 100644 --- a/tests/gpaw/files/TEST_relax.py +++ b/tests/gpaw/files/TEST_relax.py @@ -13,10 +13,10 @@ def relax(atoms, fmax=0.05, step=0.04): - name = atoms.get_chemical_formula(mode='hill') atoms.calc.set(txt='output.txt') atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn = BFGS(atoms=atoms, trajectory='output.traj', + logfile='qn.log', maxstep=step) dyn.run(fmax=fmax) slab.calc = GPAW( diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index 97fc8de..bdbaa2c 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -13,10 +13,10 @@ def relax(atoms, fmax=0.05, step=0.04): - name = atoms.get_chemical_formula(mode='hill') atoms.calc.set(txt='output.txt') atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn = BFGS(atoms=atoms, trajectory='output.traj', + logfile='qn.log', maxstep=step) dyn.run(fmax=fmax) slab.calc = GPAW( diff --git a/tests/gpaw/files/TEST_restart.py b/tests/gpaw/files/TEST_restart.py index 03745bf..5148c06 100644 --- a/tests/gpaw/files/TEST_restart.py +++ b/tests/gpaw/files/TEST_restart.py @@ -14,10 +14,10 @@ def relax(atoms, fmax=0.05, step=0.04): - name = atoms.get_chemical_formula(mode='hill') atoms.calc.set(txt='output.txt') atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', logfile='qn.log', maxstep=step) + dyn = BFGS(atoms=atoms, trajectory='output.traj', + logfile='qn.log', maxstep=step) dyn.run(fmax=fmax) slab.calc = GPAW( From 3f112f5d8863b9f342eafeba71e39c50bc96a850 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 30 Aug 2021 19:29:25 -0400 Subject: [PATCH 35/44] new script fmt to read restart files automatically --- src/dftinputgen/gpaw/gpaw.py | 165 ++++++++++++-------------- tests/gpaw/files/TEST_bulk_opt.py | 33 ++++-- tests/gpaw/files/TEST_bulk_opt_hcp.py | 33 ++++-- tests/gpaw/files/TEST_defaults.py | 24 +++- tests/gpaw/files/TEST_relax.py | 66 ++++++++--- tests/gpaw/files/TEST_relax_custom.py | 66 ++++++++--- tests/gpaw/files/TEST_restart.py | 30 ----- tests/gpaw/test_gpaw.py | 25 ++-- 8 files changed, 255 insertions(+), 187 deletions(-) delete mode 100644 tests/gpaw/files/TEST_restart.py diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 08b742d..57918a7 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -23,9 +23,9 @@ def __init__( write_location=None, gpaw_input_file=None, gpaw_restart_file=None, - restart=None, - struct_filename=None, + input_struct_filename=None, overwrite_files=None, + from_scratch=None, **kwargs, ): """ @@ -75,14 +75,9 @@ def __init__( Default: "output.gpw" - restart: bool, optional - Bool specifying if the gpaw script written should be a restart job - - Default: False - - struct_filename: str, optional - Name of the structure file readable by `ase.io.read` that the - written gpaw script will call from. + input_struct_filename: str, optional + Name of the input structure file readable by `ase.io.read` that the + written gpaw script will call from if no restart files present. Default: "input.traj" @@ -91,6 +86,11 @@ def __init__( Default: True + from_scratch: bool, optional + Even if restart files are found, whether to restart + the job from scratch (ie. fresh GPAW calculator) + each time the job is resubmitted + **kwargs: Arbitrary keyword arguments. @@ -115,10 +115,10 @@ def __init__( self.gpaw_restart_file = gpaw_restart_file self._struct_filename = "input.traj" - self.struct_filename = struct_filename + self.struct_filename = input_struct_filename - self._restart = False - self.restart = restart + self._from_scratch = False + self.from_scratch = from_scratch @property def dft_package(self): @@ -155,14 +155,14 @@ def struct_filename(self, struct_filename): self._struct_filename = struct_filename @property - def restart(self): - """Whether script should be a restart script""" - return self._restart + def from_scratch(self): + """Whether will always start jobs from scratch, even in presence of output file""" + return self._from_scratch - @restart.setter - def restart(self, restart): - if restart is not None: - self._restart = restart + @from_scratch.setter + def from_scratch(self, from_scratch): + if from_scratch is not None: + self._from_scratch = from_scratch @property def calculation_settings(self): @@ -187,7 +187,7 @@ def _get_default_input_filename(self): @property def calculator_object_as_str(self): - top = "slab.calc = GPAW(" + top = "GPAW(" calc_sett = self.calculation_settings @@ -195,39 +195,39 @@ def calculator_object_as_str(self): for p in GPAW_TAGS["parameters"]: if p in calc_sett: if type(calc_sett[p]) is str: - params.append(f"{p}='{str(calc_sett[p])}'") + params.append(f" {p}='{str(calc_sett[p])}'") else: - params.append(f"{p}={str(calc_sett[p])}") + params.append(f" {p}={str(calc_sett[p])}") return "\n".join([top, ",\n".join(params), ")"]) @property def gpaw_input_as_str(self): - header = """from gpaw import GPAW + header = f"""import os + +from gpaw import GPAW +from gpaw import restart from ase.io import read from ase.io import write from ase.optimize import BFGS -from ase.eos import EquationOfState -import numpy as np -import glob -""" - - if self.restart: - read_init_traj = """from gpaw import restart - -a = glob.glob('{}') -slab,calc = restart(a[-1]) -""".format( - self.gpaw_restart_file - ) - +from ase.parallel import barrier + +from_scratch = {self.from_scratch} +slab = None + +if os.path.isfile("{self.gpaw_restart_file}"): + try: + slab, _ = restart("{self.gpaw_restart_file}") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") else: - read_init_traj = """ -a = glob.glob('{}') -slab = read(a[-1]) -""".format( - self.struct_filename - ) + slab = read("{self.struct_filename}") + +if from_scratch: + slab.calc = {self.calculator_object_as_str} +""" calc_sett = self.calculation_settings try: @@ -237,27 +237,40 @@ def gpaw_input_as_str(self): # without any settings defined calc_type = None if calc_type == "relax": - define_relax_fn = """ -def relax(atoms, fmax=0.05, step=0.04): - atoms.calc.set(txt='output.txt') - atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', - logfile='qn.log', maxstep=step) + define_relax_fn = f"""def relax(atoms, fmax=0.05, step=0.04): + atoms.calc.attach(atoms.calc.write, 5, "{self.gpaw_restart_file}") + + def _check_file_exists(filename): + #Check if file exists and is not empty + if os.path.isfile(filename): + return os.path.getsize(filename) > 0 + else: + return False + + # check if it is a restart + barrier() + if _check_file_exists("output.traj"): + latest = read("output.traj", index=":") + # check if already restarted previously and extend history if needed + if not (_check_file_exists("history.traj")): + barrier() + write("history.traj", latest) + else: + hist = read("history.traj", index=":") + hist.extend(latest) + write("history.traj", hist) + + dyn = BFGS(atoms=atoms, trajectory="output.traj", logfile="qn.log", maxstep=step) + # if history exists, read in hessian + if _check_file_exists("history.traj"): + dyn.replay_trajectory("history.traj") + # optimize dyn.run(fmax=fmax) """ - return "\n".join( - [ - header, - read_init_traj, - define_relax_fn, - self.calculator_object_as_str, - "relax(slab)", - ] - ) + return "\n".join([header, define_relax_fn, "relax(slab)",]) elif calc_type == "bulk_opt": - define_bulk_opt_fn = """ -def bulk_opt(atoms, step=0.05): + define_bulk_opt_fn = """def bulk_opt(atoms, step=0.05): cell = atoms.get_cell() name = atoms.get_chemical_formula(mode='hill') vol=atoms.get_volume() @@ -277,18 +290,9 @@ def bulk_opt(atoms, step=0.05): dyn.run(fmax=0.05) atoms.calc.write('output.gpw') """ - return "\n".join( - [ - header, - read_init_traj, - define_bulk_opt_fn, - self.calculator_object_as_str, - "bulk_opt(slab)", - ] - ) + return "\n".join([header, define_bulk_opt_fn, "bulk_opt(slab)",]) elif calc_type == "bulk_opt_hcp": - define_bulk_opt_hcp_fn = """ -def optimize_bulk_hcp(atoms, step=0.05, nstep=5): + define_bulk_opt_hcp_fn = """def optimize_bulk_hcp(atoms, step=0.05, nstep=5): # Get initial cell cell = atoms.get_cell() a_in = cell[0] @@ -340,25 +344,12 @@ def optimize_bulk_hcp(atoms, step=0.05, nstep=5): atoms.calc.write("output.gpw") """ return "\n".join( - [ - header, - read_init_traj, - define_bulk_opt_hcp_fn, - self.calculator_object_as_str, - "bulk_opt_hcp(slab)", - ] + [header, define_bulk_opt_hcp_fn, "bulk_opt_hcp(slab)",] ) # if not a relax or bulk_opt calculation, # defaults to getting total energy of static structure - return "\n".join( - [ - header, - read_init_traj, - self.calculator_object_as_str, - "slab.get_total_energy()", - ] - ) + return "\n".join([header, "slab.get_total_energy()",]) def write_gpaw_input(self, write_location=None, filename=None): if write_location is None: diff --git a/tests/gpaw/files/TEST_bulk_opt.py b/tests/gpaw/files/TEST_bulk_opt.py index e227a97..960a4fe 100644 --- a/tests/gpaw/files/TEST_bulk_opt.py +++ b/tests/gpaw/files/TEST_bulk_opt.py @@ -1,16 +1,33 @@ # fmt: off +import os + from gpaw import GPAW +from gpaw import restart from ase.io import read from ase.io import write from ase.optimize import BFGS -from ase.eos import EquationOfState -import numpy as np -import glob +from ase.parallel import barrier +from_scratch = False +slab = None -a = glob.glob('input.traj') -slab = read(a[-1]) +if os.path.isfile("output.gpw"): + try: + slab, _ = restart("output.gpw") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") + else: + slab = read("input.traj") +if from_scratch: + slab.calc = GPAW( + h=0.16, + kpts={'size': [12, 12, 12]}, + occupations={'name': 'fermi-dirac', 'width': 0.05}, + xc='BEEF-vdW' +) def bulk_opt(atoms, step=0.05): cell = atoms.get_cell() @@ -32,10 +49,4 @@ def bulk_opt(atoms, step=0.05): dyn.run(fmax=0.05) atoms.calc.write('output.gpw') -slab.calc = GPAW( -h=0.16, -kpts={'size': [12, 12, 12]}, -occupations={'name': 'fermi-dirac', 'width': 0.05}, -xc='BEEF-vdW' -) bulk_opt(slab) diff --git a/tests/gpaw/files/TEST_bulk_opt_hcp.py b/tests/gpaw/files/TEST_bulk_opt_hcp.py index b6e4006..5ae7bda 100644 --- a/tests/gpaw/files/TEST_bulk_opt_hcp.py +++ b/tests/gpaw/files/TEST_bulk_opt_hcp.py @@ -1,16 +1,33 @@ # fmt: off +import os + from gpaw import GPAW +from gpaw import restart from ase.io import read from ase.io import write from ase.optimize import BFGS -from ase.eos import EquationOfState -import numpy as np -import glob +from ase.parallel import barrier +from_scratch = False +slab = None -a = glob.glob('input.traj') -slab = read(a[-1]) +if os.path.isfile("output.gpw"): + try: + slab, _ = restart("output.gpw") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") + else: + slab = read("input.traj") +if from_scratch: + slab.calc = GPAW( + h=0.16, + kpts={'size': [12, 12, 6]}, + occupations={'name': 'fermi-dirac', 'width': 0.05}, + xc='BEEF-vdW' +) def optimize_bulk_hcp(atoms, step=0.05, nstep=5): # Get initial cell @@ -63,10 +80,4 @@ def optimize_bulk_hcp(atoms, step=0.05, nstep=5): dyn.run(fmax=0.05) atoms.calc.write("output.gpw") -slab.calc = GPAW( -h=0.16, -kpts={'size': [12, 12, 6]}, -occupations={'name': 'fermi-dirac', 'width': 0.05}, -xc='BEEF-vdW' -) bulk_opt_hcp(slab) diff --git a/tests/gpaw/files/TEST_defaults.py b/tests/gpaw/files/TEST_defaults.py index 4d3fe08..52b4c78 100644 --- a/tests/gpaw/files/TEST_defaults.py +++ b/tests/gpaw/files/TEST_defaults.py @@ -1,17 +1,29 @@ # fmt: off +import os + from gpaw import GPAW +from gpaw import restart from ase.io import read from ase.io import write from ase.optimize import BFGS -from ase.eos import EquationOfState -import numpy as np -import glob +from ase.parallel import barrier +from_scratch = False +slab = None -a = glob.glob('input.traj') -slab = read(a[-1]) +if os.path.isfile("output.gpw"): + try: + slab, _ = restart("output.gpw") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") + else: + slab = read("input.traj") -slab.calc = GPAW( +if from_scratch: + slab.calc = GPAW( ) + slab.get_total_energy() diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py index b682603..d0c547f 100644 --- a/tests/gpaw/files/TEST_relax.py +++ b/tests/gpaw/files/TEST_relax.py @@ -1,29 +1,63 @@ # fmt: off +import os + from gpaw import GPAW +from gpaw import restart from ase.io import read from ase.io import write from ase.optimize import BFGS -from ase.eos import EquationOfState -import numpy as np -import glob +from ase.parallel import barrier +from_scratch = False +slab = None -a = glob.glob('input.traj') -slab = read(a[-1]) +if os.path.isfile("output.gpw"): + try: + slab, _ = restart("output.gpw") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") + else: + slab = read("input.traj") +if from_scratch: + slab.calc = GPAW( + h=0.16, + kpts={'size': [4, 4, 1]}, + occupations={'name': 'fermi-dirac', 'width': 0.05}, + poissonsolver={'dipolelayer': 'xy'}, + xc='BEEF-vdW' +) def relax(atoms, fmax=0.05, step=0.04): - atoms.calc.set(txt='output.txt') - atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', - logfile='qn.log', maxstep=step) + atoms.calc.attach(atoms.calc.write, 5, "output.gpw") + + def _check_file_exists(filename): + #Check if file exists and is not empty + if os.path.isfile(filename): + return os.path.getsize(filename) > 0 + else: + return False + + # check if it is a restart + barrier() + if _check_file_exists("output.traj"): + latest = read("output.traj", index=":") + # check if already restarted previously and extend history if needed + if not (_check_file_exists("history.traj")): + barrier() + write("history.traj", latest) + else: + hist = read("history.traj", index=":") + hist.extend(latest) + write("history.traj", hist) + + dyn = BFGS(atoms=atoms, trajectory="output.traj", logfile="qn.log", maxstep=step) + # if history exists, read in hessian + if _check_file_exists("history.traj"): + dyn.replay_trajectory("history.traj") + # optimize dyn.run(fmax=fmax) -slab.calc = GPAW( -h=0.16, -kpts={'size': [4, 4, 1]}, -occupations={'name': 'fermi-dirac', 'width': 0.05}, -poissonsolver={'dipolelayer': 'xy'}, -xc='BEEF-vdW' -) relax(slab) diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index bdbaa2c..005d60b 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -1,29 +1,63 @@ # fmt: off +import os + from gpaw import GPAW +from gpaw import restart from ase.io import read from ase.io import write from ase.optimize import BFGS -from ase.eos import EquationOfState -import numpy as np -import glob +from ase.parallel import barrier +from_scratch = True +slab = None -a = glob.glob('CH3.traj') -slab = read(a[-1]) +if os.path.isfile("C3N4.gpw"): + try: + slab, _ = restart("C3N4.gpw") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") + else: + slab = read("C3N4.traj") +if from_scratch: + slab.calc = GPAW( + h=0.18, + kpts={'size': [6, 6, 1]}, + occupations={'name': 'fermi-dirac', 'width': 0.05}, + poissonsolver={'dipolelayer': 'xy'}, + xc='PBE' +) def relax(atoms, fmax=0.05, step=0.04): - atoms.calc.set(txt='output.txt') - atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', - logfile='qn.log', maxstep=step) + atoms.calc.attach(atoms.calc.write, 5, "C3N4.gpw") + + def _check_file_exists(filename): + #Check if file exists and is not empty + if os.path.isfile(filename): + return os.path.getsize(filename) > 0 + else: + return False + + # check if it is a restart + barrier() + if _check_file_exists("output.traj"): + latest = read("output.traj", index=":") + # check if already restarted previously and extend history if needed + if not (_check_file_exists("history.traj")): + barrier() + write("history.traj", latest) + else: + hist = read("history.traj", index=":") + hist.extend(latest) + write("history.traj", hist) + + dyn = BFGS(atoms=atoms, trajectory="output.traj", logfile="qn.log", maxstep=step) + # if history exists, read in hessian + if _check_file_exists("history.traj"): + dyn.replay_trajectory("history.traj") + # optimize dyn.run(fmax=fmax) -slab.calc = GPAW( -h=0.18, -kpts={'size': [6, 6, 1]}, -occupations={'name': 'fermi-dirac', 'width': 0.05}, -poissonsolver={'dipolelayer': 'xy'}, -xc='PBE' -) relax(slab) diff --git a/tests/gpaw/files/TEST_restart.py b/tests/gpaw/files/TEST_restart.py deleted file mode 100644 index 5148c06..0000000 --- a/tests/gpaw/files/TEST_restart.py +++ /dev/null @@ -1,30 +0,0 @@ -# fmt: off -from gpaw import GPAW -from ase.io import read -from ase.io import write -from ase.optimize import BFGS -from ase.eos import EquationOfState -import numpy as np -import glob - -from gpaw import restart - -a = glob.glob('C3N4.gpw') -slab,calc = restart(a[-1]) - - -def relax(atoms, fmax=0.05, step=0.04): - atoms.calc.set(txt='output.txt') - atoms.calc.attach(atoms.calc.write, 5, 'output.gpw') - dyn = BFGS(atoms=atoms, trajectory='output.traj', - logfile='qn.log', maxstep=step) - dyn.run(fmax=fmax) - -slab.calc = GPAW( -h=0.16, -kpts={'size': [4, 4, 1]}, -occupations={'name': 'fermi-dirac', 'width': 0.05}, -poissonsolver={'dipolelayer': 'xy'}, -xc='BEEF-vdW' -) -relax(slab) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 9cbc97f..a0e7501 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -15,8 +15,6 @@ bulk_opt_hcp_in = fr.read() with open(os.path.join(test_data_dir, "TEST_relax.py"), "r") as fr: relax_in = fr.read() -with open(os.path.join(test_data_dir, "TEST_restart.py"), "r") as fr: - restart_in = fr.read() with open(os.path.join(test_data_dir, "TEST_relax_custom.py"), "r") as fr: relax_custom_in = fr.read() with open(os.path.join(test_data_dir, "TEST_defaults.py"), "r") as fr: @@ -100,9 +98,9 @@ def test_surface_relax_calculation_presets_settings(): def test_calculator_object_as_str(): # Tests generation of calculator object as string gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) - assert gig.calculator_object_as_str == "slab.calc = GPAW(\n\n)" + assert gig.calculator_object_as_str == "GPAW(\n\n)" gig.calculation_presets = "bulk_opt" - calc_obj = "\n".join(bulk_opt_in.splitlines()[34:-1]) + calc_obj = "\n".join(["GPAW(", "\n".join(bulk_opt_in.splitlines()[25:30])]) assert gig.calculator_object_as_str == calc_obj @@ -126,10 +124,10 @@ def test_gpaw_input_as_str(): gig = GPAWInputGenerator( crystal_structure=cu_bulk_struct, calculation_presets="surface_relax", - restart=True, + from_scratch=True, gpaw_restart_file="C3N4.gpw", + gpaw_input_file="C3N4.traj", ) - assert gig.gpaw_input_as_str == "\n".join(restart_in.splitlines()[1:]) def test_write_gpaw_input(): @@ -147,7 +145,9 @@ def test_write_gpaw_input(): _tmp_file = tempfile.NamedTemporaryFile(mode="w", delete=True) filename = _tmp_file.name write_location = os.path.dirname(filename) - gig.struct_filename = "CH3.traj" + gig.gpaw_restart_file = "C3N4.gpw" + gig.struct_filename = "C3N4.traj" + gig.from_scratch = True gig.custom_sett_dict = { "kpts": {"size": [6, 6, 1]}, "xc": "PBE", @@ -155,7 +155,8 @@ def test_write_gpaw_input(): } gig.write_gpaw_input(write_location=write_location, filename=filename) with open(filename, "r") as fr: - assert fr.read() == "\n".join(relax_custom_in.splitlines()[1:]) + written_file = fr.read() + assert written_file == "\n".join(relax_custom_in.splitlines()[1:]) def test_write_input_files(): @@ -166,7 +167,9 @@ def test_write_input_files(): write_location = os.path.dirname(filename) gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) gig.calculation_presets = "surface_relax" - gig.struct_filename = "CH3.traj" + gig.struct_filename = "C3N4.traj" + gig.gpaw_restart_file = "C3N4.gpw" + gig.from_scratch = True gig.custom_sett_dict = { "kpts": {"size": [6, 6, 1]}, "xc": "PBE", @@ -176,4 +179,6 @@ def test_write_input_files(): gig.gpaw_input_file = filename gig.write_input_files() with open(filename, "r") as fr: - assert fr.read() == "\n".join(relax_custom_in.splitlines()[1:]) + written_file = fr.read() + check = "\n".join(relax_custom_in.splitlines()[1:]) + assert written_file == check From a0b5d5acf72c86d2cc2bb5d65f712d4029c3647d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 18 Oct 2021 17:30:02 -0400 Subject: [PATCH 36/44] various fixes - fix imports in generated gpaw scripts - change func name bulk_opt(_hcp) to optimize_bulk(_hcp) - fix restart logic in generated gpaw scripts - update tests --- src/dftinputgen/gpaw/gpaw.py | 13 ++++++++++--- tests/gpaw/files/TEST_bulk_opt.py | 9 +++++++-- tests/gpaw/files/TEST_bulk_opt_hcp.py | 7 ++++++- tests/gpaw/files/TEST_defaults.py | 5 +++++ tests/gpaw/files/TEST_relax.py | 5 +++++ tests/gpaw/files/TEST_relax_custom.py | 5 +++++ tests/gpaw/test_gpaw.py | 2 +- 7 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index 57918a7..e7dbe77 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -204,6 +204,7 @@ def calculator_object_as_str(self): @property def gpaw_input_as_str(self): header = f"""import os +import numpy as np from gpaw import GPAW from gpaw import restart @@ -211,6 +212,7 @@ def gpaw_input_as_str(self): from ase.io import write from ase.optimize import BFGS from ase.parallel import barrier +from ase.eos import EquationOfState from_scratch = {self.from_scratch} slab = None @@ -224,6 +226,9 @@ def gpaw_input_as_str(self): slab = read("output.traj") else: slab = read("{self.struct_filename}") +else: + slab = read("{self.struct_filename}") + from_scratch = True if from_scratch: slab.calc = {self.calculator_object_as_str} @@ -270,7 +275,7 @@ def _check_file_exists(filename): return "\n".join([header, define_relax_fn, "relax(slab)",]) elif calc_type == "bulk_opt": - define_bulk_opt_fn = """def bulk_opt(atoms, step=0.05): + define_bulk_opt_fn = """def optimize_bulk(atoms, step=0.05): cell = atoms.get_cell() name = atoms.get_chemical_formula(mode='hill') vol=atoms.get_volume() @@ -290,7 +295,9 @@ def _check_file_exists(filename): dyn.run(fmax=0.05) atoms.calc.write('output.gpw') """ - return "\n".join([header, define_bulk_opt_fn, "bulk_opt(slab)",]) + return "\n".join( + [header, define_bulk_opt_fn, "optimize_bulk(slab)",] + ) elif calc_type == "bulk_opt_hcp": define_bulk_opt_hcp_fn = """def optimize_bulk_hcp(atoms, step=0.05, nstep=5): # Get initial cell @@ -344,7 +351,7 @@ def _check_file_exists(filename): atoms.calc.write("output.gpw") """ return "\n".join( - [header, define_bulk_opt_hcp_fn, "bulk_opt_hcp(slab)",] + [header, define_bulk_opt_hcp_fn, "optimize_bulk_hcp(slab)",] ) # if not a relax or bulk_opt calculation, diff --git a/tests/gpaw/files/TEST_bulk_opt.py b/tests/gpaw/files/TEST_bulk_opt.py index 960a4fe..9a29720 100644 --- a/tests/gpaw/files/TEST_bulk_opt.py +++ b/tests/gpaw/files/TEST_bulk_opt.py @@ -1,5 +1,6 @@ # fmt: off import os +import numpy as np from gpaw import GPAW from gpaw import restart @@ -7,6 +8,7 @@ from ase.io import write from ase.optimize import BFGS from ase.parallel import barrier +from ase.eos import EquationOfState from_scratch = False slab = None @@ -20,6 +22,9 @@ slab = read("output.traj") else: slab = read("input.traj") +else: + slab = read("input.traj") + from_scratch = True if from_scratch: slab.calc = GPAW( @@ -29,7 +34,7 @@ xc='BEEF-vdW' ) -def bulk_opt(atoms, step=0.05): +def optimize_bulk(atoms, step=0.05): cell = atoms.get_cell() name = atoms.get_chemical_formula(mode='hill') vol=atoms.get_volume() @@ -49,4 +54,4 @@ def bulk_opt(atoms, step=0.05): dyn.run(fmax=0.05) atoms.calc.write('output.gpw') -bulk_opt(slab) +optimize_bulk(slab) diff --git a/tests/gpaw/files/TEST_bulk_opt_hcp.py b/tests/gpaw/files/TEST_bulk_opt_hcp.py index 5ae7bda..2a0a7de 100644 --- a/tests/gpaw/files/TEST_bulk_opt_hcp.py +++ b/tests/gpaw/files/TEST_bulk_opt_hcp.py @@ -1,5 +1,6 @@ # fmt: off import os +import numpy as np from gpaw import GPAW from gpaw import restart @@ -7,6 +8,7 @@ from ase.io import write from ase.optimize import BFGS from ase.parallel import barrier +from ase.eos import EquationOfState from_scratch = False slab = None @@ -20,6 +22,9 @@ slab = read("output.traj") else: slab = read("input.traj") +else: + slab = read("input.traj") + from_scratch = True if from_scratch: slab.calc = GPAW( @@ -80,4 +85,4 @@ def optimize_bulk_hcp(atoms, step=0.05, nstep=5): dyn.run(fmax=0.05) atoms.calc.write("output.gpw") -bulk_opt_hcp(slab) +optimize_bulk_hcp(slab) diff --git a/tests/gpaw/files/TEST_defaults.py b/tests/gpaw/files/TEST_defaults.py index 52b4c78..7ee1ba5 100644 --- a/tests/gpaw/files/TEST_defaults.py +++ b/tests/gpaw/files/TEST_defaults.py @@ -1,5 +1,6 @@ # fmt: off import os +import numpy as np from gpaw import GPAW from gpaw import restart @@ -7,6 +8,7 @@ from ase.io import write from ase.optimize import BFGS from ase.parallel import barrier +from ase.eos import EquationOfState from_scratch = False slab = None @@ -20,6 +22,9 @@ slab = read("output.traj") else: slab = read("input.traj") +else: + slab = read("input.traj") + from_scratch = True if from_scratch: slab.calc = GPAW( diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py index d0c547f..c5635a0 100644 --- a/tests/gpaw/files/TEST_relax.py +++ b/tests/gpaw/files/TEST_relax.py @@ -1,5 +1,6 @@ # fmt: off import os +import numpy as np from gpaw import GPAW from gpaw import restart @@ -7,6 +8,7 @@ from ase.io import write from ase.optimize import BFGS from ase.parallel import barrier +from ase.eos import EquationOfState from_scratch = False slab = None @@ -20,6 +22,9 @@ slab = read("output.traj") else: slab = read("input.traj") +else: + slab = read("input.traj") + from_scratch = True if from_scratch: slab.calc = GPAW( diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index 005d60b..188a6af 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -1,5 +1,6 @@ # fmt: off import os +import numpy as np from gpaw import GPAW from gpaw import restart @@ -7,6 +8,7 @@ from ase.io import write from ase.optimize import BFGS from ase.parallel import barrier +from ase.eos import EquationOfState from_scratch = True slab = None @@ -20,6 +22,9 @@ slab = read("output.traj") else: slab = read("C3N4.traj") +else: + slab = read("C3N4.traj") + from_scratch = True if from_scratch: slab.calc = GPAW( diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index a0e7501..4e37109 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -100,7 +100,7 @@ def test_calculator_object_as_str(): gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) assert gig.calculator_object_as_str == "GPAW(\n\n)" gig.calculation_presets = "bulk_opt" - calc_obj = "\n".join(["GPAW(", "\n".join(bulk_opt_in.splitlines()[25:30])]) + calc_obj = "\n".join(["GPAW(", "\n".join(bulk_opt_in.splitlines()[30:35])]) assert gig.calculator_object_as_str == calc_obj From 735d0ebeec1afb72ba419bce3f2c5ad0e5c2f25a Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 20 Oct 2021 17:19:52 -0400 Subject: [PATCH 37/44] add gpaw cli --- src/dftinputgen/cli.py | 8 +++ src/dftinputgen/demo/gpaw.py | 107 +++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 src/dftinputgen/demo/gpaw.py diff --git a/src/dftinputgen/cli.py b/src/dftinputgen/cli.py index f5a719d..0e883fa 100644 --- a/src/dftinputgen/cli.py +++ b/src/dftinputgen/cli.py @@ -2,6 +2,8 @@ from dftinputgen.demo.pwx import build_pwx_parser from dftinputgen.demo.pwx import generate_pwx_input_files +from dftinputgen.demo.gpaw import build_gpaw_parser +from dftinputgen.demo.gpaw import generate_gpaw_input_files def get_parser(): @@ -28,6 +30,12 @@ def get_parser(): # other subparsers, to be added similarly, go here # e.g. ones for gpaw/vasp + # gpaw subparser + gpaw_help = "Generate a python input file for GPAW" + gpaw_parser = subparsers.add_parser("gpaw", help=gpaw_help) + build_gpaw_parser(gpaw_parser) + gpaw_parser.set_defaults(func=generate_gpaw_input_files) + return parser diff --git a/src/dftinputgen/demo/gpaw.py b/src/dftinputgen/demo/gpaw.py new file mode 100644 index 0000000..d422a3d --- /dev/null +++ b/src/dftinputgen/demo/gpaw.py @@ -0,0 +1,107 @@ +"""Demo generating input files for doing a calculation with pw.x.""" + +import json +import argparse + +from dftinputgen.utils import read_crystal_structure +from dftinputgen.gpaw import GPAWInputGenerator + + +def _get_default_parser(): + description = "Input file generation for pw.x." + return argparse.ArgumentParser(description=description) + + +def build_gpaw_parser(parser): + """Adds GPAW arguments to the input `argparse.ArgumentParser` object.""" + # Required: + crystal_structure = "(REQUIRED) File with the input crystal structure" + parser.add_argument( + "-i", + "--crystal-structure", + type=read_crystal_structure, + help=crystal_structure, + required=True, + ) + + # Optional: + calculation_presets = "Preset group of tags and default values to use" + parser.add_argument( + "-pre", + "--calculation-presets", + choices=["surface_relax", "bulk_opt", "bulk_opt_hcp", "molecule",], + default=None, + help=calculation_presets, + ) + + custom_settings_file = "JSON file with custom DFT settings to use" + parser.add_argument( + "-file", + "--custom-settings-file", + default=None, + help=custom_settings_file, + ) + + custom_settings_dict = """JSON string with a dictionary of custom DFT + settings to use. Example: '{"pseudo_dir": "/path/to/pseudo_dir/"}'""" + parser.add_argument( + "-dict", + "--custom-settings-dict", + default="{}", + type=json.loads, + help=custom_settings_dict, + ) + + write_location = "Directory to write the input file(s) in" + parser.add_argument("-loc", "--write-location", help=write_location) + + gpaw_input_file = "Name of the GPAW input file to be written" + parser.add_argument("-o", "--gpaw-input-file", help=gpaw_input_file) + + gpaw_restart_file = "Name of the GPAW restart file that the written script\ + will read from" + parser.add_argument("-r", "--gpaw-restart-file", help=gpaw_restart_file) + + input_struct_filename = "Name of the input structure that the written\ + script will read from" + parser.add_argument( + "-is", "--input_struct_filename", help=input_struct_filename + ) + + overwrite_files = "To overwrite files or not, that is the question" + parser.add_argument("-ov", "--overwrite_files", help=overwrite_files) + + +def generate_gpaw_input_files(args): + """Write input files for the input crystal structure.""" + gig = GPAWInputGenerator( + crystal_structure=args.crystal_structure, + calculation_presets=args.calculation_presets, + custom_sett_file=args.custom_settings_file, + custom_sett_dict=args.custom_settings_dict, + write_location=args.write_location, + gpaw_input_file=args.gpaw_input_file, + gpaw_restart_file=args.gpaw_restart_file, + input_struct_filename=args.input_struct_filename, + overwrite_files=args.overwrite_files, + ) + gig.write_input_files() + + +def run_demo(*sys_args): + """End-to-end run of pw.x input file generation.""" + parser = _get_default_parser() + build_gpaw_parser(parser) + args = parser.parse_args(*sys_args) + generate_gpaw_input_files(args) + + +if __name__ == "__main__": + """ + When run as a script, this module will generate input files to use with + GPAW, for a specified crystal structure, calculation presets, and any + custom DFT settings on top of preset defaults. + + For a list of optional arguments, run this script with "-h" argument. + """ + run_demo() # pragma: no cover From 89cd1473f2cf3acf06405623b980293694448cb5 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 21 Oct 2021 15:43:48 -0400 Subject: [PATCH 38/44] add surface adsorbate vibrations calcs --- src/dftinputgen/demo/gpaw.py | 8 +++- src/dftinputgen/gpaw/gpaw.py | 16 ++++++- .../surface_adsorbate_vibrations.json | 19 ++++++++ tests/gpaw/files/TEST_bulk_opt.py | 1 + tests/gpaw/files/TEST_bulk_opt_hcp.py | 1 + tests/gpaw/files/TEST_defaults.py | 1 + tests/gpaw/files/TEST_relax.py | 1 + tests/gpaw/files/TEST_relax_custom.py | 1 + .../TEST_surface_adsorbate_vibrations.py | 47 +++++++++++++++++++ tests/gpaw/test_gpaw.py | 12 +++-- 10 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 src/dftinputgen/gpaw/settings/calculation_presets/surface_adsorbate_vibrations.json create mode 100644 tests/gpaw/files/TEST_surface_adsorbate_vibrations.py diff --git a/src/dftinputgen/demo/gpaw.py b/src/dftinputgen/demo/gpaw.py index d422a3d..447b931 100644 --- a/src/dftinputgen/demo/gpaw.py +++ b/src/dftinputgen/demo/gpaw.py @@ -29,7 +29,13 @@ def build_gpaw_parser(parser): parser.add_argument( "-pre", "--calculation-presets", - choices=["surface_relax", "bulk_opt", "bulk_opt_hcp", "molecule",], + choices=[ + "surface_relax", + "bulk_opt", + "bulk_opt_hcp", + "molecule", + "surface_adsorbate_vibrations", + ], default=None, help=calculation_presets, ) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index e7dbe77..f8214f4 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -40,7 +40,7 @@ def __init__( calculation_presets: str, optional The "base" calculation settings to use--must be one of the - pre-defined groups of tags and values provided for pw.x. + pre-defined groups of tags and values provided for GPAW. Pre-defined settings for some common calculation types are in INSTALL_PATH/qe/settings/calculation_presets/ @@ -213,6 +213,7 @@ def gpaw_input_as_str(self): from ase.optimize import BFGS from ase.parallel import barrier from ase.eos import EquationOfState +from ase.vibrations import Vibrations from_scratch = {self.from_scratch} slab = None @@ -354,6 +355,19 @@ def _check_file_exists(filename): [header, define_bulk_opt_hcp_fn, "optimize_bulk_hcp(slab)",] ) + elif calc_type == "surface_adsorbate_vibrations": + define_surf_ads_vib = """def calculate_vibrations(atoms): + indices_to_perturb = np.where(atoms.get_tags() <= 0)[0].tolist() + barrier() + vib = Vibrations(atoms, indices = indices_to_perturb, name = "vib.log") + vib.clean(empty_files=True) + vib.run() + vib.summary(log="vib.summary") +""" + return "\n".join( + [header, define_surf_ads_vib, "calculate_vibrations(slab)"] + ) + # if not a relax or bulk_opt calculation, # defaults to getting total energy of static structure return "\n".join([header, "slab.get_total_energy()",]) diff --git a/src/dftinputgen/gpaw/settings/calculation_presets/surface_adsorbate_vibrations.json b/src/dftinputgen/gpaw/settings/calculation_presets/surface_adsorbate_vibrations.json new file mode 100644 index 0000000..d047cbb --- /dev/null +++ b/src/dftinputgen/gpaw/settings/calculation_presets/surface_adsorbate_vibrations.json @@ -0,0 +1,19 @@ +{ + "kpts": { + "size": [ + 4, + 4, + 1 + ] + }, + "xc": "BEEF-vdW", + "h": 0.16, + "occupations": { + "name": "fermi-dirac", + "width": 0.05 + }, + "poissonsolver": { + "dipolelayer": "xy" + }, + "calculation": "surface_adsorbate_vibrations" +} \ No newline at end of file diff --git a/tests/gpaw/files/TEST_bulk_opt.py b/tests/gpaw/files/TEST_bulk_opt.py index 9a29720..6074efc 100644 --- a/tests/gpaw/files/TEST_bulk_opt.py +++ b/tests/gpaw/files/TEST_bulk_opt.py @@ -9,6 +9,7 @@ from ase.optimize import BFGS from ase.parallel import barrier from ase.eos import EquationOfState +from ase.vibrations import Vibrations from_scratch = False slab = None diff --git a/tests/gpaw/files/TEST_bulk_opt_hcp.py b/tests/gpaw/files/TEST_bulk_opt_hcp.py index 2a0a7de..886b934 100644 --- a/tests/gpaw/files/TEST_bulk_opt_hcp.py +++ b/tests/gpaw/files/TEST_bulk_opt_hcp.py @@ -9,6 +9,7 @@ from ase.optimize import BFGS from ase.parallel import barrier from ase.eos import EquationOfState +from ase.vibrations import Vibrations from_scratch = False slab = None diff --git a/tests/gpaw/files/TEST_defaults.py b/tests/gpaw/files/TEST_defaults.py index 7ee1ba5..50c319b 100644 --- a/tests/gpaw/files/TEST_defaults.py +++ b/tests/gpaw/files/TEST_defaults.py @@ -9,6 +9,7 @@ from ase.optimize import BFGS from ase.parallel import barrier from ase.eos import EquationOfState +from ase.vibrations import Vibrations from_scratch = False slab = None diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py index c5635a0..38c2ff5 100644 --- a/tests/gpaw/files/TEST_relax.py +++ b/tests/gpaw/files/TEST_relax.py @@ -9,6 +9,7 @@ from ase.optimize import BFGS from ase.parallel import barrier from ase.eos import EquationOfState +from ase.vibrations import Vibrations from_scratch = False slab = None diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index 188a6af..919baab 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -9,6 +9,7 @@ from ase.optimize import BFGS from ase.parallel import barrier from ase.eos import EquationOfState +from ase.vibrations import Vibrations from_scratch = True slab = None diff --git a/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py b/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py new file mode 100644 index 0000000..c92e7a7 --- /dev/null +++ b/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py @@ -0,0 +1,47 @@ +# fmt: off +import os +import numpy as np + +from gpaw import GPAW +from gpaw import restart +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.parallel import barrier +from ase.eos import EquationOfState +from ase.vibrations import Vibrations + +from_scratch = False +slab = None + +if os.path.isfile("output.gpw"): + try: + slab, _ = restart("output.gpw") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") + else: + slab = read("input.traj") +else: + slab = read("input.traj") + from_scratch = True + +if from_scratch: + slab.calc = GPAW( + h=0.16, + kpts={'size': [4, 4, 1]}, + occupations={'name': 'fermi-dirac', 'width': 0.05}, + poissonsolver={'dipolelayer': 'xy'}, + xc='BEEF-vdW' +) + +def calculate_vibrations(atoms): + indices_to_perturb = np.where(atoms.get_tags() <= 0)[0].tolist() + barrier() + vib = Vibrations(atoms, indices = indices_to_perturb, name = "vib.log") + vib.clean(empty_files=True) + vib.run() + vib.summary(log="vib.summary") + +calculate_vibrations(slab) diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 4e37109..27fce7e 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -19,6 +19,10 @@ relax_custom_in = fr.read() with open(os.path.join(test_data_dir, "TEST_defaults.py"), "r") as fr: defaults_in = fr.read() +with open( + os.path.join(test_data_dir, "TEST_surface_adsorbate_vibrations.py"), "r" +) as fr: + vib_in = fr.read() def test_dft_package_name(): @@ -100,7 +104,7 @@ def test_calculator_object_as_str(): gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) assert gig.calculator_object_as_str == "GPAW(\n\n)" gig.calculation_presets = "bulk_opt" - calc_obj = "\n".join(["GPAW(", "\n".join(bulk_opt_in.splitlines()[30:35])]) + calc_obj = "\n".join(["GPAW(", "\n".join(bulk_opt_in.splitlines()[31:36])]) assert gig.calculator_object_as_str == calc_obj @@ -123,11 +127,9 @@ def test_gpaw_input_as_str(): assert gig.gpaw_input_as_str == "\n".join(relax_in.splitlines()[1:]) gig = GPAWInputGenerator( crystal_structure=cu_bulk_struct, - calculation_presets="surface_relax", - from_scratch=True, - gpaw_restart_file="C3N4.gpw", - gpaw_input_file="C3N4.traj", + calculation_presets="surface_adsorbate_vibrations", ) + assert gig.gpaw_input_as_str == "\n".join(vib_in.splitlines()[1:]) def test_write_gpaw_input(): From b3f0eae539dd913d22e0360ff5ad1fa5633567b8 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 21 Oct 2021 17:28:44 -0400 Subject: [PATCH 39/44] tests for gpaw cli, add from_scratch to gpaw cli --- src/dftinputgen/demo/gpaw.py | 6 +- tests/demo/files/gpaw_li_relax_custom.py | 69 +++++++++++++++ tests/demo/files/li110.traj | Bin 0 -> 1062 bytes tests/demo/files/test_sett_gpaw.json | 6 ++ tests/demo/test_gpaw_demo.py | 107 +++++++++++++++++++++++ tests/test_cli.py | 24 ++++- 6 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 tests/demo/files/gpaw_li_relax_custom.py create mode 100644 tests/demo/files/li110.traj create mode 100644 tests/demo/files/test_sett_gpaw.json create mode 100644 tests/demo/test_gpaw_demo.py diff --git a/src/dftinputgen/demo/gpaw.py b/src/dftinputgen/demo/gpaw.py index 447b931..a6c9e02 100644 --- a/src/dftinputgen/demo/gpaw.py +++ b/src/dftinputgen/demo/gpaw.py @@ -8,7 +8,7 @@ def _get_default_parser(): - description = "Input file generation for pw.x." + description = "Input file generation for gpaw." return argparse.ArgumentParser(description=description) @@ -77,6 +77,9 @@ def build_gpaw_parser(parser): overwrite_files = "To overwrite files or not, that is the question" parser.add_argument("-ov", "--overwrite_files", help=overwrite_files) + from_scratch = "Whether the calculation should be run from scratch" + parser.add_argument("-fs", "--from-scratch", help=from_scratch) + def generate_gpaw_input_files(args): """Write input files for the input crystal structure.""" @@ -90,6 +93,7 @@ def generate_gpaw_input_files(args): gpaw_restart_file=args.gpaw_restart_file, input_struct_filename=args.input_struct_filename, overwrite_files=args.overwrite_files, + from_scratch=args.from_scratch, ) gig.write_input_files() diff --git a/tests/demo/files/gpaw_li_relax_custom.py b/tests/demo/files/gpaw_li_relax_custom.py new file mode 100644 index 0000000..3365959 --- /dev/null +++ b/tests/demo/files/gpaw_li_relax_custom.py @@ -0,0 +1,69 @@ +# fmt: off +import os +import numpy as np + +from gpaw import GPAW +from gpaw import restart +from ase.io import read +from ase.io import write +from ase.optimize import BFGS +from ase.parallel import barrier +from ase.eos import EquationOfState +from ase.vibrations import Vibrations + +from_scratch = True +slab = None + +if os.path.isfile("Li.gpw"): + try: + slab, _ = restart("Li.gpw") + except: + from_scratch = True + if os.path.isfile("output.traj"): + slab = read("output.traj") + else: + slab = read("Li110.traj") +else: + slab = read("Li110.traj") + from_scratch = True + +if from_scratch: + slab.calc = GPAW( + h=0.18, + kpts={'size': [6, 6, 1]}, + occupations={'name': 'fermi-dirac', 'width': 0.05}, + poissonsolver={'dipolelayer': 'xy'}, + xc='PBE' +) + +def relax(atoms, fmax=0.05, step=0.04): + atoms.calc.attach(atoms.calc.write, 5, "Li.gpw") + + def _check_file_exists(filename): + #Check if file exists and is not empty + if os.path.isfile(filename): + return os.path.getsize(filename) > 0 + else: + return False + + # check if it is a restart + barrier() + if _check_file_exists("output.traj"): + latest = read("output.traj", index=":") + # check if already restarted previously and extend history if needed + if not (_check_file_exists("history.traj")): + barrier() + write("history.traj", latest) + else: + hist = read("history.traj", index=":") + hist.extend(latest) + write("history.traj", hist) + + dyn = BFGS(atoms=atoms, trajectory="output.traj", logfile="qn.log", maxstep=step) + # if history exists, read in hessian + if _check_file_exists("history.traj"): + dyn.replay_trajectory("history.traj") + # optimize + dyn.run(fmax=fmax) + +relax(slab) diff --git a/tests/demo/files/li110.traj b/tests/demo/files/li110.traj new file mode 100644 index 0000000000000000000000000000000000000000..8eb08e8ceaec594962763d0edf26630da2872cab GIT binary patch literal 1062 zcmcIhu}&K?7^Xxj5*--YfvK`i3>_|(&*yUzq>Agn10Z0EPA4aElBSbp^4C=kMz9`r>Nsxjyw@jywNa?DX^gGyH#hx4&uS zrRx6cOZBa79D8S6mYFELZ4mgtVh?he4{o)os4 zK0pweONrYdBrP8h*a{ Date: Tue, 26 Oct 2021 12:40:28 -0400 Subject: [PATCH 40/44] specify txt output --- src/dftinputgen/gpaw/gpaw.py | 3 ++- tests/gpaw/files/TEST_bulk_opt.py | 5 +++-- tests/gpaw/files/TEST_bulk_opt_hcp.py | 5 +++-- tests/gpaw/files/TEST_defaults.py | 4 ++-- tests/gpaw/files/TEST_relax.py | 5 +++-- tests/gpaw/files/TEST_relax_custom.py | 5 +++-- tests/gpaw/files/TEST_surface_adsorbate_vibrations.py | 5 +++-- tests/gpaw/test_gpaw.py | 4 ++-- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index f8214f4..a01a032 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -198,6 +198,7 @@ def calculator_object_as_str(self): params.append(f" {p}='{str(calc_sett[p])}'") else: params.append(f" {p}={str(calc_sett[p])}") + params.append(f' txt="output.txt"') return "\n".join([top, ",\n".join(params), ")"]) @@ -220,7 +221,7 @@ def gpaw_input_as_str(self): if os.path.isfile("{self.gpaw_restart_file}"): try: - slab, _ = restart("{self.gpaw_restart_file}") + slab, _ = restart("{self.gpaw_restart_file}", txt="output.txt") except: from_scratch = True if os.path.isfile("output.traj"): diff --git a/tests/gpaw/files/TEST_bulk_opt.py b/tests/gpaw/files/TEST_bulk_opt.py index 6074efc..50fd3e3 100644 --- a/tests/gpaw/files/TEST_bulk_opt.py +++ b/tests/gpaw/files/TEST_bulk_opt.py @@ -16,7 +16,7 @@ if os.path.isfile("output.gpw"): try: - slab, _ = restart("output.gpw") + slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True if os.path.isfile("output.traj"): @@ -32,7 +32,8 @@ h=0.16, kpts={'size': [12, 12, 12]}, occupations={'name': 'fermi-dirac', 'width': 0.05}, - xc='BEEF-vdW' + xc='BEEF-vdW', + txt="output.txt" ) def optimize_bulk(atoms, step=0.05): diff --git a/tests/gpaw/files/TEST_bulk_opt_hcp.py b/tests/gpaw/files/TEST_bulk_opt_hcp.py index 886b934..a6d34da 100644 --- a/tests/gpaw/files/TEST_bulk_opt_hcp.py +++ b/tests/gpaw/files/TEST_bulk_opt_hcp.py @@ -16,7 +16,7 @@ if os.path.isfile("output.gpw"): try: - slab, _ = restart("output.gpw") + slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True if os.path.isfile("output.traj"): @@ -32,7 +32,8 @@ h=0.16, kpts={'size': [12, 12, 6]}, occupations={'name': 'fermi-dirac', 'width': 0.05}, - xc='BEEF-vdW' + xc='BEEF-vdW', + txt="output.txt" ) def optimize_bulk_hcp(atoms, step=0.05, nstep=5): diff --git a/tests/gpaw/files/TEST_defaults.py b/tests/gpaw/files/TEST_defaults.py index 50c319b..105f78c 100644 --- a/tests/gpaw/files/TEST_defaults.py +++ b/tests/gpaw/files/TEST_defaults.py @@ -16,7 +16,7 @@ if os.path.isfile("output.gpw"): try: - slab, _ = restart("output.gpw") + slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True if os.path.isfile("output.traj"): @@ -29,7 +29,7 @@ if from_scratch: slab.calc = GPAW( - + txt="output.txt" ) slab.get_total_energy() diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py index 38c2ff5..8e0f49c 100644 --- a/tests/gpaw/files/TEST_relax.py +++ b/tests/gpaw/files/TEST_relax.py @@ -16,7 +16,7 @@ if os.path.isfile("output.gpw"): try: - slab, _ = restart("output.gpw") + slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True if os.path.isfile("output.traj"): @@ -33,7 +33,8 @@ kpts={'size': [4, 4, 1]}, occupations={'name': 'fermi-dirac', 'width': 0.05}, poissonsolver={'dipolelayer': 'xy'}, - xc='BEEF-vdW' + xc='BEEF-vdW', + txt="output.txt" ) def relax(atoms, fmax=0.05, step=0.04): diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index 919baab..026a7ad 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -16,7 +16,7 @@ if os.path.isfile("C3N4.gpw"): try: - slab, _ = restart("C3N4.gpw") + slab, _ = restart("C3N4.gpw", txt="output.txt") except: from_scratch = True if os.path.isfile("output.traj"): @@ -33,7 +33,8 @@ kpts={'size': [6, 6, 1]}, occupations={'name': 'fermi-dirac', 'width': 0.05}, poissonsolver={'dipolelayer': 'xy'}, - xc='PBE' + xc='PBE', + txt="output.txt" ) def relax(atoms, fmax=0.05, step=0.04): diff --git a/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py b/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py index c92e7a7..9a3dae8 100644 --- a/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py +++ b/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py @@ -16,7 +16,7 @@ if os.path.isfile("output.gpw"): try: - slab, _ = restart("output.gpw") + slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True if os.path.isfile("output.traj"): @@ -33,7 +33,8 @@ kpts={'size': [4, 4, 1]}, occupations={'name': 'fermi-dirac', 'width': 0.05}, poissonsolver={'dipolelayer': 'xy'}, - xc='BEEF-vdW' + xc='BEEF-vdW', + txt="output.txt" ) def calculate_vibrations(atoms): diff --git a/tests/gpaw/test_gpaw.py b/tests/gpaw/test_gpaw.py index 27fce7e..674c3fc 100644 --- a/tests/gpaw/test_gpaw.py +++ b/tests/gpaw/test_gpaw.py @@ -102,9 +102,9 @@ def test_surface_relax_calculation_presets_settings(): def test_calculator_object_as_str(): # Tests generation of calculator object as string gig = GPAWInputGenerator(crystal_structure=cu_bulk_struct) - assert gig.calculator_object_as_str == "GPAW(\n\n)" + assert gig.calculator_object_as_str == 'GPAW(\n txt="output.txt"\n)' gig.calculation_presets = "bulk_opt" - calc_obj = "\n".join(["GPAW(", "\n".join(bulk_opt_in.splitlines()[31:36])]) + calc_obj = "\n".join(["GPAW(", "\n".join(bulk_opt_in.splitlines()[31:37])]) assert gig.calculator_object_as_str == calc_obj From 4d89b93187addbfe69e3e0393434a12aa4be630f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 26 Oct 2021 12:47:31 -0400 Subject: [PATCH 41/44] fix txt output in cli test --- tests/demo/files/gpaw_li_relax_custom.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/demo/files/gpaw_li_relax_custom.py b/tests/demo/files/gpaw_li_relax_custom.py index 3365959..0784078 100644 --- a/tests/demo/files/gpaw_li_relax_custom.py +++ b/tests/demo/files/gpaw_li_relax_custom.py @@ -16,10 +16,10 @@ if os.path.isfile("Li.gpw"): try: - slab, _ = restart("Li.gpw") + slab, _ = restart("Li.gpw", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("Li110.traj") @@ -33,7 +33,8 @@ kpts={'size': [6, 6, 1]}, occupations={'name': 'fermi-dirac', 'width': 0.05}, poissonsolver={'dipolelayer': 'xy'}, - xc='PBE' + xc='PBE', + txt="output.txt" ) def relax(atoms, fmax=0.05, step=0.04): From b43e184f4f318bf5d7980f083e7b937a13f28af7 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 26 Oct 2021 12:48:15 -0400 Subject: [PATCH 42/44] fix restart logic in case of empty output traj --- src/dftinputgen/gpaw/gpaw.py | 2 +- tests/gpaw/files/TEST_bulk_opt.py | 2 +- tests/gpaw/files/TEST_bulk_opt_hcp.py | 2 +- tests/gpaw/files/TEST_defaults.py | 2 +- tests/gpaw/files/TEST_relax.py | 2 +- tests/gpaw/files/TEST_relax_custom.py | 2 +- tests/gpaw/files/TEST_surface_adsorbate_vibrations.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dftinputgen/gpaw/gpaw.py b/src/dftinputgen/gpaw/gpaw.py index a01a032..133178e 100644 --- a/src/dftinputgen/gpaw/gpaw.py +++ b/src/dftinputgen/gpaw/gpaw.py @@ -224,7 +224,7 @@ def gpaw_input_as_str(self): slab, _ = restart("{self.gpaw_restart_file}", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("{self.struct_filename}") diff --git a/tests/gpaw/files/TEST_bulk_opt.py b/tests/gpaw/files/TEST_bulk_opt.py index 50fd3e3..660eae5 100644 --- a/tests/gpaw/files/TEST_bulk_opt.py +++ b/tests/gpaw/files/TEST_bulk_opt.py @@ -19,7 +19,7 @@ slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("input.traj") diff --git a/tests/gpaw/files/TEST_bulk_opt_hcp.py b/tests/gpaw/files/TEST_bulk_opt_hcp.py index a6d34da..9e9d478 100644 --- a/tests/gpaw/files/TEST_bulk_opt_hcp.py +++ b/tests/gpaw/files/TEST_bulk_opt_hcp.py @@ -19,7 +19,7 @@ slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("input.traj") diff --git a/tests/gpaw/files/TEST_defaults.py b/tests/gpaw/files/TEST_defaults.py index 105f78c..8143e36 100644 --- a/tests/gpaw/files/TEST_defaults.py +++ b/tests/gpaw/files/TEST_defaults.py @@ -19,7 +19,7 @@ slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("input.traj") diff --git a/tests/gpaw/files/TEST_relax.py b/tests/gpaw/files/TEST_relax.py index 8e0f49c..f175e3e 100644 --- a/tests/gpaw/files/TEST_relax.py +++ b/tests/gpaw/files/TEST_relax.py @@ -19,7 +19,7 @@ slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("input.traj") diff --git a/tests/gpaw/files/TEST_relax_custom.py b/tests/gpaw/files/TEST_relax_custom.py index 026a7ad..61bf54a 100644 --- a/tests/gpaw/files/TEST_relax_custom.py +++ b/tests/gpaw/files/TEST_relax_custom.py @@ -19,7 +19,7 @@ slab, _ = restart("C3N4.gpw", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("C3N4.traj") diff --git a/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py b/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py index 9a3dae8..f0f7724 100644 --- a/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py +++ b/tests/gpaw/files/TEST_surface_adsorbate_vibrations.py @@ -19,7 +19,7 @@ slab, _ = restart("output.gpw", txt="output.txt") except: from_scratch = True - if os.path.isfile("output.traj"): + if os.path.isfile("output.traj") and os.path.getsize("output.traj") > 0: slab = read("output.traj") else: slab = read("input.traj") From 31c4d9b23f3e9c64f6155a900837c859913c22d3 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 29 Oct 2021 12:54:41 -0400 Subject: [PATCH 43/44] update readme to include gpaw --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ac8cf1..6f21c20 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,10 @@ documentation. 1. [pw.x](https://www.quantum-espresso.org/Doc/INPUT_PW.html) from the [Quantum Espresso package](https://www.quantum-espresso.org/) -2. (under development) Post-processing utilities for pw.x: +2. [GPAW](https://wiki.fysik.dtu.dk/gpaw/index.html) including functions for +`surface relaxation`, `adsorbate vibration calculations`, and `lattice +parameter optimization` (fcc, bcc, and hcp) +3. (under development) Post-processing utilities for pw.x: [dos.x](https://www.quantum-espresso.org/Doc/INPUT_DOS.html), [bands.x](https://www.quantum-espresso.org/Doc/INPUT_BANDS.html), [projwfc.x](https://www.quantum-espresso.org/Doc/INPUT_PROJWFC.html) From 0bf5a815b6edde62c65b05f0033fe71d399a7ee8 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Tue, 25 Jul 2023 12:44:31 -0700 Subject: [PATCH 44/44] line break before operator allowed per pep-8 --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c473d4a..d39df15 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,8 @@ doctests = True # D107: __init__ is self explanatory # D301: backslash is used in making docstrings for sphinx to parse # D401: Imperative mood requirement basically gets in the way -ignore = D100,D104,D105,D107,D301,D401 +# W503: Line break before binary operator is now PEP-8 compliant +ignore = D100,D104,D105,D107,D301,D401,W503 exclude = tests/*