diff --git a/bcdi/__init__.py b/bcdi/__init__.py index 0a77e491c..7b9d50d11 100644 --- a/bcdi/__init__.py +++ b/bcdi/__init__.py @@ -6,4 +6,4 @@ # authors: # Jerome Carnis, carnis_jerome@yahoo.fr """The main bcdi package, which contains the whole framework.""" -__version__ = "0.2.5" +__version__ = "0.2.6" diff --git a/bcdi/graph/graph_utils.py b/bcdi/graph/graph_utils.py index 75a385863..02c928797 100644 --- a/bcdi/graph/graph_utils.py +++ b/bcdi/graph/graph_utils.py @@ -1413,7 +1413,7 @@ def linecut( num_points = int( np.sqrt(sum((val[1] - val[0] + 1) ** 2 for _, val in enumerate(indices))) ) - print("num_points", num_points) + cut = map_coordinates( input=array, coordinates=np.vstack( diff --git a/bcdi/utils/parameters.py b/bcdi/utils/parameters.py index b678cb82c..944647eff 100644 --- a/bcdi/utils/parameters.py +++ b/bcdi/utils/parameters.py @@ -48,7 +48,17 @@ def __init__(self, key, value, allowed): class ConfigChecker(ABC): - """Validate and configure parameters.""" + """ + Validate and configure parameters. + + :param initial_params: the dictionary of parameters to validate and configure + :param default_values: an optional dictionary of default values for keys in + initial_params + :param logger: an optional Logger + :param match_length_params: a tuple of keys from initial_params which should match + a certain length (e.g. the number of scans) + :param required_params: a tuple of keys that have to be present in initial_params + """ def __init__( self, @@ -286,9 +296,13 @@ def _configure_params(self) -> None: if self.initial_params["align_q"]: if self.initial_params["ref_axis_q"] not in {"x", "y", "z"}: raise ValueError("ref_axis_q should be either 'x', 'y' or 'z'") - self._checked_params[ - "comment" - ] += f"_align-q-{self.initial_params['ref_axis_q']}" + if ( + not self._checked_params["use_rawdata"] + and self._checked_params["interpolation_method"] == "linearization" + ): + self._checked_params[ + "comment" + ] += f"_align-q-{self.initial_params['ref_axis_q']}" if ( self.initial_params["backend"].lower() == "agg" @@ -506,7 +520,7 @@ def valid_param(key: str, value: Any) -> Tuple[Any, bool]: elif key == "config_file": valid.valid_container(value, container_types=str, min_length=1, name=key) if not os.path.isfile(value): - raise ValueError(f"The file {value} does not exist") + raise ValueError(f"The file '{value}' does not exist") elif key == "correlation_threshold": valid.valid_item( value, allowed_types=Real, min_included=0, max_included=1, name=key @@ -736,7 +750,7 @@ def valid_param(key: str, value: Any) -> Tuple[Any, bool]: value = (value,) valid.valid_container( value, - container_types=list, + container_types=(tuple, list), item_types=str, min_length=1, allow_none=True, @@ -745,7 +759,7 @@ def valid_param(key: str, value: Any) -> Tuple[Any, bool]: if value is not None: for val in value: if not os.path.isfile(val): - raise ValueError(f"The file {val} does not exist") + raise ValueError(f"The file '{val}' does not exist") elif key in {"ref_axis_q", "ref_axis"}: allowed = {"x", "y", "z"} if value not in allowed: diff --git a/bcdi/utils/parser.py b/bcdi/utils/parser.py index 23538c4ab..761824804 100644 --- a/bcdi/utils/parser.py +++ b/bcdi/utils/parser.py @@ -219,7 +219,7 @@ def file_path(self, value): if pathlib.Path(value).suffix != ".yml": raise ValueError("Expecting a YAML config file") if not os.path.isfile(value): - raise ValueError(f"The file {value} does not exist") + raise ValueError(f"The config file '{value}' does not exist") self._file_path = value @staticmethod diff --git a/doc/HISTORY.rst b/doc/HISTORY.rst index 54c0b67ad..304f4e12f 100644 --- a/doc/HISTORY.rst +++ b/doc/HISTORY.rst @@ -1,5 +1,5 @@ -Future: -------- +Version 0.2.6: +-------------- * Fix deprecation warning with the method `pandas.DataFrame.append` diff --git a/doc/modules/utils/index.rst b/doc/modules/utils/index.rst index 46614d886..bc18862f6 100644 --- a/doc/modules/utils/index.rst +++ b/doc/modules/utils/index.rst @@ -44,7 +44,7 @@ API Reference parameters ^^^^^^^^^^ -Function related to the validation of configuration parameters. +Classes and functions related to the validation of configuration parameters. API Reference ------------- diff --git a/tests/postprocessing/test_postprocessing_runner.py b/tests/postprocessing/test_postprocessing_runner.py new file mode 100644 index 000000000..64c4a3184 --- /dev/null +++ b/tests/postprocessing/test_postprocessing_runner.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# BCDI: tools for pre(post)-processing Bragg coherent X-ray diffraction imaging data +# (c) 07/2017-06/2019 : CNRS UMR 7344 IM2NP +# (c) 07/2019-05/2021 : DESY PHOTON SCIENCE +# authors: +# Jerome Carnis, carnis_jerome@yahoo.fr + +from functools import reduce +import h5py +import numpy as np +from pathlib import Path +import tempfile +from typing import Dict, Optional +import unittest +import yaml + +from bcdi.postprocessing.postprocessing_runner import run +from bcdi.utils.parser import ConfigParser +from tests.config import run_tests + +here = Path(__file__).parent +THIS_DIR = str(here) +CONFIG = str(here.parents[1] / "bcdi/examples/S11_config_postprocessing.yml") + + +class TestRun(unittest.TestCase): + """Large test for the postprocessing script bcdi_strain.py.""" + + def setUp(self) -> None: + self.args: Optional[Dict] = None + self.command_line_args = { + "backend": "Agg", + "reconstruction_files": str( + here.parents[1] + / "bcdi/examples/S11_modes_252_420_392_prebinning_1_1_1.h5" + ), + } + self.parser = ConfigParser(CONFIG, self.command_line_args) + if not Path( + yaml.load(self.parser.raw_config, Loader=yaml.SafeLoader)["root_folder"] + ).is_dir(): + self.skipTest( + reason="This test can only run locally with the example dataset" + ) + + def test_run(self): + expected_q_com = [0, 2.77555, 0] + expected_volume = 23217408 + with tempfile.TemporaryDirectory() as tmpdir: + self.args = self.parser.load_arguments() + self.args["save_dir"] = (tmpdir,) + run(self.args) + + with h5py.File( + f"{tmpdir}/S11_ampdispstrain_mode_crystalframe.h5", + "r", + ) as h5file: + amp = h5file["output/amp"][()] + voxel_sizes = h5file["output/voxel_sizes"][()] + q_com = h5file["output/q_com"][()] + amp = amp / amp.max() + amp[amp < self.args["isosurface_strain"]] = 0 + amp[np.nonzero(amp)] = 1 + volume = amp.sum() * reduce(lambda x, y: x * y, voxel_sizes) + + self.assertEqual(volume, expected_volume) + self.assertTrue(np.allclose(q_com, expected_q_com)) + + +if __name__ == "__main__": + run_tests(TestRun) diff --git a/tests/postprocessing/test_postprocessing_utils.py b/tests/postprocessing/test_postprocessing_utils.py deleted file mode 100644 index 4167d2a6e..000000000 --- a/tests/postprocessing/test_postprocessing_utils.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- - -# BCDI: tools for pre(post)-processing Bragg coherent X-ray diffraction imaging data -# (c) 07/2017-06/2019 : CNRS UMR 7344 IM2NP -# (c) 07/2019-05/2021 : DESY PHOTON SCIENCE -# authors: -# Jerome Carnis, carnis_jerome@yahoo.fr - -import unittest -import bcdi.postprocessing.postprocessing_utils as pu -from tests.config import run_tests - - -class Test(unittest.TestCase): - """Tests.""" - - def test_dummy(self): - self.assertTrue(True) - - -if __name__ == "__main__": - run_tests(Test) diff --git a/tests/preprocessing/test_preprocessing_runner.py b/tests/preprocessing/test_preprocessing_runner.py new file mode 100644 index 000000000..b9377de7c --- /dev/null +++ b/tests/preprocessing/test_preprocessing_runner.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# BCDI: tools for pre(post)-processing Bragg coherent X-ray diffraction imaging data +# (c) 07/2017-06/2019 : CNRS UMR 7344 IM2NP +# (c) 07/2019-05/2021 : DESY PHOTON SCIENCE +# authors: +# Jerome Carnis, carnis_jerome@yahoo.fr + +from functools import reduce +import h5py +import numpy as np +from pathlib import Path +import tempfile +from typing import Dict, Optional +import unittest +import yaml + +from bcdi.preprocessing.preprocessing_runner import run +from bcdi.utils.parser import ConfigParser +from tests.config import run_tests + +here = Path(__file__).parent +THIS_DIR = str(here) +CONFIG = str(here.parents[1] / "bcdi/examples/S11_config_preprocessing.yml") + + +class TestRun(unittest.TestCase): + """Large test for the preprocessing script bcdi_preprocessing_BCDI.py.""" + + def setUp(self) -> None: + self.args: Optional[Dict] = None + self.command_line_args = { + "backend": "Agg", + "flag_interact": False, + } + self.parser = ConfigParser(CONFIG, self.command_line_args) + if not Path( + yaml.load(self.parser.raw_config, Loader=yaml.SafeLoader)["root_folder"] + ).is_dir(): + self.skipTest( + reason="This test can only run locally with the example dataset" + ) + + def test_run(self): + expected_bragg_inplane = 0.4926488901267543 + expected_bragg_outofplane = 35.36269069963432 + expected_bragg_peak = [127, 214, 316] + expected_q = [-0.84164063, 2.63974482, -0.03198209] + with tempfile.TemporaryDirectory() as tmpdir: + self.args = self.parser.load_arguments() + self.args["save_dir"] = (tmpdir,) + run(self.args) + + with h5py.File( + f"{tmpdir}/S11_preprocessing_norm_256_256_256_1_2_2.h5", + "r", + ) as h5file: + bragg_inplane = h5file["output/bragg_inplane"][()] + bragg_outofplane = h5file["output/bragg_outofplane"][()] + bragg_peak = h5file["output/bragg_peak"][()] + q = h5file["output/q"][()] + + self.assertAlmostEqual(bragg_inplane, expected_bragg_inplane) + self.assertAlmostEqual(bragg_outofplane, expected_bragg_outofplane) + self.assertTrue( + val1 == val2 for val1, val2 in zip(bragg_peak, expected_bragg_peak) + ) + self.assertTrue(np.allclose(q, expected_q)) + + +if __name__ == "__main__": + run_tests(TestRun) diff --git a/tests/utils/test_parameters.py b/tests/utils/test_parameters.py index b8fc46a1a..a33c2f157 100644 --- a/tests/utils/test_parameters.py +++ b/tests/utils/test_parameters.py @@ -294,6 +294,8 @@ def test_check_config_interpolate_reload_orthogonal(self): def test_check_config_align_q(self): self.checker.initial_params["align_q"] = True + self.checker._checked_params["use_rawdata"] = False + self.checker._checked_params["interpolation_method"] = "linearization" out = self.checker.check_config() self.assertEqual( out["comment"], diff --git a/tests/utils/test_parser.py b/tests/utils/test_parser.py index c5b185df5..8767a11aa 100644 --- a/tests/utils/test_parser.py +++ b/tests/utils/test_parser.py @@ -16,7 +16,7 @@ CONFIG = str(here.parents[1] / "bcdi/examples/S11_config_postprocessing.yml") -class Test_str_to_list(unittest.TestCase): +class TestStrToList(unittest.TestCase): """ Tests on the function str_to_list. @@ -146,4 +146,4 @@ def test_repr(self): if __name__ == "__main__": run_tests(TestConfigParser) - run_tests(Test_str_to_list) + run_tests(TestStrToList)