diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md index fa94779..6107962 100644 --- a/.github/ISSUE_TEMPLATE/release_checklist.md +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -13,30 +13,30 @@ assignees: "" - [ ] License information is verified as correct. If you are unsure, please comment below. - [ ] Locally rendered documentation contains all appropriate pages, including API references (check no modules are missing), tutorials, and other human-written text is up-to-date with any changes in the code. -- [ ] Installation instructions in the README, documentation, and the website (e.g., diffpy.org) are updated. +- [ ] Installation instructions in the README, documentation, and the website are updated. - [ ] Successfully run any tutorial examples or do functional testing with the latest Python version. - [ ] Grammar and writing quality are checked (no typos). - [ ] Install `pip install build twine`, run `python -m build` and `twine check dist/*` to ensure that the package can be built and is correctly formatted for PyPI release. -Please mention @sbillinge here when you are ready for PyPI/GitHub release. Include any additional comments necessary, such as version information and details about the pre-release here: +Please tag the maintainer (e.g., @username) in the comment here when you are ready for the PyPI/GitHub release. Include any additional comments necessary, such as version information and details about the pre-release here: ### PyPI/GitHub full-release preparation checklist: - [ ] Create a new conda environment and install the rc from PyPI (`pip install ==??`) - [ ] License information on PyPI is correct. -- [ ] Docs are deployed successfully to `https://www.diffpy.org/`. +- [ ] Docs are deployed successfully to `https:///`. - [ ] Successfully run all tests, tutorial examples or do functional testing. -Please let @sbillinge know that all checks are done and the package is ready for full release. +Please let the maintainer know that all checks are done and the package is ready for full release. ### conda-forge release preparation checklist: - + - [ ] Ensure that the full release has appeared on PyPI successfully. - [ ] New package dependencies listed in `conda.txt` and `test.txt` are added to `meta.yaml` in the feedstock. -- [ ] Close any open issues on the feedstock. Reach out to @bobleesj if you have questions. -- [ ] Tag @sbillinge and @bobleesj for conda-forge release. +- [ ] Close any open issues on the feedstock. Reach out to the maintainer if you have questions. +- [ ] Tag the maintainer for conda-forge release. ### Post-release checklist diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 0000000..1099d86 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,15 @@ +### What problem does this PR address? + + + +### What should the reviewer(s) do? + + + + diff --git a/.github/workflows/build-wheel-release-upload.yml b/.github/workflows/build-wheel-release-upload.yml index beb05b3..5d80dac 100644 --- a/.github/workflows/build-wheel-release-upload.yml +++ b/.github/workflows/build-wheel-release-upload.yml @@ -8,12 +8,11 @@ on: jobs: release: - uses: Billingegroup/release-scripts/.github/workflows/_build-wheel-release-upload.yml@v0 + uses: scikit-package/release-scripts/.github/workflows/_build-wheel-release-upload.yml@v0 with: project: diffpy.pdffit2 c_extension: true - github_admin_username: sbillinge - + maintainer_GITHUB_username: sbillinge secrets: PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} PAT_TOKEN: ${{ secrets.PAT_TOKEN }} diff --git a/.github/workflows/check-news-item.yml b/.github/workflows/check-news-item.yml index 0184990..bb86a92 100644 --- a/.github/workflows/check-news-item.yml +++ b/.github/workflows/check-news-item.yml @@ -7,6 +7,6 @@ on: jobs: check-news-item: - uses: Billingegroup/release-scripts/.github/workflows/_check-news-item.yml@v0 + uses: scikit-package/release-scripts/.github/workflows/_check-news-item.yml@v0 with: project: diffpy.pdffit2 diff --git a/.github/workflows/matrix-and-codecov-on-merge-to-main.yml b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml index ee58ec4..16e0072 100644 --- a/.github/workflows/matrix-and-codecov-on-merge-to-main.yml +++ b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml @@ -12,10 +12,9 @@ on: jobs: matrix-coverage: - uses: Billingegroup/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 + uses: scikit-package/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 with: project: diffpy.pdffit2 - python_versions: "3.11, 3.12, 3.13" c_extension: true headless: false secrets: diff --git a/.github/workflows/publish-docs-on-release.yml b/.github/workflows/publish-docs-on-release.yml index a9b26da..4af0c50 100644 --- a/.github/workflows/publish-docs-on-release.yml +++ b/.github/workflows/publish-docs-on-release.yml @@ -1,17 +1,12 @@ name: Deploy Documentation on Release on: - release: - types: [published] workflow_dispatch: jobs: docs: - permissions: - contents: write - uses: Billingegroup/release-scripts/.github/workflows/_publish-docs-on-release.yml@v0 + uses: scikit-package/release-scripts/.github/workflows/_publish-docs-on-release.yml@v0 with: project: diffpy.pdffit2 c_extension: true headless: false - python_version: 3.13 diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index 198624a..b0489e0 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -1,19 +1,15 @@ name: Tests on PR on: - push: - branches: - - main pull_request: workflow_dispatch: jobs: - validate: - uses: Billingegroup/release-scripts/.github/workflows/_tests-on-pr.yml@v0 + tests-on-pr: + uses: scikit-package/release-scripts/.github/workflows/_tests-on-pr.yml@v0 with: - project: diffpy.pdffi2 + project: diffpy.pdffit2 c_extension: true headless: false - python_version: 3.13 secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..47f7a01 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,13 @@ +version: 2 + +build: + os: "ubuntu-22.04" + tools: + python: "latest" + +python: + install: + - requirements: requirements/docs.txt + +sphinx: + configuration: doc/source/conf.py diff --git a/doc/source/conf.py b/doc/source/conf.py index c15e863..742d3a5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -18,6 +18,12 @@ from importlib.metadata import version from pathlib import Path +# Attempt to import the version dynamically from GitHub tag. +try: + fullversion = version("diffpy.pdffit2") +except Exception: + fullversion = "No version found. The correct version will appear in the released version." + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use Path().resolve() to make it absolute, like shown here. @@ -26,7 +32,7 @@ sys.path.insert(0, str(Path("../../src").resolve())) # abbreviations -ab_authors = "Billinge Group members and community contributors" +ab_authors = "Billinge group and community members." # -- General configuration ------------------------------------------------ @@ -43,6 +49,7 @@ "sphinx.ext.viewcode", "sphinx.ext.intersphinx", "sphinx_rtd_theme", + "sphinx_copybutton", "m2r", ] @@ -88,6 +95,11 @@ # substitute YEAR in the copyright string copyright = copyright.replace("%Y", year) +# For sphinx_copybutton extension. +# Do not copy "$" for shell commands in code-blocks. +copybutton_prompt_text = r"^\$ " +copybutton_prompt_is_regexp = True + # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["build"] @@ -123,6 +135,14 @@ # html_theme = "sphinx_rtd_theme" +html_context = { + "display_github": True, + "github_user": "diffpy", + "github_repo": "diffpy.pdffit2", + "github_version": "main", + "conf_py_path": "/doc/source/", +} + # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. @@ -221,7 +241,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ("index", "diffpy.pdffit2.tex", "diffpy.pdffit2 Documentation", ab_authors, "manual"), + ( + "index", + "diffpy.pdffit2.tex", + "diffpy.pdffit2 Documentation", + ab_authors, + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -249,7 +275,15 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [("index", "diffpy.pdffit2", "diffpy.pdffit2 Documentation", ab_authors, 1)] +man_pages = [ + ( + "index", + "diffpy.pdffit2", + "diffpy.pdffit2 Documentation", + ab_authors, + 1, + ) +] # If true, show URL addresses after external links. # man_show_urls = False diff --git a/news/setup-CI.rst b/news/setup-CI.rst new file mode 100644 index 0000000..4017a8e --- /dev/null +++ b/news/setup-CI.rst @@ -0,0 +1,23 @@ +**Added:** + +* No news needed: line length fixes and other adjustments to copyright year. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml index 8a1b15c..af52ce0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,13 +6,13 @@ build-backend = "setuptools.build_meta" name = "diffpy.pdffit2" dynamic=['version', 'dependencies'] authors = [ - { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, + { name="Simon Billinge", email="sb2896@columbia.edu" }, ] maintainers = [ - { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, + { name="Simon Billinge", email="sb2896@columbia.edu" }, ] description = "PDFfit2 - real space structure refinement program." -keywords = ["PDF", "structure refinement"] +keywords = ['PDF', 'structure refinement'] readme = "README.rst" requires-python = ">=3.11, <3.14" classifiers = [ @@ -57,7 +57,7 @@ ignore-words = ".codespell/ignore_words.txt" skip = "*.cif,*.dat,*.cc,*.h" [tool.black] -line-length = 115 +line-length = 79 include = '\.pyi?$' exclude = ''' /( diff --git a/requirements/conda.txt b/requirements/conda.txt index 9cc306b..4278ea1 100644 --- a/requirements/conda.txt +++ b/requirements/conda.txt @@ -1 +1,2 @@ diffpy.structure +numpy diff --git a/requirements/docs.txt b/requirements/docs.txt index ab17b1c..5f34c6e 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -1,4 +1,5 @@ sphinx sphinx_rtd_theme +sphinx-copybutton doctr m2r diff --git a/requirements/pip.txt b/requirements/pip.txt index 9cc306b..4278ea1 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -1 +1,2 @@ diffpy.structure +numpy diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index 4633f1a..57e2871 --- a/setup.py +++ b/setup.py @@ -62,7 +62,8 @@ def get_gsl_config(): return rv else: warnings.warn( - f"CONDA_PREFIX is set to {conda_prefix}, " "but GSL not found at those paths. Proceeding..." + f"CONDA_PREFIX is set to {conda_prefix}, " + "but GSL not found at those paths. Proceeding..." ) # 2. Check using GSL_PATH. @@ -76,7 +77,8 @@ def get_gsl_config(): return rv else: raise EnvironmentError( - f"GSL_PATH={gsl_path} is set, but {inc} or {lib} not found. " "Please verify your GSL_PATH." + f"GSL_PATH={gsl_path} is set, but {inc} or {lib} not found. " + "Please verify your GSL_PATH." ) # 3. Try using the gsl-config executable (only on Unix-like systems). @@ -92,8 +94,14 @@ def get_gsl_config(): lib_match = re.search(r"(?m)^[^#]*\s-L(\S+)", txt) if prefix_match: prefix_path = Path(prefix_match.group(1)) - inc_dir = include_match.group(1) if include_match else (prefix_path / "include") - lib_dir = lib_match.group(1) if lib_match else (prefix_path / "lib") + inc_dir = ( + include_match.group(1) + if include_match + else (prefix_path / "include") + ) + lib_dir = ( + lib_match.group(1) if lib_match else (prefix_path / "lib") + ) rv["include_dirs"].append(str(inc_dir)) rv["library_dirs"].append(str(lib_dir)) return rv @@ -164,7 +172,14 @@ def create_extensions(): compiler_type = get_compiler_type() if compiler_type in ("unix", "cygwin", "mingw32"): - extra_compile_args = ["-std=c++11", "-Wall", "-Wno-write-strings", "-O3", "-funroll-loops", "-ffast-math"] + extra_compile_args = [ + "-std=c++11", + "-Wall", + "-Wno-write-strings", + "-O3", + "-funroll-loops", + "-ffast-math", + ] # Check for static GSL libraries and add them if found. static_libs = [ os.path.join(p, "libgsl.a") @@ -189,15 +204,21 @@ def create_extensions(): "extra_link_args": extra_link_args, "extra_objects": extra_objects, } - ext = Extension("diffpy.pdffit2.pdffit2", glob.glob("src/extensions/**/*.cc"), **ext_kws) + ext = Extension( + "diffpy.pdffit2.pdffit2", + glob.glob("src/extensions/**/*.cc"), + **ext_kws, + ) return [ext] +# Extensions not included in pyproject.toml setup_args = dict( ext_modules=[], cmdclass={"build_ext": CustomBuildExt}, ) + if __name__ == "__main__": setup_args["ext_modules"] = create_extensions() setup(**setup_args) diff --git a/src/diffpy/__init__.py b/src/diffpy/__init__.py index 5b3ab1b..2efe939 100644 --- a/src/diffpy/__init__.py +++ b/src/diffpy/__init__.py @@ -3,7 +3,7 @@ # # (c) 2008 trustees of the Michigan State University. # All rights reserved. -# (c) 2024 The Trustees of Columbia University in the City of New York. +# (c) 2025 The Trustees of Columbia University in the City of New York. # All rights reserved. # # File coded by: Billinge Group members and community contributors. diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index dc8aec4..527477a 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -3,7 +3,7 @@ # # (c) 2006 trustees of the Michigan State University. # All rights reserved. -# (c) 2024 The Trustees of Columbia University in the City of New York. +# (c) 2025 The Trustees of Columbia University in the City of New York. # All rights reserved. # # File coded by: Billinge Group members and community contributors. diff --git a/src/diffpy/pdffit2/pdffit.py b/src/diffpy/pdffit2/pdffit.py index 984d67c..2e7ce74 100644 --- a/src/diffpy/pdffit2/pdffit.py +++ b/src/diffpy/pdffit2/pdffit.py @@ -138,7 +138,8 @@ def _exportAll(self, namespace): public = [ a for a in dir(self) - if "__" not in a and a not in ["_handle", "_exportAll", "selalias", "FCON", "Sctp"] + if "__" not in a + and a not in ["_handle", "_exportAll", "selalias", "FCON", "Sctp"] ] for funcname in public: namespace[funcname] = getattr(self, funcname) @@ -234,12 +235,16 @@ def read_data_string(self, data, stype, qmax, qdamp, name=""): qdamp -- instrumental Q-resolution factor name -- tag with which to label data """ - pdffit2.read_data_string(self._handle, data, stype.encode(), qmax, qdamp, name) + pdffit2.read_data_string( + self._handle, data, stype.encode(), qmax, qdamp, name + ) name = data self.data_files.append(name) return - def read_data_lists(self, stype, qmax, qdamp, r_data, Gr_data, dGr_data=None, name="list"): + def read_data_lists( + self, stype, qmax, qdamp, r_data, Gr_data, dGr_data=None, name="list" + ): """read_data_lists(stype, qmax, qdamp, r_data, Gr_data, dGr_data = None, name = "list") --> Read pdf data into memory from lists. @@ -255,7 +260,16 @@ def read_data_lists(self, stype, qmax, qdamp, r_data, Gr_data, dGr_data=None, na Raises: ValueError when the data lists are of different length """ - pdffit2.read_data_arrays(self._handle, stype.encode(), qmax, qdamp, r_data, Gr_data, dGr_data, name) + pdffit2.read_data_arrays( + self._handle, + stype.encode(), + qmax, + qdamp, + r_data, + Gr_data, + dGr_data, + name, + ) self.data_files.append(name) return @@ -296,7 +310,9 @@ def alloc(self, stype, qmax, qdamp, rmin, rmax, bin): ValueError for bad input values pdffit.unassignedError when no structure has been loaded """ - pdffit2.alloc(self._handle, stype.encode(), qmax, qdamp, rmin, rmax, bin) + pdffit2.alloc( + self._handle, stype.encode(), qmax, qdamp, rmin, rmax, bin + ) return def calc(self): @@ -876,8 +892,13 @@ def blen(self, *args): ij = (args[0], args[1]) # indices were already checked in bond_length_atoms call assert (0 <= min(ij) - 1) and (max(ij) - 1 < len(atom_symbols)) - symij = (atom_symbols[ij[0] - 1].upper(), atom_symbols[ij[1] - 1].upper()) - print(_format_bond_length(dij, ddij, ij, symij), file=output.stdout) + symij = ( + atom_symbols[ij[0] - 1].upper(), + atom_symbols[ij[1] - 1].upper(), + ) + print( + _format_bond_length(dij, ddij, ij, symij), file=output.stdout + ) # second form elif len(args) == 4: a1, a2, lb, ub = args @@ -892,7 +913,12 @@ def blen(self, *args): return # arguments are OK here, get bond length dictionary bld = pdffit2.bond_length_types(self._handle, a1, a2, lb, ub) - s = "(%s,%s) bond lengths in [%gA,%gA] for current phase :" % (a1, a2, lb, ub) + s = "(%s,%s) bond lengths in [%gA,%gA] for current phase :" % ( + a1, + a2, + lb, + ub, + ) print(s, file=output.stdout) atom_symbols = self.get_atoms() npts = len(bld["dij"]) diff --git a/src/diffpy/pdffit2/version.py b/src/diffpy/pdffit2/version.py index 6847d8e..a09785e 100644 --- a/src/diffpy/pdffit2/version.py +++ b/src/diffpy/pdffit2/version.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# (c) 2024 The Trustees of Columbia University in the City of New York. +# (c) 2025 The Trustees of Columbia University in the City of New York. # All rights reserved. # # File coded by: Billinge Group members and community contributors. @@ -38,7 +38,9 @@ def get_pypi_release_date(package_name, timeout=5): installed_version = version(package_name) release_data = data["releases"].get(installed_version, []) if not release_data: - raise ValueError(f"No release data found for version {installed_version}") + raise ValueError( + f"No release data found for version {installed_version}" + ) release_date_str = release_data[-1]["upload_time"] release_date = datetime.datetime.fromisoformat(release_date_str).date() @@ -48,7 +50,9 @@ def get_pypi_release_date(package_name, timeout=5): except (ValueError, OSError) as e: print(f"Warning: Could not fetch release date from PyPI: {e}") - release_date = datetime.datetime.fromtimestamp(package_file.stat().st_ctime).isoformat() + release_date = datetime.datetime.fromtimestamp( + package_file.stat().st_ctime + ).isoformat() return str(release_date) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 012b76d..83f2b37 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -38,12 +38,20 @@ def test_IOError(self): def test_structureError(self): """Raise pdffit2.structureError when structure is malformed.""" - self.assertRaises(pdffit2.structureError, self.P.read_struct, self.datafile("badNi.stru")) + self.assertRaises( + pdffit2.structureError, + self.P.read_struct, + self.datafile("badNi.stru"), + ) def test_structureErrorZeroVolume(self): """Raise pdffit2.structureError when unit cell volume is negative.""" # I don't know how to test for this, but it's in the library code - self.assertRaises(pdffit2.structureError, self.P.read_struct, self.datafile("badNiZeroVolume.stru")) + self.assertRaises( + pdffit2.structureError, + self.P.read_struct, + self.datafile("badNiZeroVolume.stru"), + ) class read_dataExceptions(unittest.TestCase): @@ -59,11 +67,20 @@ def tearDown(self): def test_IOError(self): """Raise IOError when data file does not exist.""" - self.assertRaises(IOError, self.P.read_data, "Nofile.dat", "X", 25.0, 0.5) + self.assertRaises( + IOError, self.P.read_data, "Nofile.dat", "X", 25.0, 0.5 + ) def test_dataError(self): """Raise pdffit2.dataError when data has improper spacing.""" - self.assertRaises(pdffit2.dataError, self.P.read_data, self.datafile("badNi.dat"), "X", 25.0, 0.5) + self.assertRaises( + pdffit2.dataError, + self.P.read_data, + self.datafile("badNi.dat"), + "X", + 25.0, + 0.5, + ) class read_data_listsExceptions(unittest.TestCase): @@ -84,26 +101,50 @@ def tearDown(self): def test_ValueError1(self): """Raise ValueError when lists are of different length.""" self.assertRaises( - ValueError, self.P.read_data_lists, "X", self.qmax, self.qdamp, self.r_data, self.Gr_data + ValueError, + self.P.read_data_lists, + "X", + self.qmax, + self.qdamp, + self.r_data, + self.Gr_data, ) def test_ValueError2(self): """Raise ValueError when qmax < 0.""" self.assertRaises( - ValueError, self.P.read_data_lists, "X", -self.qmax, self.qdamp, self.r_data, self.Gr_data + ValueError, + self.P.read_data_lists, + "X", + -self.qmax, + self.qdamp, + self.r_data, + self.Gr_data, ) def test_ValueError3(self): """Raise ValueError when qdamp < 0.""" self.assertRaises( - ValueError, self.P.read_data_lists, "X", self.qmax, -self.qdamp, self.r_data, self.Gr_data + ValueError, + self.P.read_data_lists, + "X", + self.qmax, + -self.qdamp, + self.r_data, + self.Gr_data, ) def test_dataError(self): """Raise pdffit2.dataError when data has improper spacing.""" r_data = [0.1, 0.52, 0.2] self.assertRaises( - pdffit2.dataError, self.P.read_data_lists, "X", self.qmax, self.qdamp, r_data, self.Gr_data + pdffit2.dataError, + self.P.read_data_lists, + "X", + self.qmax, + self.qdamp, + r_data, + self.Gr_data, ) @@ -123,17 +164,23 @@ def tearDown(self): def test_ValueError1(self): """Raise ValueError when iset does not exist.""" - self.assertRaises(ValueError, self.P.pdfrange, self.iset, self.rmin, self.rmax) + self.assertRaises( + ValueError, self.P.pdfrange, self.iset, self.rmin, self.rmax + ) def test_ValueError2(self): """Raise ValueError when rmax < rmin.""" self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.5) - self.assertRaises(ValueError, self.P.pdfrange, self.iset, self.rmax, self.rmin) + self.assertRaises( + ValueError, self.P.pdfrange, self.iset, self.rmax, self.rmin + ) def test_ValueError3(self): """Raise ValueError when range outside of data.""" self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.5) - self.assertRaises(ValueError, self.P.pdfrange, self.iset, -self.rmin, self.rmax) + self.assertRaises( + ValueError, self.P.pdfrange, self.iset, -self.rmin, self.rmax + ) class allocExceptions(unittest.TestCase): @@ -155,37 +202,100 @@ def tearDown(self): def test_ValueError1(self): """Raise ValueError when qmax < 0.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(ValueError, self.P.alloc, "X", -self.qmax, self.qdamp, self.rmin, self.rmax, self.bin) + self.assertRaises( + ValueError, + self.P.alloc, + "X", + -self.qmax, + self.qdamp, + self.rmin, + self.rmax, + self.bin, + ) def test_ValueError2(self): """Raise ValueError when qdamp < 0.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(ValueError, self.P.alloc, "X", self.qmax, -self.qdamp, self.rmin, self.rmax, self.bin) + self.assertRaises( + ValueError, + self.P.alloc, + "X", + self.qmax, + -self.qdamp, + self.rmin, + self.rmax, + self.bin, + ) def test_ValueError3(self): """Raise ValueError when rmin < 0.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(ValueError, self.P.alloc, "X", self.qmax, self.qdamp, -self.rmin, self.rmax, self.bin) + self.assertRaises( + ValueError, + self.P.alloc, + "X", + self.qmax, + self.qdamp, + -self.rmin, + self.rmax, + self.bin, + ) def test_ValueError4(self): """Raise ValueError when rmax < 0.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(ValueError, self.P.alloc, "X", self.qmax, self.qdamp, self.rmin, -self.rmax, self.bin) + self.assertRaises( + ValueError, + self.P.alloc, + "X", + self.qmax, + self.qdamp, + self.rmin, + -self.rmax, + self.bin, + ) def test_ValueError5(self): """Raise ValueError when bin < 0.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(ValueError, self.P.alloc, "X", self.qmax, self.qdamp, self.rmin, self.rmax, -self.bin) + self.assertRaises( + ValueError, + self.P.alloc, + "X", + self.qmax, + self.qdamp, + self.rmin, + self.rmax, + -self.bin, + ) def test_ValueError6(self): """Raise ValueError when rmax < rmin.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(ValueError, self.P.alloc, "X", self.qmax, self.qdamp, self.rmax, self.rmin, self.bin) + self.assertRaises( + ValueError, + self.P.alloc, + "X", + self.qmax, + self.qdamp, + self.rmax, + self.rmin, + self.bin, + ) def test_ValueError7(self): """Raise ValueError when qdamp < 0.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(ValueError, self.P.alloc, "X", self.qmax, self.qdamp, self.rmin, self.rmax, -self.bin) + self.assertRaises( + ValueError, + self.P.alloc, + "X", + self.qmax, + self.qdamp, + self.rmin, + self.rmax, + -self.bin, + ) class calcExceptions(unittest.TestCase): @@ -265,11 +375,15 @@ def test_IOError(self): self.P.read_struct(self.datafile("Ni.stru")) self.P.alloc("X", 30.0, 0.05, 2, 10, 100) self.P.calc() - self.assertRaises(IOError, self.P.save_pdf, 1, "nodir183160/" + self.strufile) + self.assertRaises( + IOError, self.P.save_pdf, 1, "nodir183160/" + self.strufile + ) def test_unassignedError(self): """Raise pdffit2.unassignedError when structure is undefined.""" - self.assertRaises(pdffit2.unassignedError, self.P.save_pdf, 1, self.strufile) + self.assertRaises( + pdffit2.unassignedError, self.P.save_pdf, 1, self.strufile + ) class save_difExceptions(unittest.TestCase): @@ -290,11 +404,15 @@ def test_IOError(self): self.P.alloc("X", 30.0, 0.05, 2, 10, 100) self.P.calc() self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.5) - self.assertRaises(IOError, self.P.save_dif, 1, "nodir183160/" + self.strufile) + self.assertRaises( + IOError, self.P.save_dif, 1, "nodir183160/" + self.strufile + ) def test_unassignedError(self): """Raise pdffit2.unassignedError when structure is undefined.""" - self.assertRaises(pdffit2.unassignedError, self.P.save_dif, 1, self.strufile) + self.assertRaises( + pdffit2.unassignedError, self.P.save_dif, 1, self.strufile + ) class save_resExceptions(unittest.TestCase): @@ -317,11 +435,15 @@ def test_IOError(self): self.P.setpar(1, 3.0) self.P.pdfrange(1, 2.0, 10.0) self.P.refine_step() - self.assertRaises(IOError, self.P.save_res, "nodir183160/" + self.resfile) + self.assertRaises( + IOError, self.P.save_res, "nodir183160/" + self.resfile + ) def test_unassignedError(self): """Raise pdffit2.unassignedError when structure is undefined.""" - self.assertRaises(pdffit2.unassignedError, self.P.save_res, self.resfile) + self.assertRaises( + pdffit2.unassignedError, self.P.save_res, self.resfile + ) class save_structExceptions(unittest.TestCase): @@ -341,11 +463,15 @@ def tearDown(self): def test_IOError(self): """Raise IOError when structure cannot be saved.""" self.P.read_struct(self.datafile("Ni.stru")) - self.assertRaises(IOError, self.P.save_struct, 1, "nodir183160/" + self.strufile) + self.assertRaises( + IOError, self.P.save_struct, 1, "nodir183160/" + self.strufile + ) def test_unassignedError(self): """Raise pdffit2.unassignedError when structure is undefined.""" - self.assertRaises(pdffit2.unassignedError, self.P.save_struct, 1, self.strufile) + self.assertRaises( + pdffit2.unassignedError, self.P.save_struct, 1, self.strufile + ) class constrainExceptions(unittest.TestCase): @@ -374,7 +500,9 @@ def test_constraintError(self): def test_unassignedError(self): """Raise pdffit2.unassignedError when variable is undefined.""" - self.assertRaises(pdffit2.unassignedError, self.P.constrain, self.P.x(1), self.par) + self.assertRaises( + pdffit2.unassignedError, self.P.constrain, self.P.x(1), self.par + ) return def test_ValueError(self): @@ -387,9 +515,15 @@ def test_constrainNonRefVar(self): "raise constraintError when attempting to constrain non-refinables" self.P.read_struct(self.datafile("Ni.stru")) self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.0) - self.assertRaises(pdffit2.constraintError, self.P.constrain, "rcut", "@7") - self.assertRaises(pdffit2.constraintError, self.P.constrain, "rcut", 13) - self.assertRaises(pdffit2.constraintError, self.P.constrain, "stepcut", "@17") + self.assertRaises( + pdffit2.constraintError, self.P.constrain, "rcut", "@7" + ) + self.assertRaises( + pdffit2.constraintError, self.P.constrain, "rcut", 13 + ) + self.assertRaises( + pdffit2.constraintError, self.P.constrain, "stepcut", "@17" + ) return @@ -407,7 +541,9 @@ def tearDown(self): def test_unassignedError(self): """Raise pdffit2.unassignedError when variable is undefined.""" - self.assertRaises(pdffit2.unassignedError, self.P.setvar, self.P.lat(1), self.val) + self.assertRaises( + pdffit2.unassignedError, self.P.setvar, self.P.lat(1), self.val + ) def test_ValueError(self): """Raise ValueError when a variable index does not exist.""" @@ -428,7 +564,9 @@ def tearDown(self): def test_unassignedError(self): """Raise pdffit2.unassignedError when variable is undefined.""" - self.assertRaises(pdffit2.unassignedError, self.P.getvar, self.P.pscale()) + self.assertRaises( + pdffit2.unassignedError, self.P.getvar, self.P.pscale() + ) def test_ValueError(self): """Raise ValueError when a variable index does not exist.""" @@ -572,7 +710,14 @@ def tearDown(self): def test_unassignedError1(self): """Raise pdffit2.unassignedError when set does not exist.""" - self.assertRaises(pdffit2.unassignedError, self.P.selectAtomType, self.iset, "i", "Ni", True) + self.assertRaises( + pdffit2.unassignedError, + self.P.selectAtomType, + self.iset, + "i", + "Ni", + True, + ) def test_unassignedError2(self): """Raise pdffit2.unassignedError when set does not exist.""" @@ -582,7 +727,9 @@ def test_unassignedError2(self): self.P.selectAtomType(self.iset, "i", "Ni", True) self.P.selectAtomType(self.iset, "j", "Ni", False) # but fail for phase 2 which is not present - self.assertRaises(pdffit2.unassignedError, self.P.selectAtomType, 2, "i", "Ca", True) + self.assertRaises( + pdffit2.unassignedError, self.P.selectAtomType, 2, "i", "Ca", True + ) def test_ijcharValueError(self): """Raise ValueError when ijchar is neither 'i' nor 'j'.""" @@ -590,7 +737,9 @@ def test_ijcharValueError(self): self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.0) self.P.selectAtomType(self.iset, "i", "Ni", True) self.P.selectAtomType(self.iset, "j", "Ni", True) - self.assertRaises(ValueError, self.P.selectAtomType, self.iset, "k", "Ni", True) + self.assertRaises( + ValueError, self.P.selectAtomType, self.iset, "k", "Ni", True + ) class selectAtomIndexExceptions(unittest.TestCase): @@ -608,7 +757,14 @@ def tearDown(self): def test_unassignedError1(self): """Raise pdffit2.unassignedError when set does not exist.""" - self.assertRaises(pdffit2.unassignedError, self.P.selectAtomIndex, self.iset, "i", self.i, True) + self.assertRaises( + pdffit2.unassignedError, + self.P.selectAtomIndex, + self.iset, + "i", + self.i, + True, + ) def test_unassignedError2(self): """Raise pdffit2.unassignedError when set does not exist.""" @@ -618,13 +774,17 @@ def test_unassignedError2(self): self.P.selectAtomIndex(self.iset, "i", 1, True) self.P.selectAtomIndex(self.iset, "i", 2, False) # fail for phase 2 - self.assertRaises(pdffit2.unassignedError, self.P.selectAtomIndex, 2, "i", 1, True) + self.assertRaises( + pdffit2.unassignedError, self.P.selectAtomIndex, 2, "i", 1, True + ) def test_ValueError(self): """Raise ValueError when selected atom does not exist.""" self.P.read_struct(self.datafile("Ni.stru")) self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.0) - self.assertRaises(ValueError, self.P.selectAtomIndex, self.iset, "i", 6, True) + self.assertRaises( + ValueError, self.P.selectAtomIndex, self.iset, "i", 6, True + ) class selectAllExceptions(unittest.TestCase): @@ -642,13 +802,17 @@ def tearDown(self): def test_unassignedError1(self): """Raise pdffit2.unassignedError when set does not exist.""" - self.assertRaises(pdffit2.unassignedError, self.P.selectAll, self.iset, "i") + self.assertRaises( + pdffit2.unassignedError, self.P.selectAll, self.iset, "i" + ) def test_unassignedError2(self): """Raise pdffit2.unassignedError when set does not exist.""" self.P.read_struct(self.datafile("Ni.stru")) # fail when there is no dataset - self.assertRaises(pdffit2.unassignedError, self.P.selectAll, self.iset, "i") + self.assertRaises( + pdffit2.unassignedError, self.P.selectAll, self.iset, "i" + ) # pass with dataset self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.0) self.P.selectAll(self.iset, "i") @@ -673,13 +837,17 @@ def tearDown(self): def test_unassignedError1(self): """Raise pdffit2.unassignedError when set does not exist.""" - self.assertRaises(pdffit2.unassignedError, self.P.selectNone, self.iset, "i") + self.assertRaises( + pdffit2.unassignedError, self.P.selectNone, self.iset, "i" + ) def test_unassignedError2(self): """Raise pdffit2.unassignedError when set does not exist.""" self.P.read_struct(self.datafile("Ni.stru")) # fail when there is no dataset - self.assertRaises(pdffit2.unassignedError, self.P.selectNone, self.iset, "i") + self.assertRaises( + pdffit2.unassignedError, self.P.selectNone, self.iset, "i" + ) # pass with dataset self.P.read_data(self.datafile("Ni.dat"), "X", 25.0, 0.0) self.P.selectNone(self.iset, "i") @@ -705,7 +873,9 @@ def tearDown(self): def test_unassignedError(self): """Raise pdffit2.unassignedError when phase does not exist.""" - self.assertRaises(pdffit2.unassignedError, self.P.bang, self.a1, self.a2, self.a3) + self.assertRaises( + pdffit2.unassignedError, self.P.bang, self.a1, self.a2, self.a3 + ) def test_ValueError1(self): """Raise ValueError when selected atom(s) does not exist.""" @@ -739,7 +909,9 @@ def tearDown(self): def test_unassignedError(self): """Raise pdffit2.unassignedError when no data exists.""" - self.assertRaises(pdffit2.unassignedError, self.P.blen, self.a1, self.a2) + self.assertRaises( + pdffit2.unassignedError, self.P.blen, self.a1, self.a2 + ) def test_ValueError1(self): """Raise ValueError when selected atom(s) does not exist.""" diff --git a/tests/test_pdffit.py b/tests/test_pdffit.py index ee1e715..7336f0e 100644 --- a/tests/test_pdffit.py +++ b/tests/test_pdffit.py @@ -259,7 +259,9 @@ def test_get_atoms(self): a1 = self.P.get_atoms() a2 = self.P.get_atoms(2) self.assertEqual(4 * ["NI"], a1) - self.assertEqual(8 * ["PB"] + 24 * ["O"] + 8 * ["SC"] + 8 * ["W"] + 8 * ["TI"], a2) + self.assertEqual( + 8 * ["PB"] + 24 * ["O"] + 8 * ["SC"] + 8 * ["W"] + 8 * ["TI"], a2 + ) return def test_get_atom_types(self): @@ -418,7 +420,9 @@ def doalloc(): self.P.psel("ALL") self.P.calc() Gall = self.P.getpdf_fit() - dGmax = max([abs(g1 + g2 - gall) for g1, g2, gall in zip(G1, G2, Gall)]) + dGmax = max( + [abs(g1 + g2 - gall) for g1, g2, gall in zip(G1, G2, Gall)] + ) self.assertAlmostEqual(0, dGmax, self.places) self.assertRaises(pdffit2.unassignedError, self.P.psel, 10) self.assertRaises(pdffit2.unassignedError, self.P.psel, 0) @@ -534,7 +538,9 @@ def test_bond_length_types(self): # 4 Ni atoms with coordination 12 self.assertEqual(4 * 12, len(dfcc["dij"])) # invalid element - self.assertRaises(ValueError, self.P.bond_length_types, "Ni", "Nix", 0.1, 5.0) + self.assertRaises( + ValueError, self.P.bond_length_types, "Ni", "Nix", 0.1, 5.0 + ) # check indices ij0 allij0 = sum(dfcc["ij0"], tuple()) self.assertEqual(0, min(allij0)) @@ -605,7 +611,9 @@ def test_get_scat(self): def test_set_scat(self): """Check PdfFit.set_scat()""" # raises exception when no phase exists - self.assertRaises(pdffit2.unassignedError, self.P.set_scat, "N", "Ti", -11) + self.assertRaises( + pdffit2.unassignedError, self.P.set_scat, "N", "Ti", -11 + ) # check if it is local to phase fPb = self.P.get_scat("X", "Pb") bPb = self.P.get_scat("N", "Pb") diff --git a/tests/test_phase_fractions.py b/tests/test_phase_fractions.py index 62c9c8e..ac33ec6 100644 --- a/tests/test_phase_fractions.py +++ b/tests/test_phase_fractions.py @@ -38,11 +38,21 @@ def test_xray_fractions(self): self.assertAlmostEqual(1.0, sum(ph["atom"]), self.places) self.assertAlmostEqual(1.0, sum(ph["cell"]), self.places) self.assertAlmostEqual(1.0, sum(ph["mass"]), self.places) - self.assertAlmostEqual(bb2 / bb1, ph["atom"][0] / ph["atom"][1], self.places) - self.assertAlmostEqual(bb2 / bb1 * 40.0 / 4.0, ph["cell"][0] / ph["cell"][1], self.places) + self.assertAlmostEqual( + bb2 / bb1, ph["atom"][0] / ph["atom"][1], self.places + ) + self.assertAlmostEqual( + bb2 / bb1 * 40.0 / 4.0, ph["cell"][0] / ph["cell"][1], self.places + ) mavg1 = 58.69 - mavg2 = (8 * 207.19 + 24 * 15.994 + 4 * 44.956 + 2 * 183.85 + 2 * 47.90) / 40.0 - self.assertAlmostEqual(bb2 / bb1 * mavg1 / mavg2, ph["mass"][0] / ph["mass"][1], self.places) + mavg2 = ( + 8 * 207.19 + 24 * 15.994 + 4 * 44.956 + 2 * 183.85 + 2 * 47.90 + ) / 40.0 + self.assertAlmostEqual( + bb2 / bb1 * mavg1 / mavg2, + ph["mass"][0] / ph["mass"][1], + self.places, + ) self.assertEqual(0.0, sum(ph["stdatom"])) self.assertEqual(0.0, sum(ph["stdcell"])) self.assertEqual(0.0, sum(ph["stdmass"])) @@ -52,9 +62,21 @@ def test_xray_fractions(self): self.assertAlmostEqual(1.0, sum(ph2["atom"]), self.places) self.assertAlmostEqual(1.0, sum(ph2["cell"]), self.places) self.assertAlmostEqual(1.0, sum(ph2["mass"]), self.places) - self.assertAlmostEqual(2.0, ph2["atom"][0] / ph2["atom"][1] / (ph["atom"][0] / ph["atom"][1]), self.places) - self.assertAlmostEqual(2.0, ph2["cell"][0] / ph2["cell"][1] / (ph["cell"][0] / ph["cell"][1]), self.places) - self.assertAlmostEqual(2.0, ph2["mass"][0] / ph2["mass"][1] / (ph["mass"][0] / ph["mass"][1]), self.places) + self.assertAlmostEqual( + 2.0, + ph2["atom"][0] / ph2["atom"][1] / (ph["atom"][0] / ph["atom"][1]), + self.places, + ) + self.assertAlmostEqual( + 2.0, + ph2["cell"][0] / ph2["cell"][1] / (ph["cell"][0] / ph["cell"][1]), + self.places, + ) + self.assertAlmostEqual( + 2.0, + ph2["mass"][0] / ph2["mass"][1] / (ph["mass"][0] / ph["mass"][1]), + self.places, + ) return def test_neutron_fractions(self): @@ -71,11 +93,21 @@ def test_neutron_fractions(self): self.assertAlmostEqual(1.0, sum(ph["atom"]), self.places) self.assertAlmostEqual(1.0, sum(ph["cell"]), self.places) self.assertAlmostEqual(1.0, sum(ph["mass"]), self.places) - self.assertAlmostEqual(bb2 / bb1, ph["atom"][0] / ph["atom"][1], self.places) - self.assertAlmostEqual(bb2 / bb1 * 40.0 / 4.0, ph["cell"][0] / ph["cell"][1], self.places) + self.assertAlmostEqual( + bb2 / bb1, ph["atom"][0] / ph["atom"][1], self.places + ) + self.assertAlmostEqual( + bb2 / bb1 * 40.0 / 4.0, ph["cell"][0] / ph["cell"][1], self.places + ) mavg1 = 58.69 - mavg2 = (8 * 207.19 + 24 * 15.994 + 4 * 44.956 + 2 * 183.85 + 2 * 47.90) / 40.0 - self.assertAlmostEqual(bb2 / bb1 * mavg1 / mavg2, ph["mass"][0] / ph["mass"][1], self.places) + mavg2 = ( + 8 * 207.19 + 24 * 15.994 + 4 * 44.956 + 2 * 183.85 + 2 * 47.90 + ) / 40.0 + self.assertAlmostEqual( + bb2 / bb1 * mavg1 / mavg2, + ph["mass"][0] / ph["mass"][1], + self.places, + ) self.assertEqual(0.0, sum(ph["stdatom"])) self.assertEqual(0.0, sum(ph["stdcell"])) self.assertEqual(0.0, sum(ph["stdmass"])) @@ -85,9 +117,21 @@ def test_neutron_fractions(self): self.assertAlmostEqual(1.0, sum(ph2["atom"]), self.places) self.assertAlmostEqual(1.0, sum(ph2["cell"]), self.places) self.assertAlmostEqual(1.0, sum(ph2["mass"]), self.places) - self.assertAlmostEqual(2.0, ph2["atom"][0] / ph2["atom"][1] / (ph["atom"][0] / ph["atom"][1]), self.places) - self.assertAlmostEqual(2.0, ph2["cell"][0] / ph2["cell"][1] / (ph["cell"][0] / ph["cell"][1]), self.places) - self.assertAlmostEqual(2.0, ph2["mass"][0] / ph2["mass"][1] / (ph["mass"][0] / ph["mass"][1]), self.places) + self.assertAlmostEqual( + 2.0, + ph2["atom"][0] / ph2["atom"][1] / (ph["atom"][0] / ph["atom"][1]), + self.places, + ) + self.assertAlmostEqual( + 2.0, + ph2["cell"][0] / ph2["cell"][1] / (ph["cell"][0] / ph["cell"][1]), + self.places, + ) + self.assertAlmostEqual( + 2.0, + ph2["mass"][0] / ph2["mass"][1] / (ph["mass"][0] / ph["mass"][1]), + self.places, + ) return diff --git a/tests/test_shape_factors.py b/tests/test_shape_factors.py index 62d1384..110ce55 100644 --- a/tests/test_shape_factors.py +++ b/tests/test_shape_factors.py @@ -173,7 +173,9 @@ def test_spdiameter_io(self): self.assertEqual(14.0, self.P.getvar("spdiameter")) # try to read invalid shape data sinvalid = re.sub("(?m)^shape .*", "shape invalid, 1", spd7) - self.assertRaises(pdffit2.structureError, self.P.read_struct_string, sinvalid) + self.assertRaises( + pdffit2.structureError, self.P.read_struct_string, sinvalid + ) return @@ -269,7 +271,9 @@ def test_stepcut_io(self): self.assertEqual(14.0, self.P.getvar("stepcut")) # try to read invalid shape data sinvalid = re.sub("(?m)^shape .*", "shape invalid, 1", ssc7) - self.assertRaises(pdffit2.structureError, self.P.read_struct_string, sinvalid) + self.assertRaises( + pdffit2.structureError, self.P.read_struct_string, sinvalid + ) return