From 308bfce479784c52db36d3eec759b3aa432512fb Mon Sep 17 00:00:00 2001 From: "T.Tian" Date: Thu, 28 Sep 2023 11:20:12 +0800 Subject: [PATCH 1/3] add initial implementation of single file io --- sparc/io.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/sparc/io.py b/sparc/io.py index dc420c8f..14f38346 100644 --- a/sparc/io.py +++ b/sparc/io.py @@ -670,6 +670,63 @@ def write_sparc(filename, images, **kwargs): sb._write_ion_and_inpt(atoms, **kwargs) return +# TODO: make deprecated warning +def read_sparc_ion(filename, **kwargs): + """Parse an .ion file inside the SPARC bundle using a wrapper around SparcBundle + The reader works only when other files (.inpt) exist. + + The returned Atoms object of read_ion method only contains the initial positions + """ + parent_dir = Path(filename).parent + sb = SparcBundle(directory=parent_dir) + atoms = sb._read_ion_and_inpt() + return atoms + +def write_sparc_ion(filename, atoms, **kwargs): + """Write .ion and .inpt files using the SparcBundle wrapper. + + This is only for backward compatibility + """ + label = Path(filename).with_suffix("").name + parent_dir = Path(filename).parent + sb = SparcBundle(directory=parent_dir, label=label) + sb._write_ion_and_inpt(atoms, **kwargs) + return atoms + +def read_sparc_static(filename, index=-1, **kwargs): + """Parse a .static file bundle using a wrapper around SparcBundle + The reader works only when other files (.ion, .inpt) exist. + """ + parent_dir = Path(filename).parent + sb = SparcBundle(directory=parent_dir) + atoms_or_images = sb.convert_to_ase( + index=index, **kwargs + ) + return atoms_or_images + +def read_sparc_geopt(filename, index=-1, **kwargs): + """Parse a .geopt file bundle using a wrapper around SparcBundle + The reader works only when other files (.ion, .inpt) exist. + """ + parent_dir = Path(filename).parent + sb = SparcBundle(directory=parent_dir) + atoms_or_images = sb.convert_to_ase( + index=index, **kwargs + ) + return atoms_or_images + +def read_sparc_aimd(filename, index=-1, **kwargs): + """Parse a .static file bundle using a wrapper around SparcBundle + The reader works only when other files (.ion, .inpt) exist. + """ + parent_dir = Path(filename).parent + sb = SparcBundle(directory=parent_dir) + atoms_or_images = sb.convert_to_ase( + index=index, **kwargs + ) + return atoms_or_images + + def register_ase_io_sparc(name="sparc"): """ @@ -772,6 +829,35 @@ def _new_filetype(filename, read=True, guess=True): "Atomatic format inference for sparc is not correctly registered. " "You may need to use format=sparc in ase.io.read and ase.io.write. " ) + # Add additional formats including .ion (r/w), .static, .geopt, .aimd + F( + "sparc-ion", + desc="SPARC .ion file", + module="sparc", + code="1S", + ext="ion", + ) + F( + "sparc-static", + desc="SPARC single point results", + module="sparc", + code="+S", + ext="static" + ) + F( + "sparc-geopt", + desc="SPARC geometric optimization results", + module="sparc", + code="+S", + ext="geopt" + ) + F( + "sparc-aimd", + desc="SPARC AIMD results", + module="sparc", + code="+S", + ext="aimd" + ) # TODO: remove print options as it may be redundant print("Successfully registered sparc format with ase.io!") From 67c47f34f9cd45ad5ea2110f9b92ad9ae75c8636 Mon Sep 17 00:00:00 2001 From: "T.Tian" Date: Thu, 28 Sep 2023 11:59:01 +0800 Subject: [PATCH 2/3] move deprecation to utils --- sparc/calculator.py | 18 +------------- sparc/io.py | 52 +++++++++++++++++----------------------- sparc/utils.py | 16 +++++++++++++ tests/test_000_ase_io.py | 26 ++++++++++++++++++-- 4 files changed, 63 insertions(+), 49 deletions(-) diff --git a/sparc/calculator.py b/sparc/calculator.py index f4b5d78c..90510a2e 100644 --- a/sparc/calculator.py +++ b/sparc/calculator.py @@ -5,7 +5,7 @@ import subprocess from .io import SparcBundle -from .utils import _find_default_sparc, h2gpts +from .utils import _find_default_sparc, h2gpts, deprecated from warnings import warn, warn_explicit from .api import SparcAPI import datetime @@ -27,22 +27,6 @@ defaultAPI = SparcAPI() -def deprecated(message): - def decorator(func): - def new_func(*args, **kwargs): - warn( - "Function {} is deprecated sparc-dft-api >= v0.2! {}".format( - func.__name__, message - ), - category=DeprecationWarning, - ) - return func(*args, **kwargs) - - return new_func - - return decorator - - class SPARC(FileIOCalculator): # TODO: magmom should be a possible input implemented_properties = ["energy", "forces", "fermi", "stress"] diff --git a/sparc/io.py b/sparc/io.py index 14f38346..8f63a2fe 100644 --- a/sparc/io.py +++ b/sparc/io.py @@ -27,7 +27,7 @@ from .sparc_parsers.pseudopotential import copy_psp_file, parse_psp8_header from .common import psp_dir as default_psp_dir from .download_data import is_psp_download_complete -from .utils import string2index +from .utils import string2index, deprecated from ase.calculators.singlepoint import SinglePointDFTCalculator from ase.units import Hartree @@ -670,8 +670,9 @@ def write_sparc(filename, images, **kwargs): sb._write_ion_and_inpt(atoms, **kwargs) return -# TODO: make deprecated warning -def read_sparc_ion(filename, **kwargs): + +@deprecated("Reading individual .ion is not recommended. Please use read_sparc instead.") +def read_ion(filename, **kwargs): """Parse an .ion file inside the SPARC bundle using a wrapper around SparcBundle The reader works only when other files (.inpt) exist. @@ -682,7 +683,8 @@ def read_sparc_ion(filename, **kwargs): atoms = sb._read_ion_and_inpt() return atoms -def write_sparc_ion(filename, atoms, **kwargs): +@deprecated("Writing individual .ion file is not recommended. Please use write_sparc instead.") +def write_ion(filename, atoms, **kwargs): """Write .ion and .inpt files using the SparcBundle wrapper. This is only for backward compatibility @@ -693,41 +695,37 @@ def write_sparc_ion(filename, atoms, **kwargs): sb._write_ion_and_inpt(atoms, **kwargs) return atoms -def read_sparc_static(filename, index=-1, **kwargs): + +def read_static(filename, index=-1, **kwargs): """Parse a .static file bundle using a wrapper around SparcBundle The reader works only when other files (.ion, .inpt) exist. """ parent_dir = Path(filename).parent sb = SparcBundle(directory=parent_dir) - atoms_or_images = sb.convert_to_ase( - index=index, **kwargs - ) + atoms_or_images = sb.convert_to_ase(index=index, **kwargs) return atoms_or_images -def read_sparc_geopt(filename, index=-1, **kwargs): + +def read_geopt(filename, index=-1, **kwargs): """Parse a .geopt file bundle using a wrapper around SparcBundle The reader works only when other files (.ion, .inpt) exist. """ parent_dir = Path(filename).parent sb = SparcBundle(directory=parent_dir) - atoms_or_images = sb.convert_to_ase( - index=index, **kwargs - ) + atoms_or_images = sb.convert_to_ase(index=index, **kwargs) return atoms_or_images -def read_sparc_aimd(filename, index=-1, **kwargs): + +def read_aimd(filename, index=-1, **kwargs): """Parse a .static file bundle using a wrapper around SparcBundle The reader works only when other files (.ion, .inpt) exist. """ parent_dir = Path(filename).parent sb = SparcBundle(directory=parent_dir) - atoms_or_images = sb.convert_to_ase( - index=index, **kwargs - ) + atoms_or_images = sb.convert_to_ase(index=index, **kwargs) return atoms_or_images - def register_ase_io_sparc(name="sparc"): """ Monkey patching the ase.io and ase.io.formats @@ -831,33 +829,27 @@ def _new_filetype(filename, read=True, guess=True): ) # Add additional formats including .ion (r/w), .static, .geopt, .aimd F( - "sparc-ion", + "ion", desc="SPARC .ion file", module="sparc", code="1S", ext="ion", ) F( - "sparc-static", + "static", desc="SPARC single point results", module="sparc", code="+S", - ext="static" + ext="static", ) F( - "sparc-geopt", + "geopt", desc="SPARC geometric optimization results", module="sparc", code="+S", - ext="geopt" - ) - F( - "sparc-aimd", - desc="SPARC AIMD results", - module="sparc", - code="+S", - ext="aimd" + ext="geopt", ) + F("aimd", desc="SPARC AIMD results", module="sparc", code="+S", ext="aimd") # TODO: remove print options as it may be redundant - print("Successfully registered sparc format with ase.io!") + print("Successfully registered sparc formats with ase.io!") diff --git a/sparc/utils.py b/sparc/utils.py index a6e88571..e9de716d 100644 --- a/sparc/utils.py +++ b/sparc/utils.py @@ -6,6 +6,22 @@ from typing import Union, List, Optional +def deprecated(message): + def decorator(func): + def new_func(*args, **kwargs): + warn( + "Function {} is deprecated sparc-dft-api >= v0.2! {}".format( + func.__name__, message + ), + category=DeprecationWarning, + ) + return func(*args, **kwargs) + + return new_func + + return decorator + + def string2index(string: str) -> Union[int, slice, str]: """Convert index string to either int or slice This method is a copy of ase.io.formats.string2index diff --git a/tests/test_000_ase_io.py b/tests/test_000_ase_io.py index ac25eac8..39d25c2d 100644 --- a/tests/test_000_ase_io.py +++ b/tests/test_000_ase_io.py @@ -178,9 +178,31 @@ def fake_read_sparc(filename, *args, **kwargs): assert atoms.get_chemical_formula() == "Al" -def test_ase_io_filetype(): - """If hacked ase.io.formats.filetype correctly recognized sparc format""" +def test_ase_io_filetype(fs): + """If hacked ase.io.formats.filetype correctly recognized sparc format + + Due to the implementation of ase.io.formats, single file tests should be + done on non-empty files + """ import sparc from ase.io.formats import filetype + fs.create_dir("test.sparc") + fs.create_file("test.sparc/test.ion") + fs.create_file("test.sparc/test.static") + fs.create_file("test.sparc/test.geopt") + fs.create_file("test.sparc/test.aimd") + with open("test.sparc/test.ion", "w") as fd: + fd.write("\n") + with open("test.sparc/test.static", "w") as fd: + fd.write("\n") + with open("test.sparc/test.geopt", "w") as fd: + fd.write("\n") + with open("test.sparc/test.aimd", "w") as fd: + fd.write("\n") + assert filetype("test.sparc") == "sparc" + assert filetype("test.sparc/test.ion") == "ion" + assert filetype("test.sparc/test.static") == "static" + assert filetype("test.sparc/test.geopt") == "geopt" + assert filetype("test.sparc/test.aimd") == "aimd" From 2cb2bf906b62c0018c128012ae257a123c71efd4 Mon Sep 17 00:00:00 2001 From: "T.Tian" Date: Thu, 28 Sep 2023 12:06:52 +0800 Subject: [PATCH 3/3] pep8 --- sparc/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sparc/utils.py b/sparc/utils.py index e9de716d..71aa2905 100644 --- a/sparc/utils.py +++ b/sparc/utils.py @@ -3,6 +3,7 @@ import os import shutil import numpy as np +from warnings import warn from typing import Union, List, Optional