From 52bb4aeb5265b30563731d0a8f61d93f90ed2720 Mon Sep 17 00:00:00 2001 From: Jerome Kieffer Date: Mon, 27 Nov 2023 18:03:03 +0100 Subject: [PATCH] remove old files --- freesas/app/__init__.py | 29 - freesas/app/auto_gpa.py | 69 --- freesas/app/auto_guinier.py | 69 --- freesas/app/autorg.py | 69 --- freesas/app/bift.py | 147 ----- freesas/app/cormap.py | 94 --- freesas/app/extract_ascii.py | 341 ----------- freesas/app/plot_sas.py | 144 ----- freesas/app/supycomb.py | 137 ----- freesas/test/__init__.py | 29 - freesas/test/mock_open_38.py | 98 --- freesas/test/test_align.py | 152 ----- freesas/test/test_all.py | 38 -- freesas/test/test_autorg.py | 448 -------------- freesas/test/test_average.py | 89 --- freesas/test/test_bift.py | 132 ---- freesas/test/test_cormap.py | 88 --- freesas/test/test_distance.py | 62 -- freesas/test/test_fitting.py | 938 ----------------------------- freesas/test/test_model.py | 182 ------ freesas/test/test_sas_argparser.py | 603 ------------------- freesas/test/test_sasio.py | 192 ------ freesas/test/utilstests.py | 24 - 23 files changed, 4174 deletions(-) delete mode 100644 freesas/app/__init__.py delete mode 100644 freesas/app/auto_gpa.py delete mode 100644 freesas/app/auto_guinier.py delete mode 100644 freesas/app/autorg.py delete mode 100644 freesas/app/bift.py delete mode 100644 freesas/app/cormap.py delete mode 100644 freesas/app/extract_ascii.py delete mode 100644 freesas/app/plot_sas.py delete mode 100644 freesas/app/supycomb.py delete mode 100644 freesas/test/__init__.py delete mode 100644 freesas/test/mock_open_38.py delete mode 100644 freesas/test/test_align.py delete mode 100644 freesas/test/test_all.py delete mode 100644 freesas/test/test_autorg.py delete mode 100644 freesas/test/test_average.py delete mode 100644 freesas/test/test_bift.py delete mode 100644 freesas/test/test_cormap.py delete mode 100644 freesas/test/test_distance.py delete mode 100644 freesas/test/test_fitting.py delete mode 100644 freesas/test/test_model.py delete mode 100644 freesas/test/test_sas_argparser.py delete mode 100644 freesas/test/test_sasio.py delete mode 100644 freesas/test/utilstests.py diff --git a/freesas/app/__init__.py b/freesas/app/__init__.py deleted file mode 100644 index 4776656..0000000 --- a/freesas/app/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2018 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -"""Command line applications provided by the freesas launcher.""" - -__authors__ = ["Jérôme Kieffer", "Martha Brennich"] -__license__ = "MIT" -__date__ = "2021/03/24" diff --git a/freesas/app/auto_gpa.py b/freesas/app/auto_gpa.py deleted file mode 100644 index fe02a90..0000000 --- a/freesas/app/auto_gpa.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2020 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__author__ = ["Jérôme Kieffer", "Martha Brennich"] -__license__ = "MIT" -__copyright__ = "2021, ESRF" -__date__ = "19/03/2021" - -import sys -import logging -from freesas.autorg import auto_gpa -from freesas.sas_argparser import GuinierParser -from freesas.fitting import run_guinier_fit - -logging.basicConfig(level=logging.WARNING) -logger = logging.getLogger("auto_gpa") - -if sys.version_info < (3, 6): - logger.error("This code uses F-strings and requires Python 3.6+") - - -def build_parser() -> GuinierParser: - """Build parser for input and return list of files. - :return: parser - """ - description = ( - "Calculate the radius of gyration using Guinier" - " Peak Analysis (Putnam 2016) for a set of scattering curves" - ) - epilog = """free_gpa is an open-source implementation of - the autorg algorithm originately part of the ATSAS suite. - As this tool used a different theory, some results may differ - """ - return GuinierParser( - prog="free_gpa", description=description, epilog=epilog - ) - - -def main() -> None: - """Entry point for free_gpa app""" - parser = build_parser() - run_guinier_fit(fit_function=auto_gpa, parser=parser, logger=logger) - - -if __name__ == "__main__": - main() diff --git a/freesas/app/auto_guinier.py b/freesas/app/auto_guinier.py deleted file mode 100644 index 8f918cc..0000000 --- a/freesas/app/auto_guinier.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2020 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__author__ = ["Jérôme Kieffer", "Martha Brennich"] -__license__ = "MIT" -__copyright__ = "2021, ESRF" -__date__ = "19/03/2021" - -import sys -import logging -from freesas.autorg import auto_guinier -from freesas.sas_argparser import GuinierParser -from freesas.fitting import run_guinier_fit - -logging.basicConfig(level=logging.WARNING) -logger = logging.getLogger("auto_guinier") - -if sys.version_info < (3, 6): - logger.error("This code uses F-strings and requires Python 3.6+") - - -def build_parser() -> GuinierParser: - """Build parser for input and return list of files. - :return: parser - """ - description = ( - "Calculate the radius of gyration using linear fitting of" - "logarithmic intensities for a set of scattering curves" - ) - epilog = """free_guinier is an open-source implementation of - the autorg algorithm originately part of the ATSAS suite. - As this tool used a different theory, some results may differ - """ - return GuinierParser( - prog="free_guinier", description=description, epilog=epilog - ) - - -def main() -> None: - """Entry point for free_guinier app""" - parser = build_parser() - run_guinier_fit(fit_function=auto_guinier, parser=parser, logger=logger) - - -if __name__ == "__main__": - main() diff --git a/freesas/app/autorg.py b/freesas/app/autorg.py deleted file mode 100644 index 2e9ef0c..0000000 --- a/freesas/app/autorg.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2017-2020 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__author__ = ["Jérôme Kieffer", "Martha Brennich"] -__license__ = "MIT" -__copyright__ = "2021, ESRF" -__date__ = "19/03/2021" - -import sys -import logging -from freesas.autorg import autoRg -from freesas.sas_argparser import GuinierParser -from freesas.fitting import run_guinier_fit - -logging.basicConfig(level=logging.WARNING) -logger = logging.getLogger("auto_gpa") - -if sys.version_info < (3, 6): - logger.error("This code uses F-strings and requires Python 3.6+") - - -def build_parser() -> GuinierParser: - """Build parser for input and return list of files. - :return: parser - """ - description = ( - "Calculate the radius of gyration using Guinier law" - " for a set of scattering curves" - ) - epilog = """free_rg is an open-source implementation of - the autorg algorithm originately part of the ATSAS suite. - As this is reverse engineered, some constants and results may differ - """ - return GuinierParser( - prog="free_rg", description=description, epilog=epilog - ) - - -def main() -> None: - """Entry point for free_rg app""" - parser = build_parser() - run_guinier_fit(fit_function=autoRg, parser=parser, logger=logger) - - -if __name__ == "__main__": - main() diff --git a/freesas/app/bift.py b/freesas/app/bift.py deleted file mode 100644 index 2a540b0..0000000 --- a/freesas/app/bift.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2017 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__author__ = "Jérôme Kieffer" -__license__ = "MIT" -__copyright__ = "2017, ESRF" -__date__ = "13/10/2020" - -import sys -import logging -import platform -import traceback -from freesas import bift -from freesas.sasio import ( - load_scattering_data, - convert_inverse_angstrom_to_nanometer, -) -from freesas.sas_argparser import SASParser -from freesas.fitting import ( - set_logging_level, - collect_files, -) - -logging.basicConfig(level=logging.WARNING) -logger = logging.getLogger("bift") - - -def build_parser() -> SASParser: - """Build parser for input and return list of files. - :return: parser - """ - - description = ( - "Calculate the density as function of distance p(r)" - " curve from an I(q) scattering curve" - ) - epilog = """free_bift is a Python implementation of the Bayesian Inverse Fourier Transform - - This code is the implementation of - Steen Hansen J. Appl. Cryst. (2000). 33, 1415-1421 - - Based on the BIFT from Jesse Hopkins, available at: - https://sourceforge.net/p/bioxtasraw/git/ci/master/tree/bioxtasraw/BIFT.py - - It aims at being a drop in replacement for datgnom of the ATSAS suite. - - """ - parser = SASParser( - prog="free_bift", description=description, epilog=epilog - ) - parser.add_file_argument(help_text="I(q) files to convert into p(r)") - parser.add_output_filename_argument() - parser.add_q_unit_argument() - parser.add_argument( - "-n", - "--npt", - default=100, - type=int, - help="number of points in p(r) curve", - ) - parser.add_argument( - "-s", - "--scan", - default=27, - type=int, - help="Initial alpha-scan size to guess the start parameter", - ) - parser.add_argument( - "-m", - "--mc", - default=100, - type=int, - help="Number of Monte-Carlo samples in post-refinement", - ) - parser.add_argument( - "-t", - "--threshold", - default=2.0, - type=float, - help="Sample at average ± threshold*sigma in MC", - ) - return parser - - -def main(): - """Entry point for bift app.""" - if platform.system() == "Windows": - sys.stdout = open(1, "w", encoding="utf-16", closefd=False) - - parser = build_parser() - args = parser.parse_args() - set_logging_level(args.verbose) - files = collect_files(args.file) - - for afile in files: - try: - data = load_scattering_data(afile) - except: - logger.error("Unable to parse file %s", afile) - else: - if args.unit == "Å": - data = convert_inverse_angstrom_to_nanometer(data) - try: - bo = bift.auto_bift(data, npt=args.npt, scan_size=args.scan) - except Exception as err: - print("%s: %s %s" % (afile, err.__class__.__name__, err)) - if logging.root.level < logging.WARNING: - traceback.print_exc(file=sys.stdout) - else: - try: - stats = bo.monte_carlo_sampling( - args.mc, args.threshold, npt=args.npt - ) - except RuntimeError as err: - print("%s: %s %s" % (afile, err.__class__.__name__, err)) - if logging.root.level < logging.WARNING: - traceback.print_exc(file=sys.stdout) - else: - dest = afile.stem + ".out" - print(stats.save(dest, source=afile)) - - -if __name__ == "__main__": - main() diff --git a/freesas/app/cormap.py b/freesas/app/cormap.py deleted file mode 100644 index 042ee70..0000000 --- a/freesas/app/cormap.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 - -__author__ = "Jérôme Kieffer" -__license__ = "MIT" -__copyright__ = "2015, ESRF" -__date__ = "20/04/2020" - -import os -import logging -import freesas -from freesas.cormap import gof - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("cormap") -import numpy -from itertools import combinations -from collections import namedtuple -from freesas.sasio import load_scattering_data -from freesas.sas_argparser import SASParser - -datum = namedtuple("datum", ["index", "filename", "data"]) - -import platform - -operatingSystem = platform.system() -if operatingSystem == "Windows": - import glob - - -def parse(): - """Parse input and return list of files. - :return: list of input files - """ - description = "Measure pair-wise similarity of spectra " - epilog = """cormapy is an open-source implementation of - the cormap algorithm in datcmp (from ATSAS). - It does not scale the data and assume they are already scaled - """ - parser = SASParser(prog="cormapy", description=description, epilog=epilog) - parser.add_file_argument(help_text="dat files to compare") - - args = parser.parse_args() - - if args.verbose: - logging.root.setLevel(logging.DEBUG) - files = [i for i in args.file if os.path.exists(i)] - if operatingSystem == "Windows" and files == []: - files = glob.glob(args.file[0]) - files.sort() - input_len = len(files) - logger.debug("%s input files" % input_len) - return files - - -def compare(lstfiles): - res = [ - "Pair-wise Correlation Map", - "" " C Pr(>C)", - ] - data = [] - for i, f in enumerate(lstfiles): - try: - ary = load_scattering_data(f) - except ValueError as e: - print(e) - if ary.ndim > 1 and ary.shape[1] > 1: - ary = ary[:, 1] - d = datum(i + 1, f, ary) - data.append(d) - for a, b in combinations(data, 2): - r = gof(a.data, b.data) - res.append( - "%6i vs. %6i %6i %8.6f" % (a.index, b.index, r.c, r.P) - ) - res.append("") - for a in data: - res.append( - "%6i %8f + %8f * %s" % (a.index, 0.0, 1.0, a.filename) - ) - res.append("") - print(os.linesep.join(res)) - return res - - -def main(): - """main entry point""" - f = parse() - if f: - compare(f) - - -if __name__ == "__main__": - main() diff --git a/freesas/app/extract_ascii.py b/freesas/app/extract_ascii.py deleted file mode 100644 index 30e8fc0..0000000 --- a/freesas/app/extract_ascii.py +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2020 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__author__ = "Jérôme Kieffer" -__license__ = "MIT" -__copyright__ = "2020, ESRF" -__date__ = "15/01/2021" - -import io -import os -import sys -import logging -import glob -import platform -import posixpath -from collections import namedtuple, OrderedDict -import json -import copy -import pyFAI -from pyFAI.io import Nexus -from freesas.sas_argparser import SASParser - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("extract_ascii") - -if sys.version_info[0] < 3: - logger.error("This code requires Python 3.4+") - -NexusJuice = namedtuple( - "NexusJuice", - "filename h5path npt unit q I poni mask energy polarization signal2d error2d buffer concentration", -) - - -def parse(): - - """Parse input and return list of files. - :return: list of input files - """ - description = "Extract the SAXS data from a Nexus files as a 3 column ascii (q, I, err). Metadata are exported in the headers as needed." - epilog = """extract_ascii.py allows you to export the data in inverse nm or inverse A with possible intensity scaling. - """ - parser = SASParser( - prog="extract-ascii.py", description=description, epilog=epilog - ) - # Commented option need to be implemented - # parser.add_argument("-o", "--output", action='store', help="Output filename, by default the same with .dat extension", default=None, type=str) - # parser.add_argument("-u", "--unit", action='store', help="Unit for q: inverse nm or Angstrom?", default="nm", type=str) - # parser.add_argument("-n", "--normalize", action='store', help="Re-normalize all intensities with this factor ", default=1.0, type=float) - parser.add_file_argument("HDF5 input data") - parser.add_argument( - "-a", - "--all", - action="store_true", - help="extract every individual frame", - default=False, - ) - return parser.parse_args() - - -def extract_averaged(filename): - "return some infomations extracted from a HDF5 file " - results = OrderedDict() - results["filename"] = filename - # Missing: comment normalization - with Nexus(filename, "r") as nxsr: - entry_grp = nxsr.get_entries()[0] - results["h5path"] = entry_grp.name - nxdata_grp = nxsr.h5[entry_grp.attrs["default"]] - signal = nxdata_grp.attrs["signal"] - axis = nxdata_grp.attrs["axes"] - results["I"] = nxdata_grp[signal][()] - results["q"] = nxdata_grp[axis][()] - results["std"] = nxdata_grp["errors"][()] - results["unit"] = pyFAI.units.to_unit( - axis + "_" + nxdata_grp[axis].attrs["units"] - ) - integration_grp = nxdata_grp.parent - results["geometry"] = json.loads( - integration_grp["configuration/data"][()] - ) - results["polarization"] = integration_grp[ - "configuration/polarization_factor" - ][()] - - instrument_grps = nxsr.get_class(entry_grp, class_type="NXinstrument") - if instrument_grps: - detector_grp = nxsr.get_class( - instrument_grps[0], class_type="NXdetector" - )[0] - results["mask"] = detector_grp["pixel_mask"].attrs["filename"] - sample_grp = nxsr.get_class(entry_grp, class_type="NXsample")[0] - results["sample"] = posixpath.split(sample_grp.name)[-1] - results["buffer"] = sample_grp["buffer"][()] - results["storage temperature"] = sample_grp["temperature_env"][()] - results["exposure temperature"] = sample_grp["temperature"][()] - results["concentration"] = sample_grp["concentration"][()] - if "2_correlation_mapping" in entry_grp: - results["to_merge"] = entry_grp[ - "2_correlation_mapping/results/to_merge" - ][()] - return results - - -def extract_all(filename): - "return some infomations extracted from a HDF5 file for all individual frames" - res = [] - results = OrderedDict() - results["filename"] = filename - with Nexus(filename, "r") as nxsr: - entry_grp = nxsr.get_entries()[0] - results["h5path"] = entry_grp.name - nxdata_grp = nxsr.h5[entry_grp.name + "/1_integration/results"] - signal = nxdata_grp.attrs["signal"] - axis = nxdata_grp.attrs["axes"][1] - I = nxdata_grp[signal][()] - results["q"] = nxdata_grp[axis][()] - std = nxdata_grp["errors"][()] - results["unit"] = pyFAI.units.to_unit( - axis + "_" + nxdata_grp[axis].attrs["units"] - ) - integration_grp = nxdata_grp.parent - results["geometry"] = json.loads( - integration_grp["configuration/data"][()] - ) - results["polarization"] = integration_grp[ - "configuration/polarization_factor" - ][()] - instrument_grp = nxsr.get_class(entry_grp, class_type="NXinstrument")[ - 0 - ] - detector_grp = nxsr.get_class(instrument_grp, class_type="NXdetector")[ - 0 - ] - results["mask"] = detector_grp["pixel_mask"].attrs["filename"] - sample_grp = nxsr.get_class(entry_grp, class_type="NXsample")[0] - results["sample"] = posixpath.split(sample_grp.name)[-1] - results["buffer"] = sample_grp["buffer"][()] - if "temperature_env" in sample_grp: - results["storage temperature"] = sample_grp["temperature_env"][()] - if "temperature" in sample_grp: - results["exposure temperature"] = sample_grp["temperature"][()] - if "concentration" in sample_grp: - results["concentration"] = sample_grp["concentration"][()] - # if "2_correlation_mapping" in entry_grp: - # results["to_merge"] = entry_grp["2_correlation_mapping/results/to_merge"][()] - for i, s in zip(I, std): - r = copy.copy(results) - r["I"] = i - r["std"] = s - res.append(r) - return res - - -def write_ascii(results, output=None, hdr="#", linesep=os.linesep): - """ - :param resusts: dict containing some NexusJuice - :param output: name of the 3-column ascii file to be written - :param hdr: header mark, usually '#' - :param linesep: to be able to addapt the end of lines - - Adam Round explicitelly asked for (email from Date: Tue, 04 Oct 2011 15:22:29 +0200) : - Modification from: - # BSA buffer - # Sample c= 0.0 mg/ml (these two lines are required for current DOS pipeline and can be cleaned up once we use EDNA to get to ab-initio models) - # - # Sample environment: - # Detector = Pilatus 1M - # PixelSize_1 = 0.000172 - # PixelSize_2 = 6.283185 (I think it could avoid confusion if we give teh actual pixel size as 0.000172 for X and Y and not to give the integrated sizes. Also could there also be a modification for PixelSize_1 as on the diagonal wont it be the hypotenuse (0.000243)? and thus will be on average a bit bigger than 0.000172) - # - # title = BSA buffer - # Frame 7 of 10 - # Time per frame (s) = 10 - # SampleDistance = 2.43 - # WaveLength = 9.31e-11 - # Normalization = 0.0004885 - # History-1 = saxs_angle +pass -omod n -rsys normal -da 360_deg -odim = 1 /data/id14eh3/inhouse/saxs_pilatus/Adam/EDNAtests/2d/dumdum_008_07.edf/data/id14eh3/inhouse/saxs_pilatus/Adam/EDNAtests/misc/dumdum_008_07.ang - # DiodeCurr = 0.0001592934 - # MachCurr = 163.3938 - # Mask = /data/id14eh3/archive/CALIBRATION/MASK/Pcon_01Jun_msk.edf - # SaxsDataVersion = 2.40 - # - # N 3 - # L q*nm I_BSA buffer stddev - # - # Sample Information: - # Storage Temperature (degrees C): 4 - # Measurement Temperature (degrees C): 10 - # Concentration: 0.0 - # Code: BSA - s-vector Intensity Error - s-vector Intensity Error - s-vector Intensity Error - s-vector Intensity Error - """ - hdr = str(hdr) - headers = [] - if "comments" in results: - headers.append(hdr + " " + results["comments"]) - else: - headers.append(hdr) - headers.append( - hdr + " Sample c= %s mg/ml" % results.get("concentration", -1) - ) - headers += [hdr, hdr + " Sample environment:"] - if "geometry" in results: - headers.append( - hdr + " Detector = %s" % results["geometry"]["detector"] - ) - headers.append( - hdr + " SampleDistance = %s" % results["geometry"]["dist"] - ) - headers.append( - hdr + " WaveLength = %s" % results["geometry"]["wavelength"] - ) - headers.append(hdr) - if "comments" in results: - headers.append(hdr + " title = %s" % results["comment"]) - if "to_merge" in results: - headers.append( - hdr - + " Frames merged: " - + " ".join([str(i) for i in results["to_merge"]]) - ) - if "normalization" in results: - headers.append(hdr + " Normalization = %s" % results["normalization"]) - if "mask" in results: - headers.append(hdr + " Mask = %s" % results["mask"]) - headers.append(hdr) - headers.append(hdr + (" N 3" if "std" in results else " N 2")) - line = hdr + " L " - if "unit" in results: - a, b = str(results["unit"]).split("_") - line += a + "*" + b.strip("^-1") + " I_" - else: - line += "q I_" - if "comment" in results: - line += results["comments"] - if "std" in results: - line += " stddev" - headers.append(line) - headers.append(hdr) - headers.append(hdr + " Sample Information:") - if "storage temperature" in results: - headers.append( - hdr - + " Storage Temperature (degrees C): %s" - % results["storage temperature"] - ) - if "exposure temperature" in results: - headers.append( - hdr - + " Measurement Temperature (degrees C): %s" - % results["exposure temperature"] - ) - - headers.append( - hdr + " Concentration: %s" % results.get("concentration", -1) - ) - if "buffer" in results: - headers.append(hdr + " Buffer: %s" % results["buffer"]) - headers.append(hdr + " Code: %s" % results.get("sample", "")) - - def write(headers, file_): - - file_.writelines(linesep.join(headers)) - file_.write(linesep) - - if "std" in results: - data = [ - "%14.6e\t%14.6e\t%14.6e" % (q, I, std) - for q, I, std in zip( - results["q"], results["I"], results["std"] - ) - ] - else: - data = [ - "%14.6e\t%14.6e\t" % (q, I) - for q, I in zip(results["q"], results["I"]) - ] - data.append("") - file_.writelines(linesep.join(data)) - - if output: - with open(output, "w") as f: - write(headers, f) - else: - f = io.StringIO() - write(headers, f) - f.seek(0) - return f.read() - - -def main(): - args = parse() - if args.verbose: - logging.root.setLevel(logging.DEBUG) - files = [i for i in args.file if os.path.exists(i)] - if platform.system() == "Windows" and files == []: - files = glob.glob(args.file[0]) - files.sort() - input_len = len(files) - logger.debug("%s input files", input_len) - for src in files: - if args.all: - dest = os.path.splitext(src)[0] + "%04i.dat" - for idx, frame in enumerate(extract_all(src)): - print(src, " --> ", dest % idx) - write_ascii(frame, dest % idx) - else: - dest = os.path.splitext(src)[0] + ".dat" - write_ascii(extract_averaged(src), dest) - print(src, " --> ", dest) - - -if __name__ == "__main__": - main() diff --git a/freesas/app/plot_sas.py b/freesas/app/plot_sas.py deleted file mode 100644 index 6378b30..0000000 --- a/freesas/app/plot_sas.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2020 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -"Tool to perform a simple plotting of a set of SAS curve" - -__author__ = "Jérôme Kieffer" -__license__ = "MIT" -__copyright__ = "2020, ESRF" -__date__ = "14/05/2020" - -import platform -import logging -from pathlib import Path -from matplotlib.pyplot import switch_backend -from matplotlib.backends.backend_pdf import PdfPages -from freesas import plot -from freesas.sasio import ( - load_scattering_data, - convert_inverse_angstrom_to_nanometer, -) -from freesas.autorg import InsufficientDataError, NoGuinierRegionError -from freesas.sas_argparser import SASParser - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("plot_sas") - - -def set_backend(output: Path = None, output_format: str = None): - """Explicitely set silent backend based on format or filename - Needed on MacOS - @param output: Name of the specified output file - @param output_format: User specified format - """ - if output_format: - output_format = output_format.lower() - elif output and len(output.suffix) > 0: - output_format = output.suffix.lower()[1:] - if output_format: - if output_format == "svg": - switch_backend("svg") - elif output_format in ["ps", "eps"]: - switch_backend("ps") - elif output_format == "pdf": - switch_backend("pdf") - elif output_format == "png": - switch_backend("agg") - - -def parse(): - """Parse input and return list of files. - :return: list of input files - """ - description = "Generate typical sas plots with matplotlib" - epilog = """freesas is an open-source implementation of a bunch of - small angle scattering algorithms. """ - parser = SASParser( - prog="freesas.py", description=description, epilog=epilog - ) - parser.add_file_argument(help_text="dat files to plot") - parser.add_output_filename_argument() - parser.add_output_data_format("jpeg", "svg", "png", "pdf") - parser.add_q_unit_argument() - return parser.parse_args() - - -def create_figure(file: Path, unit: str = "nm"): - """Create multi-plot SAS figure for data from a file - @param file: filename of SAS file in q I Ierr format - @param unit: length unit of input data, supported options are Å and nm. - :return: figure with SAS plots for this file - """ - data = load_scattering_data(file) - if unit == "Å": - data = convert_inverse_angstrom_to_nanometer(data) - fig = plot.plot_all(data) - fig.suptitle(file) - return fig - - -def main(): - args = parse() - if args.verbose: - logging.root.setLevel(logging.DEBUG) - files = [Path(i) for i in args.file if Path(i).exists()] - if platform.system() == "Windows" and files == []: - files = list(Path.cwd().glob(args.file[0])) - files.sort() - input_len = len(files) - logger.debug("%s input files", input_len) - figures = [] - - if args.output and len(files) > 1: - logger.warning("Only PDF export is possible in multi-frame mode") - if args.output and platform.system() == "Darwin": - if len(files) == 1: - set_backend(args.output, args.format) - elif len(files) > 1: - set_backend(output_format="pdf") - for afile in files: - try: - fig = create_figure(afile, args.unit) - except OSError: - logger.error("Unable to load file %s", afile) - except (InsufficientDataError, NoGuinierRegionError, ValueError): - logger.error("Unable to process file %s", afile) - else: - figures.append(fig) - if args.output is None: - fig.show() - elif len(files) == 1: - fig.savefig(args.output, format=args.format) - if len(figures) > 1 and args.output: - with PdfPages(args.output) as pdf_output_file: - for fig in figures: - pdf_output_file.savefig(fig) - if not args.output: - input("Press enter to quit") - - -if __name__ == "__main__": - main() diff --git a/freesas/app/supycomb.py b/freesas/app/supycomb.py deleted file mode 100644 index 2c4f688..0000000 --- a/freesas/app/supycomb.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/python3 -__author__ = "Guillaume Bonamis" -__license__ = "MIT" -__copyright__ = "2015, ESRF" -__date__ = "09/07/2020" - -import logging -from os.path import dirname, abspath -from freesas.align import InputModels, AlignModels -from freesas.sas_argparser import SASParser - -base = dirname(dirname(abspath(__file__))) - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("supycomb") - - -def parse(): - - """Parse input and return list of files. - :return: list of args - """ - description = "Align several models and calculate NSD" - epilog = """supycomb is an open-source implementation of - [J. Appl. Cryst. (2001). 34, 33-41](doi:10.1107/S0021889800014126). - - The main difference with supcomb: the fast mode does not re-bin beads. It only refines the best matching orientation which provides a speed-up of a factor 8. - - """ - parser = SASParser(prog="supycomp", description=description, epilog=epilog) - parser.add_file_argument(help_text="pdb files to align") - parser.add_argument( - "-m", - "--mode", - dest="mode", - type=str, - choices=["SLOW", "FAST"], - default="SLOW", - help="Either SLOW or FAST, default: %(default)s)", - ) - parser.add_argument( - "-e", - "--enantiomorphs", - type=str, - choices=["YES", "NO"], - default="YES", - help="Search enantiomorphs, YES or NO, default: %(default)s)", - ) - parser.add_argument( - "-q", - "--quiet", - type=str, - choices=["ON", "OFF"], - default="ON", - help="Hide log or not, default: %(default)s", - ) - parser.add_argument( - "-g", - "--gui", - type=str, - choices=["YES", "NO"], - default="YES", - help="Use GUI for figures or not, default: %(default)s", - ) - parser.add_argument( - "-o", - "--output", - type=str, - default="aligned.pdb", - help="output filename, default: %(default)s", - ) - return parser.parse_args() - - -def main(): - """main application""" - - args = parse() - input_len = len(args.file) - logger.info("%s input files" % input_len) - selection = InputModels() - - if args.mode == "SLOW": - slow = True - logger.info("SLOW mode") - else: - slow = False - logger.info("FAST mode") - - if args.enantiomorphs == "YES": - enantiomorphs = True - else: - enantiomorphs = False - logger.info("NO enantiomorphs") - - if args.quiet == "OFF": - logger.setLevel(logging.DEBUG) - logger.info("setLevel: Debug") - - if args.gui == "NO": - save = True - logger.info( - "Figures saved automatically : \n R factor values and selection => Rfactor.png \n NSD table and selection => nsd.png" - ) - else: - save = False - - align = AlignModels(args.file, slow=slow, enantiomorphs=enantiomorphs) - if input_len == 2: - align.outputfiles = args.output - align.assign_models() - dist = align.alignment_2models() - logger.info("%s and %s aligned" % (args.file[0], args.file[1])) - logger.info("NSD after optimized alignment = %.2f" % dist) - else: - align.outputfiles = [ - "model-%02i.pdb" % (i + 1) for i in range(input_len) - ] - selection.inputfiles = args.file - selection.models_selection() - selection.rfactorplot(save=save) - align.models = selection.sasmodels - align.validmodels = selection.validmodels - - align.makeNSDarray() - align.alignment_reference() - logger.info( - "valid models aligned on the model %s" % (align.reference + 1) - ) - align.plotNSDarray(rmax=round(selection.rmax, 4), save=save) - - if not save and input_len > 2: - input("Press any key to exit") - - -if __name__ == "__main__": - main() diff --git a/freesas/test/__init__.py b/freesas/test/__init__.py deleted file mode 100644 index dbcadf1..0000000 --- a/freesas/test/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -#!usr/bin/env python -# coding: utf-8 - -__author__ = "Jérôme Kieffer" -__license__ = "MIT" -__date__ = "15/01/2021" -__copyright__ = "2015-2021, ESRF" - -import sys -import unittest -from .test_all import suite - - -def run_tests(): - """Run test complete test_suite""" - mysuite = suite() - runner = unittest.TextTestRunner() - if not runner.run(mysuite).wasSuccessful(): - print("Test suite failed") - return 1 - else: - print("Test suite succeeded") - return 0 - - -run = run_tests - -if __name__ == '__main__': - sys.exit(run_tests()) diff --git a/freesas/test/mock_open_38.py b/freesas/test/mock_open_38.py deleted file mode 100644 index cf85a66..0000000 --- a/freesas/test/mock_open_38.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -This is the Python 3.8 implementation of mock_open taken from -https://github.com/python/cpython/blob/3.8/Lib/unittest/mock.py -Hence: -"Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; -All Rights Reserved" -""" - -import io -from unittest.mock import MagicMock, DEFAULT - - -file_spec = None -#sentinel = _Sentinel() -#DEFAULT = sentinel.DEFAULT - -def _to_stream(read_data): - if isinstance(read_data, bytes): - return io.BytesIO(read_data) - else: - return io.StringIO(read_data) - - -def mock_open(mock=None, read_data=''): - """ - A helper function to create a mock to replace the use of `open`. It works - for `open` called directly or used as a context manager. - The `mock` argument is the mock object to configure. If `None` (the - default) then a `MagicMock` will be created for you, with the API limited - to methods or attributes available on standard file handles. - `read_data` is a string for the `read`, `readline` and `readlines` of the - file handle to return. This is an empty string by default. - """ - _read_data = _to_stream(read_data) - _state = [_read_data, None] - - def _readlines_side_effect(*args, **kwargs): - if handle.readlines.return_value is not None: - return handle.readlines.return_value - return _state[0].readlines(*args, **kwargs) - - def _read_side_effect(*args, **kwargs): - if handle.read.return_value is not None: - return handle.read.return_value - return _state[0].read(*args, **kwargs) - - def _readline_side_effect(*args, **kwargs): - yield from _iter_side_effect() - while True: - yield _state[0].readline(*args, **kwargs) - - def _iter_side_effect(): - if handle.readline.return_value is not None: - while True: - yield handle.readline.return_value - for line in _state[0]: - yield line - - def _next_side_effect(): - if handle.readline.return_value is not None: - return handle.readline.return_value - return next(_state[0]) - - global file_spec - if file_spec is None: - import _io - file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) - - if mock is None: - mock = MagicMock(name='open', spec=open) - - handle = MagicMock(spec=file_spec) - handle.__enter__.return_value = handle - - handle.write.return_value = None - handle.read.return_value = None - handle.readline.return_value = None - handle.readlines.return_value = None - - handle.read.side_effect = _read_side_effect - _state[1] = _readline_side_effect() - handle.readline.side_effect = _state[1] - handle.readlines.side_effect = _readlines_side_effect - handle.__iter__.side_effect = _iter_side_effect - handle.__next__.side_effect = _next_side_effect - - def reset_data(*args, **kwargs): - _state[0] = _to_stream(read_data) - if handle.readline.side_effect == _state[1]: - # Only reset the side effect if the user hasn't overridden it. - _state[1] = _readline_side_effect() - handle.readline.side_effect = _state[1] - return DEFAULT - - mock.side_effect = reset_data - mock.return_value = handle - return mock diff --git a/freesas/test/test_align.py b/freesas/test/test_align.py deleted file mode 100644 index 3137e63..0000000 --- a/freesas/test/test_align.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/python -from __future__ import print_function - -__author__ = "Guillaume" -__license__ = "MIT" -__copyright__ = "2015, ESRF" - -import numpy -import unittest -from .utilstests import get_datafile -from ..model import SASModel -from ..align import AlignModels -from ..transformations import translation_matrix, euler_matrix -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("AlignModels_test") - - -def move(mol): - """ - Random movement of the molecule. - - @param mol: 2d array, coordinates of the molecule - @return mol:2D array, coordinates of the molecule after a translation and a rotation - """ - vect = numpy.random.random(3) - translation = translation_matrix(vect) - - euler = numpy.random.random(3) - rotation = euler_matrix(euler[0], euler[1], euler[2]) - - mol = numpy.dot(rotation, mol.T) - mol = numpy.dot(translation, mol).T - - return mol - - -def assign_random_mol(inf=None, sup=None): - """ - Create a random 2d array to create a molecule - - @param inf: inf limit of coordinates values - @param sup: sup limit of coordinates values - @return molecule: 2d array, random coordinates - """ - if not inf: - inf = 0 - if not sup: - sup = 100 - molecule = numpy.random.randint(inf, sup, size=400).reshape(100, 4).astype(float) - molecule[:, -1] = 1.0 - return molecule - - -class TestAlign(unittest.TestCase): - testfile1 = get_datafile("dammif-01.pdb") - testfile2 = get_datafile("dammif-02.pdb") - - def test_alignment(self): - inputfiles = [self.testfile1, self.testfile1] - align = AlignModels(inputfiles, slow=False) - align.assign_models() - m = align.models[0] - n = align.models[1] - n.atoms = move(n.atoms) - n.centroid() - n.inertiatensor() - n.canonical_parameters() - if m.dist(n, m.atoms, n.atoms) == 0: - logger.error(m.dist(n, m.atoms, n.atoms)) - logger.error("pb of movement") - dist = align.alignment_2models(save=False) - self.assertAlmostEqual(dist, 0, 12, msg="NSD unequal 0, %s!=0" % dist) - - def test_usefull_alignment(self): - inputfiles = [self.testfile1, self.testfile2] - align = AlignModels(inputfiles, slow=False) - align.assign_models() - mol1 = align.models[0] - mol2 = align.models[1] - dist_before = mol1.dist(mol2, mol1.atoms, mol2.atoms) - symmetry, par = align.alignment_sym(mol1, mol2) - dist_after = mol1.dist_after_movement(par, mol2, symmetry) - self.assertGreaterEqual(dist_before, dist_after, "increase of distance after alignment %s<%s" % (dist_before, dist_after)) - - def test_optimisation_align(self): - inputfiles = [self.testfile1, self.testfile2] - align = AlignModels(inputfiles, slow=False) - align.assign_models() - mol1 = align.models[0] - mol2 = align.models[1] - align.slow = False - sym0, p0 = align.alignment_sym(mol1, mol2) - dist_before = mol1.dist_after_movement(p0, mol2, sym0) - align.slow = True - sym, p = align.alignment_sym(mol1, mol2) - dist_after = mol1.dist_after_movement(p, mol2, sym) - self.assertGreaterEqual(dist_before, dist_after, "increase of distance after optimized alignment %s<%s" % (dist_before, dist_after)) - - def test_alignment_intruder(self): - intruder = numpy.random.randint(0, 8) - inputfiles = [] - for i in range(8): - if i == intruder: - inputfiles.append(self.testfile2) - else: - inputfiles.append(self.testfile1) - - align = AlignModels(inputfiles, slow=False, enantiomorphs=False) - align.assign_models() - align.validmodels = numpy.ones(8) - table = align.makeNSDarray() - if table.sum() == 0: - logger.error("there is no intruders") - - averNSD = ((table.sum(axis=-1)) / (align.validmodels.sum() - 1)) - num_intr = averNSD.argmax() - - if not num_intr and num_intr != 0: - logger.error("cannot find the intruder") - self.assertEqual(num_intr, intruder, msg="not find the good intruder, %s!=%s" % (num_intr, intruder)) - - def test_reference(self): - inputfiles = [self.testfile1] * 8 - align = AlignModels(inputfiles, slow=False, enantiomorphs=False) - align.assign_models() - for i in range(8): - mol = assign_random_mol() - align.models[i].atoms = mol - align.validmodels = numpy.ones(8) - table = align.makeNSDarray() - ref = align.find_reference() - neg_dif = 0 - for i in range(8): - dif = (table[i, :] - table[ref, :]).mean() - if dif < 0: - neg_dif += 1 - self.assertEqual(neg_dif, 0, msg="pb with reference choice") - - -def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(TestAlign("test_alignment")) - testSuite.addTest(TestAlign("test_usefull_alignment")) - testSuite.addTest(TestAlign("test_optimisation_align")) - testSuite.addTest(TestAlign("test_alignment_intruder")) - testSuite.addTest(TestAlign("test_reference")) - return testSuite - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_all.py b/freesas/test/test_all.py deleted file mode 100644 index 1377672..0000000 --- a/freesas/test/test_all.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 -from __future__ import print_function - -__author__ = "Guillaume" -__license__ = "MIT" -__copyright__ = "2015, ESRF" -__date__ = "25/04/2020" - -import unittest -from . import test_model -from . import test_align -from . import test_distance -from . import test_cormap -from . import test_autorg -from . import test_bift -from . import test_sasio -from . import test_sas_argparser -from . import test_fitting - - -def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(test_bift.suite()) - testSuite.addTest(test_model.suite()) - testSuite.addTest(test_align.suite()) - testSuite.addTest(test_distance.suite()) - testSuite.addTest(test_cormap.suite()) - testSuite.addTest(test_autorg.suite()) - testSuite.addTest(test_sasio.suite()) - testSuite.addTest(test_sas_argparser.suite()) - testSuite.addTest(test_fitting.suite()) - return testSuite - - -if __name__ == "__main__": - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_autorg.py b/freesas/test/test_autorg.py deleted file mode 100644 index e27560f..0000000 --- a/freesas/test/test_autorg.py +++ /dev/null @@ -1,448 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2017 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__authors__ = ["J. Kieffer"] -__license__ = "MIT" -__date__ = "10/06/2020" - -import logging -import unittest -from math import sqrt, pi - -import numpy -from scipy.stats import linregress - -from .utilstests import get_datafile -from ..autorg import ( - autoRg, - RG_RESULT, - linear_fit, - auto_gpa, - auto_guinier, -) -from .._autorg import curate_data # pylint: disable=E0401 -from ..invariants import calc_Rambo_Tainer -from .._bift import distribution_sphere # pylint: disable=E0401 - -logger = logging.getLogger(__name__) - - -def create_synthetic_data(R0=4, I0=100): - """Create idealized data for a sphere of radius R0=4 whose Rg should be 4*sqrt(3/5)""" - npt = 1000 - Dmax = 2 * R0 - size = 5000 - r = numpy.linspace(0, Dmax, npt + 1) - p = distribution_sphere(I0, Dmax, npt) - q = numpy.linspace(0, 10, size) - qr = numpy.outer(q, r / pi) - T = (4 * pi * (r[-1] - r[0]) / npt) * numpy.sinc(qr) - I = T.dot(p) - err = numpy.sqrt(I) - return numpy.vstack((q, I, err)).T[1:] - - -class TestAutoRg(unittest.TestCase): - testfile = get_datafile("bsa_005_sub.dat") - - def __init__(self, testName, **extra_kwargs): - super().__init__(testName) - self.extra_arg = extra_kwargs - - # Reference implementation - atsas_autorg = { - "Version": "Atsas 2.6.1", - "Rg": 2.98016, - "sigma_Rg": 0.156859, - "I0": 61.3093, - "sigma_I0": 0.0606315, - "start_point": 46, - "end_point": 95, - "quality": 0.752564, - "aggregated": 0, - } - - def test_atsas(self): - logger.info("test file: %s", self.testfile) - data = numpy.loadtxt(self.testfile) - atsas_result = self.atsas_autorg.copy() - logger.debug("Reference version: %s" % atsas_result.pop("Version")) - atsas_result = RG_RESULT(**atsas_result) - free_result = autoRg(data) - logger.debug("Ref: %s" % (atsas_result,)) - logger.debug("Obt: %s" % (free_result,)) - self.assertAlmostEqual( - atsas_result.Rg, free_result.Rg, 1, "RG fits within 2 digits" - ) - self.assertAlmostEqual( - atsas_result.I0, - free_result.I0, - msg="I0 fits within +/- 1 ", - delta=1, - ) - self.assertAlmostEqual( - atsas_result.quality, - free_result.quality, - 0, - msg="quality fits within 0 digits", - ) - - def test_synthetic(self): - """Test based on sythetic data: a sphere of radius R0=4 which Rg should be 4*sqrt(3/5)""" - R0 = 4 - I0 = 100 - data = create_synthetic_data(R0=R0, I0=I0) - Rg = autoRg(data) - logger.info("auto_rg %s", Rg) - self.assertAlmostEqual( - R0 * sqrt(3 / 5), Rg.Rg, 0, "Rg matches for a sphere" - ) - self.assertGreater( - R0 * sqrt(3 / 5), - Rg.Rg - Rg.sigma_Rg, - "Rg in range matches for a sphere", - ) - self.assertLess( - R0 * sqrt(3 / 5), - Rg.Rg + Rg.sigma_Rg, - "Rg in range matches for a sphere", - ) - self.assertAlmostEqual(I0, Rg.I0, 0, "I0 matches for a sphere") - self.assertGreater(I0, Rg.I0 - Rg.sigma_I0, "I0 matches for a sphere") - self.assertLess(I0, Rg.I0 + Rg.sigma_I0, "I0 matches for a sphere") - - gpa = auto_gpa(data) - logger.info("auto_gpa %s", gpa) - self.assertAlmostEqual( - gpa.Rg / (R0 * sqrt(3.0 / 5)), 1.00, 0, "Rg matches for a sphere" - ) - self.assertAlmostEqual(gpa.I0 / I0, 1.0, 1, "I0 matches for a sphere") - - guinier = auto_guinier(data) - logger.info("auto_guinier %s", guinier) - self.assertAlmostEqual( - R0 * sqrt(3.0 / 5), guinier.Rg, 0, "Rg matches for a sphere" - ) - sigma_Rg = max(guinier.sigma_Rg, 1e-4) - sigma_I0 = max(guinier.sigma_I0, 1e-4) - self.assertGreater( - R0 * sqrt(3.0 / 5), - guinier.Rg - sigma_Rg, - "Rg in range matches for a sphere", - ) - self.assertLess( - R0 * sqrt(3.0 / 5), - guinier.Rg + sigma_Rg, - "Rg in range matches for a sphere", - ) - self.assertAlmostEqual(I0, guinier.I0, 0, "I0 matches for a sphere") - self.assertGreater( - I0, guinier.I0 - sigma_I0, "I0 matches for a sphere" - ) - self.assertLess(I0, guinier.I0 + sigma_I0, "I0 matches for a sphere") - - # Check RT invarients... - rt = calc_Rambo_Tainer(data, guinier) - self.assertIsNotNone( - rt, "Rambo-Tainer invariants are actually calculated" - ) - - def test_auto_gpa_with_outlier(self): - - """ - Test that auto_gpa gives reasonalbe results - even if one data point is excessively large (e.g. hot pixel) - """ - outlier_position = self.extra_arg["outlier_position"] - R0 = 4 - I0 = 100 - data = create_synthetic_data(R0=R0, I0=I0) - data[outlier_position, 1] *= 1000 - gpa = auto_gpa(data) - logger.info("auto_gpa %s", gpa) - self.assertAlmostEqual( - gpa.Rg / (R0 * sqrt(3.0 / 5)), - 1.00, - 0, - f"In case of outlier at {outlier_position} Rg matches for a sphere", - ) - self.assertAlmostEqual( - gpa.I0 / I0, - 1.0, - 1, - f"In case of outlier at {outlier_position} I0 matches for a sphere", - ) - - -class TestFit(unittest.TestCase): - # Testcase originally comes from wikipedia article on linear regression, expected results from scipy.stats.linregress - - def test_linear_fit_static(self): - testx = [ - 1.47, - 1.5, - 1.52, - 1.55, - 1.57, - 1.6, - 1.63, - 1.65, - 1.68, - 1.7, - 1.73, - 1.75, - 1.78, - 1.80, - 1.83, - ] - testy = [ - 52.21, - 53.12, - 54.48, - 55.84, - 57.20, - 58.57, - 59.93, - 61.29, - 63.11, - 64.47, - 66.28, - 68.1, - 69.92, - 72.19, - 74.46, - ] - testw = [1.0] * 15 - testintercept = -39.061956 - testslope = +61.2721865 - fit_result = linear_fit(testx, testy, testw) - # print(fit_result) - self.assertAlmostEqual( - fit_result.intercept, - testintercept, - 5, - "Intercept fits wihtin 4(?) digits", - ) - self.assertAlmostEqual( - fit_result.slope, testslope, 5, "Intercept fits wihtin 4(?) digits" - ) - - def test_linspace(self): - size = 100 - x = numpy.linspace(-10, 10, size) - y = numpy.linspace(10, 0, size) - w = numpy.random.random(size) - fit_result = linear_fit(x, y, w) - # print(fit_result) - self.assertAlmostEqual( - fit_result.intercept, 5, 5, "Intercept fits wihtin 4(?) digits" - ) - self.assertAlmostEqual( - fit_result.slope, -0.5, 5, "Intercept fits wihtin 4(?) digits" - ) - - def test_random(self): - - """ - Tests that our linear regression implementation - gives the same results as scipy.stats for random data - """ - size = 100 - x = numpy.random.random(size) - y = 1.6 * x + 5 + numpy.random.random(size) - w = numpy.ones(size) - fit_result = linear_fit(x, y, w) - ref = linregress(x, y) - self.assertAlmostEqual( - fit_result.intercept, - ref[1], - 5, - "Intercept fits wihtin 4(?) digits", - ) - self.assertAlmostEqual( - fit_result.slope, ref[0], 5, "Intercept fits wihtin 4(?) digits" - ) - self.assertAlmostEqual( - fit_result.R2, - ref.rvalue ** 2, - 5, - "R² value matcheswihtin 4(?) digits", - ) - - -class TestDataCuration(unittest.TestCase): - """Tests for the curate_data function.""" - - testfile = get_datafile("bsa_005_sub.dat") - - def __init__(self, testName, **extra_kwargs): - super().__init__(testName) - self.extra_arg = extra_kwargs - - def test_curate_data_BM29_bsa(self): - """Test data curration of "nice" BM29 data.""" - logger.info("test file: %s", self.testfile) - data = numpy.loadtxt(self.testfile) - DTYPE = numpy.float64 - raw_size = len(data) - q_ary = numpy.empty(raw_size, dtype=DTYPE) - i_ary = numpy.empty(raw_size, dtype=DTYPE) - sigma_ary = numpy.empty(raw_size, dtype=DTYPE) - q2_ary = numpy.empty(raw_size, dtype=DTYPE) - lgi_ary = numpy.empty(raw_size, dtype=DTYPE) - wg_ary = numpy.empty(raw_size, dtype=DTYPE) - offsets = numpy.empty(raw_size, dtype=numpy.int32) - data_range = numpy.zeros(3, dtype=numpy.int32) - - curate_data( - data, - q_ary, - i_ary, - sigma_ary, - q2_ary, - lgi_ary, - wg_ary, - offsets, - data_range, - ) - - self.assertListEqual( - list(data_range), - [2, 203, 1033], - msg="reproduced expected BM29 data range", - ) - - def test_curate_synthetic_data(self): - """Test that for idealized data the cut-off is at i0/10.""" - data = create_synthetic_data() - I_one = data[0, 1] - DTYPE = numpy.float64 - raw_size = len(data) - q_ary = numpy.empty(raw_size, dtype=DTYPE) - i_ary = numpy.empty(raw_size, dtype=DTYPE) - sigma_ary = numpy.empty(raw_size, dtype=DTYPE) - q2_ary = numpy.empty(raw_size, dtype=DTYPE) - lgi_ary = numpy.empty(raw_size, dtype=DTYPE) - wg_ary = numpy.empty(raw_size, dtype=DTYPE) - offsets = numpy.empty(raw_size, dtype=numpy.int32) - data_range = numpy.zeros(3, dtype=numpy.int32) - - curate_data( - data, - q_ary, - i_ary, - sigma_ary, - q2_ary, - lgi_ary, - wg_ary, - offsets, - data_range, - ) - - self.assertEqual( - offsets[0], - 0, - msg="curated data for artificial data starts at 0", - ) - - self.assertTrue( - data[data_range[1] - 1, 1] > I_one / 10 - and data[data_range[1] + 1, 1] < I_one / 10, - msg="curated data for artificial data ends at approx. I0/10", - ) - - def test_curate_synthetic_data_with_negative_points(self): - """Test that if one of the first three points is negative, all date before it gets ignored.""" - negative_point_index = self.extra_arg["negative_point_index"] - - data = create_synthetic_data() - DTYPE = numpy.float64 - raw_size = len(data) - data[negative_point_index, 1] = -1 - - q_ary = numpy.empty(raw_size, dtype=DTYPE) - i_ary = numpy.empty(raw_size, dtype=DTYPE) - sigma_ary = numpy.empty(raw_size, dtype=DTYPE) - q2_ary = numpy.empty(raw_size, dtype=DTYPE) - lgi_ary = numpy.empty(raw_size, dtype=DTYPE) - wg_ary = numpy.empty(raw_size, dtype=DTYPE) - offsets = numpy.empty(raw_size, dtype=numpy.int32) - data_range = numpy.zeros(3, dtype=numpy.int32) - - curate_data( - data, - q_ary, - i_ary, - sigma_ary, - q2_ary, - lgi_ary, - wg_ary, - offsets, - data_range, - ) - - self.assertEqual( - offsets[0], - negative_point_index + 1, - msg=f"curated data for artificial data starts after negative data point for negative point at {negative_point_index + 1}", - ) - - self.assertTrue( - data[offsets[data_range[1]] - 1, 1] - > data[negative_point_index + 1, 1] / 10 - and data[offsets[data_range[1]] + 1, 1] - < data[negative_point_index + 1, 1] / 10, - msg=f"curated data for artificial data ends at approx. I(point after negaitve point)/10 if negative point at {negative_point_index + 1}", - ) - - -def suite(): - """Generic builder for the test suite.""" - testSuite = unittest.TestSuite() - testSuite.addTest(TestAutoRg("test_atsas")) - testSuite.addTest(TestAutoRg("test_synthetic")) - for outlier_position in range(3): - testSuite.addTest( - TestAutoRg( - "test_auto_gpa_with_outlier", outlier_position=outlier_position - ) - ) - testSuite.addTest(TestFit("test_linear_fit_static")) - testSuite.addTest(TestFit("test_linspace")) - testSuite.addTest(TestDataCuration("test_curate_data_BM29_bsa")) - testSuite.addTest(TestDataCuration("test_curate_synthetic_data")) - for negative_point_index in range(3): - testSuite.addTest( - TestDataCuration( - "test_curate_synthetic_data_with_negative_points", - negative_point_index=negative_point_index, - ) - ) - return testSuite - - -if __name__ == "__main__": - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_average.py b/freesas/test/test_average.py deleted file mode 100644 index 317bd11..0000000 --- a/freesas/test/test_average.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -from __future__ import print_function - -__author__ = "Guillaume" -__license__ = "MIT" -__copyright__ = "2015, ESRF" - -import numpy -import unittest -from .utilstests import get_datafile -from ..model import SASModel -from ..average import Grid, AverModels - -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("AlignModels_test") - - -class TestAverage(unittest.TestCase): - testfile1 = get_datafile("model-01.pdb") - testfile2 = get_datafile("model-02.pdb") - inputfiles = [testfile1, testfile2] - grid = Grid(inputfiles) - - def test_gridsize(self): - inputfiles = self.inputfiles - grid = self.grid - size = grid.spatial_extent() - coordmax = numpy.array([size[0:3]], dtype="float") - coordmin = numpy.array([size[3:6]], dtype="float") - - pb = 0 - for i in inputfiles: - m = SASModel(i) - a = coordmin + m.atoms[:, 0:3] - b = m.atoms[:, 0:3] - coordmax - if (a >= 0.0).any() or (b >= 0.0).any(): - pb += 1 - self.assertEqual(pb, 0, msg="computed size is not the good one") - - def test_knots(self): - grid = self.grid - nbknots = numpy.random.randint(4000, 6000) - threshold = 10.0 # acceptable difference between nbknots and the effective nb of knots in percent - grid.calc_radius(nbknots) - grid.make_grid() - gap = (1.0 * (grid.nbknots - nbknots) / nbknots) * 100 - self.assertGreater(threshold, gap, msg="final number of knots too different of wanted number: %s != %s" % (nbknots, grid.nbknots)) - - def test_makegrid(self): - grid = self.grid - lattice = grid.make_grid() - m = SASModel(lattice) - self.assertAlmostEqual(m.fineness, 2 * grid.radius, 10, msg="grid do not have the computed radius") - - def test_read(self): - inputfiles = self.inputfiles - average = AverModels(inputfiles, self.grid.coordknots) - models = [SASModel(inputfiles[1]), SASModel(inputfiles[0])] - average.read_files(reference=1) - diff = 0.0 - for i in range(len(inputfiles)): - diff += (models[i].atoms - average.models[i].atoms).max() - self.assertAlmostEqual(diff, 0.0, 10, msg="Files not read properly") - - def test_occupancy(self): - average = AverModels(self.inputfiles, self.grid.coordknots) - average.read_files() - occ_grid = average.assign_occupancy() - average.grid = occ_grid - assert occ_grid.shape[-1] == 5, "problem in grid shape" - diff = occ_grid[:-1, 3] - occ_grid[1:, 3] - self.assertTrue(diff.max() >= 0.0, msg="grid is not properly sorted with occupancy") - - -def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(TestAverage("test_gridsize")) - testSuite.addTest(TestAverage("test_knots")) - testSuite.addTest(TestAverage("test_makegrid")) - testSuite.addTest(TestAverage("test_read")) - testSuite.addTest(TestAverage("test_occupancy")) - return testSuite - - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_bift.py b/freesas/test/test_bift.py deleted file mode 100644 index c1b27bb..0000000 --- a/freesas/test/test_bift.py +++ /dev/null @@ -1,132 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2017 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__authors__ = ["J. Kieffer"] -__license__ = "MIT" -__date__ = "10/06/2020" - -import numpy -import unittest -from .utilstests import get_datafile -from ..bift import auto_bift -from .._bift import BIFT, distribution_parabola, distribution_sphere, \ - ensure_edges_zero, smooth_density -import logging -logger = logging.getLogger(__name__) -import time - - -class TestBIFT(unittest.TestCase): - - DMAX = 10 - NPT = 100 - SIZE = 1000 - - @classmethod - def setUpClass(cls): - super(TestBIFT, cls).setUpClass() - cls.r = numpy.linspace(0, cls.DMAX, cls.NPT + 1) - dr = cls.DMAX / cls.NPT - cls.p = -cls.r * (cls.r - cls.DMAX) # Nice parabola - q = numpy.linspace(0, 8 * cls.DMAX / 3, cls.SIZE + 1) - sincqr = numpy.sinc(numpy.outer(q, cls.r / numpy.pi)) - I = 4 * numpy.pi * (cls.p * sincqr).sum(axis=-1) * dr - err = numpy.sqrt(I) - cls.I0 = I[0] - cls.q = q[1:] - cls.I = I[1:] - cls.err = err[1:] - cls.Rg = numpy.sqrt(0.5 * numpy.trapz(cls.p * cls.r ** 2, cls.r) / numpy.trapz(cls.p, cls.r)) - print(cls.Rg) - - @classmethod - def tearDownClass(cls): - super(TestBIFT, cls).tearDownClass() - cls.r = cls.p = cls.I = cls.q = cls.err = None - - def test_autobift(self): - data = numpy.vstack((self.q, self.I, self.err)).T - t0 = time.perf_counter() - bo = auto_bift(data) - key, value, valid = bo.get_best() -# print("key is ", key) - stats = bo.calc_stats() -# print("stat is ", stats) - logger.info("Auto_bift time: %s", time.perf_counter() - t0) - self.assertAlmostEqual(self.DMAX / key.Dmax, 1, 1, "DMax is correct") - self.assertAlmostEqual(self.I0 / stats.I0_avg, 1, 1, "I0 is correct") - self.assertAlmostEqual(self.Rg / stats.Rg_avg, 1, 2, "Rg is correct") - - def test_BIFT(self): - bift = BIFT(self.q, self.I, self.err) - # test two scan functions - key = bift.grid_scan(9, 11, 5, 10, 100, 5, 100) - # print("key is ", key) - self.assertAlmostEqual(self.DMAX / key.Dmax, 1, 2, "DMax is correct") - res = bift.monte_carlo_sampling(10, 3, 100) - # print("res is ", res) - self.assertAlmostEqual(self.DMAX / res.Dmax_avg, 1, 4, "DMax is correct") - - def test_disributions(self): - pp = numpy.asarray(distribution_parabola(self.I0, self.DMAX, self.NPT)) - ps = numpy.asarray(distribution_sphere(self.I0, self.DMAX, self.NPT)) - self.assertAlmostEqual(numpy.trapz(ps, self.r) * 4 * numpy.pi / self.I0, 1, 3, "Distribution for a sphere looks OK") - self.assertAlmostEqual(numpy.trapz(pp, self.r) * 4 * numpy.pi / self.I0, 1, 3, "Distribution for a parabola looks OK") - self.assertTrue(numpy.allclose(pp, self.p, 1e-4), "distribution matches") - - def test_fixEdges(self): - ones = numpy.ones(self.NPT) - ensure_edges_zero(ones) - self.assertAlmostEqual(ones[0], 0, msg="1st point set to 0") - self.assertAlmostEqual(ones[-1], 0, msg="last point set to 0") - self.assertTrue(numpy.allclose(ones[1:-1], numpy.ones(self.NPT-2), 1e-7), msg="non-edge points unchanged") - - def test_smoothing(self): - ones = numpy.ones(self.NPT) - empty = numpy.empty(self.NPT) - smooth_density(ones,empty) - self.assertTrue(numpy.allclose(ones, empty, 1e-7), msg="flat array smoothed into flat array") - random = numpy.random.rand(self.NPT) - smooth = numpy.empty(self.NPT) - smooth_density(random,smooth) - self.assertAlmostEqual(random[0], smooth[0], msg="first points of random array and smoothed random array match") - self.assertAlmostEqual(random[-1], smooth[-1], msg="last points of random array and smoothed random array match") - self.assertTrue(smooth[1]>=min(smooth[0], smooth[2]) and smooth[1]<=max(smooth[0], smooth[2]), msg="second point of random smoothed array between 1st and 3rd") - self.assertTrue(smooth[-2]>=min(smooth[-1], smooth[-3]) and smooth[-2]<= max(smooth[-1], smooth[-3]), msg="second to last point of random smoothed array between 3rd to last and last") - sign = numpy.sign(random[1:-3] - smooth[2:-2]) * numpy.sign(smooth[2:-2] - random[3:-1]) - self.assertTrue(numpy.allclose(sign, numpy.ones(self.NPT-4), 1e-7), msg="central points of random array and smoothed random array alternate") - -def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(TestBIFT("test_disributions")) - testSuite.addTest(TestBIFT("test_autobift")) - testSuite.addTest(TestBIFT("test_fixEdges")) - testSuite.addTest(TestBIFT("test_smoothing")) - return testSuite - - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_cormap.py b/freesas/test/test_cormap.py deleted file mode 100644 index 7f8233f..0000000 --- a/freesas/test/test_cormap.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -from __future__ import print_function - -__author__ = "Jerome" -__license__ = "MIT" -__copyright__ = "2017, ESRF" - -import numpy -import unittest - -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("test_cormap") -from .. import cormap - - -class TestCormap(unittest.TestCase): - - def test_longest(self): - size = 1000 - target = 50 - start = 100 - - data = numpy.ones(size, dtype="float32") - res = cormap.measure_longest(data) - self.assertEqual(res, size, msg="computed size is correct: positive") - - data -= 2 - res = cormap.measure_longest(data) - self.assertEqual(res, size, msg="computed size is correct: negative") - - data[:] = 0 - data[start: start + target] = 1.0 - res = cormap.measure_longest(data) - self.assertEqual(res, target, msg="computed size is correct: positive/zero") - data = numpy.zeros(size, dtype="float32") - data[start: start + target] = -1.0 - res = cormap.measure_longest(data) - self.assertEqual(res, target, msg="computed size is correct: negative/zero") - data = numpy.fromfunction(lambda n:(-1) ** n, (size,)) - data[start: start + target] = 1.0 - res = cormap.measure_longest(data) - self.assertEqual(res, target + 1, msg="computed size is correct: positive/alternating") - data = numpy.fromfunction(lambda n:(-1) ** n, (size,)) - data[start: start + target] = -1.0 - res = cormap.measure_longest(data) - self.assertEqual(res, target + 1, msg="computed size is correct: negative/alternating") - - def test_stats(self): - self.assertEqual(cormap.LROH.A(10, 0), 1) - self.assertEqual(cormap.LROH.A(10, 1), 144) - self.assertEqual(cormap.LROH.A(10, 2), 504) - self.assertEqual(cormap.LROH.A(10, 10), 1024) - self.assertEqual(cormap.LROH.A(10, 11), 1024) - - self.assertEqual(cormap.LROH.A(0, 3), 1) - self.assertEqual(cormap.LROH.A(1, 3), 2) - self.assertEqual(cormap.LROH.A(2, 3), 4) - self.assertEqual(cormap.LROH.A(3, 3), 8) - self.assertEqual(cormap.LROH.A(4, 3), 15) - self.assertEqual(cormap.LROH.A(5, 3), 29) - self.assertEqual(cormap.LROH.A(6, 3), 56) - self.assertEqual(cormap.LROH.A(7, 3), 108) - self.assertEqual(cormap.LROH.A(8, 3), 208) - - self.assertAlmostEqual(cormap.LROH(200, 0), 1) - self.assertAlmostEqual(cormap.LROH(200, 4), 0.97, 2) - self.assertAlmostEqual(cormap.LROH(200, 5), 0.80, 2) - self.assertAlmostEqual(cormap.LROH(200, 6), 0.54, 2) - self.assertAlmostEqual(cormap.LROH(200, 7), 0.32, 2) - self.assertAlmostEqual(cormap.LROH(200, 8), 0.17, 2) - self.assertAlmostEqual(cormap.LROH(200, 9), 0.09, 2) - self.assertAlmostEqual(cormap.LROH(200, 10), 0.05, 2) - self.assertAlmostEqual(cormap.LROH(200, 11), 0.02, 2) - - -def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(TestCormap("test_longest")) - testSuite.addTest(TestCormap("test_longest")) - testSuite.addTest(TestCormap("test_stats")) - return testSuite - - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_distance.py b/freesas/test/test_distance.py deleted file mode 100644 index 490e6a6..0000000 --- a/freesas/test/test_distance.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import print_function - -__author__ = "Jérôme Kieffer" -__license__ = "MIT" -__copyright__ = "2015, ESRF" -__date__ = "16/12/2015" - -import numpy -import unittest -from .utilstests import get_datafile -from ..model import SASModel -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("cdistance_test") - - -class TestDistance(unittest.TestCase): - testfile1 = get_datafile("model-01.pdb") - testfile2 = get_datafile("dammif-01.pdb") - - def test_invariants(self): - m = SASModel() - m.read(self.testfile1) - f_np, r_np, d_np = m.calc_invariants(False) - f_cy, r_cy, d_cy = m.calc_invariants(True) - self.assertAlmostEqual(f_np, f_cy, 10, "fineness is the same %s!=%s" % (f_np, f_cy)) - self.assertAlmostEqual(r_np, r_cy, 10, "Rg is the same %s!=%s" % (r_np, r_cy)) - self.assertAlmostEqual(d_np, d_cy, 10, "Dmax is the same %s!=%s" % (d_np, d_cy)) - - def test_distance(self): - m = SASModel() - n = SASModel() - m.read(self.testfile1) - n.read(self.testfile2) - f_np = m.dist(n, m.atoms, n.atoms, False) - f_cy = m.dist(n, m.atoms, n.atoms, True) - self.assertAlmostEqual(f_np, f_cy, 10, "distance is the same %s!=%s" % (f_np, f_cy)) - - def test_same(self): - m = SASModel() - n = SASModel() - m.read(self.testfile1) - n.read(self.testfile1) - numpy.random.shuffle(n.atoms) - f_np = m.dist(n, m.atoms, n.atoms, False) - f_cy = m.dist(n, m.atoms, n.atoms, True) - self.assertAlmostEqual(f_np, 0, 10, "NSD not nul with np") - self.assertAlmostEqual(f_cy, 0, 10, "NSD not nul with cy") - - -def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(TestDistance("test_invariants")) - testSuite.addTest(TestDistance("test_distance")) - testSuite.addTest(TestDistance("test_same")) - return testSuite - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_fitting.py b/freesas/test/test_fitting.py deleted file mode 100644 index 1e55f33..0000000 --- a/freesas/test/test_fitting.py +++ /dev/null @@ -1,938 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 - -"""Test the functionality of fitting module.""" - -__authors__ = ["Martha Brennich"] -__license__ = "MIT" -__date__ = "16/07/2021" - - -import unittest -from unittest.mock import patch, MagicMock -import logging -import sys -import importlib -import platform -from io import StringIO -import pathlib -import contextlib -from types import SimpleNamespace -from typing import Callable -from errno import ENOENT -import numpy -from ..fitting import ( - set_logging_level, - get_output_destination, - get_guinier_header, - rg_result_to_output_line, - get_linesep, - run_guinier_fit, -) -from ..autorg import RG_RESULT, InsufficientDataError, NoGuinierRegionError -from ..sas_argparser import GuinierParser - -if sys.version_info.minor > 6: - from unittest.mock import mock_open -else: - from .mock_open_38 import mock_open - - -logger = logging.getLogger(__name__) - - -def reload_os_and_fitting(): - """Some tests patch os and need to reload the modules.""" - os = importlib.import_module("os") - os = importlib.reload(os) - fit = importlib.import_module("..fitting", "freesas.subpkg") - fit = importlib.reload(fit) - return fit - - -def get_dummy_guinier_parser(**parse_output): - """Function which provides a fake GuinierParser with a predefined parse result.""" - - def get_mock_parse(**kwargs): - def mock_parse(): - return SimpleNamespace(**kwargs) - - return mock_parse - - parser = GuinierParser(prog="test", description="test", epilog="test") - parser.parse_args = get_mock_parse(**parse_output) - return parser - - -def patch_linesep(test_function): - """Patch fitting.linesep to "linesep".""" - - linesep_patch = patch( - "freesas.fitting.get_linesep", - MagicMock(return_value="linesep"), - ) - return linesep_patch(test_function) - - -def patch_collect_files(test_function): - """Patch fitting.collect_files to return Paths "test" and "test2".""" - - collect_files_patch = patch( - "freesas.fitting.collect_files", - MagicMock(return_value=[pathlib.Path("test"), pathlib.Path("test2")]), - ) - return collect_files_patch(test_function) - - -def counted(function: Callable) -> Callable: - """Wrapper for functions to keep track on how often it has been called.""" - - def wrapped(*args, **kwargs): - wrapped.calls += 1 - return function(*args, **kwargs) - - wrapped.calls = 0 - return wrapped - - -def build_mock_for_load_scattering_with_Errors(erronous_file: dict): - - """Create mock for loading of data from a file. - The resulting function will raise an error, - for files for which an error is provided in errenous_file, - and an ndarry for all other files.""" - - def mock_for_load_scattering(file: pathlib.Path): - if file.name in erronous_file: - raise erronous_file[file.name] - else: - return numpy.array( - [[1.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ) - - return mock_for_load_scattering - - -class TestFitting(unittest.TestCase): - def test_set_logging_level_does_not_change_logging_level_if_input_lower_1( - self, - ): - - """ - Test that the logging level only gets changed if the requested level is > 0. - """ - - initial_logging_level = logging.root.level - set_logging_level(0) - self.assertEqual( - logging.root.level, - initial_logging_level, - msg="setting verbosity to 0 dos not affect logging level", - ) - set_logging_level(-2) - self.assertEqual( - logging.root.level, - initial_logging_level, - msg="settting verbosity to -2 does not affect logging level", - ) - # Ensure that initial logging level is restored - logging.root.setLevel(initial_logging_level) - - def test_set_logging_level_sets_logging_to_INFO_if_input_is_1( - self, - ): - - """ - Test that the logging level gets changed to INFO if verbosity is 1. - """ - initial_logging_level = logging.root.level - # Ensure that the function actually changes the level - logging.root.setLevel(logging.WARNING) - - set_logging_level(1) - self.assertEqual( - logging.root.level, - logging.INFO, - msg="settting verbosity to 1 sets logging level to INFO", - ) - - # Ensure that initial logging level is restored - logging.root.setLevel(initial_logging_level) - - def test_set_logging_level_sets_logging_to_DEBUG_if_input_is_2_or_more( - self, - ): - - """ - Test that the logging level gets changed to DEBUG if verbosity is 2 or larger. - """ - initial_logging_level = logging.root.level - # Ensure that the function actually changes the level - logging.root.setLevel(logging.WARNING) - - set_logging_level(2) - self.assertEqual( - logging.root.level, - logging.DEBUG, - msg="settting verbosity to 2 sets logging level to DEBUG", - ) - - set_logging_level(3) - self.assertEqual( - logging.root.level, - logging.DEBUG, - msg="settting verbosity to 3 sets logging level to DEBUG", - ) - - # Ensure that initial logging level is restored - logging.root.setLevel(initial_logging_level) - - @patch.dict("sys.modules", {"nt": MagicMock()}) - def test_get_linesep_returns_rn_if_output_is_stdout_on_windows(self): - - """ - Test that get_linesep() returns \r\n if output destination is sys.stdout on Windows. - """ - # Reload to apply patches - with patch("sys.builtin_module_names", ["nt"]): - fit = reload_os_and_fitting() - - self.assertEqual(fit.get_linesep(sys.stdout), "\r\n") - - # Cleanup - reload_os_and_fitting() - - @unittest.skipIf(platform.system() == "Windows", "Only POSIX") - def test_get_linesep_returns_n_if_output_is_stdout_on_posix( - self, - ): - - """ - Test that get_linesep() returns \n if output destination is sys.stdout on Posix. - Only should run on posix. - """ - self.assertEqual(get_linesep(sys.stdout), "\n") - - @patch.dict("sys.modules", {"nt": MagicMock()}) - def test_get_linesep_returns_n_if_output_is_filestream_on_windows(self): - - """ - Test that get_linesep() returns \n if output destination is a filestream on Windows. - """ - # Reload to apply patches - with patch("sys.builtin_module_names", ["nt"]): - fit = reload_os_and_fitting() - output_dest = StringIO() - self.assertEqual(fit.get_linesep(output_dest), "\n") - - # Cleanup - _ = reload_os_and_fitting() - - @unittest.skipIf(platform.system() == "Windows", "Only POSIX") - def test_get_linesep_returns_n_if_output_is_filestream_on_posix( - self, - ): - - """ - Test that get_linesep() returns \n if output destination is filestream on Posix. - Only should run on posix. - """ - output_dest = StringIO() - self.assertEqual(get_linesep(output_dest), "\n") - - def test_get_output_destination_with_path_input_returns_writable_io( - self, - ): - - """Test that by calling get_output_destination with a Path as input - we obtain write access to the file of Path.""" - mocked_open = mock_open() - with patch("builtins.open", mocked_open): - with get_output_destination(pathlib.Path("test")) as destination: - self.assertTrue( - destination.writable(), - msg="file destination is writable", - ) - mocked_open.assert_called_once_with(pathlib.Path("test"), "w") - - def test_get_output_destination_without_input_returns_stdout( - self, - ): - - """Test that by calling get_output_destination without input - we obtain sys.stdout.""" - with get_output_destination() as destination: - self.assertEqual( - destination, - sys.stdout, - msg="default destination is sys.stdout", - ) - - def test_closing_get_output_destination_does_not_close_stdout( - self, - ): - - """Test that get_output_destination() can be safely used without closing sys.stdout.""" - - with get_output_destination() as _: - pass - output_catcher = StringIO() - with contextlib.redirect_stdout(output_catcher): - sys.stdout.write("test after context closed") - self.assertEqual( - output_catcher.getvalue(), - "test after context closed", - msg="Can write to sys.stdout after closing desitnation context", - ) - - def test_get_guinier_header_for_csv( - self, - ): - - """Test that by calling get_guinier_header with input csv we get the correct line.""" - - header = get_guinier_header("linesep", "csv") - self.assertEqual( - header, - "File,Rg,Rg StDev,I(0),I(0) StDev,First point,Last point,Quality,Aggregatedlinesep", - msg="csv header is correct", - ) - - def test_get_guinier_header_for_ssv( - self, - ): - - """Test that by calling get_guinier_header with input ssv we get an empty string.""" - - header = get_guinier_header("linesep", "ssv") - self.assertEqual( - header, - "", - msg="ssv header is correct", - ) - - def test_get_guinier_header_for_native( - self, - ): - - """Test that by calling get_guinier_header with input native we get an empty string.""" - - header = get_guinier_header("linesep", "native") - self.assertEqual( - header, - "", - msg="native header is correct", - ) - - def test_get_guinier_header_without_input_format( - self, - ): - - """Test that by calling get_guinier_header without input format we get an empty string.""" - - header = get_guinier_header("linesep", None) - self.assertEqual( - header, - "", - msg="header for undefined format is correct", - ) - - def test_collect_files_only_returns_existing_files(self): - - """Test that collect_files discards strings that do not match an existing file.""" - - def os_stat_mock(path, **_): - if "good" in pathlib.Path(path).name: - pass - else: - if sys.version_info.minor > 7: - raise ValueError - else: - raise OSError(ENOENT, "dummy") - - mocked_stat = MagicMock(side_effect=os_stat_mock) - with patch("os.stat", mocked_stat): - local_pathlib = importlib.import_module("pathlib") - local_pathlib = importlib.reload(local_pathlib) - fit = importlib.import_module("..fitting", "freesas.subpkg") - fit = importlib.reload(fit) - self.assertEqual( - fit.collect_files(["testgood", "testbad"]), - [local_pathlib.Path("testgood")], - ) - # Reload without the patch - local_pathlib = importlib.reload(local_pathlib) - reload_os_and_fitting() - - @patch("platform.system", MagicMock(return_value="Windows")) - def test_collect_files_globs_on_windows(self): - - """Test that collect_files globs on Windows if no existent files provided.""" - - def os_stat_mock(path): - if sys.version_info.minor > 7: - raise ValueError - else: - raise OSError(ENOENT, "dummy") - - mocked_stat = MagicMock(side_effect=os_stat_mock) - mocked_glob = MagicMock( - side_effect=[ - (p for p in [pathlib.Path("pathA"), pathlib.Path("pathB")]) - ] - ) - with patch("os.stat", mocked_stat): - with patch.object(pathlib.Path, "glob", mocked_glob): - fit = importlib.import_module("..fitting", "freesas.subpkg") - fit = importlib.reload(fit) - self.assertEqual( - fit.collect_files(["testgood"]), - [pathlib.Path("pathA"), pathlib.Path("pathB")], - msg="collect_files on windows returns list if fiel argument does not exist", - ) - mocked_glob.assert_called_once() - - # Reload without the patch - reload_os_and_fitting() - - def test_rg_result_line_csv(self): - - """Test the formatting of a csv result line for a Guinier fit.""" - - test_result = RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - expected_line = "test.file,3.1000,0.1000,103.0000,2.5000, 13,207,50.1000,0.0500lineend" - obtained_line = rg_result_to_output_line( - rg_result=test_result, - afile=pathlib.Path("test.file"), - output_format="csv", - linesep="lineend", - ) - self.assertEqual( - obtained_line, expected_line, msg="csv line for RG_Result correct" - ) - - def test_rg_result_line_ssv(self): - - """Test the formatting of a ssv result line for a Guinier fit.""" - - test_result = RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - expected_line = "3.1000 0.1000 103.0000 2.5000 13 207 50.1000 0.0500 test.filelineend" - obtained_line = rg_result_to_output_line( - rg_result=test_result, - afile=pathlib.Path("test.file"), - output_format="ssv", - linesep="lineend", - ) - self.assertEqual( - obtained_line, expected_line, msg="ssv line for RG_Result correct" - ) - - def test_rg_result_line_native(self): - """Test the formatting of a native result line for a Guinier fit.""" - test_result = RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - expected_line = "test.file Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% lineend" - obtained_line = rg_result_to_output_line( - rg_result=test_result, - afile=pathlib.Path("test.file"), - output_format="native", - linesep="lineend", - ) - self.assertEqual( - obtained_line, - expected_line, - msg="native line for RG_Result correct", - ) - - def test_rg_result_line_no_format(self): - - """Test the formatting of a native result line for a Guinier fit.""" - - test_result = RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - expected_line = "test.file Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% lineend" - obtained_line = rg_result_to_output_line( - rg_result=test_result, - afile=pathlib.Path("test.file"), - linesep="lineend", - ) - self.assertEqual( - obtained_line, - expected_line, - msg="line for RG_Result without format specification correct", - ) - - @patch( - "freesas.fitting.collect_files", - MagicMock(return_value=[pathlib.Path("test")]), - ) - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - return_value=numpy.array( - [[1.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ) - ), - ) - @patch( - "freesas.fitting.get_linesep", - MagicMock(return_value="linesep"), - ) - def test_run_guinier_fit_uses_provided_fit_function(self): - """Test that run_guinier_fit uses fit function provided in the arguments - and outputs its result in a line.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test")], - output=None, - format=None, - unit="nm", - ) - output_catcher = StringIO() - with contextlib.redirect_stdout(output_catcher): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_output = "test Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - self.assertEqual( - output_catcher.getvalue(), - expected_output, - msg="run_guinier_fit provides expected output", - ) - self.assertEqual( - dummy_fit_function.calls, - 1, - msg="Provided fit function was called once", - ) - - @patch( - "freesas.fitting.collect_files", - MagicMock(return_value=[pathlib.Path("test"), pathlib.Path("test")]), - ) - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - return_value=numpy.array( - [[1.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ) - ), - ) - @patch( - "freesas.fitting.get_linesep", - MagicMock(return_value="linesep"), - ) - def test_run_guinier_fit_iterates_over_files(self): - - """Test that run_guinier_fit calls the provided fit function for each provided file.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test"), pathlib.Path("test")], - output=None, - format=None, - unit="nm", - ) - output_catcher = StringIO() - with contextlib.redirect_stdout(output_catcher): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_output = ( - "test Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - * 2 - ) - self.assertEqual( - output_catcher.getvalue(), - expected_output, - msg="run_guinier_fit provides expected output", - ) - self.assertEqual( - dummy_fit_function.calls, - 2, - msg="Provided fit function was called twice", - ) - - @unittest.skip("Unreliable") - @patch( - "freesas.fitting.collect_files", - MagicMock(return_value=[pathlib.Path("test"), pathlib.Path("test2")]), - ) - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - side_effect=build_mock_for_load_scattering_with_Errors( - {pathlib.Path("test").name: OSError} - ) - ), - ) - @patch( - "freesas.fitting.get_linesep", - MagicMock(return_value="linesep"), - ) - def test_run_guinier_outputs_error_if_file_not_found(self): - """Test that run_guinier_fit outputs an error if data loading raises OSError - and continues to the next file.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test"), pathlib.Path("test2")], - output=None, - format=None, - unit="nm", - ) - output_catcher_stdout = StringIO() - output_catcher_stderr = StringIO() - with contextlib.redirect_stdout(output_catcher_stdout): - with contextlib.redirect_stderr(output_catcher_stderr): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_stdout_output = "test2 Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - self.assertEqual( - output_catcher_stdout.getvalue(), - expected_stdout_output, - msg="run_guinier_fit provides expected stdout output", - ) - expected_stderr_output = ( - "ERROR:freesas.test.test_fitting:Unable to read file test" - ) - self.assertTrue( - expected_stderr_output in output_catcher_stderr.getvalue(), - msg="run_guinier_fit provides expected stderr output", - ) - self.assertEqual( - dummy_fit_function.calls, - 1, - msg="Provided fit function was called once", - ) - - @unittest.skip("Unreliable") - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - side_effect=build_mock_for_load_scattering_with_Errors( - {pathlib.Path("test").name: ValueError} - ) - ), - ) - @patch_collect_files - @patch_linesep - def test_run_guinier_outputs_error_if_file_not_parsable(self): - - """Test that run_guinier_fit outputs an error if data loading raises ValueError - and continues to the next file.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test"), pathlib.Path("test2")], - output=None, - format=None, - unit="nm", - ) - output_catcher_stdout = StringIO() - output_catcher_stderr = StringIO() - with contextlib.redirect_stdout(output_catcher_stdout): - with contextlib.redirect_stderr(output_catcher_stderr): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_stdout_output = "test2 Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - self.assertEqual( - output_catcher_stdout.getvalue(), - expected_stdout_output, - msg="run_guinier_fit provides expected stdout output", - ) - expected_stderr_output = ( - "ERROR:freesas.test.test_fitting:Unable to parse file test" - ) - self.assertTrue( - expected_stderr_output in output_catcher_stderr.getvalue(), - msg="run_guinier_fit provides expected stderr output", - ) - self.assertEqual( - dummy_fit_function.calls, - 1, - msg="Provided fit function was called once", - ) - - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - side_effect=[ - numpy.array( - [[0.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - numpy.array( - [[2.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - ] - ), - ) - @patch_collect_files - @patch_linesep - def test_run_guinier_outputs_error_if_fitting_raises_insufficient_data_error( - self, - ): - - """Test that run_guinier_fit outputs an error if fitting raises InsufficientDataError - and continues to the next file.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - if input_data[0, 0] <= 0.1: - raise InsufficientDataError - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test"), pathlib.Path("test2")], - output=None, - format=None, - unit="nm", - ) - output_catcher_stdout = StringIO() - output_catcher_stderr = StringIO() - with contextlib.redirect_stdout(output_catcher_stdout): - with contextlib.redirect_stderr(output_catcher_stderr): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_stdout_output = "test2 Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - self.assertEqual( - output_catcher_stdout.getvalue(), - expected_stdout_output, - msg="run_guinier_fit provides expected stdout output", - ) - expected_stderr_output = "test, InsufficientDataError: " - self.assertTrue( - expected_stderr_output in output_catcher_stderr.getvalue(), - msg="run_guinier_fit provides expected stderr output for fitting InsufficientError", - ) - self.assertEqual( - dummy_fit_function.calls, - 2, - msg="Provided fit function was called twice", - ) - - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - side_effect=[ - numpy.array( - [[0.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - numpy.array( - [[2.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - ] - ), - ) - @patch_collect_files - @patch_linesep - def test_run_guinier_outputs_error_if_fitting_raises_no_guinier_region_error( - self, - ): - - """Test that run_guinier_fit outputs an error if fitting raises NoGuinierRegionError - and continues to the next file.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - if input_data[0, 0] <= 0.1: - raise NoGuinierRegionError - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test"), pathlib.Path("test2")], - output=None, - format=None, - unit="nm", - ) - output_catcher_stdout = StringIO() - output_catcher_stderr = StringIO() - with contextlib.redirect_stdout(output_catcher_stdout): - with contextlib.redirect_stderr(output_catcher_stderr): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_stdout_output = "test2 Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - self.assertEqual( - output_catcher_stdout.getvalue(), - expected_stdout_output, - msg="run_guinier_fit provides expected stdout output", - ) - expected_stderr_output = "test, NoGuinierRegionError: " - self.assertTrue( - expected_stderr_output in output_catcher_stderr.getvalue(), - msg="run_guinier_fit provides expected stderr output fitting NoGuinierRegionError", - ) - self.assertEqual( - dummy_fit_function.calls, - 2, - msg="Provided fit function was called twice", - ) - - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - side_effect=[ - numpy.array( - [[0.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - numpy.array( - [[2.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - ] - ), - ) - @patch_collect_files - @patch_linesep - def test_run_guinier_outputs_error_if_fitting_raises_value_error( - self, - ): - - """Test that run_guinier_fit outputs an error if fitting raises ValueError - and continues to the next file.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - if input_data[0, 0] <= 0.1: - raise ValueError - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test"), pathlib.Path("test2")], - output=None, - format=None, - unit="nm", - ) - output_catcher_stdout = StringIO() - output_catcher_stderr = StringIO() - with contextlib.redirect_stdout(output_catcher_stdout): - with contextlib.redirect_stderr(output_catcher_stderr): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_stdout_output = "test2 Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - self.assertEqual( - output_catcher_stdout.getvalue(), - expected_stdout_output, - msg="run_guinier_fit provides expected stdout output", - ) - expected_stderr_output = "test, ValueError: " - self.assertTrue( - expected_stderr_output in output_catcher_stderr.getvalue(), - msg="run_guinier_fit provides expected stderr output fitting ValueError", - ) - self.assertEqual( - dummy_fit_function.calls, - 2, - msg="Provided fit function was called twice", - ) - - @patch( - "freesas.fitting.load_scattering_data", - MagicMock( - side_effect=[ - numpy.array( - [[0.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - numpy.array( - [[2.0, 1.0, 1.0], [2.0, 2.0, 1.0], [3.0, 3.0, 3.0]] - ), - ] - ), - ) - @patch_collect_files - @patch_linesep - def test_run_guinier_outputs_error_if_fitting_raises_index_error( - self, - ): - - """Test that run_guinier_fit outputs an error if fitting raises IndexError - and continues to the next file.""" - - @counted - def dummy_fit_function(input_data: numpy.ndarray) -> RG_RESULT: - if input_data[0, 0] <= 0.1: - raise IndexError - return RG_RESULT(3.1, 0.1, 103, 2.5, 13, 207, 50.1, 0.05) - - dummy_parser = get_dummy_guinier_parser( - verbose=0, - file=[pathlib.Path("test"), pathlib.Path("test2")], - output=None, - format=None, - unit="nm", - ) - output_catcher_stdout = StringIO() - output_catcher_stderr = StringIO() - with contextlib.redirect_stdout(output_catcher_stdout): - with contextlib.redirect_stderr(output_catcher_stderr): - run_guinier_fit( - fit_function=dummy_fit_function, - parser=dummy_parser, - logger=logger, - ) - expected_stdout_output = "test2 Rg=3.1000(±0.1000) I0=103.0000(±2.5000) [13-207] 5010.00% linesep" - self.assertEqual( - output_catcher_stdout.getvalue(), - expected_stdout_output, - msg="run_guinier_fit provides expected stdout output", - ) - expected_stderr_output = "test, IndexError: " - self.assertTrue( - expected_stderr_output in output_catcher_stderr.getvalue(), - msg="run_guinier_fit provides expected stderr output for fitting IndexError", - ) - self.assertEqual( - dummy_fit_function.calls, - 2, - msg="Provided fit function was called twice", - ) - - -def suite(): - """Build a test suite from the TestFitting class.""" - - test_suite = unittest.TestSuite() - for class_element in dir(TestFitting): - if class_element.startswith("test"): - test_suite.addTest(TestFitting(class_element)) - return test_suite - - -if __name__ == "__main__": - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_model.py b/freesas/test/test_model.py deleted file mode 100644 index bdbeb9c..0000000 --- a/freesas/test/test_model.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 -from __future__ import print_function - -__author__ = "Guillaume" -__license__ = "MIT" -__copyright__ = "2015, ESRF" - -import numpy -import unittest -import os -import tempfile -from .utilstests import get_datafile -from ..model import SASModel -from ..transformations import translation_from_matrix, euler_from_matrix -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("SASModel_test") - - -def assign_random_mol(inf=None, sup=None): - if not inf: - inf = 0 - if not sup: - sup = 100 - molecule = numpy.random.randint(inf, sup, size=400).reshape(100, 4).astype(float) - molecule[:, -1] = 1.0 - m = SASModel(molecule) - return m - - -class TesttParser(unittest.TestCase): - testfile = get_datafile("model-01.pdb") - - def setUp(self): - unittest.TestCase.setUp(self) - self.tmpdir = tempfile.mkdtemp() - self.outfile = os.path.join(self.tmpdir, "out.pdb") - - def tearDown(self): - unittest.TestCase.tearDown(self) - for fn in (self.outfile, self.tmpdir): - if os.path.exists(fn): - if os.path.isdir(fn): - os.rmdir(fn) - else: - os.unlink(fn) - - def test_same(self): - m = SASModel() - m.read(self.testfile) - m.save(self.outfile) - infile = open(self.testfile).read() - outfile = open(self.outfile).read() - self.assertEqual(infile, outfile, msg="file content is the same") - - def test_rfactor(self): - m = SASModel() - m.read(self.testfile) - n = SASModel() - n.read(self.testfile) - self.assertEqual(m.rfactor, n.rfactor, msg="R-factor is not the same %s != %s" % (m.rfactor, n.rfactor)) - - def test_init(self): - m = SASModel() - m.read(self.testfile) - n = SASModel(self.testfile) - param1 = m.rfactor - param2 = n.rfactor - self.assertEqual(param1, param2, msg="pdb file not read correctly") - - def test_centroid(self): - m = assign_random_mol() - m.centroid() - if len(m.com) != 3: - logger.error("center of mass has not been saved correctly : length of COM position vector = %s!=3" % (len(m.com))) - mol_centered = m.atoms[:, 0:3] - m.com - center = mol_centered.mean(axis=0) - norm = (center * center).sum() - self.assertAlmostEqual(norm, 0, 12, msg="molecule is not centered : norm of the COM position vector %s!=0" % (norm)) - - def test_inertia_tensor(self): - m = assign_random_mol() - m.inertiatensor() - tensor = m.inertensor - assert tensor.shape == (3, 3), "inertia tensor has not been saved correctly : shape of inertia matrix = %s" % (tensor.shape) - - def test_canonical_translate(self): - m = assign_random_mol() - trans = m.canonical_translate() - if trans.shape != (4, 4): - logger.error("pb with translation matrix shape: shape=%s" % (trans.shape)) - com = m.com - com_componants = [com[0], com[1], com[2]] - trans_vect = [-trans[0, -1], -trans[1, -1], -trans[2, -1]] - self.assertEqual(com_componants, trans_vect, msg="do not translate on canonical position") - - def test_canonical_rotate(self): - m = assign_random_mol() - rot = m.canonical_rotate() - if rot.shape != (4, 4): - logger.error("pb with rotation matrix shape") - if not m.enantiomer: - logger.error("enantiomer has not been selected") - det = numpy.linalg.det(rot) - self.assertAlmostEqual(det, 1, 10, msg="rotation matrix determinant is not 1: %s" % (det)) - - def test_canonical_parameters(self): - m = assign_random_mol() - m.canonical_parameters() - can_param = m.can_param - if len(can_param) != 6: - logger.error("canonical parameters has not been saved properly") - com_trans = translation_from_matrix(m.canonical_translate()) - euler_rot = euler_from_matrix(m.canonical_rotate()) - out_param = [com_trans[0], com_trans[1], com_trans[2], euler_rot[0], euler_rot[1], euler_rot[2]] - self.assertEqual(can_param, out_param, msg="canonical parameters are not the good ones") - - def test_dist(self): - m = assign_random_mol() - n = SASModel(m.atoms) - distance = m.dist(n, m.atoms, n.atoms) - self.assertEqual(distance, 0, msg="NSD different of 0: %s!=0" % (distance)) - - def test_can_transform(self): - m = assign_random_mol() - m.canonical_parameters() - p0 = m.can_param - mol1 = m.transform(p0, [1, 1, 1]) - if abs(mol1 - m.atoms).max() == 0: - logger.error("molecule did not move") - m.atoms = mol1 - m.centroid() - m.inertiatensor() - com = m.com - tensor = m.inertensor - diag = numpy.eye(3) - matrix = tensor - tensor * diag - self.assertAlmostEqual(abs(com).sum(), 0, 10, msg="molecule not on its center of mass") - self.assertAlmostEqual(abs(matrix).sum(), 0, 10, "inertia moments unaligned ") - - def test_dist_move(self): - m = assign_random_mol() - n = SASModel(m.atoms) - m.canonical_parameters() - n.canonical_parameters() - if abs(n.atoms - m.atoms).max() != 0: - logger.error("molecules are different") - p0 = m.can_param - dist_after_mvt = m.dist_after_movement(p0, n, [1, 1, 1]) - self.assertEqual(dist_after_mvt, 0, msg="NSD different of 0: %s!=0" % (dist_after_mvt)) - - def test_reverse_transform(self): - m = assign_random_mol() - n = SASModel(m.atoms) - m.canonical_parameters() - m.atoms = m.transform(m.can_param, [1, 1, 1], reverse=None) - m.atoms = m.transform(m.can_param, [1, 1, 1], reverse=True) - dist = m.dist(n, m.atoms, n.atoms) - self.assertAlmostEqual(dist, 0.0, 10, msg="pb with reverse transformation : %s != 0.0" % dist) - - -def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(TesttParser("test_same")) - testSuite.addTest(TesttParser("test_rfactor")) - testSuite.addTest(TesttParser("test_init")) - testSuite.addTest(TesttParser("test_centroid")) - testSuite.addTest(TesttParser("test_inertia_tensor")) - testSuite.addTest(TesttParser("test_canonical_translate")) - testSuite.addTest(TesttParser("test_canonical_rotate")) - testSuite.addTest(TesttParser("test_canonical_parameters")) - testSuite.addTest(TesttParser("test_dist")) - testSuite.addTest(TesttParser("test_can_transform")) - testSuite.addTest(TesttParser("test_dist_move")) - testSuite.addTest(TesttParser("test_reverse_transform")) - return testSuite - - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_sas_argparser.py b/freesas/test/test_sas_argparser.py deleted file mode 100644 index 3575bea..0000000 --- a/freesas/test/test_sas_argparser.py +++ /dev/null @@ -1,603 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 - -"""Test the functionality of SASParser and GuinierParser""" - -__authors__ = ["Martha Brennich"] -__license__ = "MIT" -__date__ = "25/03/2021" - - -import unittest -import logging -import io -import contextlib -from pathlib import Path -from .. import dated_version as freesas_version -from ..sas_argparser import SASParser, GuinierParser - - -logger = logging.getLogger(__name__) - - -class TestSasArgParser(unittest.TestCase): - def test_minimal_guinier_parser_requires_file_argument(self): - """ - Test that Guinier parser provides error if no file argument is provided. - """ - basic_parser = GuinierParser("program", "description", "epilog") - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args() - except SystemExit: - pass - - self.assertTrue( - basic_parser.usage in output_catcher.getvalue(), - msg="GuinierParser provides usage if no file provided", - ) - self.assertTrue( - "the following arguments are required: FILE" - in output_catcher.getvalue(), - msg="GuinierParser states that the FILE argument is missing if no file provided", - ) - - def test_minimal_guinier_parser_parses_list_of_files(self): - """ - Test that the Guinier parsers parses a list of files. - """ - basic_parser = GuinierParser("program", "description", "epilog") - - parsed_arguments = basic_parser.parse_args(["afile", "bfile", "cfile"]) - - self.assertEqual( - set(parsed_arguments.file), - {"afile", "bfile", "cfile"}, - msg="GuinierParser parses list of files", - ) - - def test_add_file_argument_enables_SASParser_to_recognize_file_lists( - self, - ): - """ - Test that add_file_argument adds the ability to parse a file list to SASParser. - """ - basic_parser = SASParser("program", "description", "epilog") - - # Before running add_file_argument a file argument is not recognized - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["afile"]) - except SystemExit: - pass - self.assertTrue( - "unrecognized arguments: afile" in output_catcher.getvalue(), - msg="Minimal SASParser does not recognize file argument", - ) - - basic_parser.add_file_argument(help_text="file help") - parsed_arguments = basic_parser.parse_args(["afile", "bfile", "cfile"]) - - self.assertEqual( - set(parsed_arguments.file), - {"afile", "bfile", "cfile"}, - msg="GuinierParser parses list of files", - ) - - def test_minimal_parser_usage_includes_program_name(self): - """ - Test that minimal parser includes the provided program in the usage string. - """ - basic_parser = SASParser("test❤️", "description", "epilog") - - self.assertTrue( - "test❤️" in basic_parser.usage, - msg="SASParser usage includes program name", - ) - - def test_minimal_guinier_parser_usage_includes_program_name(self): - """ - Test that minimal parser includes the provided program in the usage string. - """ - basic_parser = GuinierParser("test❤️", "description", "epilog") - - self.assertTrue( - "test❤️" in basic_parser.usage, - msg="GuinierParser usage includes program name", - ) - - def test_minimal_guinier_parser_help_includes_program_description_epilog( - self, - ): - """ - Test that minimal guinier parser includes help includes - the provided program name, description and epilog. - """ - basic_parser = GuinierParser("test❤️", "description📚", "epilog🎦") - output_catcher = io.StringIO() - - try: - with contextlib.redirect_stdout(output_catcher): - _ = basic_parser.parse_args(["--help"]) - except SystemExit: - pass - - self.assertTrue( - "test❤️" in output_catcher.getvalue(), - msg="GuinierParser outputs program name in help", - ) - - self.assertTrue( - "description📚" in output_catcher.getvalue(), - msg="GuinierParser outputs description in help", - ) - - self.assertTrue( - "epilog🎦" in output_catcher.getvalue(), - msg="GuinierParser outputs eplilog name in help", - ) - - def test_minimal_parser_help_includes_program_description_epilog(self): - """ - Test that minimal parser includes help includes - the provided program name, description and epilog. - """ - basic_parser = SASParser("test❤️", "description📚", "epilog🎦") - output_catcher = io.StringIO() - - try: - with contextlib.redirect_stdout(output_catcher): - _ = basic_parser.parse_args(["--help"]) - except SystemExit: - pass - - self.assertTrue( - "test❤️" in output_catcher.getvalue(), - msg="SASParser outputs program name in help", - ) - - self.assertTrue( - "description📚" in output_catcher.getvalue(), - msg="SASParser outputs description in help", - ) - - self.assertTrue( - "epilog🎦" in output_catcher.getvalue(), - msg="SASParser outputs eplilog name in help", - ) - - def test_minimal_parser_default_verbosity_level_is_0(self): - """ - Test that the parser sets the verbosity to 0 if no args are provided - """ - basic_parser = SASParser("program", "description", "epilog") - parsed_arguments = basic_parser.parse_args() - self.assertEqual( - parsed_arguments.verbose, - 0, - msg="SASParser default verbosity is 0", - ) - - def test_minimal_guinier_parser_default_verbosity_level_is_0(self): - """ - Test that the Guinier parser sets the verbosity to 0 if no args are provided - """ - basic_parser = GuinierParser("program", "description", "epilog") - parsed_arguments = basic_parser.parse_args(["afile"]) - self.assertEqual( - parsed_arguments.verbose, - 0, - msg="GuinierParser default verbosity is 0", - ) - - def test_minimal_parser_accumulates_verbosity_level(self): - """ - Test that the parser parser increases the verbosity level to two - if -vv argument is provided. - """ - basic_parser = SASParser("program", "description", "epilog") - parsed_arguments = basic_parser.parse_args(["-vv"]) - self.assertEqual( - parsed_arguments.verbose, - 2, - msg="SASParser verbosity increased to 2 by -vv", - ) - - def test_minimal_guinier_parser_accumulates_verbosity_level(self): - """ - Test that the parser parser increases the verbosity level to two - if -vv argument is provided. - """ - basic_parser = GuinierParser("program", "description", "epilog") - parsed_arguments = basic_parser.parse_args(["afile", "-vv"]) - self.assertEqual( - parsed_arguments.verbose, - 2, - msg="GuinierParser verbosity increased to 2 by -vv", - ) - - def test_minimal_parser_provides_correct_version(self): - """ - Test that parser provides the correct app version. - """ - basic_parser = SASParser("program", "description", "epilog") - output_catcher = io.StringIO() - try: - with contextlib.redirect_stdout(output_catcher): - _ = basic_parser.parse_args(["--version"]) - except SystemExit: - pass - - self.assertTrue( - freesas_version.version in output_catcher.getvalue(), - msg="SASParser outputs consistent version", - ) - self.assertTrue( - freesas_version.date in output_catcher.getvalue(), - msg="SASParser outputs consistent date", - ) - - def test_minimal_guinier_parser_provides_correct_version(self): - """ - Test that parser provides the correct app version. - """ - basic_parser = GuinierParser("program", "description", "epilog") - output_catcher = io.StringIO() - try: - with contextlib.redirect_stdout(output_catcher): - _ = basic_parser.parse_args(["--version"]) - except SystemExit: - pass - - self.assertTrue( - freesas_version.version in output_catcher.getvalue(), - msg="GuinierParser outputs consistent version", - ) - self.assertTrue( - freesas_version.date in output_catcher.getvalue(), - msg="GuinierParser outputs consistent date", - ) - - def test_minimal_guinier_parser_accepts_output_file_argument(self): - """ - Test that minimal Guinier parser accepts one output file argument. - """ - basic_parser = GuinierParser("program", "description", "epilog") - parsed_arguments = basic_parser.parse_args(["afile", "-o", "out.file"]) - - self.assertEqual( - parsed_arguments.output, - Path("out.file"), - msg="Minimal GuinierParser accepts output file argument", - ) - - def test_add_output_filename_argument_adds_output_file_argument_to_SASParser( - self, - ): - """ - Test that add_output_filename_argument adds one output file argument to as SASParser. - """ - basic_parser = SASParser("program", "description", "epilog") - - # Before running add_output_filename_argument -o file is not regognized - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["-o", "out.file"]) - except SystemExit: - pass - self.assertTrue( - "unrecognized arguments: -o out.file" in output_catcher.getvalue(), - msg="Minimal SASParser does not recognize -o argument", - ) - - basic_parser.add_output_filename_argument() - parsed_arguments = basic_parser.parse_args(["-o", "out.file"]) - - self.assertEqual( - parsed_arguments.output, - Path("out.file"), - msg="SASParser accepts output file argument" - "after running add_output_filename_argument()", - ) - - def test_minimal_guinier_parser_accepts_output_format_argument(self): - """ - Test that minimal Guinier parser accepts one output data format argument. - """ - basic_parser = GuinierParser("program", "description", "epilog") - parsed_arguments = basic_parser.parse_args(["afile", "-f", "aformat"]) - - self.assertEqual( - parsed_arguments.format, - "aformat", - msg="Minimal GuinierParser accepts output data format argument", - ) - - def test_add_output_data_format_adds_output_format_argument_to_SASParser( - self, - ): - """ - Test that add_output_data_format adds one output data format argument to as SASParser. - """ - basic_parser = SASParser("program", "description", "epilog") - - # Before running add_output_filename_argument -o file is not regognized - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["-f", "aformat"]) - except SystemExit: - pass - self.assertTrue( - "unrecognized arguments: -f aformat" in output_catcher.getvalue(), - msg="Minimal SASParser does not recognize -f argument", - ) - - basic_parser.add_output_data_format() - parsed_arguments = basic_parser.parse_args(["-f", "aformat"]) - - self.assertEqual( - parsed_arguments.format, - "aformat", - msg="SASParser accepts output data format argument" - "after running add_output_data_format()", - ) - - def test_minimal_guinier_parser_accepts_q_unit_argument(self): - """ - Test that minimal Guinier parser accepts a q unit argument. - """ - basic_parser = GuinierParser("program", "description", "epilog") - parsed_arguments = basic_parser.parse_args(["afile", "-u", "nm"]) - - self.assertEqual( - parsed_arguments.unit, - "nm", - msg="Minimal GuinierParser accepts q unit argument", - ) - - def test_add_q_unit_argument_adds_add_q_unit_argument_to_SASParser( - self, - ): - """ - Test that add_q_unit_argument adds a q unit argument to as SASParser. - """ - basic_parser = SASParser("program", "description", "epilog") - - # Before running add_output_filename_argument -o file is not regognized - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["-u", "nm"]) - except SystemExit: - pass - self.assertTrue( - "unrecognized arguments: -u nm" in output_catcher.getvalue(), - msg="Minimal SASParser does not recognize -u argument", - ) - - basic_parser.add_q_unit_argument() - parsed_arguments = basic_parser.parse_args(["-u", "nm"]) - - self.assertEqual( - parsed_arguments.unit, - "nm", - msg="SASParser accepts q unit argument after running add_q_unit_argument()", - ) - - def test_SASParser_q_unit_argument_allows_predefined_units( - self, - ): - """ - Test that the q unit argument of a SASparser accepts "nm", "Å", "A". - """ - basic_parser = SASParser("program", "description", "epilog") - basic_parser.add_q_unit_argument() - - parsed_arguments = basic_parser.parse_args(["-u", "nm"]) - self.assertEqual( - parsed_arguments.unit, - "nm", - msg="SASParser accepts unit format nm", - ) - - parsed_arguments = basic_parser.parse_args(["-u", "A"]) - self.assertEqual( - parsed_arguments.unit, - "Å", - msg="SASParser accepts unit format A", - ) - - parsed_arguments = basic_parser.parse_args(["-u", "Å"]) - self.assertEqual( - parsed_arguments.unit, - "Å", - msg="SASParser accepts unit format A", - ) - - def test_SASParser_q_unit_argument_does_not_allow_not_predefined_units( - self, - ): - """ - Test that the q unit argument of a SASparser does not accept a - unit that is not "nm", "Å", "A". - """ - basic_parser = SASParser("program", "description", "epilog") - basic_parser.add_q_unit_argument() - - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["-u", "m"]) - except SystemExit: - pass - self.assertTrue( - "argument -u/--unit: invalid choice: 'm' (choose from 'nm', 'Å', 'A')" - in output_catcher.getvalue(), - msg="SASParser does not accept '-u m' argument", - ) - - def test_SASParser_q_unit_A_gets_converted_to_Å( - self, - ): - """ - Test that for a SASParder q unit input "A" gets converted to "Å". - """ - basic_parser = SASParser("program", "description", "epilog") - basic_parser.add_q_unit_argument() - - parsed_arguments = basic_parser.parse_args(["-u", "A"]) - self.assertEqual( - parsed_arguments.unit, - "Å", - msg="SASParser converts unit input 'A' to 'Å'", - ) - - def test_GuinierParser_q_unit_argument_allows_predefined_units( - self, - ): - """ - Test that the q unit argument of a Guinierparser accepts "nm", "Å", "A". - """ - basic_parser = GuinierParser("program", "description", "epilog") - - parsed_arguments = basic_parser.parse_args(["afile", "-u", "nm"]) - self.assertEqual( - parsed_arguments.unit, - "nm", - msg="SASParser accepts unit format nm", - ) - - parsed_arguments = basic_parser.parse_args(["afile", "-u", "A"]) - self.assertEqual( - parsed_arguments.unit, - "Å", - msg="SASParser accepts unit format A", - ) - - parsed_arguments = basic_parser.parse_args(["afile", "-u", "Å"]) - self.assertEqual( - parsed_arguments.unit, - "Å", - msg="SASParser accepts unit format A", - ) - - def test_GuinierParser_q_unit_argument_does_not_allow_not_predefined_units( - self, - ): - """ - Test that the q unit argument of a Guinierparser does not accept a - unit that is not "nm", "Å", "A". - """ - basic_parser = GuinierParser("program", "description", "epilog") - - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["afile", "-u", "m"]) - except SystemExit: - pass - self.assertTrue( - "argument -u/--unit: invalid choice: 'm' (choose from 'nm', 'Å', 'A')" - in output_catcher.getvalue(), - msg="SASParser does not accept '-u m' argument", - ) - - def test_GuinierParser_q_unit_A_gets_converted_to_Å( - self, - ): - """ - Test that for a GuinierParser q unit input "A" gets converted to "Å". - """ - basic_parser = GuinierParser("program", "description", "epilog") - - parsed_arguments = basic_parser.parse_args(["afile", "-u", "A"]) - self.assertEqual( - parsed_arguments.unit, - "Å", - msg="SASParser converts unit input 'A' to 'Å'", - ) - - def test_add_argument_adds_an_argument_to_a_SASParser( - self, - ): - """ - Test that new arguments can be added to SASParser. - """ - basic_parser = SASParser("program", "description", "epilog") - - # Before running add_argument -c - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["-c"]) - except SystemExit: - pass - self.assertTrue( - "unrecognized arguments: -c" in output_catcher.getvalue(), - msg="Minimal SASParser does not recognize -c argument", - ) - - basic_parser.add_argument( - "-c", - "--check", - action="store_true", - ) - - parsed_arguments = basic_parser.parse_args(["-c"]) - self.assertEqual( - parsed_arguments.check, - True, - msg="-c argument added to SASParser", - ) - - def test_add_argument_adds_an_argument_to_a_GuinierParser( - self, - ): - """ - Test that new arguments can be added to GuinierParser. - """ - basic_parser = GuinierParser("program", "description", "epilog") - - # Before running add_argument -c - output_catcher = io.StringIO() - try: - with contextlib.redirect_stderr(output_catcher): - _ = basic_parser.parse_args(["afile", "-c"]) - except SystemExit: - pass - print(output_catcher.getvalue()) - self.assertTrue( - "unrecognized arguments: -c" in output_catcher.getvalue(), - msg="Minimal GuinierParser does not recognize -c argument", - ) - - basic_parser.add_argument( - "-c", - "--check", - action="store_true", - ) - - parsed_arguments = basic_parser.parse_args(["afile", "-c"]) - self.assertEqual( - parsed_arguments.check, - True, - msg="-c argument added to GuinierParser", - ) - - -def suite(): - """Build a test suite from the TestSasArgParser class""" - test_suite = unittest.TestSuite() - for class_element in dir(TestSasArgParser): - if class_element.startswith("test"): - test_suite.addTest(TestSasArgParser(class_element)) - return test_suite - - -if __name__ == "__main__": - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/test_sasio.py b/freesas/test/test_sasio.py deleted file mode 100644 index c897b21..0000000 --- a/freesas/test/test_sasio.py +++ /dev/null @@ -1,192 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Project: freesas -# https://github.com/kif/freesas -# -# Copyright (C) 2017-2022 European Synchrotron Radiation Facility, Grenoble, France -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__authors__ = ["Martha Brennich", "Jérôme Kieffer"] -__license__ = "MIT" -__date__ = "16/09/2022" - -import unittest -import logging -import io -from numpy import array, allclose -from ..sasio import parse_ascii_data, load_scattering_data, \ - convert_inverse_angstrom_to_nanometer -logger = logging.getLogger(__name__) - -class TestSasIO(unittest.TestCase): - - def test_parse_3_ok(self): - """ - Test for successful parsing of file with some invalid lines - """ - file_content = ["Test data for", - "file parsing", - "1 1 1", - "2 a 2", - "3 3 3", - "some stuff at the end", - ] - expected_result = array([[1.0, 1.0, 1.0], [3.0, 3.0, 3.0]]) - data = parse_ascii_data(file_content, number_of_columns=3) - self.assertTrue(allclose(data, expected_result, 1e-7), - msg="3 column parse returns expected result") - - def test_parse_no_data(self): - """ - Test that an empty input list raises a ValueError - """ - file_content = [] - with self.assertRaises(ValueError, msg="Empty list cannot be parsed"): - parse_ascii_data(file_content, number_of_columns=3) - - def test_parse_no_valid_data(self): - """ - Test that an input list with no valid data raises a ValueError - """ - file_content = ["a a a", "2 4", "3 4 5 6", "# 3 4 6"] - with self.assertRaises(ValueError, - msg="File with no float float float data" - " cannot be parsed"): - parse_ascii_data(file_content, number_of_columns=3) - - def test_load_clean_data(self): - """ - Test that clean float float float data is loaded correctly. - """ - file_content = ["# Test data for" - "# file parsing", - "1 1 1", - "2.0 2.0 1.0", - "3 3 3", - "#REMARK some stuff at the end", - ] - expected_result = array([[1.0, 1.0, 1.0], - [2.0, 2.0, 1.0], - [3.0, 3.0, 3.0]]) - file_data = "\n".join(file_content) - mocked_file = io.StringIO(file_data) - data = load_scattering_data(mocked_file) - self.assertTrue(allclose(data, expected_result, 1e-7), - msg="Sunny data loaded correctly") - - def test_load_data_with_unescaped_header(self): - """ - Test that an unescaped header does not hinder loading. - """ - file_content = ["Test data for", - "file parsing", - "1 1 1", - "2.0 2.0 1.0", - "3 3 3", - ] - expected_result = array([[1.0, 1.0, 1.0], - [2.0, 2.0, 1.0], - [3.0, 3.0, 3.0]]) - file_data = "\n".join(file_content) - mocked_file = io.StringIO(file_data) - data = load_scattering_data(mocked_file) - - self.assertTrue(allclose(data, expected_result, 1e-7), - msg="Sunny data loaded correctly") - - def test_load_data_with_unescaped_footer(self): - """ - Test that an unescaped footer does not hinder loading. - """ - file_content = [ - "1 1 1", - "2.0 2.0 1.0", - "3 3 3", - "REMARK some stuff at the end" - ] - expected_result = array([[1.0, 1.0, 1.0], - [2.0, 2.0, 1.0], - [3.0, 3.0, 3.0]]) - file_data = "\n".join(file_content) - mocked_file = io.StringIO(file_data) - data = load_scattering_data(mocked_file) - - self.assertTrue(allclose(data, expected_result, 1e-7), - msg="Sunny data loaded correctly") - - - def test_load_invalid_data(self): - """ - Test that invalid data raises a ValueError. - """ - file_content = ["a a a", "2 4", "3 4 5 6", "# 3 4 6"] - file_data = "\n".join(file_content) - mocked_file = io.StringIO(file_data) - with self.assertRaises(ValueError, - msg="File with no float float float " - "data cannot be loaded"): - load_scattering_data(mocked_file) - - def test_convert_inverse_angstrom_to_nanometer(self): - """ - Test conversion of data with q in 1/Å to 1/nm - """ - input_data = array([[1.0, 1.0, 1.0], - [2.0, 2.0, 1.0], - [3.0, 3.0, 3.0]]) - expected_result = array([[10, 1.0, 1.0], - [20, 2.0, 1.0], - [30, 3.0, 3.0]]) - result = convert_inverse_angstrom_to_nanometer(input_data) - self.assertTrue(allclose(result, expected_result, 1e-7), - msg="Converted to 1/nm from 1/Å") - - def test_unit_conversion_creates_new_array(self): - """ - Test conversion of data does not change original data - """ - input_data = array([[1.0, 1.0, 1.0], - [2.0, 2.0, 1.0], - [3.0, 3.0, 3.0]]) - expected_data = array([[1.0, 1.0, 1.0], - [2.0, 2.0, 1.0], - [3.0, 3.0, 3.0]]) - _ = convert_inverse_angstrom_to_nanometer(input_data) - self.assertTrue(allclose(input_data, expected_data, 1e-7), - msg="Conversion function does not change its input") - - -def suite(): - test_suite = unittest.TestSuite() - test_suite.addTest(TestSasIO("test_parse_3_ok")) - test_suite.addTest(TestSasIO("test_parse_no_data")) - test_suite.addTest(TestSasIO("test_parse_no_valid_data")) - test_suite.addTest(TestSasIO("test_load_clean_data")) - test_suite.addTest(TestSasIO("test_load_data_with_unescaped_header")) - test_suite.addTest(TestSasIO("test_load_data_with_unescaped_footer")) - test_suite.addTest(TestSasIO("test_load_invalid_data")) - test_suite.addTest(TestSasIO("test_convert_inverse_angstrom_to_nanometer")) - test_suite.addTest(TestSasIO("test_unit_conversion_creates_new_array")) - return test_suite - - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(suite()) diff --git a/freesas/test/utilstests.py b/freesas/test/utilstests.py deleted file mode 100644 index d899f57..0000000 --- a/freesas/test/utilstests.py +++ /dev/null @@ -1,24 +0,0 @@ -#!usr/bin/env python -# coding: utf-8 - -__author__ = "Jérôme Kieffer" -__license__ = "MIT" -__date__ = "19/07/2021" -__copyright__ = "2015-2021, ESRF" - -import logging -logger = logging.getLogger("utilstest") -from silx.resources import ExternalResources -downloader = ExternalResources("freesas", "http://www.silx.org/pub/freesas/testdata", "FREESAS_TESTDATA") - - -def get_datafile(name): - """Provides the full path of a test file, - downloading it from the internet if needed - - :param name: name of the file to get - :return: full path of the datafile - """ - logger.info(f"Download file {name}") - fullpath = downloader.getfile(name) - return fullpath