diff --git a/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON.json.md5 b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON.json.md5 new file mode 100644 index 000000000000..d91952380119 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON.json.md5 @@ -0,0 +1 @@ +3cd81ba34aa7513d3230a7089e2772ba diff --git a/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_atomic_displacements_data_0.txt.md5 b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_atomic_displacements_data_0.txt.md5 new file mode 100644 index 000000000000..343d9e7bd022 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_atomic_displacements_data_0.txt.md5 @@ -0,0 +1 @@ +f32b26a2342571f1f6bc348a4d3dd2cc diff --git a/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_atomic_displacements_data_1.txt.md5 b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_atomic_displacements_data_1.txt.md5 new file mode 100644 index 000000000000..343d9e7bd022 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_atomic_displacements_data_1.txt.md5 @@ -0,0 +1 @@ +f32b26a2342571f1f6bc348a4d3dd2cc diff --git a/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_data.txt.md5 b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_data.txt.md5 new file mode 100644 index 000000000000..305b3ad700c0 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_abinsdata_LoadJSON_data.txt.md5 @@ -0,0 +1 @@ +1339c62d0bd3081ca95d0d1512236ba8 diff --git a/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON.json.md5 b/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON.json.md5 new file mode 100644 index 000000000000..6129dbe588df --- /dev/null +++ b/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON.json.md5 @@ -0,0 +1 @@ +e188ddc55518578b269fdfb4a03c6687 diff --git a/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON_atomic_displacements_data_0.txt.md5 b/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON_atomic_displacements_data_0.txt.md5 new file mode 100644 index 000000000000..029bf0095ae7 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON_atomic_displacements_data_0.txt.md5 @@ -0,0 +1 @@ +265f00377a073b32897f86d2095a76aa diff --git a/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON_data.txt.md5 b/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON_data.txt.md5 new file mode 100644 index 000000000000..155a61d9d9fa --- /dev/null +++ b/Testing/Data/UnitTest/NH3_euphonic_fc_LoadJSON_data.txt.md5 @@ -0,0 +1 @@ +881d2ba21adb714d1d85aca8d24ae44c diff --git a/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON.json.md5 b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON.json.md5 new file mode 100644 index 000000000000..7d575f89bdaa --- /dev/null +++ b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON.json.md5 @@ -0,0 +1 @@ +811084a43f90e92e2a23999fdf072425 diff --git a/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_atomic_displacements_data_0.txt.md5 b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_atomic_displacements_data_0.txt.md5 new file mode 100644 index 000000000000..343d9e7bd022 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_atomic_displacements_data_0.txt.md5 @@ -0,0 +1 @@ +f32b26a2342571f1f6bc348a4d3dd2cc diff --git a/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_atomic_displacements_data_1.txt.md5 b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_atomic_displacements_data_1.txt.md5 new file mode 100644 index 000000000000..343d9e7bd022 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_atomic_displacements_data_1.txt.md5 @@ -0,0 +1 @@ +f32b26a2342571f1f6bc348a4d3dd2cc diff --git a/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_data.txt.md5 b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_data.txt.md5 new file mode 100644 index 000000000000..fee22c8e44b9 --- /dev/null +++ b/Testing/Data/UnitTest/NH3_euphonic_modes_LoadJSON_data.txt.md5 @@ -0,0 +1 @@ +42f00fe94801f14612a02a2da5167527 diff --git a/docs/source/release/v6.10.0/Framework/Algorithms/New_features/36849.rst b/docs/source/release/v6.10.0/Framework/Algorithms/New_features/36849.rst new file mode 100644 index 000000000000..43590a33bbca --- /dev/null +++ b/docs/source/release/v6.10.0/Framework/Algorithms/New_features/36849.rst @@ -0,0 +1,12 @@ +- Abins and Abins2D now support JSON file import. Supported formats are: + + - AbinsData (dump of internal object, intended for development and testing) + - euphonic.QpointPhononModes (an equivalent set of data dumped from + the Euphonic library) + - euphonic.ForceConstants (force constants which may be manipulated + in Euphonic, and will be converted to phonon modes on a q-point + mesh when Abins(2D) is run) + + The Euphonic JSON formats are convenient to create with Python + scripts, and recommended for users who wish to somehow customise or + manipulate their data before using it with Abins(2D). diff --git a/scripts/abins/abinsalgorithm.py b/scripts/abins/abinsalgorithm.py index e32279b4e24c..e0ee8afdbc41 100644 --- a/scripts/abins/abinsalgorithm.py +++ b/scripts/abins/abinsalgorithm.py @@ -25,6 +25,7 @@ import abins from abins.constants import AB_INITIO_FILE_EXTENSIONS, ALL_INSTRUMENTS, ATOM_PREFIX +from abins.input.jsonloader import abins_supported_json_formats, JSONLoader from abins.instruments import get_instrument, Instrument @@ -98,7 +99,7 @@ def declare_common_properties(self) -> None: name="AbInitioProgram", direction=Direction.Input, defaultValue="CASTEP", - validator=StringListValidator(["CASTEP", "CRYSTAL", "DMOL3", "FORCECONSTANTS", "GAUSSIAN", "VASP"]), + validator=StringListValidator(["CASTEP", "CRYSTAL", "DMOL3", "FORCECONSTANTS", "GAUSSIAN", "JSON", "VASP"]), doc="An ab initio program which was used for vibrational or phonon calculation.", ) @@ -223,6 +224,7 @@ def validate_common_inputs(self, issues: dict = None) -> Dict[str, str]: "DMOL3": self._validate_dmol3_input_file, "FORCECONSTANTS": self._validate_euphonic_input_file, "GAUSSIAN": self._validate_gaussian_input_file, + "JSON": self._validate_json_input_file, "VASP": self._validate_vasp_input_file, } ab_initio_program = self.getProperty("AbInitioProgram").value @@ -806,6 +808,22 @@ def _validate_euphonic_input_file(cls, filename_full_path: str) -> dict: # Did not return already: No problems found return dict(Invalid=False, Comment="") + @classmethod + def _validate_json_input_file(cls, filename_full_path: str) -> dict: + logger.information("Validate JSON file with vibrational or phonon data.") + output = cls._validate_ab_initio_file_extension( + ab_initio_program="JSON", filename_full_path=filename_full_path, expected_file_extension=".json" + ) + if output["Invalid"]: + output["Comment"] = ".json extension is expected for a JSON file" + return output + + json_format = JSONLoader.check_json_format(filename_full_path) + if json_format in abins_supported_json_formats: + return dict(Invalid=False, Comment=f"Found JSON file format: {json_format.name}") + + return dict(Invalid=True, Comment=f"Found unsupported JSON file format: {json_format.name}") + @classmethod def _validate_vasp_input_file(cls, filename_full_path: str) -> dict: logger.information("Validate VASP file with vibrational or phonon data.") diff --git a/scripts/abins/constants.py b/scripts/abins/constants.py index 05c5a5187117..19214ec4155e 100644 --- a/scripts/abins/constants.py +++ b/scripts/abins/constants.py @@ -262,7 +262,7 @@ MAX_ORDER = 4 # max quantum order event ALL_SUPPORTED_AB_INITIO_PROGRAMS = ["CRYSTAL", "CASTEP", "DMOL3", "GAUSSIAN", "VASP"] -AB_INITIO_FILE_EXTENSIONS = ["phonon", "out", "outmol", "log", "LOG", "xml", "yaml", "castep_bin", "hdf5"] +AB_INITIO_FILE_EXTENSIONS = ["phonon", "out", "outmol", "log", "LOG", "xml", "yaml", "castep_bin", "hdf5", "json"] ONE_DIMENSIONAL_INSTRUMENTS = ["TOSCA", "Lagrange"] TWO_DIMENSIONAL_CHOPPER_INSTRUMENTS = ["MAPS", "MARI", "MERLIN"] diff --git a/scripts/abins/input/__init__.py b/scripts/abins/input/__init__.py index 1739c8a4e0aa..403dc1f0a4f1 100644 --- a/scripts/abins/input/__init__.py +++ b/scripts/abins/input/__init__.py @@ -8,6 +8,7 @@ from .dmol3loader import DMOL3Loader from .euphonicloader import EuphonicLoader from .gaussianloader import GAUSSIANLoader +from .jsonloader import JSONLoader from .vasploader import VASPLoader from .tester import Tester @@ -17,6 +18,7 @@ "CRYSTAL": CRYSTALLoader, "DMOL3": DMOL3Loader, "GAUSSIAN": GAUSSIANLoader, + "JSON": JSONLoader, "VASP": VASPLoader, "FORCECONSTANTS": EuphonicLoader, } diff --git a/scripts/abins/input/abinitioloader.py b/scripts/abins/input/abinitioloader.py index 3300ab422ee5..fab36db21270 100644 --- a/scripts/abins/input/abinitioloader.py +++ b/scripts/abins/input/abinitioloader.py @@ -6,6 +6,7 @@ # SPDX - License - Identifier: GPL - 3.0 + from abc import ABCMeta, abstractmethod +from pathlib import Path from typing import Sequence from mantid.kernel import logger @@ -29,11 +30,20 @@ class AbInitioLoader(metaclass=NamedAbstractClass): read_formatted_data() if necessary and caching the results. """ - def __init__(self, input_ab_initio_filename=None): - self._sample_form = None - self._ab_initio_program = None + def __init__(self, input_ab_initio_filename: str = None): + """An object for loading phonon data from ab initio output files""" + + if not isinstance(input_ab_initio_filename, str): + raise TypeError("Filename must be a string") + elif not Path(input_ab_initio_filename).is_file(): + raise IOError(f"Ab initio file {input_ab_initio_filename} not found.") + self._clerk = abins.IO(input_filename=input_ab_initio_filename, group_name=abins.parameters.hdf_groups["ab_initio_data"]) + @property + @abstractmethod + def _ab_initio_program(self) -> str: ... + @abstractmethod def read_vibrational_or_phonon_data(self) -> abins.AbinsData: """ diff --git a/scripts/abins/input/casteploader.py b/scripts/abins/input/casteploader.py index d53a8bec33a4..4d286ac423a5 100644 --- a/scripts/abins/input/casteploader.py +++ b/scripts/abins/input/casteploader.py @@ -17,13 +17,9 @@ class CASTEPLoader(AbInitioLoader): Functions to read phonon file taken from SimulatedDensityOfStates (credits for Elliot Oram.). """ - def __init__(self, input_ab_initio_filename): - """ - - :param input_ab_initio_filename: name of file with phonon data (foo.phonon) - """ - super().__init__(input_ab_initio_filename=input_ab_initio_filename) - self._ab_initio_program = "CASTEP" + @property + def _ab_initio_program(self) -> str: + return "CASTEP" def read_vibrational_or_phonon_data(self) -> AbinsData: """ diff --git a/scripts/abins/input/crystalloader.py b/scripts/abins/input/crystalloader.py index a4abcaa8bdfe..c340379c8b02 100644 --- a/scripts/abins/input/crystalloader.py +++ b/scripts/abins/input/crystalloader.py @@ -35,7 +35,9 @@ def __init__(self, input_ab_initio_filename=None): self._inv_expansion_matrix = np.eye(3, dtype=FLOAT_TYPE) self._parser = TextParser() - self._ab_initio_program = "CRYSTAL" + @property + def _ab_initio_program(self) -> str: + return "CRYSTAL" def read_vibrational_or_phonon_data(self): """ diff --git a/scripts/abins/input/dmol3loader.py b/scripts/abins/input/dmol3loader.py index 9a37104c246a..a83515db2ad8 100644 --- a/scripts/abins/input/dmol3loader.py +++ b/scripts/abins/input/dmol3loader.py @@ -42,9 +42,12 @@ def __init__(self, input_ab_initio_filename): :param input_ab_initio_filename: name of file with vibrational data (foo.outmol) """ super().__init__(input_ab_initio_filename=input_ab_initio_filename) - self._ab_initio_program = "DMOL3" self._norm = 0 + @property + def _ab_initio_program(self) -> str: + return "DMOL3" + def read_vibrational_or_phonon_data(self): """ Reads vibrational data from DMOL3 output files. Saves frequencies, weights of k-point vectors, diff --git a/scripts/abins/input/euphonicloader.py b/scripts/abins/input/euphonicloader.py index c0886191f40b..a5d26f7390d3 100644 --- a/scripts/abins/input/euphonicloader.py +++ b/scripts/abins/input/euphonicloader.py @@ -5,7 +5,6 @@ # Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS # SPDX - License - Identifier: GPL - 3.0 + -from pathlib import Path from typing import Any, Dict from euphonic import QpointPhononModes @@ -19,18 +18,9 @@ class EuphonicLoader(AbInitioLoader): """Get frequencies/eigenvalues from force constants using Euphonic""" - def __init__(self, input_ab_initio_filename): - """ - - :param input_ab_initio_filename: name of file with phonon data (foo.phonon) - """ - if not isinstance(input_ab_initio_filename, str): - raise TypeError("Filename must be a string") - elif not Path(input_ab_initio_filename).is_file(): - raise IOError(f"Ab initio file {input_ab_initio_filename} not found.") - - super().__init__(input_ab_initio_filename=input_ab_initio_filename) - self._ab_initio_program = "FORCECONSTANTS" + @property + def _ab_initio_program(self) -> str: + return "FORCECONSTANTS" @staticmethod def data_dict_from_modes(modes: QpointPhononModes) -> Dict[str, Any]: @@ -72,10 +62,6 @@ def read_vibrational_or_phonon_data(self): cutoff = sampling_parameters["force_constants"]["qpt_cutoff"] modes = euphonic_calculate_modes(filename=self._clerk.get_input_filename(), cutoff=cutoff) file_data = self.data_dict_from_modes(modes) - - # save stuff to hdf file - save_keys = ["frequencies", "weights", "k_vectors", "atomic_displacements", "unit_cell", "atoms"] - data_to_save = {key: file_data[key] for key in save_keys} - self.save_ab_initio_data(data=data_to_save) + self.save_ab_initio_data(data=file_data) return self._rearrange_data(data=file_data) diff --git a/scripts/abins/input/gaussianloader.py b/scripts/abins/input/gaussianloader.py index 1d0abe084cb1..cf969a634e62 100644 --- a/scripts/abins/input/gaussianloader.py +++ b/scripts/abins/input/gaussianloader.py @@ -14,7 +14,6 @@ from .abinitioloader import AbInitioLoader from .textparser import TextParser from abins.constants import COMPLEX_TYPE, FLOAT_TYPE, ROTATIONS_AND_TRANSLATIONS -from abins.abinsdata import AbinsData from mantid.kernel import Atom @@ -28,12 +27,15 @@ def __init__(self, input_ab_initio_filename) -> None: :param input_ab_initio_filename: name of file with vibrational data (foo.log or foo.LOG) """ super().__init__(input_ab_initio_filename=input_ab_initio_filename) - self._ab_initio_program = "GAUSSIAN" self._active_atoms = None self._num_atoms = None self._num_read_freq = 0 - def read_vibrational_or_phonon_data(self) -> AbinsData: + @property + def _ab_initio_program(self) -> str: + return "GAUSSIAN" + + def read_vibrational_or_phonon_data(self): """ Reads vibrational data from GAUSSIAN output files. Saves frequencies and atomic displacements (only molecular calculations), hash of file with vibrational data to <>.hdf5. diff --git a/scripts/abins/input/jsonloader.py b/scripts/abins/input/jsonloader.py new file mode 100644 index 000000000000..9cf28ee2cbce --- /dev/null +++ b/scripts/abins/input/jsonloader.py @@ -0,0 +1,145 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source, +# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +# SPDX - License - Identifier: GPL - 3.0 + + +from enum import auto, Enum +import json +from pathlib import Path +from typing import Dict + +from euphonic import QpointPhononModes +import numpy as np + +from .abinitioloader import AbInitioLoader +from .euphonicloader import EuphonicLoader +from abins.abinsdata import AbinsData +from abins.constants import COMPLEX_TYPE, FLOAT_TYPE +from abins.parameters import sampling as sampling_parameters +from dos.load_euphonic import euphonic_calculate_modes + +# json-stream implementation converts data lazily so we can quickly check the +# top-level keys even if a data file is huge. +try: + from json_stream import load as json_load +except ImportError: + from json import load as json_load + + +class PhononJSON(Enum): + EUPHONIC_FREQUENCIES = auto() + EUPHONIC_MODES = auto() + EUPHONIC_FORCE_CONSTANTS = auto() + ABINS_DATA = auto() + UNKNOWN = auto() + + +abins_supported_json_formats = {PhononJSON.EUPHONIC_MODES, PhononJSON.EUPHONIC_FORCE_CONSTANTS, PhononJSON.ABINS_DATA} + + +class JSONLoader(AbInitioLoader): + """Get frequencies/eigenvalues from a JSON file using Euphonic""" + + @property + def _ab_initio_program(self) -> str: + return "JSON" + + @staticmethod + def check_json_format(json_file: str | Path) -> PhononJSON: + """Check if JSON file is a known phonon data format""" + + for class_key in "__euphonic_class__", "__abins_class__": + with open(json_file, "r") as fd: + data = json_load(fd) + data_class = data.get(class_key) + + if data_class is not None: + break + else: + class_key = "" + + match class_key, data_class: + case ("__euphonic_class__", "QpointPhononModes"): + return PhononJSON.EUPHONIC_MODES + case ("__euphonic_class__", "QpointFrequencies"): + return PhononJSON.EUPHONIC_FREQUENCIES + case ("__euphonic_class__", "ForceConstants"): + return PhononJSON.EUPHONIC_FORCE_CONSTANTS + case ("__abins_class__", "AbinsData"): + return PhononJSON.ABINS_DATA + case _: + return PhononJSON.UNKNOWN + + @staticmethod + def array_from_dict(data: Dict[str | int, np.ndarray], complex=False) -> np.ndarray: + """Convert from dict of n-d arrays to (n+1-d) array + + AbinsData uses these dicts so that frequency data rows can have + different lengths after imaginary modes are removed. e.g. + + {"0": np.array([1, 2, 3, 4]), "1": np.array([11, 12, 13, 14, 15, 16])} + + + This method is not intended to handle such ragged arrays gracefully: it + is for serialising the data before anything is removed. + + """ + row_shape = data[next(iter(data))].shape + + new_array = np.empty([len(data)] + list(row_shape), dtype=(COMPLEX_TYPE if complex else FLOAT_TYPE)) + for i in range(len(new_array)): + new_array[i] = data[str(i)] + + return new_array + + def save_from_abins_data(self, abins_data: AbinsData) -> None: + """Save data to hdf5 cache from AbinsData format + + Usually we construct a data dict for the cache and then use it to + construct AbinsData. Sometimes it makes sense to do it the other way + around, so this method provides the reverse operation. + """ + data = abins_data.get_kpoints_data().extract() + data["atoms"] = abins_data.get_atoms_data().extract() + for key in ("weights", "k_vectors", "frequencies"): + data[key] = self.array_from_dict(data[key]) + data["atomic_displacements"] = self.array_from_dict(data["atomic_displacements"], complex=True) + + self.save_ab_initio_data(data=data) + + def read_vibrational_or_phonon_data(self) -> AbinsData: + """Get AbinsData (structure and modes) from force constants data. + + Frequencies/displacements are interpolated using the Euphonic library + over a regular q-point mesh. The mesh is determined by a Moreno-Soler + realspace cutoff, related to the size of an equivalent + supercell. Meshes are rounded up so a very small cutoff will yield + gamma-point-only sampling. + + """ + json_file = self._clerk.get_input_filename() + json_format = self.check_json_format(json_file) + + match json_format: + case PhononJSON.ABINS_DATA: + with open(json_file, "r") as fd: + data = json.load(fd) + abins_data = AbinsData.from_dict(data) + self.save_from_abins_data(abins_data) + return abins_data + + case PhononJSON.EUPHONIC_MODES: + modes = QpointPhononModes.from_json_file(json_file) + + case PhononJSON.EUPHONIC_FORCE_CONSTANTS: + cutoff = sampling_parameters["force_constants"]["qpt_cutoff"] + modes = euphonic_calculate_modes(filename=json_file, cutoff=cutoff) + + case _: + raise ValueError(f"Cannot use JSON data of type {json_format.name}") + + file_data = EuphonicLoader.data_dict_from_modes(modes) + self.save_ab_initio_data(data=file_data) + return self._rearrange_data(data=file_data) diff --git a/scripts/abins/input/tester.py b/scripts/abins/input/tester.py index 76987a8a0561..e034f30e5900 100644 --- a/scripts/abins/input/tester.py +++ b/scripts/abins/input/tester.py @@ -26,6 +26,7 @@ class Tester(object): "DMOL3Loader": "outmol", "EuphonicLoader": "castep_bin", "GAUSSIANLoader": "log", + "JSONLoader": "json", "VASPLoader": "xml", "VASPOUTCARLoader": "OUTCAR", } diff --git a/scripts/abins/input/vasploader.py b/scripts/abins/input/vasploader.py index 482a2d907afe..0fde5134fd58 100644 --- a/scripts/abins/input/vasploader.py +++ b/scripts/abins/input/vasploader.py @@ -33,13 +33,9 @@ class VASPLoader(AbInitioLoader): """ - def __init__(self, input_ab_initio_filename: str = None) -> None: - """ - - :param input_ab_initio_filename: name of file with phonon data (foo.phonon) - """ - super().__init__(input_ab_initio_filename=input_ab_initio_filename) - self._ab_initio_program = "VASP" + @property + def _ab_initio_program(self) -> str: + return "VASP" def read_vibrational_or_phonon_data(self, logger: Logger = None) -> AbinsData: input_filename = self._clerk.get_input_filename() diff --git a/scripts/test/Abins/AbinsLoadCASTEPTest.py b/scripts/test/Abins/AbinsLoadCASTEPTest.py index b2d6c80045b6..861cf03e818d 100644 --- a/scripts/test/Abins/AbinsLoadCASTEPTest.py +++ b/scripts/test/Abins/AbinsLoadCASTEPTest.py @@ -40,7 +40,7 @@ def test_non_existing_file(self): bad_castep_reader = CASTEPLoader(input_ab_initio_filename="NonExistingFile.txt") bad_castep_reader.read_vibrational_or_phonon_data() - with self.assertRaises(ValueError): + with self.assertRaises(TypeError): # noinspection PyUnusedLocal CASTEPLoader(input_ab_initio_filename=1) diff --git a/scripts/test/Abins/AbinsLoadJSONTest.py b/scripts/test/Abins/AbinsLoadJSONTest.py new file mode 100644 index 000000000000..6ee6b519e3d5 --- /dev/null +++ b/scripts/test/Abins/AbinsLoadJSONTest.py @@ -0,0 +1,37 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source, +# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +# SPDX - License - Identifier: GPL - 3.0 + +import unittest + +import abins.input +import abins.test_helpers + + +class AbinsLoadJSONTest(unittest.TestCase, abins.input.Tester): + def setUp(self): + # A small cutoff is used to limit number of data files + self.default_cutoff = abins.parameters.sampling["force_constants"]["qpt_cutoff"] + abins.parameters.sampling["force_constants"]["qpt_cutoff"] = 4.0 + + def tearDown(self): + abins.test_helpers.remove_output_files(list_of_names=["_LoadJSON"]) + abins.parameters.sampling["force_constants"]["qpt_cutoff"] = self.default_cutoff + + def test_json_1(self): + """Load from Euphonic QpointPhononModes dump""" + self.check(name="NH3_euphonic_modes_LoadJSON", loader=abins.input.JSONLoader) + + def test_json_2(self): + """Load from Euphonic ForceConstants dump""" + self.check(name="NH3_euphonic_fc_LoadJSON", loader=abins.input.JSONLoader, max_displacement_kpt=-1) + + def test_json_3(self): + """Load from AbinsData dump""" + self.check(name="NH3_abinsdata_LoadJSON", loader=abins.input.JSONLoader) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test/Abins/AbinsLoadVASPTest.py b/scripts/test/Abins/AbinsLoadVASPTest.py index 89a7013aa3bd..839bb850eaf2 100644 --- a/scripts/test/Abins/AbinsLoadVASPTest.py +++ b/scripts/test/Abins/AbinsLoadVASPTest.py @@ -20,7 +20,7 @@ def test_non_existing_file(self): bad_vasp_reader = VASPLoader(input_ab_initio_filename="NonExistingFile.txt") bad_vasp_reader.read_vibrational_or_phonon_data() - with self.assertRaises(ValueError): + with self.assertRaises(TypeError): _ = VASPLoader(input_ab_initio_filename=1) # Not a real vibration calc; check the appropriate error is raised diff --git a/scripts/test/Abins/CMakeLists.txt b/scripts/test/Abins/CMakeLists.txt index 8cb5909e7836..28d4d325f905 100644 --- a/scripts/test/Abins/CMakeLists.txt +++ b/scripts/test/Abins/CMakeLists.txt @@ -15,6 +15,7 @@ set(TEST_PY_FILES AbinsLoadDMOL3Test.py AbinsLoadDMOL3InternalsTest.py AbinsLoadGAUSSIANTest.py + AbinsLoadJSONTest.py AbinsLoadPhonopyTest.py AbinsLoadVASPTest.py AbinsPowderDataTest.py