diff --git a/.codecov.yml b/.codecov.yml index 86671410..4af5eb24 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,2 +1,14 @@ -fixes: - - ".*/site-packages/::src/" +coverage: + status: + project: # more options at https://docs.codecov.com/docs/commit-status + default: + target: auto # use the coverage from the base commit, fail if coverage is lower + threshold: 0% # allow the coverage to drop by + +comment: + layout: " diff, flags, files" + behavior: default + require_changes: false + require_base: false # [true :: must have a base report to post] + require_head: false # [true :: must have a head report to post] + hide_project_coverage: false # [true :: only show coverage on the git diff aka patch coverage] diff --git a/.codespell/ignore_lines.txt b/.codespell/ignore_lines.txt new file mode 100644 index 00000000..07fa7c8c --- /dev/null +++ b/.codespell/ignore_lines.txt @@ -0,0 +1,2 @@ +;; Please include filenames and explanations for each ignored line. +;; See https://docs.openverse.org/meta/codespell.html for docs. diff --git a/.codespell/ignore_words.txt b/.codespell/ignore_words.txt new file mode 100644 index 00000000..7ca2aeac --- /dev/null +++ b/.codespell/ignore_words.txt @@ -0,0 +1,17 @@ +;; Please include explanations for each ignored word (lowercase). +;; See https://docs.openverse.org/meta/codespell.html for docs. + +;; abbreviation for "materials" often used in a journal title +mater + +;; Frobenius norm used in np.linalg.norm +fro + +;; structure file format +discus + +;; variable name within pdfbaseline module +aline + +;; variable name within TestSFAverageObjCryst class +fo diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index d0a0d16d..00000000 --- a/.coveragerc +++ /dev/null @@ -1,22 +0,0 @@ -# Configuration of the coverage.py tool for reporting test coverage. - -[report] -# RE patterns for lines to be excluded from consideration. -exclude_lines = - ## Have to re-enable the standard pragma - pragma: no cover - ## Don't complain if tests don't hit defensive assertion code: - raise AssertionError - raise NotImplementedError - ^[ ]*assert False - - ## Don't complain if non-runnable code isn't run: - ^[ ]*@unittest.skip\b - ^[ ]{4}unittest.main() - if __name__ == .__main__.: - - -[run] -omit = - ## exclude debug.py from codecov report - */tests/debug.py diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..7b2865c1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,13 @@ +# As of now, flake8 does not natively support configuration via pyproject.toml +# https://github.com/microsoft/vscode-flake8/issues/135 +[flake8] +exclude = + .git, + __pycache__, + build, + dist, + doc/source/conf.py +max-line-length = 79 +# Ignore some style 'errors' produced while formatting by 'black' +# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#labels-why-pycodestyle-warnings +extend-ignore = E203 diff --git a/.gitarchive.cfg b/.gitarchive.cfg deleted file mode 100644 index 95e1448c..00000000 --- a/.gitarchive.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[DEFAULT] -commit = $Format:%H$ -date = $Format:%ci$ -timestamp = $Format:%ct$ -refnames = $Format:%D$ diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 2c3906b0..00000000 --- a/.gitattributes +++ /dev/null @@ -1,7 +0,0 @@ -/.gitattributes export-ignore -/.gitignore export-ignore -/.travis.yml export-ignore -/conda-recipe/ export-ignore -/devutils export-ignore -/doc export-ignore -.gitarchive.cfg export-subst diff --git a/.github/ISSUE_TEMPLATE/bug_feature.md b/.github/ISSUE_TEMPLATE/bug_feature.md new file mode 100644 index 00000000..b3454deb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_feature.md @@ -0,0 +1,16 @@ +--- +name: Bug Report or Feature Request +about: Report a bug or suggest a new feature! +title: "" +labels: "" +assignees: "" +--- + +### Problem + + + +### Proposed solution diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md new file mode 100644 index 00000000..6107962c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -0,0 +1,46 @@ +--- +name: Release +about: Checklist and communication channel for PyPI and GitHub release +title: "Ready for PyPI/GitHub release" +labels: "release" +assignees: "" +--- + +### PyPI/GitHub rc-release preparation checklist: + +- [ ] All PRs/issues attached to the release are merged. +- [ ] All the badges on the README are passing. +- [ ] 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 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 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:///`. +- [ ] Successfully run all tests, tutorial examples or do functional testing. + +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 the maintainer if you have questions. +- [ ] Tag the maintainer for conda-forge release. + +### Post-release checklist + + + +- [ ] Run tutorial examples and conduct functional testing using the installation guide in the README. Attach screenshots/results as comments. +- [ ] Documentation (README, tutorials, API references, and websites) is deployed without broken links or missing figures. 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 00000000..1099d862 --- /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 new file mode 100644 index 00000000..caaf5a0a --- /dev/null +++ b/.github/workflows/build-wheel-release-upload.yml @@ -0,0 +1,18 @@ +name: Release (GitHub/PyPI) and Deploy Docs + +on: + workflow_dispatch: + push: + tags: + - "*" # Trigger on all tags initially, but tag and release privilege are verified in _build-wheel-release-upload.yml + +jobs: + release: + uses: scikit-package/release-scripts/.github/workflows/_build-wheel-release-upload.yml@v0 + with: + project: diffpy.srreal + c_extension: true + 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 new file mode 100644 index 00000000..88908d6f --- /dev/null +++ b/.github/workflows/check-news-item.yml @@ -0,0 +1,12 @@ +name: Check for News + +on: + pull_request_target: + branches: + - main + +jobs: + check-news-item: + uses: scikit-package/release-scripts/.github/workflows/_check-news-item.yml@v0 + with: + project: diffpy.srreal diff --git a/.github/workflows/matrix-and-codecov-on-merge-to-main.yml b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml new file mode 100644 index 00000000..9d2881b3 --- /dev/null +++ b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + branches: + - main + release: + types: + - prereleased + - published + workflow_dispatch: + +jobs: + matrix-coverage: + uses: scikit-package/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 + with: + project: diffpy.srreal + c_extension: true + headless: false + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/publish-docs-on-release.yml b/.github/workflows/publish-docs-on-release.yml new file mode 100644 index 00000000..108cc184 --- /dev/null +++ b/.github/workflows/publish-docs-on-release.yml @@ -0,0 +1,12 @@ +name: Deploy Documentation on Release + +on: + workflow_dispatch: + +jobs: + docs: + uses: scikit-package/release-scripts/.github/workflows/_publish-docs-on-release.yml@v0 + with: + project: diffpy.srreal + c_extension: true + headless: false diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml new file mode 100644 index 00000000..e5546783 --- /dev/null +++ b/.github/workflows/tests-on-pr.yml @@ -0,0 +1,52 @@ +name: Tests on PR + +on: + push: + branches: + - main + - cookie + pull_request: + workflow_dispatch: + +jobs: + validate: + defaults: + run: + shell: bash -l {0} + + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-13, macos-14] + python-version: [3.11, 3.12, 3.13] + + steps: + - name: Check out diffpy.srreal + uses: actions/checkout@v4 + + - name: Initialize miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + auto-activate-base: false + python-version: ${{ matrix.python-version }} + - name: Conda config + run: | + conda config --add channels conda-forge + - name: Install diffpy.srreal and requirements + run: | + conda create -n test python=${{ matrix.python-version }} -y + source $(conda info --base)/etc/profile.d/conda.sh + conda activate test + conda install pip -y + conda config --set always_yes yes --set changeps1 no + conda install --file requirements/conda.txt -y + conda install --file requirements/test.txt -y + python -m pip install . --no-deps + + - name: Validate diffpy.pdfgui + run: | + source $(conda info --base)/etc/profile.d/conda.sh + conda activate test + pytest tests diff --git a/.gitignore b/.gitignore index 120238d6..099e2948 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1,93 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ *.py[cod] +*$py.class # C extensions *.so -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -temp -develop-eggs +# Distribution / packaging +.Python +env/ +build/ +_build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +venv/ +*.egg-info/ .installed.cfg -lib -lib64 -tags +*.egg +bin/ +temp/ +tags/ errors.err +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + # Installer logs pip-log.txt +pip-delete-this-directory.txt MANIFEST # Unit test / coverage reports +htmlcov/ +.tox/ .coverage -.tox +.coverage.* +.cache nosetests.xml +coverage.xml +*,cover +.hypothesis/ # Translations *.mo +*.pot # Mr Developer .mr.developer.cfg .project .pydevproject -.settings - -# SCons build files -.gdb_history -.sconf_temp/ -.sconsign.dblite -config.log -/sconscript.local -/sconsvars.py - -# version information -setup.cfg -/src/diffpy/*/version.cfg + +# Django stuff: +*.log + +# Sphinx documentation +docs/build/ +docs/source/generated/ + +# pytest +.pytest_cache/ + +# PyBuilder +target/ + +# Editor files +# mac +.DS_Store +*~ + +# vim +*.swp +*.swo + +# pycharm +.idea/ + +# VSCode +.vscode/ + +# Ipython Notebook +.ipynb_checkpoints diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..86f162b8 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,5 @@ +[settings] +# Keep import statement below line_length character limit +line_length = 79 +multi_line_output = 3 +include_trailing_comma = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..0e4a84d1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,66 @@ +default_language_version: + python: python3 +ci: + autofix_commit_msg: | + [pre-commit.ci] auto fixes from pre-commit hooks + autofix_prs: true + autoupdate_branch: "pre-commit-autoupdate" + autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate" + autoupdate_schedule: monthly + skip: [no-commit-to-branch] + submodules: false +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-case-conflict + - id: check-merge-conflict + - id: check-toml + - id: check-added-large-files + - repo: https://github.com/psf/black + rev: 24.4.2 + hooks: + - id: black + - repo: https://github.com/pycqa/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + args: ["--profile", "black"] + - repo: https://github.com/kynan/nbstripout + rev: 0.7.1 + hooks: + - id: nbstripout + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: no-commit-to-branch + name: Prevent Commit to Main Branch + args: ["--branch", "main"] + stages: [pre-commit] + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + additional_dependencies: + - tomli + # prettier - multi formatter for .json, .yml, and .md files + - repo: https://github.com/pre-commit/mirrors-prettier + rev: f12edd9c7be1c20cfa42420fd0e6df71e42b51ea # frozen: v4.0.0-alpha.8 + hooks: + - id: prettier + additional_dependencies: + - "prettier@^3.2.4" + # docformatter - PEP 257 compliant docstring formatter + - repo: https://github.com/s-weigand/docformatter + rev: 5757c5190d95e5449f102ace83df92e7d3b06c6c + hooks: + - id: docformatter + additional_dependencies: [tomli] + args: [--in-place, --config, ./pyproject.toml] diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..47f7a017 --- /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/.travis.yml b/.travis.yml deleted file mode 100644 index de49f467..00000000 --- a/.travis.yml +++ /dev/null @@ -1,130 +0,0 @@ -dist: xenial -language: generic - -os: - - linux - - osx - -env: - - MYUSEMC=true MYPYTHON_VERSION=2.7 - - MYUSEMC=true MYPYTHON_VERSION=3.5 - - MYUSEMC=true MYPYTHON_VERSION=3.6 - - MYUSEMC=true MYPYTHON_VERSION=3.7 - - MYUSEMC=false - -git: - depth: 999999 - -branches: - except: - - /^v[0-9]/ - - -before_install: - - MYNAME=diffpy.srreal - - MYCOMMIT="$(git rev-parse HEAD)" - - umask 022 - - git fetch origin --tags - - MYPYTHON=python; MYPIP=pip - - NOSYS=true; NOAPT=true; NOBREW=true; NOMC=true - - if ${MYUSEMC}; then - NOMC=false; - elif [[ ${TRAVIS_OS_NAME} == linux ]]; then - NOAPT=false; NOSYS=false; - MYPIPFLAGS="--user"; - elif [[ ${TRAVIS_OS_NAME} == osx ]]; then - NOBREW=false; NOSYS=false; - MYPYTHON=python3; - MYPIP=pip3; - MYPIPFLAGS="--user"; - fi - - MYMCREPO=https://repo.anaconda.com/miniconda - - case ${TRAVIS_OS_NAME} in - linux) - MYMCBUNDLE=Miniconda3-latest-Linux-x86_64.sh ;; - osx) - MYMCBUNDLE=Miniconda3-latest-MacOSX-x86_64.sh ;; - *) - echo "Unsupported operating system." >&2; - exit 2 ;; - esac - - MYRUNDIR=${PWD}/build/rundir - - - mkdir -p ~/pkgs - - mkdir -p ${MYRUNDIR} - - cp .coveragerc ${MYRUNDIR}/ - - - $NOMC || pushd ~/pkgs - - $NOMC || wget --timestamping ${MYMCREPO}/${MYMCBUNDLE} - - $NOMC || test -x ~/mc/bin/conda || bash ${MYMCBUNDLE} -b -f -p ~/mc - - $NOMC || popd - - $NOMC || source ~/mc/bin/activate base - - $NOMC || conda update --yes conda - - $NOMC || conda install --yes conda-build conda-verify jinja2 numpy - - $NOMC || conda create --name=testenv --yes python=${MYPYTHON_VERSION} coverage - - $NOMC || conda config --add channels diffpy - - - $NOAPT || test "${TRAVIS_OS_NAME}" = "linux" || exit $? - - $NOAPT || PATH="$(echo "$PATH" | sed 's,:/opt/pyenv/[^:]*,,g')" - - $NOAPT || test "$(which python)" = "/usr/bin/python" || ( - which python; exit 1) - - $NOAPT || sudo apt-get update -qq - - $NOAPT || sudo apt-get install -y - libgsl0-dev libboost-all-dev python-dev - python-setuptools python-numpy python-pyparsing - python-lxml python-pip build-essential scons - - - $NOBREW || test "${TRAVIS_OS_NAME}" = "osx" || exit $? - - $NOBREW || brew update - - $NOBREW || brew install gcc || brew link --overwrite gcc - - $NOBREW || brew install boost-python3 - - $NOBREW || brew install gsl - - $NOBREW || brew install scons - - - $NOSYS || devutils/makesdist - - $NOSYS || MYTARBUNDLE="$(ls -t "${PWD}"/dist/*.tar.gz | head -1)" - - $NOSYS || pushd ~/pkgs - - $NOSYS || git clone https://github.com/diffpy/libobjcryst.git - - $NOSYS || git clone https://github.com/diffpy/libdiffpy.git - - $NOSYS || popd - - -install: - - $NOMC || conda build --python=${MYPYTHON_VERSION} conda-recipe - - $NOMC || conda render --python=${MYPYTHON_VERSION} --output conda-recipe | - sed 's,.*/,,; s/[.]tar[.]bz2$//; s/-/=/g' > /tmp/mypackage.txt - - $NOMC || source activate testenv - - $NOMC || conda install --yes --use-local --file=/tmp/mypackage.txt - - - MYSUDO= - - $NOAPT || MYSUDO=sudo - - $NOSYS || $MYPIP install $MYPIPFLAGS coverage - - $NOSYS || $MYPIP install $MYPIPFLAGS periodictable - - $NOSYS || $MYPIP install $MYPIPFLAGS pycifrw - - $NOSYS || $MYPIP install $MYPIPFLAGS diffpy.structure - - - $NOSYS || $MYSUDO scons -C ~/pkgs/libobjcryst install - - $NOSYS || $MYPIP install $MYPIPFLAGS pyobjcryst - - $NOSYS || $MYSUDO scons -C ~/pkgs/libdiffpy install - - $NOSYS || $MYPIP install $MYPIPFLAGS "${MYTARBUNDLE}" - - - cd ${MYRUNDIR} - - MYGIT_REV=$($MYPYTHON -c "import ${MYNAME}.version as v; print(v.__git_commit__)") - - if [[ "${MYCOMMIT}" != "${MYGIT_REV}" ]]; then - echo "Version mismatch ${MYCOMMIT} vs ${MYGIT_REV}."; - exit 1; - fi - - -before_script: - - $NOBREW || USER_BASE="$(python3 -c 'import site; print(site.USER_BASE)')" - - $NOBREW || PATH="${USER_BASE}/bin:${PATH}" - - -script: - - coverage run --source ${MYNAME} -m ${MYNAME}.tests.run - - -after_success: - - $MYPIP install $MYPIPFLAGS codecov - - codecov diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 00000000..ad15dbc7 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,12 @@ +Authors +======= + +Pavol Juhas, +Chris Farrow, +Simon J.L. Billinge + +Contributors +------------ + +For a list of contributors, visit +https://github.com/diffpy/diffpy.srreal/graphs/contributors diff --git a/AUTHORS.txt b/AUTHORS.txt deleted file mode 100644 index 00374f30..00000000 --- a/AUTHORS.txt +++ /dev/null @@ -1,9 +0,0 @@ -Authors: - -Pavol Juhas -Chris Farrow -Simon J.L. Billinge - -Contributors: - -https://github.com/diffpy/diffpy.srreal/graphs/contributors diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 8c521cc6..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,41 +0,0 @@ -# Release notes - -## Version 1.3.0 – 2019-03-13 - -Main differences from version 1.2. - -### Added - -- Support for Python 3.7, 3.6, 3.5 in addition to 2.7. -- Validation of compiler options from `python-config`. -- Make scons scripts compatible with Python 3 and Python 2. -- `ConstantPeakWidth` attributes `uisowidth`, `bisowidth` to ease - PDF simulation with uniform isotropic atom displacements. - -### Changed - -- Require libdiffpy 1.4 or later. -- Build Anaconda package with Anaconda C++ compiler. -- Allow language standard c++11. -- Pickle format for `PDFCalculator`, `DebyePDFCalculator`, - `OverlapCalculator`, `PeakWidthModel`, `PeakProfile`, `PDFEnvelope`, - `PDFBaseline`, and `ScatteringFactorTable` objects. - -### Deprecated - -- Variable `__gitsha__` in the `version` module renamed to `__git_commit__`. -- `libdiffpy_version_info` attribute `git_sha` renamed to `git_commit`. - -### Removed - -- Unused method `BVParam.__hash__`. -- Disable pickling of `BasePairQuantity` as it is in effect abstract. -- Pickling of Python-added attributes to exported C++ classes. -- Function `get_libdiffpy_version_info` from the `version` module. - -### Fixed - -- Return value conversion of `CrystalStructureAdapter` methods - `expandLatticeAtom` and `getEquivalentAtoms` methods. - Make them return a `list` of `Atom` objects. -- Name suffix resolution of `boost_python` shared library. diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 00000000..79c75d7d --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,47 @@ +============= +Release notes +============= + +.. current developments + + +Version 1.3.0 2019-03-13 +========================= + +Main differences from version 1.2. + +**Added:** + +* Support for Python 3.7, 3.6, 3.5 in addition to 2.7. +* Validation of compiler options from `python-config`. +* Make scons scripts compatible with Python 3 and Python 2. +* `ConstantPeakWidth` attributes `uisowidth`, `bisowidth` to ease + PDF simulation with uniform isotropic atom displacements. + +**Changed:** + +* Require libdiffpy 1.4 or later. +* Build Anaconda package with Anaconda C++ compiler. +* Allow language standard c++11. +* Pickle format for `PDFCalculator`, `DebyePDFCalculator`, + `OverlapCalculator`, `PeakWidthModel`, `PeakProfile`, `PDFEnvelope`, + `PDFBaseline`, and `ScatteringFactorTable` objects. + +**Deprecated:** + +* Variable `__gitsha__` in the `version` module renamed to `__git_commit__`. +* `libdiffpy_version_info` attribute `git_sha` renamed to `git_commit`. + +**Removed** + +* Unused method `BVParam.__hash__`. +* Disable pickling of `BasePairQuantity` as it is in effect abstract. +* Pickling of Python-added attributes to exported C++ classes. +* Function `get_libdiffpy_version_info` from the `version` module. + +**Fixed** + +* Return value conversion of `CrystalStructureAdapter` methods + `expandLatticeAtom` and `getEquivalentAtoms` methods. + Make them return a `list` of `Atom` objects. +* Name suffix resolution of `boost_python` shared library. diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst new file mode 100644 index 00000000..e8199ca5 --- /dev/null +++ b/CODE_OF_CONDUCT.rst @@ -0,0 +1,133 @@ +===================================== + Contributor Covenant Code of Conduct +===================================== + +Our Pledge +---------- + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socioeconomic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +Our Standards +------------- + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +Enforcement Responsibilities +---------------------------- + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +Scope +----- + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +Enforcement +----------- + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +sb2896@columbia.edu. All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +Enforcement Guidelines +---------------------- + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +1. Correction +**************** + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +2. Warning +************* + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +3. Temporary Ban +****************** + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +4. Permanent Ban +****************** + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +Attribution +----------- + +This Code of Conduct is adapted from the `Contributor Covenant `_. + +Community Impact Guidelines were inspired by `Mozilla's code of conduct enforcement ladder `_. + +For answers to common questions about this code of conduct, see the `FAQ `_. `Translations are available `_ diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 00000000..c983d73f --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,142 @@ +OPEN SOURCE LICENSE AGREEMENT +============================= + +Copyright (c) 2009-2011, University of Tennessee + +Copyright (c) 1989, 1991 Free Software Foundation, Inc. + +Copyright (c) 2006, The Regents of the University of California through Lawrence Berkeley National Laboratory + +Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") + +Copyright (c) 2006-2007, Board of Trustees of Michigan State University + +Copyright (c) 2008-2012, The Trustees of Columbia University in the City of New York + +Copyright (c) 2014-2019, Brookhaven Science Associates, Brookhaven National Laboratory + +Copyright (c) 2024, The Trustees of Columbia University in the City of New York. +All rights reserved. + +The "DiffPy-CMI" is distributed subject to the following license conditions: + +.. code-block:: text + + SOFTWARE LICENSE AGREEMENT + + Software: DiffPy-CMI + + + (1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either + source code, or binary form and accompanying documentation). + + Part of the software was derived from the DANSE, ObjCryst++ (with permission), + PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of + which the original Copyrights are contained in each individual file. + + Each licensee is addressed as "you" or "Licensee." + + + (2) The copyright holders shown above and their third-party Licensors hereby + grant licensee a royalty-free nonexclusive license, subject to the limitations + stated herein and U.S. Government license rights. + + + (3) You may modify and make a copy or copies of the software for use within + your organization, if you meet the following conditions: + + (a) Copies in source code must include the copyright notice and this + software license agreement. + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy. + + + (4) You may modify a copy or copies of the Software or any portion of it, thus + forming a work based on the Software, and distribute copies of such work + outside your organization, if you meet all of the following conditions: + + (a) Copies in source code must include the copyright notice and this + Software License Agreement; + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy; + + (c) Modified copies and works based on the Software must carry prominent + notices stating that you changed specified portions of the Software. + + (d) Neither the name of Brookhaven Science Associates or Brookhaven + National Laboratory nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + written permission. + + + (5) Portions of the Software resulted from work developed under a U.S. + Government contract and are subject to the following license: + The Government is granted for itself and others acting on its behalf a + paid-up, nonexclusive, irrevocable worldwide license in this computer software + to reproduce, prepare derivative works, and perform publicly and display + publicly. + + + (6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT + WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY + LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND + THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL + LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF + THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE + PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION + UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. + + + (7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR + THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF + ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, + CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING + BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, + WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING + NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS + BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. + + +Brookhaven National Laboratory Notice +===================================== + +Acknowledgment of sponsorship +----------------------------- + +This software was produced by the Brookhaven National Laboratory, under +Contract DE-AC02-98CH10886 with the Department of Energy. + + +Government disclaimer of liability +---------------------------------- + +Neither the United States nor the United States Department of Energy, nor +any of their employees, makes any warranty, express or implied, or assumes +any legal liability or responsibility for the accuracy, completeness, or +usefulness of any data, apparatus, product, or process disclosed, or +represents that its use would not infringe privately owned rights. + + +Brookhaven disclaimer of liability +---------------------------------- + +Brookhaven National Laboratory makes no representations or warranties, +express or implied, nor assumes any liability for the use of this software. + + +Maintenance of notice +--------------------- + +In the interest of clarity regarding the origin and status of this +software, Brookhaven National Laboratory requests that any recipient of it +maintain this notice affixed to any distribution by the recipient that +contains a copy or derivative of this software. + + +END OF LICENSE diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index f6d92af7..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1,137 +0,0 @@ -OPEN SOURCE LICENSE AGREEMENT -============================= - -Copyright (c) 2009-2011, University of Tennessee -Copyright (c) 1989, 1991 Free Software Foundation, Inc. -Copyright (c) 2006, The Regents of the University of California through - Lawrence Berkeley National Laboratory -Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") -Copyright (c) 2006-2007, Board of Trustees of Michigan State University -Copyright (c) 2008-2012, The Trustees of Columbia University in the City - of New York - -Copyright (c) 2014-2019, Brookhaven Science Associates, Brookhaven National - Laboratory - - -The "DiffPy-CMI" is distributed subject to the following license conditions: - - -SOFTWARE LICENSE AGREEMENT - - Software: DiffPy-CMI - - -(1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either -source code, or binary form and accompanying documentation). - -Part of the software was derived from the DANSE, ObjCryst++ (with permission), -PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of -which the original Copyrights are contained in each individual file. - -Each licensee is addressed as "you" or "Licensee." - - -(2) The copyright holders shown above and their third-party Licensors hereby -grant licensee a royalty-free nonexclusive license, subject to the limitations -stated herein and U.S. Government license rights. - - -(3) You may modify and make a copy or copies of the software for use within -your organization, if you meet the following conditions: - - (a) Copies in source code must include the copyright notice and this - software license agreement. - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy. - - -(4) You may modify a copy or copies of the Software or any portion of it, thus -forming a work based on the Software, and distribute copies of such work -outside your organization, if you meet all of the following conditions: - - (a) Copies in source code must include the copyright notice and this - Software License Agreement; - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy; - - (c) Modified copies and works based on the Software must carry prominent - notices stating that you changed specified portions of the Software. - - (d) Neither the name of Brookhaven Science Associates or Brookhaven - National Laboratory nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - written permission. - - -(5) Portions of the Software resulted from work developed under a U.S. -Government contract and are subject to the following license: -The Government is granted for itself and others acting on its behalf a -paid-up, nonexclusive, irrevocable worldwide license in this computer software -to reproduce, prepare derivative works, and perform publicly and display -publicly. - - -(6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT -WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY -LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND -THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING -BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL -LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF -THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE -PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION -UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. - - -(7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR -THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF -ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, -CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING -BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, -WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING -NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS -BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. - - -Brookhaven National Laboratory Notice -===================================== - -Acknowledgment of sponsorship ------------------------------ - -This software was produced by the Brookhaven National Laboratory, under -Contract DE-AC02-98CH10886 with the Department of Energy. - - -Government disclaimer of liability ----------------------------------- - -Neither the United States nor the United States Department of Energy, nor -any of their employees, makes any warranty, express or implied, or assumes -any legal liability or responsibility for the accuracy, completeness, or -usefulness of any data, apparatus, product, or process disclosed, or -represents that its use would not infringe privately owned rights. - - -Brookhaven disclaimer of liability ----------------------------------- - -Brookhaven National Laboratory makes no representations or warranties, -express or implied, nor assumes any liability for the use of this software. - - -Maintenance of notice ---------------------- - -In the interest of clarity regarding the origin and status of this -software, Brookhaven National Laboratory requests that any recipient of it -maintain this notice affixed to any distribution by the recipient that -contains a copy or derivative of this software. - - -END OF LICENSE diff --git a/LICENSE_DANSE.txt b/LICENSE_DANSE.rst similarity index 78% rename from LICENSE_DANSE.txt rename to LICENSE_DANSE.rst index f5f9f3ea..1c0ab5db 100644 --- a/LICENSE_DANSE.txt +++ b/LICENSE_DANSE.rst @@ -1,24 +1,32 @@ +LICENSE +======= + This program is part of the DiffPy and DANSE open-source projects and is available subject to the conditions and terms laid out below. -Copyright (c) 2008-2012, The Trustees of Columbia University in -the City of New York. All rights reserved. +Copyright (c) 2008-2012, The Trustees of Columbia University in the City of New York. +All rights reserved. + +Copyright (c) 2024, The Trustees of Columbia University in the City of New York. +All rights reserved. For more information please visit the project web-page at + http://www.diffpy.org + or email Prof. Simon Billinge at sb2896@columbia.edu. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of the copyright holder nor the names of its +- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/MANIFEST.in b/MANIFEST.in index f27ccfbe..f1a78eec 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,17 +1,12 @@ -recursive-include src * -include SConstruct -include AUTHORS.txt LICENSE*.txt README.rst -recursive-exclude src *.pyc *.so -global-exclude .gitattributes .gitignore .gitarchive.cfg -global-exclude .DS_Store +graft src +graft tests +graft requirements -# Avoid user content in setup.cfg to make distribution reproducible. -exclude setup.cfg +include AUTHORS.rst LICENSE*.rst README.rst -# Exclude git-tracked files spuriously added by setuptools_scm -exclude .coveragerc -exclude .travis* -prune conda-recipe -prune devutils -prune doc -prune examples +# Exclude all bytecode files and __pycache__ directories +global-exclude *.py[cod] # Exclude all .pyc, .pyo, and .pyd files. +global-exclude .DS_Store # Exclude Mac filesystem artifacts. +global-exclude __pycache__ # Exclude Python cache directories. +global-exclude .git* # Exclude git files and directories. +global-exclude .idea # Exclude PyCharm project settings. diff --git a/README.rst b/README.rst index 3a87d0a5..502f3233 100644 --- a/README.rst +++ b/README.rst @@ -1,13 +1,41 @@ -.. image:: https://travis-ci.org/diffpy/diffpy.srreal.svg?branch=master - :target: https://travis-ci.org/diffpy/diffpy.srreal +|Icon| |title|_ +=============== -.. image:: https://codecov.io/gh/diffpy/diffpy.srreal/branch/master/graph/badge.svg - :target: https://codecov.io/gh/diffpy/diffpy.srreal +.. |title| replace:: diffpy.srreal +.. _title: https://diffpy.github.io/diffpy.srreal -diffpy.srreal -======================================================================== +.. |Icon| image:: https://avatars.githubusercontent.com/diffpy + :target: https://diffpy.github.io/diffpy.srreal + :height: 100px -Calculators for PDF, bond valence sum and other pair quantities +|PyPI| |Forge| |PythonVersion| |PR| + +|CI| |Codecov| |Black| |Tracking| + +.. |Black| image:: https://img.shields.io/badge/code_style-black-black + :target: https://github.com/psf/black + +.. |CI| image:: https://github.com/diffpy/diffpy.srreal/actions/workflows/matrix-and-codecov-on-merge-to-main.yml/badge.svg + :target: https://github.com/diffpy/diffpy.srreal/actions/workflows/matrix-and-codecov-on-merge-to-main.yml + +.. |Codecov| image:: https://codecov.io/gh/diffpy/diffpy.srreal/branch/main/graph/badge.svg + :target: https://codecov.io/gh/diffpy/diffpy.srreal + +.. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/diffpy.srreal + :target: https://anaconda.org/conda-forge/diffpy.srreal + +.. |PR| image:: https://img.shields.io/badge/PR-Welcome-29ab47ff + +.. |PyPI| image:: https://img.shields.io/pypi/v/diffpy.srreal + :target: https://pypi.org/project/diffpy.srreal/ + +.. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/diffpy.srreal + :target: https://pypi.org/project/diffpy.srreal/ + +.. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue + :target: https://github.com/diffpy/diffpy.srreal/issues + +Calculators for PDF, bond valence sum, and other quantities based on atom pair interaction. The diffpy.srreal package provides calculators for atomic pair distribution function (PDF), bond valence sums (BVS), atom overlaps for a hard-sphere @@ -39,126 +67,91 @@ calculate PDF with a user-defined profile function. A new calculator class can be also defined for any quantity that is obtained by iteration over atom pairs, by defining only the function that processes atom-pair contributions. -For more information about the diffpy.srreal library, see users manual at -http://diffpy.github.io/diffpy.srreal. - - -REQUIREMENTS ------------------------------------------------------------------------- - -The diffpy.srreal package requires Python 3.7, 3.6, 3.5 or 2.7, -C++ compiler and the following software: - -* ``setuptools`` - tools for installing Python packages -* ``NumPy`` - library for scientific computing with Python -* ``python-dev`` - header files for interfacing Python with C -* ``libboost-all-dev`` - Boost C++ libraries and development files -* ``libdiffpy`` - C++ library for PDF, bond valence sum and other pair - quantity calculators https://github.com/diffpy/libdiffpy -* ``diffpy.structure`` - simple storage and manipulation of atomic structures - https://github.com/diffpy/diffpy.structure -* ``scons`` - software construction tool (optional) +For more information about the diffpy.srreal library, please consult our `online documentation `_. -Optional software: +Citation +-------- -* ``periodictable`` - periodic table of elements in Python - http://www.reflectometry.org/danse/elements.html -* ``pyobjcryst`` - Python bindings to ObjCryst++, the Object Oriented - Crystallographic library for C++, https://github.com/diffpy/pyobjcryst. +If you use diffpy.srreal in a scientific publication, we would like you to cite this package as -We recommend to use `Anaconda Python `_ -as it allows to install all software dependencies together with -diffpy.srreal. For other Python distributions it is necessary to -install the required software separately. As an example, on Ubuntu -Linux some of the required software can be installed using :: + diffpy.srreal Package, https://github.com/diffpy/diffpy.srreal - sudo apt-get install \ - python-setuptools python-numpy scons \ - build-essential python-dev libboost-all-dev +Installation +------------ -To install the remaining packages see the installation instructions -at their respective web pages. +The preferred method is to use `Miniconda Python +`_ +and install from the "conda-forge" channel of Conda packages. +To add "conda-forge" to the conda channels, run the following in a terminal. :: -INSTALLATION ------------------------------------------------------------------------- + conda config --add channels conda-forge -The preferred method is to use Anaconda Python and install from the -"diffpy" channel of Anaconda packages :: +We want to install our packages in a suitable conda environment. +The following creates and activates a new environment named ``diffpy.srreal_env`` :: - conda config --add channels diffpy - conda install diffpy.srreal + conda create -n diffpy.srreal_env diffpy.srreal + conda activate diffpy.srreal_env -diffpy.srreal is also included in the "diffpy-cmi" collection -of packages for structure analysis :: +To confirm that the installation was successful, type :: - conda install diffpy-cmi + python -c "import diffpy.srreal; print(diffpy.srreal.__version__)" -If you prefer to install from sources, make sure all required software -packages are in place and then run :: +The output should print the latest version displayed on the badges above. - python setup.py install +If the above does not work, you can use ``pip`` to download and install the latest release from +`Python Package Index `_. +To install using ``pip`` into your ``diffpy.srreal_env`` environment, type :: -You may need to use ``sudo`` with system Python so the process is -allowed to copy files to the system directories. If administrator (root) -access is not available, see the output from -``python setup.py install --help`` for options to install to -a user-writable location. The installation integrity can be -verified by executing the included tests with :: + pip install diffpy.srreal - python -m diffpy.srreal.tests.run +If you prefer to install from sources, after installing the dependencies, obtain the source archive from +`GitHub `_. Once installed, ``cd`` into your ``diffpy.srreal`` directory +and run the following :: -An alternative way of installing diffpy.srreal is to use the SCons tool, -which can speed up the process by compiling C++ files in several -parallel jobs (-j4) :: + pip install . - sudo scons -j4 install +Getting Started +--------------- -See ``scons -h`` for decription of build targets and options. +You may consult our `online documentation `_ for tutorials and API references. +Support and Contribute +---------------------- -DEVELOPMENT ------------------------------------------------------------------------- +`Diffpy user group `_ is the discussion forum for general questions and discussions about the use of diffpy.srreal. Please join the diffpy.srreal users community by joining the Google group. The diffpy.srreal project welcomes your expertise and enthusiasm! -diffpy.srreal is an open-source software developed as a part of the -DiffPy-CMI complex modeling initiative at the Brookhaven National -Laboratory. The diffpy.srreal sources are hosted at -https://github.com/diffpy/diffpy.srreal. +If you see a bug or want to request a feature, please `report it as an issue `_ and/or `submit a fix as a PR `_. You can also post it to the `Diffpy user group `_. -Feel free to fork the project and contribute. To install diffpy.srreal -in a development mode, where the sources are directly used by Python -rather than copied to a system directory, use :: +Feel free to fork the project and contribute. To install diffpy.srreal +in a development mode, with its sources being directly used by Python +rather than copied to a package directory, use the following in the root +directory :: - python setup.py develop --user + pip install -e . -To rebuild the C++ extension module and then optionally test the code -integrity, use :: +To ensure code quality and to prevent accidental commits into the default branch, please set up the use of our pre-commit +hooks. - scons -j4 build=debug develop [test] +1. Install pre-commit in your working environment by running ``conda install pre-commit``. -When developing with Anaconda Python it is essential to specify -header path, library path and runtime library path for the active -Anaconda environment. This can be achieved by setting the ``CPATH``, -``LIBRARY_PATH`` and ``LDFLAGS`` environment variables as follows:: +2. Initialize pre-commit (one time only) ``pre-commit install``. - # resolve the prefix directory P of the active Anaconda environment - P="$(conda info --json | grep default_prefix | cut -d\" -f4)" - export CPATH=$P/include - export LIBRARY_PATH=$P/lib - export LDFLAGS=-Wl,-rpath,$P/lib - # compile and re-install diffpy.srreal - scons -j4 build=debug develop +Thereafter your code will be linted by black and isort and checked against flake8 before you can commit. +If it fails by black or isort, just rerun and it should pass (black and isort will modify the files so should +pass after they are modified). If the flake8 test fails please see the error messages and fix them manually before +trying to commit again. -Note the Anaconda package for the required libdiffpy library is built -with a C++ compiler provided by Anaconda. This may cause incompatibility -with system C++. In such case use Anaconda C++ to build diffpy.srreal. +Improvements and fixes are always appreciated. +Before contributing, please read our `Code of Conduct `_. -CONTACTS ------------------------------------------------------------------------- +Contact +------- -For more information on diffpy.srreal please visit the project web-page +For more information on diffpy.srreal please visit the project `web-page `_ or email Simon Billinge at sb2896@columbia.edu. -http://www.diffpy.org +Acknowledgements +---------------- -or email Prof. Simon Billinge at sb2896@columbia.edu. +``diffpy.srreal`` is built and maintained with `scikit-package `_. diff --git a/SConstruct b/SConstruct deleted file mode 100644 index 23023b4e..00000000 --- a/SConstruct +++ /dev/null @@ -1,177 +0,0 @@ -# This SConstruct is for faster parallel builds. -# Use "setup.py" for normal installation. - -MY_SCONS_HELP = """\ -SCons rules for compiling and installing diffpy.srreal. -SCons build is much faster when run with parallel jobs (-j4). -Usage: scons [target] [var=value] - -Targets: - -module build Python extension module srreal_ext.so [default] -install install to default Python package location -develop copy extension module to src/diffpy/srreal/ directory -test execute unit tests - -Build configuration variables: -%s -Variables can be also assigned in a user script sconsvars.py. -SCons construction environment can be customized in sconscript.local script. -""" - -import os -import re -import subprocess -import platform - -def subdictionary(d, keyset): - return dict(kv for kv in d.items() if kv[0] in keyset) - -def getsyspaths(*names): - pall = sum((os.environ.get(n, '').split(os.pathsep) for n in names), []) - rv = [p for p in pall if os.path.exists(p)] - return rv - -def pyoutput(cmd): - proc = subprocess.Popen([env['python'], '-c', cmd], - stdout=subprocess.PIPE, - universal_newlines=True) - out = proc.communicate()[0] - return out.rstrip() - -def pyconfigvar(name): - cmd = ('from distutils.sysconfig import get_config_var\n' - 'print(get_config_var(%r))\n') % name - return pyoutput(cmd) - -# copy system environment variables related to compilation -DefaultEnvironment(ENV=subdictionary(os.environ, ''' - PATH PYTHONPATH GIT_DIR - CPATH CPLUS_INCLUDE_PATH LIBRARY_PATH LD_RUN_PATH - LD_LIBRARY_PATH DYLD_LIBRARY_PATH DYLD_FALLBACK_LIBRARY_PATH - MACOSX_DEPLOYMENT_TARGET LANG - _PYTHON_SYSCONFIGDATA_NAME - _CONDA_PYTHON_SYSCONFIGDATA_NAME - '''.split()) -) - -# Create construction environment -env = DefaultEnvironment().Clone() - -# Variables definitions below work only with 0.98 or later. -env.EnsureSConsVersion(0, 98) - -# Customizable compile variables -vars = Variables('sconsvars.py') - -vars.Add(PathVariable('prefix', - 'installation prefix directory', None)) -vars.Add(EnumVariable('build', - 'compiler settings', 'fast', - allowed_values=('debug', 'fast'))) -vars.Add(EnumVariable('tool', - 'C++ compiler toolkit to be used', 'default', - allowed_values=('default', 'intelc'))) -vars.Add(BoolVariable('profile', - 'build with profiling information', False)) -vars.Add('python', - 'Python executable to use for installation.', 'python') -vars.Update(env) -env.Help(MY_SCONS_HELP % vars.GenerateHelpText(env)) - -# Use Intel C++ compiler if requested by the user. -icpc = None -if env['tool'] == 'intelc': - icpc = env.WhereIs('icpc') - if not icpc: - print("Cannot find the Intel C/C++ compiler 'icpc'.") - Exit(1) - env.Tool('intelc', topdir=icpc[:icpc.rfind('/bin')]) - -# Figure out compilation switches, filter away C-related items. -good_python_flag = lambda n : ( - not isinstance(n, str) or - not re.match(r'(-g|-Wstrict-prototypes|-O\d|-fPIC)$', n)) -# Determine python-config script name. -pyversion = pyoutput('import sys; print("%i.%i" % sys.version_info[:2])') -pycfgname = 'python%s-config' % (pyversion if pyversion[0] == '3' else '') -pybindir = os.path.dirname(env.WhereIs(env['python'])) -pythonconfig = os.path.join(pybindir, pycfgname) -# Verify python-config comes from the same path as the target python. -xpython = env.WhereIs(env['python']) -xpythonconfig = env.WhereIs(pythonconfig) -if os.path.dirname(xpython) != os.path.dirname(xpythonconfig): - print("Inconsistent paths of %r and %r" % (xpython, xpythonconfig)) - Exit(1) -# Process the python-config flags here. -env.ParseConfig(pythonconfig + " --cflags") -env.Replace(CCFLAGS=[f for f in env['CCFLAGS'] if good_python_flag(f)]) -env.Replace(CPPDEFINES='') -# the CPPPATH directories are checked by scons dependency scanner -cpppath = getsyspaths('CPLUS_INCLUDE_PATH', 'CPATH') -env.AppendUnique(CPPPATH=cpppath) -# Insert LIBRARY_PATH explicitly because some compilers -# ignore it in the system environment. -env.PrependUnique(LIBPATH=getsyspaths('LIBRARY_PATH')) -# Add shared libraries. -# Note: libdiffpy and boost_python are added from SConscript.configure. - -fast_linkflags = ['-s'] -fast_shlinkflags = pyconfigvar('LDSHARED').split()[1:] - -# Specify minimum C++ standard. Allow later standard from sconscript.local. -# In case of multiple `-std` options the last option holds. -env.PrependUnique(CXXFLAGS='-std=c++11', delete_existing=1) - -# Platform specific intricacies. -if env['PLATFORM'] == 'darwin': - env.AppendUnique(CXXFLAGS='-ftemplate-depth-256') - darwin_shlinkflags = [n for n in env['SHLINKFLAGS'] - if n != '-dynamiclib'] - env.Replace(SHLINKFLAGS=darwin_shlinkflags) - env.AppendUnique(SHLINKFLAGS=['-bundle']) - env.AppendUnique(SHLINKFLAGS=['-undefined', 'dynamic_lookup']) - fast_linkflags[:] = [] - -# Compiler specific options -if icpc: - # options for Intel C++ compiler on hpc dev-intel07 - env.AppendUnique(CCFLAGS=['-w1', '-fp-model', 'precise']) - env.PrependUnique(LIBS=['imf']) - fast_optimflags = ['-fast', '-no-ipo'] -else: - # g++ options - env.AppendUnique(CCFLAGS=['-Wall']) - fast_optimflags = ['-ffast-math'] - -# Configure build variants -if env['build'] == 'debug': - env.AppendUnique(CCFLAGS='-g') -elif env['build'] == 'fast': - env.AppendUnique(CCFLAGS=['-O3'] + fast_optimflags) - env.AppendUnique(CPPDEFINES='NDEBUG') - env.AppendUnique(LINKFLAGS=fast_linkflags) - env.AppendUnique(SHLINKFLAGS=fast_shlinkflags) - -if env['profile']: - env.AppendUnique(CCFLAGS='-pg') - env.AppendUnique(LINKFLAGS='-pg') - -builddir = env.Dir('build/%s-%s' % (env['build'], platform.machine())) - -Export('env', 'pyconfigvar', 'pyoutput', 'pyversion') - -def GlobSources(pattern): - """Same as Glob but also require that source node is a valid file. - """ - rv = [f for f in Glob(pattern) if f.srcnode().isfile()] - return rv - -Export('GlobSources') - -if os.path.isfile('sconscript.local'): - env.SConscript('sconscript.local') - -env.SConscript('src/extensions/SConscript', variant_dir=builddir) - -# vim: ft=python diff --git a/conda-recipe/build.sh b/conda-recipe/build.sh deleted file mode 100644 index 16f60848..00000000 --- a/conda-recipe/build.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -MYNCPU=$(( (CPU_COUNT > 4) ? 4 : CPU_COUNT )) - -# Apply sconscript.local customizations. -cp ${RECIPE_DIR}/sconscript.local ./ - -# Install srreal with scons to utilize multiple CPUs. -scons -j $MYNCPU install prefix=$PREFIX diff --git a/conda-recipe/conda_build_config.yaml b/conda-recipe/conda_build_config.yaml deleted file mode 100644 index 4c9fb940..00000000 --- a/conda-recipe/conda_build_config.yaml +++ /dev/null @@ -1,17 +0,0 @@ -python: - - 3.7 - - 3.6 - - 3.5 - - 2.7 - -numpy: - - 1.11 - -libdiffpy: - - 1.4.* - -boost: - - 1.67 - -pin_run_as_build: - boost: x.x diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml deleted file mode 100644 index b3d23d70..00000000 --- a/conda-recipe/meta.yaml +++ /dev/null @@ -1,83 +0,0 @@ -{% set setupdata = load_setup_py_data() %} - -package: - name: diffpy.srreal - version: {{ setupdata['version'] }} - -source: - # git_url: https://github.com/diffpy/diffpy.srreal - git_url: .. - -build: - preserve_egg_dir: True # [py2k] - - # If this is a new build for the same version, increment the build - # number. If you do not include this key, it defaults to 0. - # number: 0 - -requirements: - build: - - {{ compiler('cxx') }} - host: - - python {{ python }} - - setuptools - - scons - - numpy {{ numpy }} - - libdiffpy {{ libdiffpy }} - - boost {{ boost }} - - run: - # NOTE libdiffpy is added implicitly from libdiffpy run_exports - - python - - setuptools - - {{ pin_compatible('numpy', min_pin='x.x', max_pin='x') }} - - boost - - diffpy.structure - - pyobjcryst 2.1.* - - periodictable - -test: - # Python imports - imports: - - diffpy - - diffpy.srreal - - diffpy.srreal.tests - - commands: - # Test if any module can be imported as the first one. - - python -s -c "import diffpy.srreal.atomradiitable" - - python -s -c "import diffpy.srreal.attributes" - - python -s -c "import diffpy.srreal.bondcalculator" - - python -s -c "import diffpy.srreal.bvparameterstable" - - python -s -c "import diffpy.srreal.bvscalculator" - - python -s -c "import diffpy.srreal.eventticker" - - python -s -c "import diffpy.srreal.overlapcalculator" - - python -s -c "import diffpy.srreal.pairquantity" - - python -s -c "import diffpy.srreal.pdfbaseline" - - python -s -c "import diffpy.srreal.pdfcalculator" - - python -s -c "import diffpy.srreal.pdfenvelope" - - python -s -c "import diffpy.srreal.peakprofile" - - python -s -c "import diffpy.srreal.peakwidthmodel" - - python -s -c "import diffpy.srreal.scatteringfactortable" - - python -s -c "import diffpy.srreal.srreal_ext" - - python -s -c "import diffpy.srreal.structureadapter" - - python -s -c "import diffpy.srreal.structureconverters" - - python -s -c "import diffpy.srreal.version" - - - # You can also put a file called run_test.py in the recipe that will be run - # at test time. - - # requires: - # Put any additional test requirements here. For example - # - nose - -about: - home: https://github.com/diffpy/diffpy.srreal - summary: Calculators for PDF, bond valence sum and other - pair-interaction quantities. - license: Modified BSD License - license_file: LICENSE.txt - -# See http://docs.continuum.io/conda/build.html -# for more information about meta.yaml. diff --git a/conda-recipe/run_test.py b/conda-recipe/run_test.py deleted file mode 100644 index d39487ff..00000000 --- a/conda-recipe/run_test.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python - -import diffpy.srreal.tests -assert diffpy.srreal.tests.test().wasSuccessful() diff --git a/conda-recipe/sconscript.local b/conda-recipe/sconscript.local deleted file mode 100644 index f8740395..00000000 --- a/conda-recipe/sconscript.local +++ /dev/null @@ -1,18 +0,0 @@ -# Customize scons build environment. - -Import('env') - -import os - -# Apply environment settings for Anaconda compilers -env.Replace(CXX=os.environ['CXX']) -env.MergeFlags(os.environ['CFLAGS']) -env.MergeFlags(os.environ['CPPFLAGS']) -env.MergeFlags(os.environ['CXXFLAGS']) -env.MergeFlags(os.environ['LDFLAGS']) - -# Silence copious warnings from the boost headers. -P = os.environ['PREFIX'] -env.Prepend(CCFLAGS=['-isystem{}/include'.format(P)]) - -# vim: ft=python diff --git a/devutils/makesdist b/devutils/makesdist deleted file mode 100755 index 06e07d7c..00000000 --- a/devutils/makesdist +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python - -'''Create source distribution tar.gz archive, where each file belongs -to a root user and modification time is set to the git commit time. -''' - -import sys -import os -import subprocess -import glob -import tarfile -import gzip - -BASEDIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) -sys.path.insert(0, BASEDIR) - -from setup import versiondata, FALLBACK_VERSION -timestamp = versiondata.getint('DEFAULT', 'timestamp') - -vfb = versiondata.get('DEFAULT', 'version').split('.post')[0] + '.post0' -emsg = "Invalid FALLBACK_VERSION. Expected %r got %r." -assert vfb == FALLBACK_VERSION, emsg % (vfb, FALLBACK_VERSION) - -def inform(s): - sys.stdout.write(s) - sys.stdout.flush() - return - -inform('Run "setup.py sdist --formats=tar" ') -cmd_sdist = ([sys.executable, '-Wignore:Cannot detect name suffix'] + - 'setup.py sdist --formats=tar'.split()) -ec = subprocess.call(cmd_sdist, cwd=BASEDIR, stdout=open(os.devnull, 'w')) -if ec: sys.exit(ec) -inform("[done]\n") - -tarname = max(glob.glob(BASEDIR + '/dist/*.tar'), key=os.path.getmtime) - -tfin = tarfile.open(tarname) -fpout = gzip.GzipFile(tarname + '.gz', 'w', mtime=0) -tfout = tarfile.open(fileobj=fpout, mode='w') - -def fixtarinfo(tinfo): - tinfo.uid = tinfo.gid = 0 - tinfo.uname = tinfo.gname = 'root' - tinfo.mtime = timestamp - tinfo.mode &= ~0o022 - return tinfo - -inform('Filter %s --> %s.gz ' % (2 * (os.path.basename(tarname),))) -for ti in tfin: - tfout.addfile(fixtarinfo(ti), tfin.extractfile(ti)) - -tfin.close() -os.remove(tarname) -inform("[done]\n") diff --git a/doc/manual/Makefile b/doc/Makefile similarity index 83% rename from doc/manual/Makefile rename to doc/Makefile index a21894cf..0c241efa 100644 --- a/doc/manual/Makefile +++ b/doc/Makefile @@ -6,6 +6,12 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build +BASENAME = $(subst .,,$(subst $() $(),,diffpy.srreal)) + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 @@ -14,7 +20,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) sou # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext publish +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @@ -29,17 +35,20 @@ help: @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: - -rm -rf $(BUILDDIR)/* + rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @@ -77,17 +86,17 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SrReal.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/$(BASENAME).qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SrReal.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/$(BASENAME).qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/SrReal" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SrReal" + @echo "# mkdir -p $$HOME/.local/share/devhelp/$(BASENAME)" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/$(BASENAME)" @echo "# devhelp" epub: @@ -108,6 +117,12 @@ latexpdf: $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 00000000..ac53d5bd --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build +set SPHINXPROJ=PackagingScientificPython + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/doc/manual/source/api/diffpy.srreal.rst b/doc/manual/source/api/diffpy.srreal.rst deleted file mode 100644 index 24629776..00000000 --- a/doc/manual/source/api/diffpy.srreal.rst +++ /dev/null @@ -1,163 +0,0 @@ -:tocdepth: 2 - -.. default-role:: py:obj - -diffpy.srreal package -===================== - -.. automodule:: diffpy.srreal - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -diffpy.srreal.atomradiitable module ------------------------------------ - -.. automodule:: diffpy.srreal.atomradiitable - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.attributes module -------------------------------- - -.. automodule:: diffpy.srreal.attributes - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.bondcalculator module ------------------------------------ - -.. automodule:: diffpy.srreal.bondcalculator - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.bvparameterstable module --------------------------------------- - -.. automodule:: diffpy.srreal.bvparameterstable - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.bvscalculator module ----------------------------------- - -.. automodule:: diffpy.srreal.bvscalculator - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.eventticker module --------------------------------- - -.. automodule:: diffpy.srreal.eventticker - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.overlapcalculator module --------------------------------------- - -.. automodule:: diffpy.srreal.overlapcalculator - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.pairquantity module ---------------------------------- - -.. automodule:: diffpy.srreal.pairquantity - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.parallel module ------------------------------ - -.. automodule:: diffpy.srreal.parallel - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.pdfbaseline module --------------------------------- - -.. automodule:: diffpy.srreal.pdfbaseline - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.pdfcalculator module ----------------------------------- - -.. automodule:: diffpy.srreal.pdfcalculator - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.pdfenvelope module --------------------------------- - -.. automodule:: diffpy.srreal.pdfenvelope - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.peakprofile module --------------------------------- - -.. automodule:: diffpy.srreal.peakprofile - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.peakwidthmodel module ------------------------------------ - -.. automodule:: diffpy.srreal.peakwidthmodel - :members: - :undoc-members: - :show-inheritance: - -diffpy.srreal.scatteringfactortable module ------------------------------------------- - -.. automodule:: diffpy.srreal.scatteringfactortable - :members: ScatteringFactorTable, SFTXray, - SFTElectron, SFTNeutron, SFTElectronNumber - :undoc-members: - :show-inheritance: - -diffpy.srreal.sfaverage module ------------------------------- - -.. automodule:: diffpy.srreal.sfaverage - :members: - :undoc-members: - :no-show-inheritance: - -diffpy.srreal.structureadapter module -------------------------------------- - -.. automodule:: diffpy.srreal.structureadapter - :members: - :undoc-members: - :show-inheritance: - -.. skip internal module diffpy.srreal.structureconverters - -diffpy.srreal.version module ----------------------------- - -.. automodule:: diffpy.srreal.version - :members: - :undoc-members: - :show-inheritance: - -.. skip internal module diffpy.srreal.wraputils diff --git a/doc/manual/source/license.rst b/doc/manual/source/license.rst deleted file mode 100644 index 7d78e94f..00000000 --- a/doc/manual/source/license.rst +++ /dev/null @@ -1,142 +0,0 @@ -.. index:: license - -License -####### - -OPEN SOURCE LICENSE AGREEMENT -============================= - -| Copyright (c) 2009-2011, University of Tennessee -| Copyright (c) 1989, 1991 Free Software Foundation, Inc. -| Copyright (c) 2006, The Regents of the University of California through - Lawrence Berkeley National Laboratory -| Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") -| Copyright (c) 2006-2007, Board of Trustees of Michigan State University -| Copyright (c) 2008-2012, The Trustees of Columbia University in - the City of New York -| Copyright (c) 2014-2019, Brookhaven Science Associates, - Brookhaven National Laboratory - - -The "DiffPy-CMI" is distributed subject to the following license conditions: - - -SOFTWARE LICENSE AGREEMENT -========================== - -Software: **DiffPy-CMI** - - -(1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either -source code, or binary form and accompanying documentation). - -Part of the software was derived from the DANSE, ObjCryst++ (with permission), -PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of -which the original Copyrights are contained in each individual file. - -Each licensee is addressed as "you" or "Licensee." - - -(2) The copyright holders shown above and their third-party Licensors hereby -grant licensee a royalty-free nonexclusive license, subject to the limitations -stated herein and U.S. Government license rights. - - -(3) You may modify and make a copy or copies of the software for use within -your organization, if you meet the following conditions: - - (a) Copies in source code must include the copyright notice and this - software license agreement. - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy. - - -(4) You may modify a copy or copies of the Software or any portion of it, thus -forming a work based on the Software, and distribute copies of such work -outside your organization, if you meet all of the following conditions: - - (a) Copies in source code must include the copyright notice and this - Software License Agreement; - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy; - - (c) Modified copies and works based on the Software must carry prominent - notices stating that you changed specified portions of the Software. - - (d) Neither the name of Brookhaven Science Associates or Brookhaven - National Laboratory nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - written permission. - - -(5) Portions of the Software resulted from work developed under a U.S. -Government contract and are subject to the following license: -The Government is granted for itself and others acting on its behalf a -paid-up, nonexclusive, irrevocable worldwide license in this computer software -to reproduce, prepare derivative works, and perform publicly and display -publicly. - - -(6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT -WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY -LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND -THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING -BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL -LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF -THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE -PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION -UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. - - -(7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR -THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF -ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, -CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING -BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, -WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING -NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS -BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. - - -Brookhaven National Laboratory Notice -===================================== - -Acknowledgment of sponsorship ------------------------------ - -This software was produced by the Brookhaven National Laboratory, under -Contract DE-AC02-98CH10886 with the Department of Energy. - - -Government disclaimer of liability ----------------------------------- - -Neither the United States nor the United States Department of Energy, nor -any of their employees, makes any warranty, express or implied, or assumes -any legal liability or responsibility for the accuracy, completeness, or -usefulness of any data, apparatus, product, or process disclosed, or -represents that its use would not infringe privately owned rights. - - -Brookhaven disclaimer of liability ----------------------------------- - -Brookhaven National Laboratory makes no representations or warranties, -express or implied, nor assumes any liability for the use of this software. - - -Maintenance of notice ---------------------- - -In the interest of clarity regarding the origin and status of this -software, Brookhaven National Laboratory requests that any recipient of it -maintain this notice affixed to any distribution by the recipient that -contains a copy or derivative of this software. - - -.. rubric:: END OF LICENSE diff --git a/doc/manual/source/release.rst b/doc/manual/source/release.rst deleted file mode 100644 index 7ec4f81d..00000000 --- a/doc/manual/source/release.rst +++ /dev/null @@ -1,3 +0,0 @@ -.. index:: release notes - -.. mdinclude:: ../../../CHANGELOG.md diff --git a/doc/source/_static/.placeholder b/doc/source/_static/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/doc/source/api/diffpy.srreal.example_package.rst b/doc/source/api/diffpy.srreal.example_package.rst new file mode 100644 index 00000000..439a6b4f --- /dev/null +++ b/doc/source/api/diffpy.srreal.example_package.rst @@ -0,0 +1,31 @@ +.. _example_package documentation: + +|title| +======= + +.. |title| replace:: diffpy.srreal.example_package package + +.. automodule:: diffpy.srreal.example_package + :members: + :undoc-members: + :show-inheritance: + +|foo| +----- + +.. |foo| replace:: diffpy.srreal.example_package.foo module + +.. automodule:: diffpy.srreal.example_package.foo + :members: + :undoc-members: + :show-inheritance: + +|bar| +----- + +.. |bar| replace:: diffpy.srreal.example_package.bar module + +.. automodule:: diffpy.srreal.example_package.foo + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srreal.rst b/doc/source/api/diffpy.srreal.rst new file mode 100644 index 00000000..0f1bb4b7 --- /dev/null +++ b/doc/source/api/diffpy.srreal.rst @@ -0,0 +1,30 @@ +:tocdepth: -1 + +|title| +======= + +.. |title| replace:: diffpy.srreal package + +.. automodule:: diffpy.srreal + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + diffpy.srreal.example_package + +Submodules +---------- + +|module| +-------- + +.. |module| replace:: diffpy.srreal.example_submodule module + +.. automodule:: diffpy.srreal.example_submodule + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/manual/source/conf.py b/doc/source/conf.py similarity index 68% rename from doc/manual/source/conf.py rename to doc/source/conf.py index 5d2c4878..a05707e1 100644 --- a/doc/manual/source/conf.py +++ b/doc/source/conf.py @@ -2,9 +2,10 @@ # -*- coding: utf-8 -*- # # diffpy.srreal documentation build configuration file, created by -# sphinx-quickstart on Tue Oct 22 12:02:48 2013. +# sphinx-quickstart on Thu Jan 30 15:49:41 2014. # -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set to its +# containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. @@ -13,58 +14,69 @@ # serve to show the default. import sys -import os import time +from importlib.metadata import version +from pathlib import Path + +# Attempt to import the version dynamically from GitHub tag. +try: + fullversion = version("diffpy.srreal") +except Exception: + fullversion = "No version found. The correct version will appear in the released version." # noqa: E501 # 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 os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) -sys.path.insert(0, os.path.abspath('../../..')) +# documentation root, use Path().resolve() to make it absolute, like shown here. +# sys.path.insert(0, str(Path(".").resolve())) +sys.path.insert(0, str(Path("../..").resolve())) +sys.path.insert(0, str(Path("../../src").resolve())) # abbreviations -ab_authors = u'Pavol Juhás, Christopher L. Farrow, Simon J.L. Billinge group' +ab_authors = "Pavol Juhás, Christopher L. Farrow, Simon J.L. Billinge group" -# -- General configuration ----------------------------------------------------- +# -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.napoleon', - 'sphinx.ext.intersphinx', - 'm2r', + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "sphinx_rtd_theme", + "sphinx_copybutton", + "m2r", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'diffpy.srreal' -copyright = '%Y, Brookhaven National Laboratory' +project = "diffpy.srreal" +copyright = "%Y, The Trustees of Columbia University in the City of New York" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -from setup import versiondata -fullversion = versiondata.get('DEFAULT', 'version') + # The short X.Y version. -version = ''.join(fullversion.split('.post')[:1]) +version = "".join(fullversion.split(".post")[:1]) # The full version, including alpha/beta/rc tags. release = fullversion @@ -75,19 +87,24 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' -today_seconds = versiondata.getint('DEFAULT', 'timestamp') -today = time.strftime('%B %d, %Y', time.localtime(today_seconds)) +today = time.strftime("%B %d, %Y", time.localtime()) year = today.split()[-1] # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # substitute YEAR in the copyright string -copyright = copyright.replace('%Y', year) +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 = [] +exclude_patterns = ["build"] -# The reST default role (used for this markup: `text`) to use for all documents. +# The reST default role (used for this markup: `text`) to use for all +# documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. @@ -102,26 +119,35 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['diffpy.srreal'] +modindex_common_prefix = ["diffpy.srreal"] # Display all warnings for missing links. nitpicky = True -# -- Options for HTML output --------------------------------------------------- +# -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_py3doc_enhanced_theme' +# +html_theme = "sphinx_rtd_theme" + +html_context = { + "display_github": True, + "github_user": "diffpy", + "github_repo": "diffpy.srreal", + "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. +# html_theme_options = { - 'collapsiblesidebar' : 'true', - 'navigation_with_keys' : 'true', + "navigation_with_keys": "true", } # Add any paths that contain custom themes here, relative to this directory. @@ -148,6 +174,11 @@ # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' @@ -190,27 +221,32 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'srrealdoc' +basename = "diffpy.srreal".replace(" ", "").replace(".", "") +htmlhelp_basename = basename + "doc" -# -- Options for LaTeX output -------------------------------------------------- +# -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -# 'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -# 'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -# 'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'diffpy.srreal.tex', 'diffpy.srreal Documentation', - ab_authors, 'manual'), + ( + "index", + "diffpy.srreal.tex", + "diffpy.srreal Documentation", + ab_authors, + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -234,28 +270,39 @@ # latex_domain_indices = True -# -- Options for manual page output -------------------------------------------- +# -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'diffpy.srreal', 'diffpy.srreal Documentation', - ab_authors, 1) + ( + "index", + "diffpy.srreal", + "diffpy.srreal Documentation", + ab_authors, + 1, + ) ] # If true, show URL addresses after external links. # man_show_urls = False -# -- Options for Texinfo output ------------------------------------------------ +# -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'diffpy.srreal', 'diffpy.srreal Documentation', - ab_authors, 'diffpy.srreal', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "diffpy.srreal", + "diffpy.srreal Documentation", + ab_authors, + "diffpy.srreal", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. @@ -267,9 +314,12 @@ # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False + # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - 'numpy': ('https://docs.scipy.org/doc/numpy', None), - 'python' : ('https://docs.python.org/3.7', None), + "numpy": ("https://numpy.org/doc/stable/", None), + "python": ("https://docs.python.org/3", None), } diff --git a/doc/source/examples.rst b/doc/source/examples.rst new file mode 100644 index 00000000..ef8715f5 --- /dev/null +++ b/doc/source/examples.rst @@ -0,0 +1,4 @@ +.. _examples: + +Examples +######## diff --git a/examples/compareC60PDFs.py b/doc/source/examples/compareC60PDFs.py similarity index 73% rename from examples/compareC60PDFs.py rename to doc/source/examples/compareC60PDFs.py index 622c350c..844cd7a6 100755 --- a/examples/compareC60PDFs.py +++ b/doc/source/examples/compareC60PDFs.py @@ -1,23 +1,27 @@ #!/usr/bin/env python """Plot C60 PDFs calculated with PDFCalculator and DebyePDFCalculator. + The C60 molecule is held in a diffpy Structure object. """ -import sys import os -from matplotlib.pyplot import plot, show, clf +import sys + +from matplotlib.pyplot import clf, plot, show + +from diffpy.srreal.pdfcalculator import DebyePDFCalculator, PDFCalculator from diffpy.structure import Structure -from diffpy.srreal.pdfcalculator import PDFCalculator, DebyePDFCalculator mydir = os.path.dirname(os.path.abspath(sys.argv[0])) -buckyfile = os.path.join(mydir, 'datafiles', 'C60bucky.stru') +buckyfile = os.path.join(mydir, "datafiles", "C60bucky.stru") # load C60 molecule as a diffpy.structure object bucky = Structure(filename=buckyfile) -cfg = { 'qmax' : 25, - 'rmin' : 0, - 'rmax' : 10.001, +cfg = { + "qmax": 25, + "rmin": 0, + "rmax": 10.001, } # calculate PDF by real-space summation diff --git a/examples/compareC60PDFs_objcryst.py b/doc/source/examples/compareC60PDFs_objcryst.py similarity index 78% rename from examples/compareC60PDFs_objcryst.py rename to doc/source/examples/compareC60PDFs_objcryst.py index 35f3a293..62e705d0 100755 --- a/examples/compareC60PDFs_objcryst.py +++ b/doc/source/examples/compareC60PDFs_objcryst.py @@ -1,21 +1,23 @@ #!/usr/bin/env python """Plot C60 PDFs calculated with PDFCalculator and DebyePDFCalculator. + The C60 molecule are stored in a pyobjcryst object. """ -from matplotlib.pyplot import plot, show, clf -from diffpy.structure import Structure +from matplotlib.pyplot import clf, plot, show from pyobjcryst.crystal import Crystal from pyobjcryst.molecule import Molecule from pyobjcryst.scatteringpower import ScatteringPowerAtom -from diffpy.srreal.pdfcalculator import PDFCalculator, DebyePDFCalculator + +from diffpy.srreal.pdfcalculator import DebyePDFCalculator, PDFCalculator +from diffpy.structure import Structure # load C60 molecule as a diffpy.structure object -bucky_diffpy = Structure(filename='datafiles/C60bucky.stru') +bucky_diffpy = Structure(filename="datafiles/C60bucky.stru") # convert to an ObjCryst molecule -c60 = Crystal(1, 1, 1, 'P1') +c60 = Crystal(1, 1, 1, "P1") mc60 = Molecule(c60, "C60") c60.AddScatterer(mc60) # Create the scattering power object for the carbon atoms @@ -26,10 +28,11 @@ mc60.AddAtom(a.xyz_cartn[0], a.xyz_cartn[1], a.xyz_cartn[2], sp, cname) # PDF configuration -cfg = { 'qmax' : 25, - 'rmin' : 0, - 'rmax' : 10.001, - 'rstep' : 0.05, +cfg = { + "qmax": 25, + "rmin": 0, + "rmax": 10.001, + "rstep": 0.05, } # calculate PDF by real-space summation diff --git a/examples/datafiles/C60bucky.stru b/doc/source/examples/datafiles/C60bucky.stru similarity index 100% rename from examples/datafiles/C60bucky.stru rename to doc/source/examples/datafiles/C60bucky.stru diff --git a/examples/datafiles/menthol.cif b/doc/source/examples/datafiles/menthol.cif similarity index 98% rename from examples/datafiles/menthol.cif rename to doc/source/examples/datafiles/menthol.cif index c8c7e6e2..1ff0ca8b 100644 --- a/examples/datafiles/menthol.cif +++ b/doc/source/examples/datafiles/menthol.cif @@ -2,26 +2,26 @@ ########################################################################### # # Cambridge Crystallographic Data Centre -# CCDC -# +# CCDC +# ########################################################################### # # This CIF contains data generated directly from one or more entries in -# the Cambridge Structural Database and will include bibliographic, +# the Cambridge Structural Database and will include bibliographic, # chemical, crystal, experimental, refinement, and atomic coordinate data, # as available. -# +# # Copyright 2008 The Cambridge Crystallographic Data Centre -# -# This CIF is provided on the understanding that it is used for bona fide +# +# This CIF is provided on the understanding that it is used for bona fide # research purposes only. It may contain copyright material of the CCDC -# or of third parties, and may not be copied or further disseminated in -# any form, whether machine-readable or not, except for the purpose of +# or of third parties, and may not be copied or further disseminated in +# any form, whether machine-readable or not, except for the purpose of # generating routine backup copies on your local computer system. -# -# For further information about the CCDC, data deposition and data -# retrieval see . Bona fide researchers may freely -# download Mercury and enCIFer from this site to visualise CIF-encoded +# +# For further information about the CCDC, data deposition and data +# retrieval see . Bona fide researchers may freely +# download Mercury and enCIFer from this site to visualise CIF-encoded # structures and to carry out CIF format checking respectively. # ########################################################################### diff --git a/examples/datafiles/sphalerite.cif b/doc/source/examples/datafiles/sphalerite.cif similarity index 100% rename from examples/datafiles/sphalerite.cif rename to doc/source/examples/datafiles/sphalerite.cif diff --git a/examples/distanceprinter.py b/doc/source/examples/distanceprinter.py similarity index 54% rename from examples/distanceprinter.py rename to doc/source/examples/distanceprinter.py index 45ae0e65..870c7216 100755 --- a/examples/distanceprinter.py +++ b/doc/source/examples/distanceprinter.py @@ -1,31 +1,32 @@ #!/usr/bin/env python -"""Demonstration of using PairQuantity class for a printout -of pair distances in periodic and non-periodic structures. -""" +"""Demonstration of using PairQuantity class for a printout of pair +distances in periodic and non-periodic structures.""" from diffpy.srreal.pairquantity import PairQuantity from diffpy.structure import Structure -class DistancePrinter(PairQuantity): - '''This PairQuantity class simply prints the visited pair distances - and the indices of the contributing atoms. - ''' +class DistancePrinter(PairQuantity): + """This PairQuantity class simply prints the visited pair distances + and the indices of the contributing atoms.""" def _resetValue(self): self.count = 0 def _addPairContribution(self, bnds, sumscale): self.count += bnds.multiplicity() * sumscale / 2.0 - print("%i %g %i %i" % ( - self.count, bnds.distance(), bnds.site0(), bnds.site1())) + print( + "%i %g %i %i" + % (self.count, bnds.distance(), bnds.site0(), bnds.site1()) + ) return + # class DistancePrinter # define nickel structure data -nickel_discus_data = ''' +nickel_discus_data = """ title Ni spcgr P1 cell 3.523870, 3.523870, 3.523870, 90.000000, 90.000000, 90.000000 @@ -35,47 +36,53 @@ def _addPairContribution(self, bnds, sumscale): NI 0.00000000 0.50000000 0.50000000 0.1000 NI 0.50000000 0.00000000 0.50000000 0.1000 NI 0.50000000 0.50000000 0.00000000 0.1000 -''' +""" nickel = Structure() -nickel.readStr(nickel_discus_data, format='discus') +nickel.readStr(nickel_discus_data, format="discus") -bucky = Structure(filename='datafiles/C60bucky.stru', format='discus') +bucky = Structure(filename="datafiles/C60bucky.stru", format="discus") distprint = DistancePrinter() -distprint._setDoubleAttr('rmax', 10) +distprint._setDoubleAttr("rmax", 10) + def get_pyobjcryst_sphalerite(): from pyobjcryst import loadCrystal - crst = loadCrystal('datafiles/sphalerite.cif') + + crst = loadCrystal("datafiles/sphalerite.cif") return crst + def main(): - s = input('Enter rmin: ') - if s.strip(): distprint.rmin = float(s) + s = input("Enter rmin: ") + if s.strip(): + distprint.rmin = float(s) print("rmin = %g" % distprint.rmin) - s = input('Enter rmax: ') - if s.strip(): distprint.rmax = float(s) + s = input("Enter rmax: ") + if s.strip(): + distprint.rmax = float(s) print("rmax = %g" % distprint.rmax) print() - linesep = 78 * '-' + linesep = 78 * "-" # C60bucky print(linesep) - input('Press enter for distances in C60 molecule.') + input("Press enter for distances in C60 molecule.") distprint.eval(bucky) # nickel print(linesep) - input('Press enter for distances in a nickel crystal.') + input("Press enter for distances in a nickel crystal.") distprint.eval(nickel) # objcryst sphalerite print(linesep) - input('Press enter for distances in objcryst loaded sphalerite.cif.') + input("Press enter for distances in objcryst loaded sphalerite.cif.") crst = get_pyobjcryst_sphalerite() distprint.eval(crst) print(linesep) - input('Press enter for distances in diffpy.structure sphalerite.cif.') - crst = Structure(filename='datafiles/sphalerite.cif') + input("Press enter for distances in diffpy.structure sphalerite.cif.") + crst = Structure(filename="datafiles/sphalerite.cif") distprint.eval(crst) return -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/examples/lennardjones/Makefile b/doc/source/examples/lennardjones/Makefile similarity index 100% rename from examples/lennardjones/Makefile rename to doc/source/examples/lennardjones/Makefile diff --git a/examples/lennardjones/README.txt b/doc/source/examples/lennardjones/README.txt similarity index 100% rename from examples/lennardjones/README.txt rename to doc/source/examples/lennardjones/README.txt diff --git a/examples/lennardjones/lj50.xyz b/doc/source/examples/lennardjones/lj50.xyz similarity index 100% rename from examples/lennardjones/lj50.xyz rename to doc/source/examples/lennardjones/lj50.xyz diff --git a/examples/lennardjones/ljcalculator.cpp b/doc/source/examples/lennardjones/ljcalculator.cpp similarity index 100% rename from examples/lennardjones/ljcalculator.cpp rename to doc/source/examples/lennardjones/ljcalculator.cpp diff --git a/examples/lennardjones/ljcalculator.py b/doc/source/examples/lennardjones/ljcalculator.py similarity index 82% rename from examples/lennardjones/ljcalculator.py rename to doc/source/examples/lennardjones/ljcalculator.py index eebd7740..4e1e1511 100755 --- a/examples/lennardjones/ljcalculator.py +++ b/doc/source/examples/lennardjones/ljcalculator.py @@ -1,15 +1,17 @@ #!/usr/bin/env python -"""Demonstration of using PairQuantity class for calculation -of Lennard Jones potential. +"""Demonstration of using PairQuantity class for calculation of Lennard +Jones potential. Vij = 4 * ( rij ** -12 - rij ** -6 ) """ import sys + from diffpy.srreal.pairquantity import PairQuantity from diffpy.structure import Structure + class LennardJonesCalculator(PairQuantity): # Initialization. The size of the result array is always 1. @@ -18,16 +20,13 @@ def __init__(self): self._resizeValue(1) return - def __call__(self, structure): - '''Return LJ potential for a specified structure - ''' + """Return LJ potential for a specified structure.""" values = self.eval(structure) return values[0] - def _addPairContribution(self, bnds, sumscale): - '''Add Lennard-Jones contribution from a single pair of atoms. + """Add Lennard-Jones contribution from a single pair of atoms. bnds -- a BaseBondGenerator instance that contains information about the current pair of atom sites. @@ -35,12 +34,13 @@ def _addPairContribution(self, bnds, sumscale): be negative in case of fast value updates. No return value. Updates _value, the internal results array. - ''' + """ rij = bnds.distance() - ljij = 4 * (rij ** -12 - rij ** -6) + ljij = 4 * (rij**-12 - rij**-6) self._value[0] += sumscale * ljij / 2.0 return + # class LennardJonesCalculator @@ -55,5 +55,5 @@ def main(): print("LJ potential of %s is %g" % (filename, ljcalc(stru))) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/parallelPDF.py b/doc/source/examples/parallelPDF.py similarity index 74% rename from examples/parallelPDF.py rename to doc/source/examples/parallelPDF.py index 9040b28d..199ebaa1 100755 --- a/examples/parallelPDF.py +++ b/doc/source/examples/parallelPDF.py @@ -1,37 +1,45 @@ #!/usr/bin/env python """Demonstration of parallel PDF calculation using the multiprocessing -package. A PDF of menthol structure is first calculated on a single core -and then on all computer CPUs. The script then compares both results -and prints elapsed time per each calculation. +package. + +A PDF of menthol structure is first calculated on a single core and then +on all computer CPUs. The script then compares both results and prints +elapsed time per each calculation. """ +import multiprocessing +import optparse import os import sys -import optparse import time -import multiprocessing -from diffpy.structure import Structure -from diffpy.srreal.pdfcalculator import PDFCalculator + +from matplotlib.pyplot import clf, plot, show + from diffpy.srreal.parallel import createParallelCalculator +from diffpy.srreal.pdfcalculator import PDFCalculator +from diffpy.structure import Structure mydir = os.path.dirname(os.path.abspath(__file__)) -mentholcif = os.path.join(mydir, 'datafiles', 'menthol.cif') +mentholcif = os.path.join(mydir, "datafiles", "menthol.cif") Uisodefault = 0.005 # configure options parsing -parser = optparse.OptionParser("%prog [options]\n" + - __doc__) -parser.add_option("--pyobjcryst", action="store_true", - help="Use pyobjcryst to load the CIF file.") +parser = optparse.OptionParser("%prog [options]\n" + __doc__) +parser.add_option( + "--pyobjcryst", + action="store_true", + help="Use pyobjcryst to load the CIF file.", +) parser.allow_interspersed_args = True opts, args = parser.parse_args(sys.argv[1:]) # load menthol structure and make sure Uiso values are non-zero if opts.pyobjcryst: # use pyobjcryst if requested by the user - from pyobjcryst import loadCrystal from numpy import pi + from pyobjcryst import loadCrystal + menthol = loadCrystal(mentholcif) for sc in menthol.GetScatteringComponentList(): sp = sc.mpScattPow @@ -43,9 +51,10 @@ a.Uisoequiv = a.Uisoequiv or Uisodefault # configuration of a PDF calculator -cfg = { 'qmax' : 25, - 'rmin' : 0, - 'rmax' : 30, +cfg = { + "qmax": 25, + "rmin": 0, + "rmax": 30, } # number of CPUs for parallel calculation @@ -62,7 +71,7 @@ pool = multiprocessing.Pool(processes=ncpu) t1 = time.time() -# create a proxy parrallel calculator to PDFCalculator pc0, +# create a proxy parallel calculator to PDFCalculator pc0, # that uses ncpu parallel jobs submitted via pool.imap_unordered pc1 = createParallelCalculator(pc0, ncpu, pool.imap_unordered) r1, g1 = pc1(menthol) @@ -71,7 +80,7 @@ print("Time ratio: %g" % (t0 / t1)) # plot both results and the difference curve -from matplotlib.pyplot import plot, show, clf + clf() gd = g0 - g1 plot(r0, g0, r1, g1, r0, gd - 3) diff --git a/doc/manual/source/index.rst b/doc/source/index.rst similarity index 76% rename from doc/manual/source/index.rst rename to doc/source/index.rst index 4dab5ec8..1b845f1b 100644 --- a/doc/manual/source/index.rst +++ b/doc/source/index.rst @@ -1,10 +1,12 @@ -#################################################### -diffpy.srreal documentation -#################################################### +####### +|title| +####### -diffpy.srreal - calculators for PDF, bond valence sum and other pair quantities. +.. |title| replace:: diffpy.srreal documentation -| Software version |release|. +``diffpy.srreal`` - Calculators for PDF, bond valence sum, and other quantities based on atom pair interaction. + +| Software version |release| | Last updated |today|. The diffpy.srreal package provides calculators for atomic pair distribution @@ -37,9 +39,9 @@ calculate PDF with a user-defined profile function. A new calculator class can be also defined for any quantity that is obtained by iteration over atom pairs, by defining only the function that processes atom-pair contributions. -======================================== +======= Authors -======================================== +======= diffpy.srreal is developed by members of the Billinge Group at Columbia University and at Brookhaven National Laboratory including @@ -48,28 +50,42 @@ Pavol Juhás, Christopher L. Farrow, Simon J.L. Billinge. For a detailed list of contributors see https://github.com/diffpy/diffpy.srreal/graphs/contributors. -====================================== +Reference +========= + +If you use this program for a scientific research that leads +to publication, we ask that you acknowledge use of the program +by citing the following paper in your publication: + + diffpy.srreal Package, https://github.com/diffpy/diffpy.srreal + +============ Installation -====================================== +============ -See the `README `_ +See the `README `_ file included with the distribution. -====================================== -Table of contents -====================================== +================ +Acknowledgements +================ + +``diffpy.srreal`` is built and maintained with `scikit-package `_. +================= +Table of contents +================= .. toctree:: :titlesonly: license release + examples Package API -====================================== +======= Indices -====================================== +======= * :ref:`genindex` -* :ref:`modindex` * :ref:`search` diff --git a/doc/source/license.rst b/doc/source/license.rst new file mode 100644 index 00000000..03c1eae0 --- /dev/null +++ b/doc/source/license.rst @@ -0,0 +1,149 @@ +:tocdepth: -1 + +.. index:: license + +License +####### + +OPEN SOURCE LICENSE AGREEMENT +============================= + +Copyright (c) 2009-2011, University of Tennessee + +Copyright (c) 1989, 1991 Free Software Foundation, Inc. + +Copyright (c) 2006, The Regents of the University of California through Lawrence Berkeley National Laboratory + +Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") + +Copyright (c) 2006-2007, Board of Trustees of Michigan State University + +Copyright (c) 2008-2012, The Trustees of Columbia University in the City of New York + +Copyright (c) 2014-2019, Brookhaven Science Associates, Brookhaven National Laboratory + +Copyright (c) 2025, The Trustees of Columbia University in the City of New York. +All rights reserved. + +The "DiffPy-CMI" is distributed subject to the following license conditions: + +.. code-block:: text + + SOFTWARE LICENSE AGREEMENT + + Software: DiffPy-CMI + + + (1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either + source code, or binary form and accompanying documentation). + + Part of the software was derived from the DANSE, ObjCryst++ (with permission), + PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of + which the original Copyrights are contained in each individual file. + + Each licensee is addressed as "you" or "Licensee." + + + (2) The copyright holders shown above and their third-party Licensors hereby + grant licensee a royalty-free nonexclusive license, subject to the limitations + stated herein and U.S. Government license rights. + + + (3) You may modify and make a copy or copies of the software for use within + your organization, if you meet the following conditions: + + (a) Copies in source code must include the copyright notice and this + software license agreement. + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy. + + + (4) You may modify a copy or copies of the Software or any portion of it, thus + forming a work based on the Software, and distribute copies of such work + outside your organization, if you meet all of the following conditions: + + (a) Copies in source code must include the copyright notice and this + Software License Agreement; + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy; + + (c) Modified copies and works based on the Software must carry prominent + notices stating that you changed specified portions of the Software. + + (d) Neither the name of Brookhaven Science Associates or Brookhaven + National Laboratory nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + written permission. + + + (5) Portions of the Software resulted from work developed under a U.S. + Government contract and are subject to the following license: + The Government is granted for itself and others acting on its behalf a + paid-up, nonexclusive, irrevocable worldwide license in this computer software + to reproduce, prepare derivative works, and perform publicly and display + publicly. + + + (6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT + WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY + LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND + THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL + LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF + THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE + PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION + UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. + + + (7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR + THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF + ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, + CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING + BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, + WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING + NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS + BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. + + +Brookhaven National Laboratory Notice +===================================== + +Acknowledgment of sponsorship +----------------------------- + +This software was produced by the Brookhaven National Laboratory, under +Contract DE-AC02-98CH10886 with the Department of Energy. + + +Government disclaimer of liability +---------------------------------- + +Neither the United States nor the United States Department of Energy, nor +any of their employees, makes any warranty, express or implied, or assumes +any legal liability or responsibility for the accuracy, completeness, or +usefulness of any data, apparatus, product, or process disclosed, or +represents that its use would not infringe privately owned rights. + + +Brookhaven disclaimer of liability +---------------------------------- + +Brookhaven National Laboratory makes no representations or warranties, +express or implied, nor assumes any liability for the use of this software. + + +Maintenance of notice +--------------------- + +In the interest of clarity regarding the origin and status of this +software, Brookhaven National Laboratory requests that any recipient of it +maintain this notice affixed to any distribution by the recipient that +contains a copy or derivative of this software. + + +END OF LICENSE diff --git a/doc/source/release.rst b/doc/source/release.rst new file mode 100644 index 00000000..27cd0cc9 --- /dev/null +++ b/doc/source/release.rst @@ -0,0 +1,5 @@ +:tocdepth: -1 + +.. index:: release notes + +.. include:: ../../CHANGELOG.rst diff --git a/news/TEMPLATE.rst b/news/TEMPLATE.rst new file mode 100644 index 00000000..790d30b1 --- /dev/null +++ b/news/TEMPLATE.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/news/pc-blackv2.rst b/news/pc-blackv2.rst new file mode 100644 index 00000000..64feaf3d --- /dev/null +++ b/news/pc-blackv2.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Configure ``black`` to have a linelength requirement of 79 characters. + +**Security:** + +* diff --git a/news/pre-commit-auto.rst b/news/pre-commit-auto.rst new file mode 100644 index 00000000..e720e3b2 --- /dev/null +++ b/news/pre-commit-auto.rst @@ -0,0 +1,23 @@ +**Added:** + +* Update linelength to sk-package standard of 79 characters. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/news/skpkg-migration.rst b/news/skpkg-migration.rst new file mode 100644 index 00000000..b0ec659f --- /dev/null +++ b/news/skpkg-migration.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Support ``scikit-package`` Level 5 standard (https://scikit-package.github.io/scikit-package/). + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..840710d8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,85 @@ +[build-system] +requires = ["setuptools>=62.0", "setuptools-git-versioning>=2.0", "numpy"] +build-backend = "setuptools.build_meta" + +[project] +name = "diffpy.srreal" +dynamic=['version', 'dependencies'] +authors = [ + { name="Simon Billinge", email="sb2896@columbia.edu" }, +] +maintainers = [ + { name="Simon Billinge", email="sb2896@columbia.edu" }, +] +description = "Calculators for PDF, bond valence sum, and other quantities based on atom pair interaction." +keywords = ['PDF', 'BVS', 'atom', 'overlap', 'calculator', 'real-space'] +readme = "README.rst" +requires-python = ">=3.11, <3.14" +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: BSD License', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Topic :: Scientific/Engineering :: Physics', + 'Topic :: Scientific/Engineering :: Chemistry', +] + +[project.urls] +Homepage = "https://github.com/diffpy/diffpy.srreal/" +Issues = "https://github.com/diffpy/diffpy.srreal/issues/" + +[tool.setuptools-git-versioning] +enabled = true +template = "{tag}" +dev_template = "{tag}" +dirty_template = "{tag}" + +[tool.setuptools.packages.find] +where = ["src"] # list of folders that contain the packages (["."] by default) +include = ["*"] # package names should match these glob patterns (["*"] by default) +exclude = [] # exclude packages matching these glob patterns (empty by default) +namespaces = false # to disable scanning PEP 420 namespaces (true by default) + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements/pip.txt"]} + +[tool.codespell] +exclude-file = ".codespell/ignore_lines.txt" +ignore-words = ".codespell/ignore_words.txt" +skip = "*.cif,*.dat" + +[tool.docformatter] +recursive = true +wrap-summaries = 72 +wrap-descriptions = 72 + +[tool.black] +line-length = 79 +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | \.rst + | \.txt + | _build + | buck-out + | build + | dist + + # The following are specific to Black, you probably don't want those. + | blib2to3 + | tests/data +)/ +''' diff --git a/requirements/build.txt b/requirements/build.txt new file mode 100644 index 00000000..dde224fc --- /dev/null +++ b/requirements/build.txt @@ -0,0 +1,4 @@ +python +boost +numpy +# libobjcryst (if libdiffpy built with objcryst) diff --git a/requirements/conda.txt b/requirements/conda.txt new file mode 100644 index 00000000..a9b021c0 --- /dev/null +++ b/requirements/conda.txt @@ -0,0 +1,6 @@ +libdiffpy=1.4.1rc1 +libboost-devel +libobjcryst +pyobjcryst +diffpy.structure +periodictable diff --git a/requirements/docs.txt b/requirements/docs.txt new file mode 100644 index 00000000..5f34c6ed --- /dev/null +++ b/requirements/docs.txt @@ -0,0 +1,5 @@ +sphinx +sphinx_rtd_theme +sphinx-copybutton +doctr +m2r diff --git a/requirements/pip.txt b/requirements/pip.txt new file mode 100644 index 00000000..e69de29b diff --git a/requirements/test.txt b/requirements/test.txt new file mode 100644 index 00000000..a7277865 --- /dev/null +++ b/requirements/test.txt @@ -0,0 +1,6 @@ +flake8 +pytest +codecov +coverage +pytest-cov +pytest-env diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index 6256a9f5..d781a2a9 --- a/setup.py +++ b/setup.py @@ -1,195 +1,121 @@ #!/usr/bin/env python -# Installation script for diffpy.srreal - """diffpy.srreal - calculators for PDF, bond valence sum, and other quantities based on atom pair interaction. Packages: diffpy.srreal """ +import glob import os -import re import sys -import glob -from setuptools import setup, find_packages -from setuptools import Extension -from numpy.distutils.misc_util import get_numpy_include_dirs +from ctypes.util import find_library +from pathlib import Path +import numpy +from setuptools import Extension, setup -# Use this version when git data are not available, like in git zip archive. -# Update when tagging a new release. -FALLBACK_VERSION = '1.3.0.post0' - -# define extension arguments here -ext_kws = { - 'libraries' : ['diffpy'], - 'extra_compile_args' : ['-std=c++11'], - 'extra_link_args' : [], - 'include_dirs' : get_numpy_include_dirs(), -} - -# determine if we run with Python 3. -PY3 = (sys.version_info[0] == 3) - -# Figure out the tagged name of boost_python library. def get_boost_libraries(): - """Check for installed boost_python shared library. - - Returns list of required boost_python shared libraries that are installed - on the system. If required libraries are not found, an Exception will be - thrown. - """ - baselib = "boost_python" - major, minor = (str(x) for x in sys.version_info[:2]) - pytags = [major + minor, major, ''] - mttags = ['', '-mt'] - boostlibtags = [(pt + mt) for mt in mttags for pt in pytags] + [''] - from ctypes.util import find_library - for tag in boostlibtags: - lib = baselib + tag - found = find_library(lib) - if found: break - - # Show warning when library was not detected. - if not found: - import platform - import warnings - ldevname = 'LIBRARY_PATH' - if platform.system() == 'Darwin': - ldevname = 'DYLD_FALLBACK_LIBRARY_PATH' - wmsg = ("Cannot detect name suffix for the %r library. " - "Consider setting %s.") % (baselib, ldevname) - warnings.warn(wmsg) - - libs = [lib] + base_lib = "boost_python" + major, minor = str(sys.version_info[0]), str(sys.version_info[1]) + tags = [f"{major}{minor}", major, ""] + mttags = ["", "-mt"] + candidates = [base_lib + tag for tag in tags for mt in mttags] + [base_lib] + for lib in candidates: + if find_library(lib): + return [lib] + raise RuntimeError("Cannot find a suitable Boost.Python library.") + + +def get_boost_config(): + boost_path = os.environ.get("BOOST_PATH", "") + if boost_path: + inc = Path(boost_path) / "include" + lib = Path(boost_path) / "lib" + else: + conda_prefix = os.environ.get("CONDA_PREFIX") + if not conda_prefix: + raise EnvironmentError( + "Neither BOOST_PATH nor CONDA_PREFIX are set. " + "Please install Boost or set BOOST_PATH." + ) + if os.name == "nt": + inc = Path(conda_prefix) / "Library" / "include" + lib = Path(conda_prefix) / "Library" / "lib" + else: + inc = Path(conda_prefix) / "include" + lib = Path(conda_prefix) / "lib" + return {"include_dirs": [str(inc)], "library_dirs": [str(lib)]} + + +def get_objcryst_libraries(): + conda_prefix = os.environ.get("CONDA_PREFIX") + if not conda_prefix: + raise EnvironmentError( + "CONDA_PREFIX is not set. " + "Please install ObjCryst using conda and activate the environment." + ) + if os.name == "nt": + libdir = Path(conda_prefix) / "Library" / "lib" + else: + libdir = Path(conda_prefix) / "lib" + + libs = [] + for fn in os.listdir(libdir): + stem = Path(fn).stem + if "objcryst" not in stem.lower(): + continue + # strip a leading "lib" + # so that setuptools does -lObjCryst, not -llibObjCryst + if os.name != "nt" and stem.startswith("lib"): + stem = stem[3:] + libs.append(stem) + + if not libs: + raise RuntimeError(f"No ObjCryst libraries found in {libdir}") return libs -def create_extensions(): - "Initialize Extension objects for the setup function." - blibs = [n for n in get_boost_libraries() - if not n in ext_kws['libraries']] - ext_kws['libraries'] += blibs - ext = Extension('diffpy.srreal.srreal_ext', - glob.glob('src/extensions/*.cpp'), - **ext_kws) - return [ext] +if os.name == "nt": + compile_args = ["/std:c++14"] + macros = [("_USE_MATH_DEFINES", None)] + extra_link_args = ["/FORCE:MULTIPLE"] +else: + compile_args = ["-std=c++11"] + macros = [] + extra_link_args = [] +boost_cfg = get_boost_config() +objcryst_libs = get_objcryst_libraries() -# versioncfgfile holds version data for git commit hash and date. -# It must reside in the same directory as version.py. -MYDIR = os.path.dirname(os.path.abspath(__file__)) -versioncfgfile = os.path.join(MYDIR, 'src/diffpy/srreal/version.cfg') -gitarchivecfgfile = os.path.join(MYDIR, '.gitarchive.cfg') +ext_kws = { + "libraries": ["diffpy"] + get_boost_libraries() + objcryst_libs, + "extra_compile_args": compile_args, + "extra_link_args": extra_link_args, + "include_dirs": [numpy.get_include()] + boost_cfg["include_dirs"], + "library_dirs": boost_cfg["library_dirs"], + # "runtime_library_dirs": boost_cfg["library_dirs"], + "define_macros": macros, +} -def gitinfo(): - from subprocess import Popen, PIPE - kw = dict(stdout=PIPE, cwd=MYDIR, universal_newlines=True) - proc = Popen(['git', 'describe', '--match=v[[:digit:]]*'], **kw) - desc = proc.stdout.read() - proc = Popen(['git', 'log', '-1', '--format=%H %ct %ci'], **kw) - glog = proc.stdout.read() - rv = {} - rv['version'] = '.post'.join(desc.strip().split('-')[:2]).lstrip('v') - rv['commit'], rv['timestamp'], rv['date'] = glog.strip().split(None, 2) - return rv +def create_extensions(): + "Initialize Extension objects for the setup function." + ext = Extension( + "diffpy.srreal.srreal_ext", + glob.glob("src/extensions/*.cpp"), + **ext_kws, + ) + return [ext] -def getversioncfg(): - if PY3: - from configparser import RawConfigParser - else: - from ConfigParser import RawConfigParser - vd0 = dict(version=FALLBACK_VERSION, commit='', date='', timestamp=0) - # first fetch data from gitarchivecfgfile, ignore if it is unexpanded - g = vd0.copy() - cp0 = RawConfigParser(vd0) - cp0.read(gitarchivecfgfile) - if len(cp0.get('DEFAULT', 'commit')) > 20: - g = cp0.defaults() - mx = re.search(r'\btag: v(\d[^,]*)', g.pop('refnames')) - if mx: - g['version'] = mx.group(1) - # then try to obtain version data from git. - gitdir = os.path.join(MYDIR, '.git') - if os.path.exists(gitdir) or 'GIT_DIR' in os.environ: - try: - g = gitinfo() - except OSError: - pass - # finally, check and update the active version file - cp = RawConfigParser() - cp.read(versioncfgfile) - d = cp.defaults() - rewrite = not d or (g['commit'] and ( - g['version'] != d.get('version') or g['commit'] != d.get('commit'))) - if rewrite: - cp.set('DEFAULT', 'version', g['version']) - cp.set('DEFAULT', 'commit', g['commit']) - cp.set('DEFAULT', 'date', g['date']) - cp.set('DEFAULT', 'timestamp', g['timestamp']) - with open(versioncfgfile, 'w') as fp: - cp.write(fp) - return cp - -versiondata = getversioncfg() - -with open(os.path.join(MYDIR, 'README.rst')) as fp: - long_description = fp.read() - -# define distribution +# Extensions not included in pyproject.toml setup_args = dict( - name = "diffpy.srreal", - version = versiondata.get('DEFAULT', 'version'), - packages = find_packages(os.path.join(MYDIR, 'src')), - package_dir = {'' : 'src'}, - test_suite = 'diffpy.srreal.tests', - include_package_data = True, - ext_modules = [], - install_requires = [ - 'diffpy.structure', - ], - zip_safe = False, - - author = "Simon J.L. Billinge group", - author_email = "sb2896@columbia.edu", - maintainer = "Pavol Juhas", - maintainer_email = "pavol.juhas@gmail.com", - description = ("calculators for PDF, bond valence sum, and other " - "quantities based on atom pair interaction."), - long_description = long_description, - long_description_content_type = 'text/x-rst', - license = 'BSD-style license', - url = "https://github.com/diffpy/diffpy.srreal/", - keywords = "PDF BVS atom overlap calculator real-space", - classifiers = [ - # List of possible values at - # http://pypi.python.org/pypi?:action=list_classifiers - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Education', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Programming Language :: C++', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Scientific/Engineering :: Chemistry', - 'Topic :: Scientific/Engineering :: Physics', - 'Topic :: Software Development :: Libraries', - ], + ext_modules=[], ) -if __name__ == '__main__': - setup_args['ext_modules'] = create_extensions() - setup(**setup_args) -# End of file +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 93b57f65..67de5256 100644 --- a/src/diffpy/__init__.py +++ b/src/diffpy/__init__.py @@ -1,26 +1,22 @@ #!/usr/bin/env python ############################################################################## # -# diffpy by DANSE Diffraction group -# Simon J. L. Billinge -# (c) 2008 The Trustees of Columbia University -# in the City of New York. All rights reserved. +# (c) 2025 The Trustees of Columbia University in the City of New York. +# All rights reserved. # -# File coded by: Pavol Juhas +# File coded by: Billinge Group members and community contributors. # -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE_DANSE.txt for license information. +# See GitHub contributions for a more detailed list of contributors. +# https://github.com/diffpy/diffpy.srreal/graphs/contributors +# +# See LICENSE.rst for license information. # ############################################################################## - -"""diffpy - tools for structure analysis by diffraction. - -Blank namespace package. -""" +"""Blank namespace package for module diffpy.""" from pkgutil import extend_path -__path__ = extend_path(__path__, __name__) +__path__ = extend_path(__path__, __name__) # End of file diff --git a/src/diffpy/srreal/__init__.py b/src/diffpy/srreal/__init__.py index 1d3d7206..2beff97f 100644 --- a/src/diffpy/srreal/__init__.py +++ b/src/diffpy/srreal/__init__.py @@ -1,23 +1,21 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.srreal by DANSE Diffraction group -# Simon J. L. Billinge -# (c) 2008 The Trustees of Columbia University -# in the City of New York. All rights reserved. +# (c) 2025 The Trustees of Columbia University in the City of New York. +# All rights reserved. # -# File coded by: Pavol Juhas +# File coded by: Billinge Group members and community contributors. # -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE_DANSE.txt for license information. +# See GitHub contributions for a more detailed list of contributors. +# https://github.com/diffpy/diffpy.srreal/graphs/contributors +# +# See LICENSE.rst for license information. # ############################################################################## - -"""Tools for real space structure analysis. -""" +"""Tools for real space structure analysis.""" # package version -from diffpy.srreal._version_data import __version__ +from diffpy.srreal.version import __version__ # silence the pyflakes syntax checker assert __version__ or True diff --git a/src/diffpy/srreal/_cleanup.py b/src/diffpy/srreal/_cleanup.py index dbb41b13..19fdffd2 100644 --- a/src/diffpy/srreal/_cleanup.py +++ b/src/diffpy/srreal/_cleanup.py @@ -12,9 +12,7 @@ # See LICENSE.txt for license information. # ############################################################################## - -""" -Cancel registration of Python-extended C++ classes when Python exits. +"""Cancel registration of Python-extended C++ classes when Python exits. Note ---- @@ -30,9 +28,9 @@ class prototypes is implemented in libdiffpy. Any Python-extended classes import atexit import weakref - # Routine to be used from srreal_ext module ---------------------------------- + def registerForCleanUp(obj): """Remember to clean up the specified prototype at Python exit. @@ -48,21 +46,21 @@ def registerForCleanUp(obj): _cleanup_handler.add(obj) return + # ---------------------------------------------------------------------------- + class _DerivedClassesCleanUpHandler(object): def __init__(self): self._references = set() return - def add(self, obj): wr = weakref.ref(obj) self._references.add(wr) return - def clean(self): while self._references: wr = self._references.pop() @@ -71,6 +69,7 @@ def clean(self): obj._deregisterType(obj.type()) return + # end of class _DerivedClassesCleanUpHandler diff --git a/src/diffpy/srreal/_docstrings.py b/src/diffpy/srreal/_docstrings.py index cbb1a6d6..a743a985 100644 --- a/src/diffpy/srreal/_docstrings.py +++ b/src/diffpy/srreal/_docstrings.py @@ -12,15 +12,14 @@ # See LICENSE.txt for license information. # ############################################################################## - -""" -Docstrings for classes and functions in srreal_ext module. -""" +"""Docstrings for classes and functions in srreal_ext module.""" # Shared docstrings for classes derived from HasClassRegistry ---------------- + def get_registry_docstrings(cls): - """Build a dictionary of docstrings per each HasClassRegistry method. + """Build a dictionary of docstrings per each HasClassRegistry + method. Parameters ---------- @@ -31,18 +30,21 @@ def get_registry_docstrings(cls): Returns a dictionary mapping Python method names to their docstrins. """ n = cls.__name__ - rv = {k : v.replace('@NAME@', n) for k, v in ( - ("create", doc_HasClassRegistry_create), - ("clone", doc_HasClassRegistry_clone), - ("type", doc_HasClassRegistry_type), - ("_registerThisType", doc_HasClassRegistry__registerThisType), - ("_aliasType", doc_HasClassRegistry__aliasType), - ("_deregisterType", doc_HasClassRegistry__deregisterType), - ("createByType", doc_HasClassRegistry_createByType), - ("isRegisteredType", doc_HasClassRegistry_isRegisteredType), - ("getAliasedTypes", doc_HasClassRegistry_getAliasedTypes), - ("getRegisteredTypes", doc_HasClassRegistry_getRegisteredTypes), - )} + rv = { + k: v.replace("@NAME@", n) + for k, v in ( + ("create", doc_HasClassRegistry_create), + ("clone", doc_HasClassRegistry_clone), + ("type", doc_HasClassRegistry_type), + ("_registerThisType", doc_HasClassRegistry__registerThisType), + ("_aliasType", doc_HasClassRegistry__aliasType), + ("_deregisterType", doc_HasClassRegistry__deregisterType), + ("createByType", doc_HasClassRegistry_createByType), + ("isRegisteredType", doc_HasClassRegistry_isRegisteredType), + ("getAliasedTypes", doc_HasClassRegistry_getAliasedTypes), + ("getRegisteredTypes", doc_HasClassRegistry_getRegisteredTypes), + ) + } return rv diff --git a/src/diffpy/srreal/_final_imports.py b/src/diffpy/srreal/_final_imports.py index 9e75dfae..b61e0dc1 100644 --- a/src/diffpy/srreal/_final_imports.py +++ b/src/diffpy/srreal/_final_imports.py @@ -12,9 +12,7 @@ # See LICENSE.txt for license information. # ############################################################################## - -""" -Finalize tweak of classes from the extension module srreal_ext. +"""Finalize tweak of classes from the extension module srreal_ext. This private module handles loading of Python-level tweaks of the extension-defined classes. Any client that imports this module @@ -27,26 +25,27 @@ def import_now(): - ''' - Import all Python modules that tweak extension-defined classes. - ''' + """Import all Python modules that tweak extension-defined + classes.""" global _import_now_called if _import_now_called: return _import_now_called = True from importlib import import_module - import_module('diffpy.srreal.attributes') - import_module('diffpy.srreal.atomradiitable') - import_module('diffpy.srreal.bondcalculator') - import_module('diffpy.srreal.bvscalculator') - import_module('diffpy.srreal.overlapcalculator') - import_module('diffpy.srreal.pdfbaseline') - import_module('diffpy.srreal.pdfenvelope') - import_module('diffpy.srreal.peakprofile') - import_module('diffpy.srreal.peakwidthmodel') - import_module('diffpy.srreal.scatteringfactortable') - import_module('diffpy.srreal.pdfcalculator') - import_module('diffpy.srreal.structureconverters') + + import_module("diffpy.srreal.attributes") + import_module("diffpy.srreal.atomradiitable") + import_module("diffpy.srreal.bondcalculator") + import_module("diffpy.srreal.bvscalculator") + import_module("diffpy.srreal.overlapcalculator") + import_module("diffpy.srreal.pdfbaseline") + import_module("diffpy.srreal.pdfenvelope") + import_module("diffpy.srreal.peakprofile") + import_module("diffpy.srreal.peakwidthmodel") + import_module("diffpy.srreal.scatteringfactortable") + import_module("diffpy.srreal.pdfcalculator") + import_module("diffpy.srreal.structureconverters") return + _import_now_called = False diff --git a/src/diffpy/srreal/_version_data.py b/src/diffpy/srreal/_version_data.py deleted file mode 100644 index a0e532b6..00000000 --- a/src/diffpy/srreal/_version_data.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srreal by DANSE Diffraction group -# Simon J. L. Billinge -# (c) 2008 The Trustees of Columbia University -# in the City of New York. All rights reserved. -# -# File coded by: Pavol Juhas -# -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE_DANSE.txt for license information. -# -############################################################################## - -""" -Extraction of version data for diffpy.srreal package - -Does not import any extension module unlike the `version` module. -""" - -__all__ = ['__date__', '__git_commit__', '__timestamp__', '__version__'] - -import os.path - -from pkg_resources import resource_filename - - -# obtain version information from the version.cfg file -cp = dict(version='', date='', commit='', timestamp='0') -fcfg = resource_filename(__name__, 'version.cfg') -if not os.path.isfile(fcfg): # pragma: no cover - from warnings import warn - warn('Package metadata not found, execute "./setup.py egg_info".') - fcfg = os.devnull -with open(fcfg) as fp: - kwords = [[w.strip() for w in line.split(' = ', 1)] - for line in fp if line[:1].isalpha() and ' = ' in line] -assert all(w[0] in cp for w in kwords), "received unrecognized keyword" -cp.update(kwords) - -__version__ = cp['version'] -__date__ = cp['date'] -__git_commit__ = cp['commit'] -__timestamp__ = int(cp['timestamp']) - -# TODO remove deprecated __gitsha__ in version 1.4. -__gitsha__ = __git_commit__ - -del cp, fcfg, fp, kwords - -# End of file diff --git a/src/diffpy/srreal/atomradiitable.py b/src/diffpy/srreal/atomradiitable.py index 4a141961..9bcf9bd2 100644 --- a/src/diffpy/srreal/atomradiitable.py +++ b/src/diffpy/srreal/atomradiitable.py @@ -12,35 +12,36 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -"""class AtomRadiiTable -- storage of empirical atom radii. -""" +"""Class AtomRadiiTable -- storage of empirical atom radii.""" # exported items, these also makes them show in pydoc. -__all__ = ['AtomRadiiTable', 'ConstantRadiiTable', 'CovalentRadiiTable'] +__all__ = ["AtomRadiiTable", "ConstantRadiiTable", "CovalentRadiiTable"] from diffpy.srreal.srreal_ext import AtomRadiiTable, ConstantRadiiTable # class CovalentRadiiTable --------------------------------------------------- + class CovalentRadiiTable(AtomRadiiTable): - '''Covalent radii from Cordero et al., 2008, doi:10.1039/b801115j. + """Covalent radii from Cordero et al., 2008, doi:10.1039/b801115j. + Instantiation of this class requires the periodictable module. - ''' + """ # class variable that holds the periodictable.elements object _elements = None def _standardLookup(self, smbl): - '''Return covalent atom radius in Angstroms. + """Return covalent atom radius in Angstroms. smbl -- string symbol of an element Return float. Raise ValueError for unknown element symbol. - ''' + """ if CovalentRadiiTable._elements is None: from periodictable import elements + CovalentRadiiTable._elements = elements e = self._elements.isotope(smbl) rv = e.covalent_radius @@ -52,27 +53,25 @@ def _standardLookup(self, smbl): # HasClassRegistry overloads: def create(self): - '''Create new instance of the CovalentRadiiTable. - ''' + """Create new instance of the CovalentRadiiTable.""" return CovalentRadiiTable() - def clone(self): - '''Return a new duplicate instance of self. - ''' + """Return a new duplicate instance of self.""" import copy - return copy.copy(self) + return copy.copy(self) def type(self): - '''Unique string identifier of the CovalentRadiiTable type. - This is used for class registration and as an argument for the + """Unique string identifier of the CovalentRadiiTable type. This + is used for class registration and as an argument for the createByType function. Return string. - ''' + """ return "covalent" + # End of class CovalentRadiiTable CovalentRadiiTable()._registerThisType() diff --git a/src/diffpy/srreal/attributes.py b/src/diffpy/srreal/attributes.py index cb843738..3e188caa 100644 --- a/src/diffpy/srreal/attributes.py +++ b/src/diffpy/srreal/attributes.py @@ -12,31 +12,32 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - """class Attributes -- wrapper to C++ class diffpy::Attributes A base to PairQuantity and quite a few other classes. """ -__all__ = ['Attributes'] +__all__ = ["Attributes"] from diffpy.srreal.srreal_ext import Attributes # Inject the __getattr__ and __setattr__ methods to the Attributes class + def _getattr(self, name): - '''Lookup a C++ double attribute if standard Python lookup fails. + """Lookup a C++ double attribute if standard Python lookup fails. Raise AttributeError if C++ double attribute does not exist. - ''' + """ rv = self._getDoubleAttr(name) return rv + Attributes.__getattr__ = _getattr def _setattr(self, name, value): - '''Assign to C++ double attribute if Python attribute does not exist. - ''' + """Assign to C++ double attribute if Python attribute does not + exist.""" try: object.__getattribute__(self, name) except AttributeError: @@ -46,16 +47,24 @@ def _setattr(self, name, value): object.__setattr__(self, name, value) return + Attributes.__setattr__ = _setattr # Helper accessor functions used by the _registerDoubleAttribute method + def _pyattrgetter(name): - f = lambda obj: object.__getattribute__(obj, name) + def f(obj): + return object.__getattribute__(obj, name) + return f + def _pyattrsetter(name): - f = lambda obj, value: object.__setattr__(obj, name, value) + def f(obj, value): + object.__setattr__(obj, name, value) + return f + # End of file diff --git a/src/diffpy/srreal/bondcalculator.py b/src/diffpy/srreal/bondcalculator.py index ac89ebee..98d2fe89 100644 --- a/src/diffpy/srreal/bondcalculator.py +++ b/src/diffpy/srreal/bondcalculator.py @@ -12,54 +12,58 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -"""class BondCalculator -- distances between atoms in the structure. -""" +"""Class BondCalculator -- distances between atoms in the structure.""" # exported items, these also makes them show in pydoc. -__all__ = ['BondCalculator'] +__all__ = ["BondCalculator"] -from diffpy.srreal.wraputils import propertyFromExtDoubleAttr -from diffpy.srreal.wraputils import setattrFromKeywordArguments from diffpy.srreal.srreal_ext import BondCalculator +from diffpy.srreal.wraputils import ( + propertyFromExtDoubleAttr, + setattrFromKeywordArguments, +) # property wrappers to C++ double attributes -BondCalculator.rmin = propertyFromExtDoubleAttr('rmin', - '''Lower bound for the bond distances. - [0 A]''') +BondCalculator.rmin = propertyFromExtDoubleAttr( + "rmin", + """Lower bound for the bond distances. + [0 A]""", +) -BondCalculator.rmax = propertyFromExtDoubleAttr('rmax', - '''Upper bound for the bond distances. - [5 A]''') +BondCalculator.rmax = propertyFromExtDoubleAttr( + "rmax", + """Upper bound for the bond distances. + [5 A]""", +) # method overrides that support keyword arguments + def _init_kwargs(self, **kwargs): - '''Create a new instance of BondCalculator. - Keyword arguments can be used to configure - calculator properties, for example: + """Create a new instance of BondCalculator. Keyword arguments can be + used to configure calculator properties, for example: bdc = BondCalculator(rmin=1.5, rmax=2.5) Raise ValueError for invalid keyword argument. - ''' + """ BondCalculator.__boostpython__init(self) setattrFromKeywordArguments(self, **kwargs) return def _call_kwargs(self, structure=None, **kwargs): - '''Return sorted bond distances in the specified structure. + """Return sorted bond distances in the specified structure. structure -- structure to be evaluated, an instance of diffpy Structure or pyobjcryst Crystal. Reuse the last structure when None. kwargs -- optional parameter settings for this calculator Return a sorted numpy array. - ''' + """ setattrFromKeywordArguments(self, **kwargs) self.eval(structure) return self.distances diff --git a/src/diffpy/srreal/bvparameterstable.py b/src/diffpy/srreal/bvparameterstable.py index 1363226e..6d6d9242 100644 --- a/src/diffpy/srreal/bvparameterstable.py +++ b/src/diffpy/srreal/bvparameterstable.py @@ -12,14 +12,12 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -"""class BVParametersTable -- storage of bond valence parameters -class BVParam -- bond valence data associated with specific cation-anion pair -""" +"""Class BVParametersTable -- storage of bond valence parameters class BVParam +-- bond valence data associated with specific cation-anion pair.""" # exported items, these also makes them show in pydoc. -__all__ = ['BVParam', 'BVParametersTable'] +__all__ = ["BVParam", "BVParametersTable"] from diffpy.srreal.srreal_ext import BVParam, BVParametersTable diff --git a/src/diffpy/srreal/bvscalculator.py b/src/diffpy/srreal/bvscalculator.py index 47cb2cef..82ff5a3c 100644 --- a/src/diffpy/srreal/bvscalculator.py +++ b/src/diffpy/srreal/bvscalculator.py @@ -12,59 +12,68 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -"""class BVSCalculator -- bond valence sums calculator -""" +"""Class BVSCalculator -- bond valence sums calculator.""" # exported items -__all__ = ['BVSCalculator'] +__all__ = ["BVSCalculator"] from diffpy.srreal.srreal_ext import BVSCalculator -from diffpy.srreal.wraputils import propertyFromExtDoubleAttr -from diffpy.srreal.wraputils import setattrFromKeywordArguments +from diffpy.srreal.wraputils import ( + propertyFromExtDoubleAttr, + setattrFromKeywordArguments, +) # Property wrappers to C++ double attributes -BVSCalculator.valenceprecision = propertyFromExtDoubleAttr('valenceprecision', - '''Cutoff value for valence contributions at long distances. - [1e-5]''') +BVSCalculator.valenceprecision = propertyFromExtDoubleAttr( + "valenceprecision", + """Cutoff value for valence contributions at long distances. + [1e-5]""", +) -BVSCalculator.rmaxused = propertyFromExtDoubleAttr('rmaxused', - '''Effective bound for bond lengths, where valence contributions +BVSCalculator.rmaxused = propertyFromExtDoubleAttr( + "rmaxused", + """Effective bound for bond lengths, where valence contributions become smaller than valenceprecission, read-only. Always smaller or equal to rmax. The value depends on ions present in the structure. - ''') - -BVSCalculator.rmin = propertyFromExtDoubleAttr('rmin', - '''Lower bound for the summed bond lengths. - [0 A]''') - -BVSCalculator.rmax = propertyFromExtDoubleAttr('rmax', - '''Upper bound for the summed bond lengths. The calculation is + """, +) + +BVSCalculator.rmin = propertyFromExtDoubleAttr( + "rmin", + """Lower bound for the summed bond lengths. + [0 A]""", +) + +BVSCalculator.rmax = propertyFromExtDoubleAttr( + "rmax", + """Upper bound for the summed bond lengths. The calculation is actually cut off much earlier when valence contributions get below valenceprecission. See also rmaxused and valenceprecission. - [1e6 A]''') + [1e6 A]""", +) # method overrides that support keyword arguments + def _init_kwargs(self, **kwargs): - '''Create a new instance of BVSCalculator. + """Create a new instance of BVSCalculator. Keyword arguments can be used to configure the calculator properties, for example: bvscalc = BVSCalculator(valenceprecision=0.001) Raise ValueError for invalid keyword argument. - ''' + """ BVSCalculator.__boostpython__init(self) setattrFromKeywordArguments(self, **kwargs) return def _call_kwargs(self, structure=None, **kwargs): - '''Return bond valence sums at each atom site in the structure. + """Return bond valence sums at each atom site in the structure. structure -- structure to be evaluated, an instance of diffpy Structure or pyobjcryst Crystal. Reuse the last structure when None. @@ -72,7 +81,7 @@ def _call_kwargs(self, structure=None, **kwargs): Return an array of calculated valence sums. See valences for the expected values. - ''' + """ setattrFromKeywordArguments(self, **kwargs) rv = self.eval(structure) return rv diff --git a/devutils/tunePeakPrecision.py b/src/diffpy/srreal/devutils/tunePeakPrecision.py similarity index 72% rename from devutils/tunePeakPrecision.py rename to src/diffpy/srreal/devutils/tunePeakPrecision.py index 60c76f45..d899ea5c 100755 --- a/devutils/tunePeakPrecision.py +++ b/src/diffpy/srreal/devutils/tunePeakPrecision.py @@ -1,11 +1,21 @@ #!/usr/bin/env python -"""Tune the peak precision parameter so that PDFCalculator -gives equivalent results to diffpy.pdffit2. +"""Tune the peak precision parameter so that PDFCalculator gives +equivalent results to diffpy.pdffit2. Usage: tunePeakPrecision.py [qmax] [peakprecision] [createplot] """ +# global imports +import sys +import time + +import numpy + +import diffpy.pdffit2 +from diffpy.srreal.pdf_ext import PDFCalculator +from diffpy.structure import Structure + # Results: # Qmax peakprecision CPU Notes # 15 3.2e-6 clear minimum @@ -21,20 +31,11 @@ peakprecision = None createplot = False -# global imports -import sys -import time - -import numpy - -from diffpy.structure import Structure -from diffpy.srreal.pdf_ext import PDFCalculator -import diffpy.pdffit2 # make PdfFit silent -diffpy.pdffit2.redirect_stdout(open('/dev/null', 'w')) +diffpy.pdffit2.redirect_stdout(open("/dev/null", "w")) # define nickel structure data -nickel_discus_data = ''' +nickel_discus_data = """ title Ni spcgr P1 cell 3.523870, 3.523870, 3.523870, 90.000000, 90.000000, 90.000000 @@ -44,14 +45,14 @@ NI 0.00000000 0.50000000 0.50000000 0.1000 NI 0.50000000 0.00000000 0.50000000 0.1000 NI 0.50000000 0.50000000 0.00000000 0.1000 -''' +""" nickel = Structure() -nickel.readStr(nickel_discus_data, format='discus') +nickel.readStr(nickel_discus_data, format="discus") def Gpdffit2(qmax): - """Calculate reference nickel PDF using diffpy.pdffit2 + """Calculate reference nickel PDF using diffpy.pdffit2. qmax -- vawevector cutoff value in 1/A @@ -59,7 +60,7 @@ def Gpdffit2(qmax): """ # calculate reference data using pdffit2 pf2 = diffpy.pdffit2.PdfFit() - pf2.alloc('X', qmax, 0.0, rmin, rmax, int((rmax - rmin) / rstep + 1)) + pf2.alloc("X", qmax, 0.0, rmin, rmax, int((rmax - rmin) / rstep + 1)) pf2.add_structure(nickel) pf2.calc() rg = numpy.array((pf2.getR(), pf2.getpdf_fit())) @@ -67,7 +68,7 @@ def Gpdffit2(qmax): def Gsrreal(qmax, peakprecision=None): - """Calculate nickel PDF using PDFCalculator from diffpy.srreal + """Calculate nickel PDF using PDFCalculator from diffpy.srreal. qmax -- vawevector cutoff value in 1/A peakprecision -- precision factor affecting peak cutoff, @@ -81,7 +82,7 @@ def Gsrreal(qmax, peakprecision=None): pdfcalc._setDoubleAttr("rmax", rmax + rstep * 1e-4) pdfcalc._setDoubleAttr("rstep", rstep) if peakprecision is not None: - pdfcalc._setDoubleAttr('peakprecision', peakprecision) + pdfcalc._setDoubleAttr("peakprecision", peakprecision) pdfcalc.eval(nickel) rg = numpy.array([pdfcalc.rgrid, pdfcalc.pdf]) return rg @@ -105,30 +106,33 @@ def comparePDFCalculators(qmax, peakprecision=None): t0, t1 -- CPU times used by pdffit2 and PDFCalculator calls """ rv = {} - rv['qmax'] = qmax - rv['peakprecision'] = (peakprecision is None and - PDFCalculator()._getDoubleAttr('peakprecision') or peakprecision) + rv["qmax"] = qmax + rv["peakprecision"] = ( + peakprecision is None + and PDFCalculator()._getDoubleAttr("peakprecision") + or peakprecision + ) ttic = time.clock() rg0 = Gpdffit2(qmax) ttoc = time.clock() - rv['r'] = rg0[0] - rv['g0'] = rg0[1] - rv['t0'] = ttoc - ttic + rv["r"] = rg0[0] + rv["g0"] = rg0[1] + rv["t0"] = ttoc - ttic ttic = time.clock() rg1 = Gsrreal(qmax, peakprecision) ttoc = time.clock() - assert numpy.all(rv['r'] == rg1[0]) - rv['g1'] = rg1[1] - rv['t1'] = ttoc - ttic - rv['gdiff'] = rv['g0'] - rv['g1'] - rv['grmsd'] = numpy.sqrt(numpy.mean(rv['gdiff']**2)) + assert numpy.all(rv["r"] == rg1[0]) + rv["g1"] = rg1[1] + rv["t1"] = ttoc - ttic + rv["gdiff"] = rv["g0"] - rv["g1"] + rv["grmsd"] = numpy.sqrt(numpy.mean(rv["gdiff"] ** 2)) return rv def processCommandLineArguments(): global qmax, peakprecision, createplot argc = len(sys.argv) - if set(['-h', '--help']).intersection(sys.argv): + if set(["-h", "--help"]).intersection(sys.argv): print(__doc__) sys.exit() if argc > 1: @@ -136,7 +140,7 @@ def processCommandLineArguments(): if argc > 2: peakprecision = float(sys.argv[2]) if argc > 3: - createplot = sys.argv[3].lower() in ('y', 'yes', '1', 'true') + createplot = sys.argv[3].lower() in ("y", "yes", "1", "true") return @@ -148,22 +152,23 @@ def plotComparison(cmpdata): No return value. """ import pylab + pylab.clf() pylab.subplot(211) - pylab.title('qmax=%(qmax)g peakprecision=%(peakprecision)g' % cmpdata) - pylab.ylabel('G') - r = cmpdata['r'] - g0 = cmpdata['g0'] - g1 = cmpdata['g1'] - gdiff = cmpdata['gdiff'] + pylab.title("qmax=%(qmax)g peakprecision=%(peakprecision)g" % cmpdata) + pylab.ylabel("G") + r = cmpdata["r"] + g0 = cmpdata["g0"] + g1 = cmpdata["g1"] + gdiff = cmpdata["gdiff"] pylab.plot(r, g0, r, g1) pylab.subplot(212) - pylab.plot(r, gdiff, 'r') + pylab.plot(r, gdiff, "r") slope = numpy.sum(r * gdiff) / numpy.sum(r**2) - pylab.plot(r, slope * r, '--') - pylab.xlabel('r') - pylab.ylabel('Gdiff') - pylab.title('slope = %g' % slope) + pylab.plot(r, slope * r, "--") + pylab.xlabel("r") + pylab.ylabel("Gdiff") + pylab.title("slope = %g" % slope) pylab.draw() return @@ -171,14 +176,20 @@ def plotComparison(cmpdata): def main(): processCommandLineArguments() cmpdata = comparePDFCalculators(qmax, peakprecision) - print(("qmax = %(qmax)g pkprec = %(peakprecision)g " + - "grmsd = %(grmsd)g t0 = %(t0).3f t1 = %(t1).3f") % cmpdata) + print( + ( + "qmax = %(qmax)g pkprec = %(peakprecision)g " + + "grmsd = %(grmsd)g t0 = %(t0).3f t1 = %(t1).3f" + ) + % cmpdata + ) if createplot: plotComparison(cmpdata) import pylab + pylab.show() return -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/diffpy/srreal/eventticker.py b/src/diffpy/srreal/eventticker.py index 3a3d5795..58d95565 100644 --- a/src/diffpy/srreal/eventticker.py +++ b/src/diffpy/srreal/eventticker.py @@ -12,14 +12,12 @@ # See LICENSE.txt for license information. # ############################################################################## - -""" -class EventTicker -- storage of modification times of dependent objects -""" +"""Class EventTicker -- storage of modification times of dependent +objects.""" # exported items -__all__ = ['EventTicker'] +__all__ = ["EventTicker"] from diffpy.srreal.srreal_ext import EventTicker diff --git a/src/diffpy/srreal/overlapcalculator.py b/src/diffpy/srreal/overlapcalculator.py index e0813597..e0fcaa47 100644 --- a/src/diffpy/srreal/overlapcalculator.py +++ b/src/diffpy/srreal/overlapcalculator.py @@ -12,59 +12,66 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -"""class OverlapCalculator -- calculator of atom overlaps in a structure. -""" +"""Class OverlapCalculator -- calculator of atom overlaps in a +structure.""" # exported items, these also makes them show in pydoc. -__all__ = ['OverlapCalculator'] +__all__ = ["OverlapCalculator"] -from diffpy.srreal.wraputils import propertyFromExtDoubleAttr -from diffpy.srreal.wraputils import setattrFromKeywordArguments from diffpy.srreal.srreal_ext import OverlapCalculator +from diffpy.srreal.wraputils import ( + propertyFromExtDoubleAttr, + setattrFromKeywordArguments, +) # property wrappers to C++ double attributes -OverlapCalculator.rmin = propertyFromExtDoubleAttr('rmin', - '''Lower bound for the bond distances. - [0 A]''') - -OverlapCalculator.rmax = propertyFromExtDoubleAttr('rmax', - '''Upper bound for the bond distances. - [5 A]''') - -OverlapCalculator.rmaxused = propertyFromExtDoubleAttr('rmaxused', - '''Effective upper bound for the bond distances. +OverlapCalculator.rmin = propertyFromExtDoubleAttr( + "rmin", + """Lower bound for the bond distances. + [0 A]""", +) + +OverlapCalculator.rmax = propertyFromExtDoubleAttr( + "rmax", + """Upper bound for the bond distances. + [5 A]""", +) + +OverlapCalculator.rmaxused = propertyFromExtDoubleAttr( + "rmaxused", + """Effective upper bound for the bond distances. rmaxused equals either a double of the maximum atom radius in the structure or rmax. - ''') + """, +) # method overrides that support keyword arguments + def _init_kwargs(self, **kwargs): - '''Create a new instance of OverlapCalculator. - Keyword arguments can be used to configure - calculator properties, for example: + """Create a new instance of OverlapCalculator. Keyword arguments can + be used to configure calculator properties, for example: olc = OverlapCalculator(rmax=2.5) Raise ValueError for invalid keyword argument. - ''' + """ OverlapCalculator.__boostpython__init(self) setattrFromKeywordArguments(self, **kwargs) return def _call_kwargs(self, structure=None, **kwargs): - '''Return siteSquareOverlaps per each site of the structure. + """Return siteSquareOverlaps per each site of the structure. structure -- structure to be evaluated, an instance of diffpy Structure or pyobjcryst Crystal. Reuse the last structure when None. kwargs -- optional parameter settings for this calculator Return a numpy array. - ''' + """ setattrFromKeywordArguments(self, **kwargs) self.eval(structure) return self.sitesquareoverlaps diff --git a/src/diffpy/srreal/pairquantity.py b/src/diffpy/srreal/pairquantity.py index f6a537b9..7097e7c8 100644 --- a/src/diffpy/srreal/pairquantity.py +++ b/src/diffpy/srreal/pairquantity.py @@ -12,14 +12,12 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -""" -class PairQuantity -- base class for Python defined calculators. -""" +"""Class PairQuantity -- base class for Python defined +calculators.""" # exported items -__all__ = ['PairQuantity'] +__all__ = ["PairQuantity"] from diffpy.srreal.srreal_ext import PairQuantity diff --git a/src/diffpy/srreal/parallel.py b/src/diffpy/srreal/parallel.py index c5a3d75b..179f092b 100644 --- a/src/diffpy/srreal/parallel.py +++ b/src/diffpy/srreal/parallel.py @@ -12,23 +12,24 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - """ParallelPairQuantity -- proxy class for converting PairQuantity types into parallel calculators. """ # exported items -__all__ = ['createParallelCalculator'] +__all__ = ["createParallelCalculator"] import copy import inspect + from diffpy.srreal.attributes import Attributes # ---------------------------------------------------------------------------- + def createParallelCalculator(pqobj, ncpu, pmap): - '''Create a proxy parallel calculator to a PairQuantity instance. + """Create a proxy parallel calculator to a PairQuantity instance. pqobj -- instance of PairQuantity calculator to be run in parallel ncpu -- number of parallel jobs @@ -39,80 +40,83 @@ def createParallelCalculator(pqobj, ncpu, pmap): Return a proxy calculator instance that has the same interface, but executes the calculation in parallel split among ncpu jobs. - ''' + """ class ParallelPairQuantity(Attributes): - - '''Class for running parallel calculations. This is a proxy class - to the wrapper PairQuantity type with the same interface. + """Class for running parallel calculations. This is a proxy + class to the wrapper PairQuantity type with the same interface. Instance data: pqobj -- the master PairQuantity object to be evaluated in parallel ncpu -- number of parallel jobs pmap -- a parallel map function used to submit job to workers - ''' + """ def __init__(self, pqobj, ncpu, pmap): - '''Initialize a parallel proxy to the PairQuantity instance. + """Initialize a parallel proxy to the PairQuantity instance. pqobj -- instance of PairQuantity calculator to be run in parallel ncpu -- number of parallel jobs pmap -- a parallel map function used to submit job to workers - ''' + """ # use explicit assignment to avoid setattr forwarding to the pqobj - object.__setattr__(self, 'pqobj', pqobj) - object.__setattr__(self, 'ncpu', ncpu) - object.__setattr__(self, 'pmap', pmap) + object.__setattr__(self, "pqobj", pqobj) + object.__setattr__(self, "ncpu", ncpu) + object.__setattr__(self, "pmap", pmap) # parallel calculations support only the BASIC evaluation - self.pqobj.evaluatortype = 'BASIC' + self.pqobj.evaluatortype = "BASIC" return - def eval(self, stru=None): - '''Perform parallel calculation and return internal value array. + """Perform parallel calculation and return internal value + array. stru -- object that can be converted to StructureAdapter, e.g., example diffpy Structure or pyobjcryst Crystal. Use the last structure when None. Return numpy array. - ''' + """ # use StructureAdapter for faster pickles from diffpy.srreal.structureadapter import createStructureAdapter + if stru is None: struadpt = self.pqobj.getStructure() else: struadpt = createStructureAdapter(stru) self.pqobj.setStructure(struadpt) - kwd = { 'cpuindex' : None, - 'ncpu' : self.ncpu, - 'pqobj' : copy.copy(self.pqobj), - } + kwd = { + "cpuindex": None, + "ncpu": self.ncpu, + "pqobj": copy.copy(self.pqobj), + } # shallow copies of kwd dictionary each with a unique cpuindex - arglist = [kwd.copy() for kwd['cpuindex'] in range(self.ncpu)] + arglist = [kwd.copy() for kwd["cpuindex"] in range(self.ncpu)] for pdata in self.pmap(_parallelData, arglist): self.pqobj._mergeParallelData(pdata, self.ncpu) return self.pqobj.value - def __call__(self, *args, **kwargs): - '''Call the wrapped calculator using parallel evaluation. + """Call the wrapped calculator using parallel evaluation. + + The arguments and return value are the same as for the + wrapped PairQuantity calculator. + """ + savedeval = self.pqobj.__dict__.get("eval") - The arguments and return value are the same as for the wrapped - PairQuantity calculator. - ''' - savedeval = self.pqobj.__dict__.get('eval') def restore_eval(): if savedeval: self.pqobj.eval = savedeval else: - self.pqobj.__dict__.pop('eval', None) + self.pqobj.__dict__.pop("eval", None) + def parallel_eval(stru): assert self.pqobj.eval is parallel_eval restore_eval() return self.eval(stru) + self.pqobj.eval = parallel_eval try: rv = self.pqobj(*args, **kwargs) @@ -120,7 +124,6 @@ def parallel_eval(stru): restore_eval() return rv - @property def evaluatortype(self): """str : Type of evaluation procedure. @@ -145,22 +148,27 @@ def evaluatortype(self, value): # create proxy methods to all public methods and some protected methods - proxy_forced = set('''_getDoubleAttr _setDoubleAttr _hasDoubleAttr + proxy_forced = set( + """_getDoubleAttr _setDoubleAttr _hasDoubleAttr _namesOfDoubleAttributes _namesOfWritableDoubleAttributes - '''.split()) + """.split() + ) def _make_proxymethod(name, f): def proxymethod(self, *args, **kwargs): - pqobj = object.__getattribute__(self, 'pqobj') + pqobj = object.__getattribute__(self, "pqobj") return f(pqobj, *args, **kwargs) + proxymethod.__name__ = name proxymethod.__doc__ = f.__doc__ return proxymethod for n, f in inspect.getmembers(pqtype, inspect.isroutine): - ignore = n not in proxy_forced and (n.startswith('_') or - hasattr(ParallelPairQuantity, n)) - if ignore: continue + ignore = n not in proxy_forced and ( + n.startswith("_") or hasattr(ParallelPairQuantity, n) + ) + if ignore: + continue setattr(ParallelPairQuantity, n, _make_proxymethod(n, f)) # create proxy properties to all properties that do not conflict with @@ -169,15 +177,31 @@ def proxymethod(self, *args, **kwargs): def _make_proxyproperty(prop): fget = fset = fdel = None if prop.fget: - def fget(self): return prop.fget(self.pqobj) + + def _fget(self): + return prop.fget(self.pqobj) + + fget = _fget + if prop.fset: - def fset(self, value): return prop.fset(self.pqobj, value) + + def _fset(self, value): + return prop.fset(self.pqobj, value) + + fset = _fset + if prop.fdel: - def fdel(self): return prop.fdel(self.pqobj) + + def _fdel(self): + return prop.fdel(self.pqobj) + + fdel = _fdel + return property(fget, fset, fdel, prop.__doc__) for n, p in inspect.getmembers(pqtype, lambda x: type(x) is property): - if hasattr(ParallelPairQuantity, n): continue + if hasattr(ParallelPairQuantity, n): + continue setattr(ParallelPairQuantity, n, _make_proxyproperty(p)) # finally create an instance of this very custom class @@ -185,11 +209,12 @@ def fdel(self): return prop.fdel(self.pqobj) def _parallelData(kwd): - '''Helper for calculating and fetching raw results from a worker node. - ''' - pqobj = kwd['pqobj'] - pqobj._setupParallelRun(kwd['cpuindex'], kwd['ncpu']) + """Helper for calculating and fetching raw results from a worker + node.""" + pqobj = kwd["pqobj"] + pqobj._setupParallelRun(kwd["cpuindex"], kwd["ncpu"]) pqobj.eval() return pqobj._getParallelData() + # End of file diff --git a/src/diffpy/srreal/pdfbaseline.py b/src/diffpy/srreal/pdfbaseline.py index 5f5af2ba..bb168467 100644 --- a/src/diffpy/srreal/pdfbaseline.py +++ b/src/diffpy/srreal/pdfbaseline.py @@ -12,7 +12,6 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - """ Classes for configuring PDF baseline: PDFBaseline, ZeroBaseline, LinearBaseline @@ -20,15 +19,14 @@ # exported items -__all__ = ''' +__all__ = """ PDFBaseline makePDFBaseline ZeroBaseline LinearBaseline - '''.split() + """.split() from diffpy.srreal import _final_imports -from diffpy.srreal.srreal_ext import PDFBaseline -from diffpy.srreal.srreal_ext import ZeroBaseline, LinearBaseline +from diffpy.srreal.srreal_ext import LinearBaseline, PDFBaseline, ZeroBaseline from diffpy.srreal.wraputils import propertyFromExtDoubleAttr # class PDFBaseline ---------------------------------------------------------- @@ -40,15 +38,19 @@ # attribute wrapper -LinearBaseline.slope = propertyFromExtDoubleAttr('slope', - '''Slope of an unscaled linear baseline. For crystal structures it - is preset to (-4 * pi * rho0).''') +LinearBaseline.slope = propertyFromExtDoubleAttr( + "slope", + """Slope of an unscaled linear baseline. For crystal structures it + is preset to (-4 * pi * rho0).""", +) # Python functions wrapper + def makePDFBaseline(name, fnc, replace=False, **dbattrs): - '''Helper function for registering Python function as a PDFBaseline. - This is required for using Python function as PDFCalculator.baseline. + """Helper function for registering Python function as a PDFBaseline. + This is required for using Python function as + PDFCalculator.baseline. name -- unique string name for registering Python function in the global registry of PDFBaseline types. This will be the @@ -81,12 +83,15 @@ def fshiftedline(x, aline, bline): pdfc = PDFCalculator() pdfc.baseline = baseline # or pdfc.baseline = "shiftedline" - ''' + """ from diffpy.srreal.wraputils import _wrapAsRegisteredUnaryFunction - rv = _wrapAsRegisteredUnaryFunction(PDFBaseline, name, fnc, - replace=replace, **dbattrs) + + rv = _wrapAsRegisteredUnaryFunction( + PDFBaseline, name, fnc, replace=replace, **dbattrs + ) return rv + # Import delayed tweaks of the extension classes. _final_imports.import_now() diff --git a/src/diffpy/srreal/pdfcalculator.py b/src/diffpy/srreal/pdfcalculator.py index f34b25d7..0e7524b7 100644 --- a/src/diffpy/srreal/pdfcalculator.py +++ b/src/diffpy/srreal/pdfcalculator.py @@ -12,108 +12,160 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - """ Top-level classes for PDF calculation: DebyePDFCalculator -- simulate PDF by evaluating Debye sum in Q-space PDFCalculator -- calculate PDF by peak summation in real space """ +from diffpy.srreal.srreal_ext import ( + DebyePDFCalculator, + PDFCalculator, + fftftog, + fftgtof, +) +from diffpy.srreal.wraputils import ( + propertyFromExtDoubleAttr, + setattrFromKeywordArguments, +) # exported items -__all__ = ''' +__all__ = """ DebyePDFCalculator PDFCalculator fftftog fftgtof - '''.split() + """.split() -from diffpy.srreal.srreal_ext import DebyePDFCalculator -from diffpy.srreal.srreal_ext import PDFCalculator -from diffpy.srreal.srreal_ext import fftftog, fftgtof -from diffpy.srreal.wraputils import propertyFromExtDoubleAttr -from diffpy.srreal.wraputils import setattrFromKeywordArguments +# imports for backward compatibility +from diffpy.srreal.pdfbaseline import ( + LinearBaseline, + PDFBaseline, + ZeroBaseline, + makePDFBaseline, +) +from diffpy.srreal.pdfenvelope import ( + PDFEnvelope, + QResolutionEnvelope, + ScaleEnvelope, + SphericalShapeEnvelope, + StepCutEnvelope, + makePDFEnvelope, +) +from diffpy.srreal.peakprofile import PeakProfile +from diffpy.srreal.peakwidthmodel import ( + ConstantPeakWidth, + DebyeWallerPeakWidth, + JeongPeakWidth, + PeakWidthModel, +) # silence the pyflakes syntax checker assert all((fftftog, fftgtof)) -# imports for backward compatibility -from diffpy.srreal.pdfbaseline import (PDFBaseline, makePDFBaseline, - ZeroBaseline, LinearBaseline) -from diffpy.srreal.pdfenvelope import (PDFEnvelope, makePDFEnvelope, - QResolutionEnvelope, ScaleEnvelope, - SphericalShapeEnvelope, StepCutEnvelope) -from diffpy.srreal.peakprofile import PeakProfile -from diffpy.srreal.peakwidthmodel import (PeakWidthModel, - ConstantPeakWidth, DebyeWallerPeakWidth, JeongPeakWidth) - # silence the pyflakes syntax checker -assert all((PDFBaseline, makePDFBaseline, ZeroBaseline, LinearBaseline, - PDFEnvelope, makePDFEnvelope, QResolutionEnvelope, ScaleEnvelope, - SphericalShapeEnvelope, StepCutEnvelope, PeakProfile, - PeakWidthModel, ConstantPeakWidth, DebyeWallerPeakWidth, - JeongPeakWidth)) +assert all( + ( + PDFBaseline, + makePDFBaseline, + ZeroBaseline, + LinearBaseline, + PDFEnvelope, + makePDFEnvelope, + QResolutionEnvelope, + ScaleEnvelope, + SphericalShapeEnvelope, + StepCutEnvelope, + PeakProfile, + PeakWidthModel, + ConstantPeakWidth, + DebyeWallerPeakWidth, + JeongPeakWidth, + ) +) # ---------------------------------------------------------------------------- -def _defineCommonInterface(cls): - - '''This function defines shared properties of PDF calculator classes. - ''' - - cls.scale = propertyFromExtDoubleAttr('scale', - '''Scale factor of the calculated PDF. Active for ScaleEnvelope. - [1.0 unitless]''') - cls.delta1 = propertyFromExtDoubleAttr('delta1', - '''Coefficient for (1/r) contribution to the peak sharpening. +def _defineCommonInterface(cls): + """This function defines shared properties of PDF calculator + classes.""" + + cls.scale = propertyFromExtDoubleAttr( + "scale", + """Scale factor of the calculated PDF. Active for ScaleEnvelope. + [1.0 unitless]""", + ) + + cls.delta1 = propertyFromExtDoubleAttr( + "delta1", + """Coefficient for (1/r) contribution to the peak sharpening. Active for JeongPeakWidth model. - [0 A]''') + [0 A]""", + ) - cls.delta2 = propertyFromExtDoubleAttr('delta2', - '''Coefficient for (1/r**2) contribution to the peak sharpening. + cls.delta2 = propertyFromExtDoubleAttr( + "delta2", + """Coefficient for (1/r**2) contribution to the peak sharpening. Active for JeongPeakWidth model. - [0 A**2]''') + [0 A**2]""", + ) - cls.qdamp = propertyFromExtDoubleAttr('qdamp', - '''PDF Gaussian dampening factor due to limited Q-resolution. + cls.qdamp = propertyFromExtDoubleAttr( + "qdamp", + """PDF Gaussian dampening factor due to limited Q-resolution. Not applied when equal to zero. Active for QResolutionEnvelope. - [0 1/A]''') + [0 1/A]""", + ) - cls.qbroad = propertyFromExtDoubleAttr('qbroad', - '''PDF peak broadening from increased intensity noise at high Q. + cls.qbroad = propertyFromExtDoubleAttr( + "qbroad", + """PDF peak broadening from increased intensity noise at high Q. Not applied when equal zero. Active for JeongPeakWidth model. - [0 1/A]''') - - cls.extendedrmin = propertyFromExtDoubleAttr('extendedrmin', - '''Low boundary of the extended r-range, read-only. - [A]''') - - cls.extendedrmax = propertyFromExtDoubleAttr('extendedrmax', - '''Upper boundary of the extended r-range, read-only. - [A]''') - - cls.maxextension = propertyFromExtDoubleAttr('maxextension', - '''Maximum extension of the r-range that accounts for contributions + [0 1/A]""", + ) + + cls.extendedrmin = propertyFromExtDoubleAttr( + "extendedrmin", + """Low boundary of the extended r-range, read-only. + [A]""", + ) + + cls.extendedrmax = propertyFromExtDoubleAttr( + "extendedrmax", + """Upper boundary of the extended r-range, read-only. + [A]""", + ) + + cls.maxextension = propertyFromExtDoubleAttr( + "maxextension", + """Maximum extension of the r-range that accounts for contributions from the out of range peaks. - [10 A]''') - - cls.rmin = propertyFromExtDoubleAttr('rmin', - '''Lower bound of the r-grid for PDF calculation. - [0 A]''') - - cls.rmax = propertyFromExtDoubleAttr('rmax', - '''Upper bound of the r-grid for PDF calculation. - [10 A]''') - - cls.rstep = propertyFromExtDoubleAttr('rstep', - '''Spacing in the calculated r-grid. r-values are at the + [10 A]""", + ) + + cls.rmin = propertyFromExtDoubleAttr( + "rmin", + """Lower bound of the r-grid for PDF calculation. + [0 A]""", + ) + + cls.rmax = propertyFromExtDoubleAttr( + "rmax", + """Upper bound of the r-grid for PDF calculation. + [10 A]""", + ) + + cls.rstep = propertyFromExtDoubleAttr( + "rstep", + """Spacing in the calculated r-grid. r-values are at the multiples of rstep. - [0.01 A]''') + [0.01 A]""", + ) def _call_kwargs(self, structure=None, **kwargs): - '''Calculate PDF for the given structure as an (r, G) tuple. - Keyword arguments can be used to configure calculator attributes, - these override any properties that may be passed from the structure, - such as spdiameter. + """Calculate PDF for the given structure as an (r, G) tuple. + Keyword arguments can be used to configure calculator + attributes, these override any properties that may be passed + from the structure, such as spdiameter. structure -- a structure object to be evaluated. Reuse the last structure when None. @@ -122,7 +174,7 @@ def _call_kwargs(self, structure=None, **kwargs): Example: pdfcalc(structure, qmax=20, spdiameter=15) Return a tuple of (r, G) numpy arrays. - ''' + """ setattrFromKeywordArguments(self, **kwargs) self.eval(structure) # apply kwargs again if structure contained any attribute @@ -130,8 +182,10 @@ def _call_kwargs(self, structure=None, **kwargs): setattrFromKeywordArguments(self, **kwargs) rv = (self.rgrid, self.pdf) return rv + cls.__call__ = _call_kwargs + # _defineCommonInterface # class DebyePDFCalculator --------------------------------------------------- @@ -142,42 +196,52 @@ def _call_kwargs(self, structure=None, **kwargs): # Property wrappers to double attributes of the C++ DebyePDFCalculator -DebyePDFCalculator.debyeprecision = propertyFromExtDoubleAttr('debyeprecision', - '''Cutoff amplitude for the sine contributions to the F(Q). - [1e-6 unitless]''') +DebyePDFCalculator.debyeprecision = propertyFromExtDoubleAttr( + "debyeprecision", + """Cutoff amplitude for the sine contributions to the F(Q). + [1e-6 unitless]""", +) -DebyePDFCalculator.qmin = propertyFromExtDoubleAttr('qmin', - '''Lower bound of the Q-grid for the calculated F(Q). +DebyePDFCalculator.qmin = propertyFromExtDoubleAttr( + "qmin", + """Lower bound of the Q-grid for the calculated F(Q). Affects the shape envelope. [0 1/A] - ''') + """, +) -DebyePDFCalculator.qmax = propertyFromExtDoubleAttr('qmax', - '''Upper bound of the Q-grid for the calculated F(Q). +DebyePDFCalculator.qmax = propertyFromExtDoubleAttr( + "qmax", + """Upper bound of the Q-grid for the calculated F(Q). Affects the termination ripples. [25 1/A] - ''') + """, +) -DebyePDFCalculator.qstep = propertyFromExtDoubleAttr('qstep', - '''Spacing in the Q-grid. Q-values are at the multiples of qstep. +DebyePDFCalculator.qstep = propertyFromExtDoubleAttr( + "qstep", + """Spacing in the Q-grid. Q-values are at the multiples of qstep. [PI/extendedrmax A] unless user overridden. - See also setOptimumQstep, isOptimumQstep.''') + See also setOptimumQstep, isOptimumQstep.""", +) # method overrides to support optional keyword arguments + def _init_kwargs0(self, **kwargs): - '''Create a new instance of the DebyePDFCalculator. + """Create a new instance of the DebyePDFCalculator. Keyword arguments can be used to configure the calculator properties, for example: dpc = DebyePDFCalculator(qmax=20, rmin=7, rmax=15) Raise ValueError for invalid keyword argument. - ''' + """ DebyePDFCalculator.__boostpython__init(self) setattrFromKeywordArguments(self, **kwargs) return + DebyePDFCalculator.__boostpython__init = DebyePDFCalculator.__init__ DebyePDFCalculator.__init__ = _init_kwargs0 @@ -191,58 +255,74 @@ def _init_kwargs0(self, **kwargs): # Property wrappers to double attributes of the C++ PDFCalculator -PDFCalculator.peakprecision = propertyFromExtDoubleAttr('peakprecision', - '''Cutoff amplitude of the peak tail relative to the peak maximum. - [3.33e-6 unitless]''') +PDFCalculator.peakprecision = propertyFromExtDoubleAttr( + "peakprecision", + """Cutoff amplitude of the peak tail relative to the peak maximum. + [3.33e-6 unitless]""", +) -PDFCalculator.qmin = propertyFromExtDoubleAttr('qmin', - '''Lower bound of the experimental Q-range used. +PDFCalculator.qmin = propertyFromExtDoubleAttr( + "qmin", + """Lower bound of the experimental Q-range used. Affects the shape envelope. - [0 1/A]''') + [0 1/A]""", +) -PDFCalculator.qmax = propertyFromExtDoubleAttr('qmax', - '''Upper bound of the experimental Q-range used. +PDFCalculator.qmax = propertyFromExtDoubleAttr( + "qmax", + """Upper bound of the experimental Q-range used. Affects the termination ripples. Not used when zero. - [0 1/A]''') + [0 1/A]""", +) -PDFCalculator.qstep = propertyFromExtDoubleAttr('qstep', - '''Spacing in the Q-grid. Q-values are at the multiples of qstep. +PDFCalculator.qstep = propertyFromExtDoubleAttr( + "qstep", + """Spacing in the Q-grid. Q-values are at the multiples of qstep. The value is padded by rsteps so that PI/qstep > extendedrmax and PI/(qstep * rstep) is a power of 2. Read-only. - [PI/(padded extendedrmax) A]''') + [PI/(padded extendedrmax) A]""", +) -PDFCalculator.slope = propertyFromExtDoubleAttr('slope', - '''Slope of the linear PDF background. Assigned according to +PDFCalculator.slope = propertyFromExtDoubleAttr( + "slope", + """Slope of the linear PDF background. Assigned according to number density of the evaluated structure at each PDF calculation. Active for LinearBaseline. - [-4*pi*numdensity unitless]''') + [-4*pi*numdensity unitless]""", +) -PDFCalculator.spdiameter = propertyFromExtDoubleAttr('spdiameter', - '''Spherical particle diameter for PDF shape damping correction. +PDFCalculator.spdiameter = propertyFromExtDoubleAttr( + "spdiameter", + """Spherical particle diameter for PDF shape damping correction. Not used when zero. Active for SphericalShapeEnvelope. - [0 A]''') + [0 A]""", +) -PDFCalculator.stepcut = propertyFromExtDoubleAttr('stepcut', - '''r-boundary for a step cutoff of the calculated PDF. +PDFCalculator.stepcut = propertyFromExtDoubleAttr( + "stepcut", + """r-boundary for a step cutoff of the calculated PDF. Not used when negative or zero. Active for StepCutEnvelope. Not used when zero. Active for StepCutEnvelope. - [0 A]''') + [0 A]""", +) # method overrides to support optional keyword arguments + def _init_kwargs1(self, **kwargs): - '''Create a new instance of PDFCalculator. + """Create a new instance of PDFCalculator. Keyword arguments can be used to configure the calculator properties, for example: pc = PDFCalculator(qmax=20, rmin=7, rmax=15) Raise ValueError for invalid keyword argument. - ''' + """ PDFCalculator.__boostpython__init(self) setattrFromKeywordArguments(self, **kwargs) return + PDFCalculator.__boostpython__init = PDFCalculator.__init__ PDFCalculator.__init__ = _init_kwargs1 diff --git a/src/diffpy/srreal/pdfenvelope.py b/src/diffpy/srreal/pdfenvelope.py index d4cb93d3..c7395175 100644 --- a/src/diffpy/srreal/pdfenvelope.py +++ b/src/diffpy/srreal/pdfenvelope.py @@ -12,7 +12,6 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - """ Classes for configuring PDF scaling envelope: PDFEnvelope, ScaleEnvelope, QResolutionEnvelope, @@ -21,18 +20,22 @@ # exported items -__all__ = ''' +__all__ = """ PDFEnvelope makePDFEnvelope QResolutionEnvelope ScaleEnvelope SphericalShapeEnvelope StepCutEnvelope - '''.split() + """.split() from diffpy.srreal import _final_imports -from diffpy.srreal.srreal_ext import PDFEnvelope -from diffpy.srreal.srreal_ext import ScaleEnvelope, QResolutionEnvelope -from diffpy.srreal.srreal_ext import SphericalShapeEnvelope, StepCutEnvelope +from diffpy.srreal.srreal_ext import ( + PDFEnvelope, + QResolutionEnvelope, + ScaleEnvelope, + SphericalShapeEnvelope, + StepCutEnvelope, +) from diffpy.srreal.wraputils import propertyFromExtDoubleAttr # class PDFEnvelope ---------------------------------------------------------- @@ -46,27 +49,37 @@ # attribute wrappers -QResolutionEnvelope.qdamp = propertyFromExtDoubleAttr('qdamp', - '''Dampening parameter in the Gaussian envelope function. - ''') - -ScaleEnvelope.scale = propertyFromExtDoubleAttr('scale', - '''Overall scale for a uniform scaling envelope. - ''') - -SphericalShapeEnvelope.spdiameter = propertyFromExtDoubleAttr('spdiameter', - '''Particle diameter in Angstroms for a spherical shape damping. - ''') - -StepCutEnvelope.stepcut = propertyFromExtDoubleAttr('stepcut', - '''Cutoff for a step-function envelope. - ''') +QResolutionEnvelope.qdamp = propertyFromExtDoubleAttr( + "qdamp", + """Dampening parameter in the Gaussian envelope function. + """, +) + +ScaleEnvelope.scale = propertyFromExtDoubleAttr( + "scale", + """Overall scale for a uniform scaling envelope. + """, +) + +SphericalShapeEnvelope.spdiameter = propertyFromExtDoubleAttr( + "spdiameter", + """Particle diameter in Angstroms for a spherical shape damping. + """, +) + +StepCutEnvelope.stepcut = propertyFromExtDoubleAttr( + "stepcut", + """Cutoff for a step-function envelope. + """, +) # Python functions wrapper + def makePDFEnvelope(name, fnc, replace=False, **dbattrs): - '''Helper function for registering Python function as a PDFEnvelope. - This is required for using Python function as PDFCalculator envelope. + """Helper function for registering Python function as a PDFEnvelope. + This is required for using Python function as PDFCalculator + envelope. name -- unique string name for registering Python function in the global registry of PDFEnvelope types. This will be the @@ -100,12 +113,15 @@ def fexpdecay(x, expscale, exptail): pdfc = PDFCalculator() pdfc.addEnvelope(envelope) # or pdfc.addEnvelope("expdecay") - ''' + """ from diffpy.srreal.wraputils import _wrapAsRegisteredUnaryFunction - rv = _wrapAsRegisteredUnaryFunction(PDFEnvelope, name, fnc, - replace=replace, **dbattrs) + + rv = _wrapAsRegisteredUnaryFunction( + PDFEnvelope, name, fnc, replace=replace, **dbattrs + ) return rv + # Import delayed tweaks of the extension classes. _final_imports.import_now() diff --git a/src/diffpy/srreal/peakprofile.py b/src/diffpy/srreal/peakprofile.py index e3745fa7..7692e1d7 100644 --- a/src/diffpy/srreal/peakprofile.py +++ b/src/diffpy/srreal/peakprofile.py @@ -12,7 +12,6 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - """ Class for configuring PDF profile function: PeakProfile @@ -21,15 +20,14 @@ # exported items -__all__ = [ - 'PeakProfile', - 'GaussianProfile', - 'CroppedGaussianProfile' -] +__all__ = ["PeakProfile", "GaussianProfile", "CroppedGaussianProfile"] from diffpy.srreal import _final_imports -from diffpy.srreal.srreal_ext import PeakProfile -from diffpy.srreal.srreal_ext import GaussianProfile, CroppedGaussianProfile +from diffpy.srreal.srreal_ext import ( + CroppedGaussianProfile, + GaussianProfile, + PeakProfile, +) from diffpy.srreal.wraputils import propertyFromExtDoubleAttr # class PeakProfile ---------------------------------------------------------- @@ -41,10 +39,12 @@ # add attribute wrappers for PeakProfile and derived classes -PeakProfile.peakprecision = propertyFromExtDoubleAttr('peakprecision', - '''Profile amplitude relative to the peak maximum for evaluating peak +PeakProfile.peakprecision = propertyFromExtDoubleAttr( + "peakprecision", + """Profile amplitude relative to the peak maximum for evaluating peak bounds xboundlo and xboundhi. [3.33e-6 unitless] - ''') + """, +) # Import delayed tweaks of the extension classes. diff --git a/src/diffpy/srreal/peakwidthmodel.py b/src/diffpy/srreal/peakwidthmodel.py index 59274712..7c73e8e8 100644 --- a/src/diffpy/srreal/peakwidthmodel.py +++ b/src/diffpy/srreal/peakwidthmodel.py @@ -12,7 +12,6 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - """ Classes for configuring peak width evaluation in PDF calculations: PeakWidthModel, @@ -22,41 +21,54 @@ # exported items __all__ = [ - 'PeakWidthModel', - 'ConstantPeakWidth', - 'DebyeWallerPeakWidth', - 'JeongPeakWidth' + "PeakWidthModel", + "ConstantPeakWidth", + "DebyeWallerPeakWidth", + "JeongPeakWidth", ] from diffpy.srreal import _final_imports -from diffpy.srreal.srreal_ext import PeakWidthModel, ConstantPeakWidth -from diffpy.srreal.srreal_ext import DebyeWallerPeakWidth, JeongPeakWidth +from diffpy.srreal.srreal_ext import ( + ConstantPeakWidth, + DebyeWallerPeakWidth, + JeongPeakWidth, + PeakWidthModel, +) from diffpy.srreal.wraputils import propertyFromExtDoubleAttr # class PeakWidthModel ------------------------------------------------------- # add attribute wrappers for the derived classes -ConstantPeakWidth.width = propertyFromExtDoubleAttr('width', - '''Constant FWHM value returned by this model. - ''') +ConstantPeakWidth.width = propertyFromExtDoubleAttr( + "width", + """Constant FWHM value returned by this model. + """, +) -ConstantPeakWidth.bisowidth = propertyFromExtDoubleAttr('bisowidth', - '''Equivalent uniform Biso for this constant `width`. - ''') +ConstantPeakWidth.bisowidth = propertyFromExtDoubleAttr( + "bisowidth", + """Equivalent uniform Biso for this constant `width`. + """, +) -ConstantPeakWidth.uisowidth = propertyFromExtDoubleAttr('uisowidth', - '''Equivalent uniform Uiso for this constant `width`. - ''') +ConstantPeakWidth.uisowidth = propertyFromExtDoubleAttr( + "uisowidth", + """Equivalent uniform Uiso for this constant `width`. + """, +) -JeongPeakWidth.delta1 = propertyFromExtDoubleAttr('delta1', - 'Coefficient for (1/r) contribution to the peak sharpening.') +JeongPeakWidth.delta1 = propertyFromExtDoubleAttr( + "delta1", "Coefficient for (1/r) contribution to the peak sharpening." +) -JeongPeakWidth.delta2 = propertyFromExtDoubleAttr('delta2', - 'Coefficient for (1/r**2) contribution to the peak sharpening.') +JeongPeakWidth.delta2 = propertyFromExtDoubleAttr( + "delta2", "Coefficient for (1/r**2) contribution to the peak sharpening." +) -JeongPeakWidth.qbroad = propertyFromExtDoubleAttr('qbroad', - 'PDF peak broadening from increased intensity noise at high Q.') +JeongPeakWidth.qbroad = propertyFromExtDoubleAttr( + "qbroad", "PDF peak broadening from increased intensity noise at high Q." +) # Import delayed tweaks of the extension classes. diff --git a/src/diffpy/srreal/scatteringfactortable.py b/src/diffpy/srreal/scatteringfactortable.py index 5e4e1d6f..9fa232ac 100644 --- a/src/diffpy/srreal/scatteringfactortable.py +++ b/src/diffpy/srreal/scatteringfactortable.py @@ -12,23 +12,28 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -""" -class ScatteringFactorTable -- scattering factors for atoms, ions and isotopes. -""" +"""Class ScatteringFactorTable -- scattering factors for atoms, ions and +isotopes.""" # exported items, these also makes them show in pydoc. -__all__ = ['ScatteringFactorTable', - 'SFTXray', 'SFTElectron', 'SFTNeutron', 'SFTElectronNumber', - 'SFAverage'] +__all__ = [ + "ScatteringFactorTable", + "SFTXray", + "SFTElectron", + "SFTNeutron", + "SFTElectronNumber", + "SFAverage", +] -from diffpy.srreal.srreal_ext import ScatteringFactorTable -from diffpy.srreal.srreal_ext import SFTXray -from diffpy.srreal.srreal_ext import SFTElectron -from diffpy.srreal.srreal_ext import SFTNeutron -from diffpy.srreal.srreal_ext import SFTElectronNumber from diffpy.srreal.sfaverage import SFAverage +from diffpy.srreal.srreal_ext import ( + ScatteringFactorTable, + SFTElectron, + SFTElectronNumber, + SFTNeutron, + SFTXray, +) # Pickling Support ----------------------------------------------------------- diff --git a/src/diffpy/srreal/sfaverage.py b/src/diffpy/srreal/sfaverage.py index fd1795f3..d524b0c3 100644 --- a/src/diffpy/srreal/sfaverage.py +++ b/src/diffpy/srreal/sfaverage.py @@ -12,10 +12,7 @@ # See LICENSE.txt for license information. # ############################################################################## - -""" -Compositional averaging of atom scattering factors. - +"""Compositional averaging of atom scattering factors. Examples -------- @@ -39,9 +36,9 @@ # class SFAverage ------------------------------------------------------------ + class SFAverage(object): - """\ - Calculate compositional statistics of atom scattering factors. + """Calculate compositional statistics of atom scattering factors. Compositional averages can be calculated for an array of Q-values. Results are stored in the class attributes. @@ -64,7 +61,7 @@ class SFAverage(object): Compositional average of squared scattering factors. Float or NumPy array. composition : - Dictionary of atom symbols and their total abundancies. + Dictionary of atom symbols and their total abundance. """ f1sum = 0 @@ -76,8 +73,7 @@ class SFAverage(object): @classmethod def fromStructure(cls, stru, sftb, q=0): - """\ - Calculate average scattering factors from a structure object. + """Calculate average scattering factors from a structure object. Parameters ---------- @@ -103,12 +99,13 @@ def fromStructure(cls, stru, sftb, q=0): The calculated scattering factor averages. """ # a bit of duck-typing for faster handling of diffpy.structure - if hasattr(type(stru), 'composition'): + if hasattr(type(stru), "composition"): composition = stru.composition if isinstance(composition, dict): return cls.fromComposition(composition, sftb, q) # otherwise let's convert to a known structure type from diffpy.srreal.structureadapter import createStructureAdapter + adpt = createStructureAdapter(stru) composition = {} for i in range(adpt.countSites()): @@ -117,11 +114,10 @@ def fromStructure(cls, stru, sftb, q=0): composition[smbl] = composition.get(smbl, 0) + cnt return cls.fromComposition(composition, sftb, q) - @classmethod def fromComposition(cls, composition, sftb, q=0): - """\ - Calculate average scattering factors from atom concentrations. + """Calculate average scattering factors from atom + concentrations. Parameters ---------- @@ -142,20 +138,24 @@ def fromComposition(cls, composition, sftb, q=0): The calculated scattering factor averages. """ from diffpy.srreal.scatteringfactortable import ScatteringFactorTable + sfa = cls() sfa.composition = {} if isinstance(composition, dict): sfa.composition.update(composition) else: for smbl, cnt in composition: - if not smbl in sfa.composition: + if smbl not in sfa.composition: sfa.composition[smbl] = 0 sfa.composition[smbl] += cnt sfa.f1sum = 0.0 * q sfa.f2sum = 0.0 * q # resolve the lookup table object `tb` - tb = (sftb if not isinstance(sftb, str) - else ScatteringFactorTable.createByType(sftb)) + tb = ( + sftb + if not isinstance(sftb, str) + else ScatteringFactorTable.createByType(sftb) + ) for smbl, cnt in sfa.composition.items(): sfq = tb.lookup(smbl, q) sfa.f1sum += cnt * sfq @@ -166,4 +166,5 @@ def fromComposition(cls, composition, sftb, q=0): sfa.f2avg = sfa.f2sum / denom return sfa + # End of class SFAverage diff --git a/src/diffpy/srreal/structureadapter.py b/src/diffpy/srreal/structureadapter.py index 4740977b..a878ab97 100644 --- a/src/diffpy/srreal/structureadapter.py +++ b/src/diffpy/srreal/structureadapter.py @@ -12,9 +12,8 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -"""class StructureAdapter -- adapter of any structure object to the interface - expected by srreal PairQuantity calculators +"""Class StructureAdapter -- adapter of any structure object to the +interface expected by srreal PairQuantity calculators. Routines: @@ -29,9 +28,25 @@ EMPTY -- singleton instance of an empty structure. """ +# import of srreal_ext calls RegisterStructureAdapter, therefore it has +# to be at the end of this module. + +from diffpy.srreal.srreal_ext import ( + Atom, + AtomicStructureAdapter, + BaseBondGenerator, + CrystalStructureAdapter, + PeriodicStructureAdapter, + StructureAdapter, + StructureDifference, + _emptyStructureAdapter, + nometa, + nosymmetry, +) + + def createStructureAdapter(stru): - ''' - Create StructureAdapter from a Python object. + """Create StructureAdapter from a Python object. stru -- an object that is convertible to StructureAdapter, i.e., it has a registered factory that converts Python structure object to @@ -39,14 +54,17 @@ def createStructureAdapter(stru): Return a StructureAdapter instance. Raise TypeError if stru cannot be converted to StructureAdapter. - ''' - if isinstance(stru, StructureAdapter): return stru + """ + if isinstance(stru, StructureAdapter): + return stru import inspect + # build fully-qualified names of Python types in method resolution order cls = type(stru) fqnames = [str(tp).split("'")[1] for tp in inspect.getmro(cls)] for fqn in fqnames: - if not fqn in _adapter_converters_registry: continue + if fqn not in _adapter_converters_registry: + continue factory = _adapter_converters_registry[fqn] return factory(stru) # none of the registered factories could convert the stru object @@ -55,11 +73,11 @@ def createStructureAdapter(stru): def RegisterStructureAdapter(fqname, fnc=None): - '''Function decorator that marks it as a converter of specified - object type to StructureAdapter class in diffpy.srreal. The registered - structure object types can be afterwards directly used with calculators - in diffpy.srreal as they would be implicitly converted to the internal - diffpy.srreal structure type. + """Function decorator that marks it as a converter of specified + object type to StructureAdapter class in diffpy.srreal. The + registered structure object types can be afterwards directly used + with calculators in diffpy.srreal as they would be implicitly + converted to the internal diffpy.srreal structure type. fqname -- fully qualified class name for the convertible objects. This is the quoted string included in "str(type(obj))". @@ -75,34 +93,34 @@ def convertMyStructure(stru): ... See diffpy.srreal.structureconverters module for usage example. - ''' + """ + def __wrapper(fnc): _adapter_converters_registry[fqname] = fnc return fnc + if fnc is None: return __wrapper return __wrapper(fnc) -_adapter_converters_registry = {} -# import of srreal_ext calls RegisterStructureAdapter, therefore it has -# to be at the end of this module. - -from diffpy.srreal.srreal_ext import StructureAdapter -from diffpy.srreal.srreal_ext import Atom, AtomicStructureAdapter -from diffpy.srreal.srreal_ext import PeriodicStructureAdapter -from diffpy.srreal.srreal_ext import CrystalStructureAdapter -from diffpy.srreal.srreal_ext import StructureDifference -from diffpy.srreal.srreal_ext import nometa, nosymmetry -from diffpy.srreal.srreal_ext import _emptyStructureAdapter -from diffpy.srreal.srreal_ext import BaseBondGenerator +_adapter_converters_registry = {} EMPTY = _emptyStructureAdapter() del _emptyStructureAdapter # silence the pyflakes syntax checker -assert all((Atom, AtomicStructureAdapter, PeriodicStructureAdapter, - CrystalStructureAdapter, StructureDifference, - nometa, nosymmetry, BaseBondGenerator)) +assert all( + ( + Atom, + AtomicStructureAdapter, + PeriodicStructureAdapter, + CrystalStructureAdapter, + StructureDifference, + nometa, + nosymmetry, + BaseBondGenerator, + ) +) # End of file diff --git a/src/diffpy/srreal/structureconverters.py b/src/diffpy/srreal/structureconverters.py index e504587d..5f9f6cb5 100644 --- a/src/diffpy/srreal/structureconverters.py +++ b/src/diffpy/srreal/structureconverters.py @@ -12,24 +12,36 @@ # See LICENSE.txt for license information. # ############################################################################## +"""Converters from other structure representations in Python to +diffpy.srreal StructureAdapter classes.""" + +from diffpy.srreal.srreal_ext import ( + AtomicStructureAdapter, + PeriodicStructureAdapter, + convertObjCrystCrystal, + convertObjCrystMolecule, +) +from diffpy.srreal.structureadapter import RegisterStructureAdapter -""" -Converters from other structure representations in Python to diffpy.srreal -StructureAdapter classes. -""" +# Converters for Molecule and Crystal from pyobjcryst ------------------------ -from diffpy.srreal.structureadapter import RegisterStructureAdapter -from diffpy.srreal.srreal_ext import AtomicStructureAdapter -from diffpy.srreal.srreal_ext import PeriodicStructureAdapter + +RegisterStructureAdapter( + "pyobjcryst._pyobjcryst.Molecule", convertObjCrystMolecule +) +RegisterStructureAdapter( + "pyobjcryst._pyobjcryst.Crystal", convertObjCrystCrystal +) # Converter for Structure class from diffpy.structure ------------------------ + # TODO prune in version 1.4. -@RegisterStructureAdapter('diffpy.Structure.structure.Structure') -@RegisterStructureAdapter('diffpy.structure.structure.Structure') +@RegisterStructureAdapter("diffpy.Structure.structure.Structure") +@RegisterStructureAdapter("diffpy.structure.structure.Structure") def convertDiffPyStructure(stru): - 'Adapt Structure class from diffpy.structure package.' - haslattice = ((1, 1, 1, 90, 90, 90) != stru.lattice.abcABG()) + "Adapt Structure class from diffpy.structure package." + haslattice = (1, 1, 1, 90, 90, 90) != stru.lattice.abcABG() isperiodic = haslattice hasmeta = _DiffPyStructureMetadata.hasMetadata(stru) if hasmeta: @@ -46,62 +58,54 @@ def convertDiffPyStructure(stru): _fetchDiffPyStructureData(adpt, stru) return adpt -# Converters for Molecule and Crystal from pyobjcryst ------------------------ - -from diffpy.srreal.srreal_ext import convertObjCrystMolecule -RegisterStructureAdapter( - 'pyobjcryst._pyobjcryst.Molecule', convertObjCrystMolecule) - -from diffpy.srreal.srreal_ext import convertObjCrystCrystal -RegisterStructureAdapter( - 'pyobjcryst._pyobjcryst.Crystal', convertObjCrystCrystal) # Adapter classes and helpers for diffpy.structure class --------------------- -class _DiffPyStructureMetadata(object): +class _DiffPyStructureMetadata(object): "Base class for handling metadata information in the pdffit attribute." pdffit = None @staticmethod def hasMetadata(stru): - """True if Structure object carries data in its pdffit attribute. - """ - rv = hasattr(stru, 'pdffit') and bool(stru.pdffit) + """True if Structure object carries data in its pdffit + attribute.""" + rv = hasattr(stru, "pdffit") and bool(stru.pdffit) return rv - def _customPQConfig(self, pqobj): - """Apply PDF-related metadata if defined in PDFFit structure format. - """ + """Apply PDF-related metadata if defined in PDFFit structure + format.""" pqname = type(pqobj).__name__ - if not pqname in ('PDFCalculator', 'DebyePDFCalculator'): return - if not self.pdffit: return + if pqname not in ("PDFCalculator", "DebyePDFCalculator"): + return + if not self.pdffit: + return # scale envtps = pqobj.usedenvelopetypes - if 'scale' not in envtps: - pqobj.addEnvelope('scale') - pqobj.scale = self.pdffit['scale'] + if "scale" not in envtps: + pqobj.addEnvelope("scale") + pqobj.scale = self.pdffit["scale"] # spdiameter if "spdiameter" in self.pdffit: - if not 'sphericalshape' in envtps: - pqobj.addEnvelope('sphericalshape') - pqobj.spdiameter = self.pdffit['spdiameter'] + if "sphericalshape" not in envtps: + pqobj.addEnvelope("sphericalshape") + pqobj.spdiameter = self.pdffit["spdiameter"] # stepcut if "stepcut" in self.pdffit: - if not 'stepcut' in envtps: - pqobj.addEnvelope('stepcut') - pqobj.stepcut = self.pdffit['stepcut'] + if "stepcut" not in envtps: + pqobj.addEnvelope("stepcut") + pqobj.stepcut = self.pdffit["stepcut"] # delta1, delta2 - set these only when using JeongPeakWidth model if pqobj.peakwidthmodel.type() == "jeong": - pqobj.delta1 = self.pdffit['delta1'] - pqobj.delta2 = self.pdffit['delta2'] + pqobj.delta1 = self.pdffit["delta1"] + pqobj.delta2 = self.pdffit["delta2"] return - def _fetchMetadata(self, stru): - """Copy data from the pdffit attribute of diffpy Structure object + """Copy data from the pdffit attribute of diffpy Structure + object. stru -- instance of Structure class from diffpy.structure @@ -114,16 +118,19 @@ def _fetchMetadata(self, stru): self.pdffit.update(stru.pdffit) return + # end of class _DiffPyStructureMetadata class DiffPyStructureAtomicAdapter( - _DiffPyStructureMetadata, AtomicStructureAdapter): + _DiffPyStructureMetadata, AtomicStructureAdapter +): pass class DiffPyStructurePeriodicAdapter( - _DiffPyStructureMetadata, PeriodicStructureAdapter): + _DiffPyStructureMetadata, PeriodicStructureAdapter +): pass @@ -136,6 +143,7 @@ def _fetchDiffPyStructureData(adpt, stru): No return value. """ from diffpy.srreal.srreal_ext import Atom as AdapterAtom + # copy atoms del adpt[:] adpt.reserve(len(stru)) @@ -148,10 +156,11 @@ def _fetchDiffPyStructureData(adpt, stru): aa.xyz_cartn = a0.xyz aa.uij_cartn = a0.U adpt.append(aa) - if hasattr(adpt, 'setLatPar'): + if hasattr(adpt, "setLatPar"): adpt.setLatPar(*stru.lattice.abcABG()) for aa in adpt: adpt.toCartesian(aa) return + # End of file diff --git a/src/diffpy/srreal/tests/__init__.py b/src/diffpy/srreal/tests/__init__.py deleted file mode 100644 index f1b64d71..00000000 --- a/src/diffpy/srreal/tests/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srreal by DANSE Diffraction group -# Simon J. L. Billinge -# (c) 2012 The Trustees of Columbia University -# in the City of New York. All rights reserved. -# -# File coded by: Pavol Juhas -# -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE_DANSE.txt for license information. -# -############################################################################## - -"""Unit tests for diffpy.srreal. -""" - -import unittest -import logging - -# create logger instance for the tests subpackage -logging.basicConfig() -logger = logging.getLogger(__name__) -del logging - - -def testsuite(pattern=''): - '''Create a unit tests suite for diffpy.srreal package. - - Parameters - ---------- - pattern : str, optional - Regular expression pattern for selecting test cases. - Select all tests when empty. Ignore the pattern when - any of unit test modules fails to import. - - Returns - ------- - suite : `unittest.TestSuite` - The TestSuite object containing the matching tests. - ''' - import re - from os.path import dirname - from itertools import chain - from pkg_resources import resource_filename - loader = unittest.defaultTestLoader - thisdir = resource_filename(__name__, '') - depth = __name__.count('.') + 1 - topdir = thisdir - for i in range(depth): - topdir = dirname(topdir) - suite_all = loader.discover(thisdir, top_level_dir=topdir) - # always filter the suite by pattern to test-cover the selection code. - suite = unittest.TestSuite() - rx = re.compile(pattern) - tsuites = list(chain.from_iterable(suite_all)) - tsok = all(isinstance(ts, unittest.TestSuite) for ts in tsuites) - if not tsok: # pragma: no cover - return suite_all - tcases = chain.from_iterable(tsuites) - for tc in tcases: - tcwords = tc.id().split('.') - shortname = '.'.join(tcwords[-3:]) - if rx.search(shortname): - suite.addTest(tc) - # verify all tests are found for an empty pattern. - assert pattern or suite_all.countTestCases() == suite.countTestCases() - return suite - - -def test(): - '''Execute all unit tests for the diffpy.srreal package. - - Returns - ------- - result : `unittest.TestResult` - ''' - suite = testsuite() - runner = unittest.TextTestRunner() - result = runner.run(suite) - return result - - -# End of file diff --git a/src/diffpy/srreal/tests/debug.py b/src/diffpy/srreal/tests/debug.py deleted file mode 100644 index be3127be..00000000 --- a/src/diffpy/srreal/tests/debug.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srreal Complex Modeling Initiative -# (c) 2016 Brookhaven Science Associates, -# Brookhaven National Laboratory. -# All rights reserved. -# -# File coded by: Pavol Juhas -# -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE.txt for license information. -# -############################################################################## - -""" -Convenience module for debugging the unit tests using - -python -m diffpy.srreal.tests.debug - -Exceptions raised by failed tests or other errors are not caught. -""" - - -if __name__ == '__main__': - import sys - from diffpy.srreal.tests import testsuite - pattern = sys.argv[1] if len(sys.argv) > 1 else '' - suite = testsuite(pattern) - suite.debug() - - -# End of file diff --git a/src/diffpy/srreal/tests/run.py b/src/diffpy/srreal/tests/run.py deleted file mode 100644 index 6f88d607..00000000 --- a/src/diffpy/srreal/tests/run.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srreal by DANSE Diffraction group -# Simon J. L. Billinge -# (c) 2010 The Trustees of Columbia University -# in the City of New York. All rights reserved. -# -# File coded by: Pavol Juhas -# -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE_DANSE.txt for license information. -# -############################################################################## - -"""Convenience module for executing all unit tests with - -python -m diffpy.srreal.tests.run -""" - - -if __name__ == '__main__': - import sys - # show warnings by default - if not sys.warnoptions: - import os, warnings - warnings.simplefilter("default") - # also affect subprocesses - os.environ["PYTHONWARNINGS"] = "default" - from diffpy.srreal.tests import test - # produce zero exit code for a successful test - sys.exit(not test().wasSuccessful()) - -# End of file diff --git a/src/diffpy/srreal/tests/testatomradiitable.py b/src/diffpy/srreal/tests/testatomradiitable.py deleted file mode 100644 index c96a6fb8..00000000 --- a/src/diffpy/srreal/tests/testatomradiitable.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env python - -"""Unit tests for the AtomRadiiTable class. -""" - - -import unittest -import pickle -from diffpy.srreal.tests.testutils import has_periodictable, _msg_noperiodictable -from diffpy.srreal.atomradiitable import AtomRadiiTable, CovalentRadiiTable -from diffpy.srreal.atomradiitable import ConstantRadiiTable - -# ---------------------------------------------------------------------------- - -class TestAtomRadiiTable(unittest.TestCase): - - def setUp(self): - self.rtb = AtomRadiiTable() - self.ctb = ConstantRadiiTable() - return - - def tearDown(self): - return - - def test_pickling(self): - '''check pickling and unpickling of AtomRadiiTable. - ''' - ctb1 = pickle.loads(pickle.dumps(self.ctb)) - self.assertTrue(type(ctb1) is ConstantRadiiTable) - self.assertEqual({}, ctb1.getAllCustom()) - self.ctb.setCustom('Na', 1.3) - self.ctb.foobar = 'foo' - self.ctb.setDefault(3.7) - ctb2 = pickle.loads(pickle.dumps(self.ctb)) - self.assertEqual({'Na' : 1.3}, ctb2.getAllCustom()) - self.assertFalse(hasattr(ctb2, 'foobar')) - self.assertEqual(3.7, ctb2.getDefault()) - return - - def test__standardLookup(self): - """check AtomRadiiTable._standardLookup() - """ - self.assertRaises(RuntimeError, self.rtb._standardLookup, - 'anything') - self.assertEqual(0.0, self.ctb._standardLookup('anything')) - self.ctb.setDefault(7.3) - self.assertEqual(7.3, self.ctb._standardLookup('anything')) - return - - def test_fromString(self): - """check AtomRadiiTable.fromString() - """ - self.rtb.fromString('H:0.33, B:0.42') - self.assertEqual({'H' : 0.33, 'B' : 0.42}, self.rtb.getAllCustom()) - self.assertRaises(ValueError, self.rtb.fromString, - 'C:2.3, U:asdf') - self.assertEqual({'H' : 0.33, 'B' : 0.42}, self.rtb.getAllCustom()) - self.rtb.fromString('C:2.3,,,') - self.assertEqual(3, len(self.rtb.getAllCustom())) - self.assertEqual(2.3, self.rtb.lookup('C')) - self.rtb.fromString('H:3.3') - self.assertEqual(3, len(self.rtb.getAllCustom())) - self.assertEqual(3.3, self.rtb.lookup('H')) - return - - def test_getAllCustom(self): - """check AtomRadiiTable.getAllCustom() - """ - self.assertEqual({}, self.rtb.getAllCustom()) - return - - def test_lookup(self): - """check AtomRadiiTable.lookup() - """ - self.assertRaises(RuntimeError, self.rtb.lookup, 'C') - self.assertEqual(0.0, self.ctb.lookup('C')) - self.rtb.setCustom('C', 1.23) - self.assertEqual(1.23, self.rtb.lookup('C')) - return - - def test_resetCustom(self): - """check AtomRadiiTable.resetCustom() - """ - self.rtb.setCustom('C', 1.23) - self.assertTrue(self.rtb.getAllCustom()) - self.rtb.resetAll() - self.assertFalse(self.rtb.getAllCustom()) - return - - def test_setCustom(self): - """check AtomRadiiTable.setCustom() - """ - self.rtb.setCustom('C', 1.23) - self.assertEqual(1.23, self.rtb.lookup('C')) - self.rtb.setCustom('C', 3.3) - self.assertEqual(3.3, self.rtb.lookup('C')) - return - - def test_toString(self): - """check AtomRadiiTable.toString() - """ - rtb = self.rtb - self.assertEqual('', rtb.toString()) - self.assertEqual('', rtb.toString('; ')) - rtb.fromString('C : 1.5, B:2.0') - self.assertEqual('B:2,C:1.5', rtb.toString()) - self.assertEqual('B:2; C:1.5', rtb.toString('; ')) - return - -# End of class TestAtomRadiiTable - -# ---------------------------------------------------------------------------- - -@unittest.skipUnless(has_periodictable, _msg_noperiodictable) -class TestCovalentRadiiTable(unittest.TestCase): - - def setUp(self): - self.rtb = CovalentRadiiTable() - return - - def tearDown(self): - return - - def test_pickling(self): - '''check pickling and unpickling of CovalentRadiiTable. - ''' - rtb1 = pickle.loads(pickle.dumps(self.rtb)) - self.assertTrue(type(rtb1) is CovalentRadiiTable) - self.assertEqual({}, rtb1.getAllCustom()) - self.rtb.setCustom('Na', 1.3) - self.rtb.foobar = 'foo' - rtb2 = pickle.loads(pickle.dumps(self.rtb)) - self.assertEqual({'Na' : 1.3}, rtb2.getAllCustom()) - self.assertEqual('foo', rtb2.foobar) - return - - def test__standardLookup(self): - """check CovalentRadiiTable._standardLookup() - """ - self.assertEqual(1.22, self.rtb._standardLookup('Ga')) - return - - def test_create(self): - """check CovalentRadiiTable.create() - """ - self.rtb.setCustom('Na', 1.3) - rtb2 = self.rtb.create() - self.assertTrue(isinstance(rtb2, CovalentRadiiTable)) - self.assertEqual(1, len(self.rtb.getAllCustom())) - self.assertEqual(0, len(rtb2.getAllCustom())) - return - - def test_clone(self): - """check CovalentRadiiTable.clone() - """ - self.rtb.setCustom('Na', 1.3) - rtb2 = self.rtb.clone() - self.assertTrue(isinstance(rtb2, CovalentRadiiTable)) - self.assertEqual(1, len(rtb2.getAllCustom())) - self.assertEqual(1.3, rtb2.lookup('Na')) - return - - def test_fromString(self): - """check CovalentRadiiTable.fromString() - """ - self.rtb.fromString('Ga:2.22') - self.assertEqual(2.22, self.rtb.lookup('Ga')) - return - - def test_getAllCustom(self): - """check CovalentRadiiTable.getAllCustom() - """ - self.assertEqual({}, self.rtb.getAllCustom()) - return - - def test_lookup(self): - """check CovalentRadiiTable.lookup() - """ - self.assertEqual(1.22, self.rtb.lookup('Ga')) - self.rtb.fromString('Ga:2.22') - self.assertEqual(2.22, self.rtb.lookup('Ga')) - return - - def test_resetCustom(self): - """check CovalentRadiiTable.resetCustom() - """ - self.rtb.fromString('B:2.33, Ga:2.22') - self.rtb.resetCustom('B') - self.rtb.resetCustom('nada') - self.assertEqual(1, len(self.rtb.getAllCustom())) - self.assertEqual(0.84, self.rtb.lookup('B')) - self.assertEqual(2.22, self.rtb.lookup('Ga')) - return - - def test_setCustom(self): - """check CovalentRadiiTable.setCustom() - """ - self.assertEqual(0.84, self.rtb.lookup('B')) - self.rtb.setCustom('B', 0.9) - self.assertEqual(0.9, self.rtb.lookup('B')) - return - - def test_toString(self): - """check CovalentRadiiTable.toString() - """ - self.assertEqual('', self.rtb.toString(';---')) - return - -# End of class TestCovalentRadiiTable - -# ---------------------------------------------------------------------------- - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/src/diffpy/srreal/version.py b/src/diffpy/srreal/version.py index 8d8feeba..54d1c6b3 100644 --- a/src/diffpy/srreal/version.py +++ b/src/diffpy/srreal/version.py @@ -1,64 +1,25 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.srreal by DANSE Diffraction group -# Simon J. L. Billinge -# (c) 2008 The Trustees of Columbia University -# in the City of New York. All rights reserved. +# (c) 2025 The Trustees of Columbia University in the City of New York. +# All rights reserved. # -# File coded by: Pavol Juhas +# File coded by: Billinge Group members and community contributors. # -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE_DANSE.txt for license information. +# See GitHub contributions for a more detailed list of contributors. +# https://github.com/diffpy/diffpy.srreal/graphs/contributors +# +# See LICENSE.rst for license information. # ############################################################################## +"""Definition of __version__.""" -""" -Definitions of version-related constants and of libdiffpy_version_info. - -Notes ------ -Variable `__gitsha__` is deprecated as of version 1.3. -Use `__git_commit__` instead. - -Variable `libdiffpy_version_info.git_sha` is deprecated as of version 1.4.0. -Use `libdiffpy_version_info.git_commit` instead. -""" - -__all__ = ['__date__', '__git_commit__', '__timestamp__', '__version__', - 'libdiffpy_version_info'] - - -from diffpy.srreal._version_data import __version__ -from diffpy.srreal._version_data import __date__ -from diffpy.srreal._version_data import __git_commit__ -from diffpy.srreal._version_data import __timestamp__ - -# TODO remove deprecated __gitsha__ in version 1.4. -__gitsha__ = __git_commit__ - -# version information on the active libdiffpy shared library ----------------- +# We do not use the other three variables, but can be added back if needed. +# __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"] -from collections import namedtuple -from diffpy.srreal.srreal_ext import _get_libdiffpy_version_info_dict +# obtain version information +from importlib.metadata import version -libdiffpy_version_info = namedtuple( - 'libdiffpy_version_info', - "major minor micro patch version_number version date git_commit " + - # TODO remove git_sha in version 1.4. - "git_sha") -vd = _get_libdiffpy_version_info_dict() -libdiffpy_version_info = libdiffpy_version_info( - version = vd['version_str'], - version_number = vd['version'], - major = vd['major'], - minor = vd['minor'], - micro = vd['micro'], - patch = vd['patch'], - date = vd['date'], - git_commit = vd['git_commit'], - # TODO remove git_sha in version 1.4. - git_sha = vd['git_commit']) -del vd +__version__ = version("diffpy.srreal") # End of file diff --git a/src/diffpy/srreal/wraputils.py b/src/diffpy/srreal/wraputils.py index 79261ec3..46322152 100644 --- a/src/diffpy/srreal/wraputils.py +++ b/src/diffpy/srreal/wraputils.py @@ -12,34 +12,37 @@ # See LICENSE_DANSE.txt for license information. # ############################################################################## - -"""Local utilities helpful for tweaking interfaces to boost python classes. -""" +"""Local utilities helpful for tweaking interfaces to boost python +classes.""" import copy # Routines ------------------------------------------------------------------- + def propertyFromExtDoubleAttr(attrname, doc): - '''Create property wrapper to a DoubleAttr in C++ extension object. + """Create property wrapper to a DoubleAttr in C++ extension object. attrname -- string name of the double attribute doc -- docstring for the Python class property Return a property object. - ''' + """ + def fget(self): return self._getDoubleAttr(attrname) + def fset(self, value): self._setDoubleAttr(attrname, value) return + rv = property(fget, fset, doc=doc) return rv def setattrFromKeywordArguments(obj, **kwargs): - '''Set attributes of the obj according to keyword arguments. + """Set attributes of the obj according to keyword arguments. For example: setattrFromKeywordArguments(obj, qmax=24, scale=2) This is a shared helper function used by __init__ and __call__. @@ -47,7 +50,7 @@ def setattrFromKeywordArguments(obj, **kwargs): No return value. Raise TypeError for invalid keyword argument. - ''' + """ for n in kwargs: if not hasattr(obj, n): emsg = "Invalid keyword argument %r" % n @@ -57,8 +60,10 @@ def setattrFromKeywordArguments(obj, **kwargs): return -def _wrapAsRegisteredUnaryFunction(cls, regname, fnc, replace=False, **dbattrs): - '''Helper function for wrapping Python function as PDFBaseline or +def _wrapAsRegisteredUnaryFunction( + cls, regname, fnc, replace=False, **dbattrs +): + """Helper function for wrapping Python function as PDFBaseline or PDFEnvelope functor. Not intended for direct usage, this function is rather called from makePDFBaseline or makePDFEnvelope wrappers. @@ -79,33 +84,31 @@ def _wrapAsRegisteredUnaryFunction(cls, regname, fnc, replace=False, **dbattrs): fnc(x, **dbattrs). Return an instance of the functor class. - ''' + """ + class RegisteredUnaryFunction(cls): def create(self): - '''Create new instance of the same type as self. - ''' + """Create new instance of the same type as self.""" return RegisteredUnaryFunction() def clone(self): - '''Return a new duplicate instance of self. - ''' + """Return a new duplicate instance of self.""" return copy.copy(self) def type(self): - '''Unique string identifier of this functor type. The string - is used for class registration and as an argument for the - createByType function. + """Unique string identifier of this functor type. The + string is used for class registration and as an argument for + the createByType function. Return string identifier. - ''' + """ return regname def __call__(self, x): - '''Evaluate this functor at x. - ''' + """Evaluate this functor at x.""" if dbattrs: - kw = {n : getattr(self, n) for n in dbattrs} + kw = {n: getattr(self, n) for n in dbattrs} rv = fnc(x, **kw) else: rv = fnc(x) @@ -126,7 +129,7 @@ def __reduce__(self): if replace: RegisteredUnaryFunction._deregisterType(regname) - RegisteredUnaryFunction.__name__ = 'User' + cls.__name__ + '_' + regname + RegisteredUnaryFunction.__name__ = "User" + cls.__name__ + "_" + regname RegisteredUnaryFunction.__getstate__ = _pickle_getstate RegisteredUnaryFunction.__setstate__ = _pickle_setstate RegisteredUnaryFunction()._registerThisType() @@ -134,21 +137,27 @@ def __reduce__(self): assert type(rv) is RegisteredUnaryFunction return rv + # pickling support functions + def _regunary_create(cls, tp): return cls.createByType(tp) + def _pickle_getstate(self): state = (self.__dict__,) return state + def _pickle_setstate(self, state): if len(state) != 1: - emsg = ("expected 1-item tuple in call to __setstate__, got " + - repr(state)) + emsg = "expected 1-item tuple in call to __setstate__, got " + repr( + state + ) raise ValueError(emsg) self.__dict__.update(state[0]) return + # End of file diff --git a/src/extensions/SConscript b/src/extensions/SConscript deleted file mode 100644 index 5f96f611..00000000 --- a/src/extensions/SConscript +++ /dev/null @@ -1,69 +0,0 @@ -import sys -import os - -Import('env', 'GlobSources', 'pyoutput') - -# make sure numpy headers are available -npdirs = pyoutput( - 'from numpy.distutils.misc_util import get_numpy_include_dirs\n' - 'print("\\n".join(get_numpy_include_dirs()))') -npdirs = [d.strip() for d in npdirs.split('\n')] -env.AppendUnique(CPPPATH=npdirs) - -# configure the boost_python library, which may have different extensions -if not (GetOption('clean') or env.GetOption('help')): - SConscript('SConscript.configure') - -# python extension module -module = env.SharedLibrary('srreal_ext', GlobSources('*.cpp'), - SHLIBPREFIX='', SHLIBSUFFIX='.so') -Alias('module', module) - -# update egg info when package version changes. -basedir = Dir('#').abspath -version = pyoutput( - 'import sys\n' - 'sys.path.insert(0, %r)\n' - 'from setup import versiondata\n' - 'print(versiondata.get("DEFAULT", "version"))\n' % basedir) -egginfo = env.Command(NoCache('#/src/diffpy.srreal.egg-info/PKG-INFO'), - env.Value(version), - '$python -Wignore setup.py egg_info') - -# install extension module in a development mode. -develop = Alias('develop', [egginfo, Install('#/src/diffpy/srreal', module)]) - -test = env.Alias('test', develop, - '$python -m diffpy.srreal.tests.run') -AlwaysBuild(test) - -def resolve_distutils_target(target, source, env): - tgt = pyoutput('\n'.join([ - "from setuptools import Distribution, Extension", - "ext = Extension('diffpy.srreal.srreal_ext', [])", - "attrs = dict(ext_modules=[ext])", - "dist = Distribution(attrs)", - "bcmd = dist.get_command_obj('build_ext')", - "bcmd.finalize_options()", - "print(bcmd.get_ext_fullpath(ext.name))", - ])) - env['distsofile'] = env.File(tgt) - return 0 - -c = '$python setup.py easy_install --no-deps {} .' -p = '--prefix=$prefix' if 'prefix' in env else '' -cmd_install = c.format(p) - -install = env.Alias('install', module, [ - resolve_distutils_target, - Mkdir('$distsofile.dir'), - Copy('$distsofile', '$SOURCE'), - Touch('$distsofile'), - cmd_install, - ]) -AlwaysBuild(install) - -# default targets: -Default(module) - -# vim: ft=python diff --git a/src/extensions/SConscript.configure b/src/extensions/SConscript.configure deleted file mode 100644 index 95851774..00000000 --- a/src/extensions/SConscript.configure +++ /dev/null @@ -1,73 +0,0 @@ -Import('env', 'pyconfigvar', 'pyversion') - -# Helper functions ----------------------------------------------------------- - -def CheckOptimizerFlag(context, flag): - ccflags_save = context.env['CCFLAGS'] - context.Message('Checking if compiler allows {!r}... '.format(flag)) - context.env.Replace(CCFLAGS=[flag]) - result = context.TryCompile('int a;\n', '.cpp') - context.Result(result) - if not result: - ccflags_save.remove(flag) - context.env.Replace(CCFLAGS=ccflags_save) - return result - - -def configure_boost_library(libname): - '''Add a boost library to the configured environment allowing for any - of the boostmttags name extensions. - - libname -- boost library name without any extension - - Note: CheckLib function automatically adds library to the environment. - ''' - mttags = ['', '-mt'] - boostlibtags = mttags - # check more tags for boost_python - if libname == 'boost_python': - major, minor = pyversion.split('.') - pytags = [major + minor, major, ''] - boostlibtags = [(pt + mt) for mt in mttags for pt in pytags] - # using global conf defined below - for t in boostlibtags: - libnamefull = libname + t - if conf.CheckLib(libnamefull, language='C++'): - return - # library not found here - print('This program requires %r library.' % libname) - Exit(1) - -# Start configuration -------------------------------------------------------- - -# Anaconda Python is compiled with super fancy gcc optimizer flags. -# Remove any flags that are not supported by the current compiler. - -custom_tests = {'CheckOptimizerFlag' : CheckOptimizerFlag} -conf = Configure(env, custom_tests=custom_tests) -optflags = [o for o in env['CCFLAGS'] - if o[:2] in ('-f', '-m')] -for o in optflags: - conf.CheckOptimizerFlag(o) -conf.Finish() - -# Create configuration environment that links with Python shared_library, so -# that the boost_python check does not fail due to unresolved Python symbols. -ecfg = env.Clone() -ecfg.Append(LIBS=[]) -ecfg.MergeFlags(pyconfigvar('BLDLIBRARY')) -# make sure there are no implicit dependency nodes in added LIBS -ecfg.Replace(LIBS=[str(n) for n in ecfg['LIBS']]) -newlibsindex = len(ecfg['LIBS']) - -conf = Configure(ecfg) -if not conf.CheckLib('diffpy', language='C++'): - print("This program requires 'libdiffpy' library.") - Exit(1) -configure_boost_library('boost_python') -conf.Finish() - -# Use libraries that were found in the configuration. -env.AppendUnique(LIBS=ecfg['LIBS'][newlibsindex:]) - -# vim: ft=python diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..3e0ed938 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,77 @@ +import json +import logging +from pathlib import Path + +import pytest + +from diffpy.srreal.structureconverters import convertObjCrystCrystal + + +@pytest.fixture +def user_filesystem(tmp_path): + base_dir = Path(tmp_path) + home_dir = base_dir / "home_dir" + home_dir.mkdir(parents=True, exist_ok=True) + cwd_dir = base_dir / "cwd_dir" + cwd_dir.mkdir(parents=True, exist_ok=True) + + home_config_data = {"username": "home_username", "email": "home@email.com"} + with open(home_dir / "diffpyconfig.json", "w") as f: + json.dump(home_config_data, f) + + yield tmp_path + + +# Resolve availability of optional packages. + +# pyobjcryst + + +@pytest.fixture(scope="session") +def _msg_nopyobjcryst(): + return "No module named 'pyobjcryst'" + + +@pytest.fixture(scope="session") +def has_pyobjcryst(): + try: + import pyobjcryst.crystal + + convertObjCrystCrystal(pyobjcryst.crystal.Crystal()) + has_pyobjcryst = True + except ImportError: + has_pyobjcryst = False + logging.warning("Cannot import pyobjcryst, pyobjcryst tests skipped.") + print("Cannot import pyobjcryst, pyobjcryst tests skipped.") + except TypeError: + has_pyobjcryst = False + logging.warning("Compiled without ObjCryst, pyobjcryst tests skipped.") + print("Compiled without ObjCryst, pyobjcryst tests skipped.") + + return has_pyobjcryst + + +# periodictable + + +@pytest.fixture(scope="session") +def _msg_noperiodictable(): + return "No module named 'periodictable'" + + +@pytest.fixture(scope="session") +def has_periodictable(): + try: + import periodictable + + has_periodictable = True + + # silence the pyflakes syntax checker + del periodictable + except ImportError: + has_periodictable = False + logging.warning( + "Cannot import periodictable, periodictable tests skipped." + ) + + return has_periodictable diff --git a/tests/test_atomradiitable.py b/tests/test_atomradiitable.py new file mode 100644 index 00000000..c69b6ed3 --- /dev/null +++ b/tests/test_atomradiitable.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python + +"""Unit tests for the AtomRadiiTable class.""" + + +import pickle +import unittest + +import pytest + +from diffpy.srreal.atomradiitable import ( + AtomRadiiTable, + ConstantRadiiTable, + CovalentRadiiTable, +) + +# ---------------------------------------------------------------------------- + + +class TestAtomRadiiTable(unittest.TestCase): + def setUp(self): + self.rtb = AtomRadiiTable() + self.ctb = ConstantRadiiTable() + return + + def tearDown(self): + return + + def test_pickling(self): + """Check pickling and unpickling of AtomRadiiTable.""" + ctb1 = pickle.loads(pickle.dumps(self.ctb)) + self.assertTrue(type(ctb1) is ConstantRadiiTable) + self.assertEqual({}, ctb1.getAllCustom()) + self.ctb.setCustom("Na", 1.3) + self.ctb.foobar = "foo" + self.ctb.setDefault(3.7) + ctb2 = pickle.loads(pickle.dumps(self.ctb)) + self.assertEqual({"Na": 1.3}, ctb2.getAllCustom()) + self.assertFalse(hasattr(ctb2, "foobar")) + self.assertEqual(3.7, ctb2.getDefault()) + return + + def test__standardLookup(self): + """Check AtomRadiiTable._standardLookup()""" + self.assertRaises(RuntimeError, self.rtb._standardLookup, "anything") + self.assertEqual(0.0, self.ctb._standardLookup("anything")) + self.ctb.setDefault(7.3) + self.assertEqual(7.3, self.ctb._standardLookup("anything")) + return + + def test_fromString(self): + """Check AtomRadiiTable.fromString()""" + self.rtb.fromString("H:0.33, B:0.42") + self.assertEqual({"H": 0.33, "B": 0.42}, self.rtb.getAllCustom()) + self.assertRaises(ValueError, self.rtb.fromString, "C:2.3, U:asdf") + self.assertEqual({"H": 0.33, "B": 0.42}, self.rtb.getAllCustom()) + self.rtb.fromString("C:2.3,,,") + self.assertEqual(3, len(self.rtb.getAllCustom())) + self.assertEqual(2.3, self.rtb.lookup("C")) + self.rtb.fromString("H:3.3") + self.assertEqual(3, len(self.rtb.getAllCustom())) + self.assertEqual(3.3, self.rtb.lookup("H")) + return + + def test_getAllCustom(self): + """Check AtomRadiiTable.getAllCustom()""" + self.assertEqual({}, self.rtb.getAllCustom()) + return + + def test_lookup(self): + """Check AtomRadiiTable.lookup()""" + self.assertRaises(RuntimeError, self.rtb.lookup, "C") + self.assertEqual(0.0, self.ctb.lookup("C")) + self.rtb.setCustom("C", 1.23) + self.assertEqual(1.23, self.rtb.lookup("C")) + return + + def test_resetCustom(self): + """Check AtomRadiiTable.resetCustom()""" + self.rtb.setCustom("C", 1.23) + self.assertTrue(self.rtb.getAllCustom()) + self.rtb.resetAll() + self.assertFalse(self.rtb.getAllCustom()) + return + + def test_setCustom(self): + """Check AtomRadiiTable.setCustom()""" + self.rtb.setCustom("C", 1.23) + self.assertEqual(1.23, self.rtb.lookup("C")) + self.rtb.setCustom("C", 3.3) + self.assertEqual(3.3, self.rtb.lookup("C")) + return + + def test_toString(self): + """Check AtomRadiiTable.toString()""" + rtb = self.rtb + self.assertEqual("", rtb.toString()) + self.assertEqual("", rtb.toString("; ")) + rtb.fromString("C : 1.5, B:2.0") + self.assertEqual("B:2,C:1.5", rtb.toString()) + self.assertEqual("B:2; C:1.5", rtb.toString("; ")) + return + + +# End of class TestAtomRadiiTable + +# ---------------------------------------------------------------------------- + + +class TestCovalentRadiiTable(unittest.TestCase): + + @pytest.fixture(autouse=True) + def _check_periodictable(self, has_periodictable, _msg_noperiodictable): + if not has_periodictable: + pytest.skip(_msg_noperiodictable) + + def setUp(self): + self.rtb = CovalentRadiiTable() + return + + def tearDown(self): + return + + def test_pickling(self): + """Check pickling and unpickling of CovalentRadiiTable.""" + rtb1 = pickle.loads(pickle.dumps(self.rtb)) + self.assertTrue(type(rtb1) is CovalentRadiiTable) + self.assertEqual({}, rtb1.getAllCustom()) + self.rtb.setCustom("Na", 1.3) + self.rtb.foobar = "foo" + rtb2 = pickle.loads(pickle.dumps(self.rtb)) + self.assertEqual({"Na": 1.3}, rtb2.getAllCustom()) + self.assertEqual("foo", rtb2.foobar) + return + + def test__standardLookup(self): + """Check CovalentRadiiTable._standardLookup()""" + self.assertEqual(1.22, self.rtb._standardLookup("Ga")) + return + + def test_create(self): + """Check CovalentRadiiTable.create()""" + self.rtb.setCustom("Na", 1.3) + rtb2 = self.rtb.create() + self.assertTrue(isinstance(rtb2, CovalentRadiiTable)) + self.assertEqual(1, len(self.rtb.getAllCustom())) + self.assertEqual(0, len(rtb2.getAllCustom())) + return + + def test_clone(self): + """Check CovalentRadiiTable.clone()""" + self.rtb.setCustom("Na", 1.3) + rtb2 = self.rtb.clone() + self.assertTrue(isinstance(rtb2, CovalentRadiiTable)) + self.assertEqual(1, len(rtb2.getAllCustom())) + self.assertEqual(1.3, rtb2.lookup("Na")) + return + + def test_fromString(self): + """Check CovalentRadiiTable.fromString()""" + self.rtb.fromString("Ga:2.22") + self.assertEqual(2.22, self.rtb.lookup("Ga")) + return + + def test_getAllCustom(self): + """Check CovalentRadiiTable.getAllCustom()""" + self.assertEqual({}, self.rtb.getAllCustom()) + return + + def test_lookup(self): + """Check CovalentRadiiTable.lookup()""" + self.assertEqual(1.22, self.rtb.lookup("Ga")) + self.rtb.fromString("Ga:2.22") + self.assertEqual(2.22, self.rtb.lookup("Ga")) + return + + def test_resetCustom(self): + """Check CovalentRadiiTable.resetCustom()""" + self.rtb.fromString("B:2.33, Ga:2.22") + self.rtb.resetCustom("B") + self.rtb.resetCustom("nada") + self.assertEqual(1, len(self.rtb.getAllCustom())) + self.assertEqual(0.84, self.rtb.lookup("B")) + self.assertEqual(2.22, self.rtb.lookup("Ga")) + return + + def test_setCustom(self): + """Check CovalentRadiiTable.setCustom()""" + self.assertEqual(0.84, self.rtb.lookup("B")) + self.rtb.setCustom("B", 0.9) + self.assertEqual(0.9, self.rtb.lookup("B")) + return + + def test_toString(self): + """Check CovalentRadiiTable.toString()""" + self.assertEqual("", self.rtb.toString(";---")) + return + + +# End of class TestCovalentRadiiTable + +# ---------------------------------------------------------------------------- + +if __name__ == "__main__": + unittest.main() + +# End of file diff --git a/src/diffpy/srreal/tests/testattributes.py b/tests/test_attributes.py similarity index 52% rename from src/diffpy/srreal/tests/testattributes.py rename to tests/test_attributes.py index 7c7e2f0d..573ccf8d 100644 --- a/src/diffpy/srreal/tests/testattributes.py +++ b/tests/test_attributes.py @@ -4,28 +4,26 @@ """ +import gc import unittest import weakref -import gc from diffpy.srreal.attributes import Attributes from diffpy.srreal.pairquantity import PairQuantity from diffpy.srreal.pdfcalculator import PDFCalculator + ############################################################################## class TestAttributes(unittest.TestCase): def setUp(self): return - def tearDown(self): return - def test___setattr__(self): - """check Attributes.__setattr__() - """ + """Check Attributes.__setattr__()""" # normal attribute a = Attributes() a.x = 45 @@ -40,123 +38,112 @@ def test___setattr__(self): self.assertEqual(27, a._getDoubleAttr("x")) return - def test___getattr__(self): - """check Attributes.__getattr__() - """ + """Check Attributes.__getattr__()""" a = Attributes() - self.assertRaises(AttributeError, getattr, a, 'invalid') + self.assertRaises(AttributeError, getattr, a, "invalid") a.x = 11 self.assertEqual(11, a.x) pdfc = PDFCalculator() - pdfc._setDoubleAttr('rmax', 12.34) + pdfc._setDoubleAttr("rmax", 12.34) self.assertEqual(12.34, pdfc.rmax) return - def test_garbage_collection(self): - """check garbage collection for Python defined Attributes - """ + """Check garbage collection for Python defined Attributes.""" # check if attributes are garbage collected pq = PairQuantity() wpq = weakref.ref(pq) self.assertFalse(wpq() is None) - pq._registerDoubleAttribute('foo') + pq._registerDoubleAttribute("foo") pq.foo = 45 - self.assertEqual(45, pq._getDoubleAttr('foo')) + self.assertEqual(45, pq._getDoubleAttr("foo")) del pq self.assertTrue(wpq() is None) return - def test__getDoubleAttr(self): - """check Attributes._getDoubleAttr() - """ + """Check Attributes._getDoubleAttr()""" pdfc = PDFCalculator() pdfc.foo = 11 - self.assertRaises(AttributeError, pdfc._getDoubleAttr, 'foo') - pdfc._registerDoubleAttribute('foo') - self.assertEqual(11, pdfc._getDoubleAttr('foo')) + self.assertRaises(AttributeError, pdfc._getDoubleAttr, "foo") + pdfc._registerDoubleAttribute("foo") + self.assertEqual(11, pdfc._getDoubleAttr("foo")) pdfc.rmax = 22 - self.assertEqual(22, pdfc._getDoubleAttr('rmax')) - setattr(pdfc, 'rmax', 23) - self.assertEqual(23, pdfc._getDoubleAttr('rmax')) - self.assertRaises(Exception, setattr, pdfc, 'rmax', 'xxx') + self.assertEqual(22, pdfc._getDoubleAttr("rmax")) + setattr(pdfc, "rmax", 23) + self.assertEqual(23, pdfc._getDoubleAttr("rmax")) + self.assertRaises(Exception, setattr, pdfc, "rmax", "xxx") return - def test__hasDoubleAttr(self): - """check Attributes._hasDoubleAttr() - """ + """Check Attributes._hasDoubleAttr()""" a = Attributes() a.foo = 45 - self.assertFalse(a._hasDoubleAttr('foo')) - a._registerDoubleAttribute('foo') - self.assertTrue(a._hasDoubleAttr('foo')) + self.assertFalse(a._hasDoubleAttr("foo")) + a._registerDoubleAttribute("foo") + self.assertTrue(a._hasDoubleAttr("foo")) return - def test__namesOfDoubleAttributes(self): - """check Attributes._namesOfDoubleAttributes() - """ + """Check Attributes._namesOfDoubleAttributes()""" a = Attributes() self.assertEqual(0, len(a._namesOfDoubleAttributes())) pq = PairQuantity() self.assertNotEqual(0, len(pq._namesOfDoubleAttributes())) - self.assertFalse('bar' in pq._namesOfDoubleAttributes()) - pq._registerDoubleAttribute('bar') - self.assertTrue('bar' in pq._namesOfDoubleAttributes()) + self.assertFalse("bar" in pq._namesOfDoubleAttributes()) + pq._registerDoubleAttribute("bar") + self.assertTrue("bar" in pq._namesOfDoubleAttributes()) return - def test__namesOfWritableDoubleAttributes(self): - """check Attributes._namesOfDoubleAttributes() - """ + """Check Attributes._namesOfDoubleAttributes()""" a = Attributes() self.assertEqual(0, len(a._namesOfDoubleAttributes())) - a._registerDoubleAttribute('bar', lambda obj: 13) - self.assertEqual(13, a._getDoubleAttr('bar')) + a._registerDoubleAttribute("bar", lambda obj: 13) + self.assertEqual(13, a._getDoubleAttr("bar")) self.assertEqual(13, a.bar) self.assertEqual(1, len(a._namesOfDoubleAttributes())) self.assertEqual(0, len(a._namesOfWritableDoubleAttributes())) pdfc = PDFCalculator() - self.assertTrue('extendedrmin' in pdfc._namesOfDoubleAttributes()) - self.assertTrue('extendedrmax' in pdfc._namesOfDoubleAttributes()) + self.assertTrue("extendedrmin" in pdfc._namesOfDoubleAttributes()) + self.assertTrue("extendedrmax" in pdfc._namesOfDoubleAttributes()) nwda = pdfc._namesOfWritableDoubleAttributes() - self.assertFalse('extendedrmin' in nwda) - self.assertFalse('extendedrmax' in nwda) + self.assertFalse("extendedrmin" in nwda) + self.assertFalse("extendedrmax" in nwda) return - def test__registerDoubleAttribute(self): - """check Attributes._registerDoubleAttribute() - """ - d = {'g_called' : False, 's_called' : False, 'value' : 0} + """Check Attributes._registerDoubleAttribute()""" + d = {"g_called": False, "s_called": False, "value": 0} + def g(obj): - d['g_called'] = True - return d['value'] + d["g_called"] = True + return d["value"] + def s(obj, value): - d['s_called'] = True - d['value'] = value + d["s_called"] = True + d["value"] = value return + a = Attributes() wa = weakref.ref(a) - a._registerDoubleAttribute('a1', g, s) - self.assertFalse('a1' in a.__dict__) - self.assertFalse(d['g_called']) - self.assertFalse(d['s_called']) + a._registerDoubleAttribute("a1", g, s) + self.assertFalse("a1" in a.__dict__) + self.assertFalse(d["g_called"]) + self.assertFalse(d["s_called"]) self.assertEqual(0, a.a1) - self.assertTrue(d['g_called']) - self.assertFalse(d['s_called']) + self.assertTrue(d["g_called"]) + self.assertFalse(d["s_called"]) a.a1 = 47 - self.assertTrue(d['s_called']) - self.assertEqual(47, d['value']) - self.assertTrue(hasattr(a, 'a1')) - a._registerDoubleAttribute('a1readonly', g) + self.assertTrue(d["s_called"]) + self.assertEqual(47, d["value"]) + self.assertTrue(hasattr(a, "a1")) + a._registerDoubleAttribute("a1readonly", g) self.assertEqual(47, a.a1readonly) - self.assertTrue(hasattr(a, 'a1readonly')) - self.assertRaises(AttributeError, a._setDoubleAttr, 'a1readonly', 7) - self.assertRaises(AttributeError, setattr, a, 'a1readonly', 5) + self.assertTrue(hasattr(a, "a1readonly")) + self.assertRaises(AttributeError, a._setDoubleAttr, "a1readonly", 7) + self.assertRaises(AttributeError, setattr, a, "a1readonly", 5) self.assertEqual(47, a.a1readonly) a.a1 = 9 self.assertEqual(9, a.a1readonly) @@ -166,19 +153,18 @@ def s(obj, value): self.assertTrue(wa() is None) return - def test__setDoubleAttr(self): - """check Attributes._setDoubleAttr() - """ + """Check Attributes._setDoubleAttr()""" pdfc = PDFCalculator() - pdfc._setDoubleAttr('scale', 1.23) - self.assertFalse('scale' in pdfc.__dict__) + pdfc._setDoubleAttr("scale", 1.23) + self.assertFalse("scale" in pdfc.__dict__) self.assertEqual(1.23, pdfc.scale) return + # End of class TestAttributes -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testbondcalculator.py b/tests/test_bondcalculator.py similarity index 74% rename from src/diffpy/srreal/tests/testbondcalculator.py rename to tests/test_bondcalculator.py index fba47a37..4b280397 100644 --- a/src/diffpy/srreal/tests/testbondcalculator.py +++ b/tests/test_bondcalculator.py @@ -1,41 +1,41 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.bondcalculator -""" +"""Unit tests for diffpy.srreal.bondcalculator.""" -import unittest import pickle +import unittest + import numpy +import pytest +from testutils import ( + loadDiffPyStructure, + loadObjCrystCrystal, + pickle_with_attr, +) -from diffpy.srreal.tests.testutils import has_pyobjcryst, _msg_nopyobjcryst -from diffpy.srreal.tests.testutils import loadDiffPyStructure -from diffpy.srreal.tests.testutils import loadObjCrystCrystal -from diffpy.srreal.tests.testutils import pickle_with_attr from diffpy.srreal.bondcalculator import BondCalculator # ---------------------------------------------------------------------------- + class TestBondCalculator(unittest.TestCase): def setUp(self): self.bdc = BondCalculator() - if not hasattr(self, 'rutile'): - type(self).rutile = loadDiffPyStructure('rutile.cif') - if not hasattr(self, 'nickel'): - type(self).nickel = loadDiffPyStructure('Ni.stru') - if not hasattr(self, 'niprim'): - type(self).niprim = loadDiffPyStructure('Ni_primitive.stru') + if not hasattr(self, "rutile"): + type(self).rutile = loadDiffPyStructure("rutile.cif") + if not hasattr(self, "nickel"): + type(self).nickel = loadDiffPyStructure("Ni.stru") + if not hasattr(self, "niprim"): + type(self).niprim = loadDiffPyStructure("Ni_primitive.stru") return - def tearDown(self): return - def test___init__(self): - """check BondCalculator.__init__() - """ + """Check BondCalculator.__init__()""" self.assertEqual(0, self.bdc.rmin) self.assertEqual(5, self.bdc.rmax) self.assertEqual(0, len(self.bdc.distances)) @@ -45,10 +45,8 @@ def test___init__(self): self.assertRaises(TypeError, BondCalculator, invalid=55) return - def test___call__(self): - """check BondCalculator.__call__() - """ + """Check BondCalculator.__call__()""" bdc = self.bdc bdc.rmax = 0 self.assertEqual(0, len(bdc(self.rutile))) @@ -62,10 +60,8 @@ def test___call__(self): self.assertEqual(2, bdc.rmax) return - def test_pickling(self): - '''check pickling and unpickling of BondCalculator. - ''' + """Check pickling and unpickling of BondCalculator.""" bdc = self.bdc bdc.rmin = 0.1 bdc.rmax = 12.3 @@ -79,14 +75,14 @@ def test_pickling(self): self.assertFalse(bdc1.getPairMask(1, 2)) self.assertTrue(bdc1.getPairMask(0, 0)) self.assertTrue(numpy.array_equal(bdc.distances, bdc1.distances)) - self.assertRaises(RuntimeError, pickle_with_attr, bdc, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, bdc, foo="bar") return - def test_pickling_derived_structure(self): - '''check pickling of BondCalculator with DerivedStructureAdapter. - ''' - from diffpy.srreal.tests.testutils import DerivedStructureAdapter + """Check pickling of BondCalculator with + DerivedStructureAdapter.""" + from testutils import DerivedStructureAdapter + bdc = self.bdc stru0 = DerivedStructureAdapter() bdc.setStructure(stru0) @@ -100,20 +96,17 @@ def test_pickling_derived_structure(self): self.assertEqual(1, stru1.cpqcount) return - def test_distances(self): - """check BondCalculator.distances - """ + """Check BondCalculator.distances.""" self.bdc.eval(self.nickel) dst = self.bdc.distances - self.assertTrue(numpy.array_equal(dst, - BondCalculator()(self.nickel))) + self.assertTrue(numpy.array_equal(dst, BondCalculator()(self.nickel))) self.assertTrue(numpy.array_equal(dst, numpy.sort(dst))) self.bdc.maskAllPairs(False) for i in range(4): self.bdc.setPairMask(0, i, True) dst0a = self.bdc(self.nickel) - idx0a = (self.bdc.sites0 == 0) + idx0a = self.bdc.sites0 == 0 self.bdc.maskAllPairs(False) for i in range(4): self.bdc.setPairMask(3, i, True) @@ -124,20 +117,16 @@ def test_distances(self): self.assertTrue(numpy.allclose(dst0a[idx0a], dstp)) return - def test_directions(self): - """check BondCalculator.directions - """ + """Check BondCalculator.directions.""" dst = self.bdc(self.rutile) drs = self.bdc.directions nms = numpy.sqrt(numpy.sum(numpy.power(drs, 2), axis=1)) self.assertTrue(numpy.allclose(dst, nms)) return - def test_sites(self): - """check BondCalculator.sites - """ + """Check BondCalculator.sites.""" bdc = self.bdc dst = bdc(self.rutile) self.assertEqual(len(dst), len(bdc.sites0)) @@ -146,8 +135,10 @@ def test_sites(self): self.assertEqual(5, numpy.max(bdc.sites0)) self.assertEqual(0, numpy.min(bdc.sites1)) self.assertEqual(5, numpy.max(bdc.sites1)) - dij = [(tuple(d) + (i0, i1)) for d, i0, i1 in zip( - bdc.directions, bdc.sites0, bdc.sites1)] + dij = [ + (tuple(d) + (i0, i1)) + for d, i0, i1 in zip(bdc.directions, bdc.sites0, bdc.sites1) + ] self.assertEqual(len(dij), len(set(dij))) bdc.maskAllPairs(False) bdc(self.rutile) @@ -158,16 +149,14 @@ def test_sites(self): self.assertEqual(set([3]), set(bdc.sites0).union(bdc.sites1)) return - def test_types(self): - """check BondCalculator.types - """ + """Check BondCalculator.types.""" bdc = self.bdc dst = bdc(self.rutile) self.assertEqual(len(dst), len(bdc.types0)) self.assertEqual(len(dst), len(bdc.types1)) - self.assertEqual(set(('Ti', 'O')), set(bdc.types0)) - self.assertEqual(set(('Ti', 'O')), set(bdc.types1)) + self.assertEqual(set(("Ti", "O")), set(bdc.types0)) + self.assertEqual(set(("Ti", "O")), set(bdc.types1)) self.assertFalse((bdc.types0 == bdc.types1).all()) bdc.maskAllPairs(False) bdc(self.rutile) @@ -176,13 +165,11 @@ def test_types(self): bdc.setPairMask(3, 3, True) bdc(self.rutile) self.assertTrue(len(bdc.types0)) - self.assertEqual(set(['O']), set(bdc.types0).union(set(bdc.types1))) + self.assertEqual(set(["O"]), set(bdc.types0).union(set(bdc.types1))) return - def test_filterCone(self): - """check BondCalculator.filterCone() - """ + """Check BondCalculator.filterCone()""" bdc = self.bdc bdc.rmax = 2.5 self.assertEqual(12, len(bdc(self.niprim))) @@ -199,10 +186,8 @@ def test_filterCone(self): self.assertEqual(12, len(bdc(self.niprim))) return - def test_filterOff(self): - """check BondCalculator.filterOff() - """ + """Check BondCalculator.filterOff()""" bdc = self.bdc bdc.rmax = 2.5 bdc.filterCone([1, 2, 3], -1) @@ -211,10 +196,8 @@ def test_filterOff(self): self.assertEqual(12, len(bdc(self.niprim))) return - def test_setPairMask(self): - '''check different setPairMask arguments. - ''' + """Check different setPairMask arguments.""" bdc = self.bdc dall = bdc(self.nickel) bdc.maskAllPairs(False) @@ -230,39 +213,37 @@ def test_setPairMask(self): dst0c = bdc(self.nickel) self.assertTrue(numpy.array_equal(dst0a, dst0c)) bdc.maskAllPairs(False) - bdc.setPairMask(0, 'all', True) + bdc.setPairMask(0, "all", True) dst0d = bdc(self.nickel) self.assertTrue(numpy.array_equal(dst0a, dst0d)) - bdc.setPairMask('all', 'all', False) + bdc.setPairMask("all", "all", False) self.assertEqual(0, len(bdc(self.nickel))) - bdc.setPairMask('all', range(4), True) + bdc.setPairMask("all", range(4), True) dall2 = bdc(self.nickel) self.assertTrue(numpy.array_equal(dall, dall2)) - self.assertRaises(ValueError, bdc.setPairMask, 'fooo', 2, True) - self.assertRaises(ValueError, bdc.setPairMask, 'aLL', 2, True) + self.assertRaises(ValueError, bdc.setPairMask, "fooo", 2, True) + self.assertRaises(ValueError, bdc.setPairMask, "aLL", 2, True) return - def test_setTypeMask(self): - '''check different setTypeMask arguments. - ''' + """Check different setTypeMask arguments.""" bdc = self.bdc dall = bdc(self.rutile) - bdc.setTypeMask('all', 'All', False) + bdc.setTypeMask("all", "All", False) self.assertTrue(numpy.array_equal(dall, bdc(self.rutile))) - bdc.setTypeMask('all', 'ALL', False) + bdc.setTypeMask("all", "ALL", False) self.assertEqual(0, len(bdc(self.rutile))) bdc.maskAllPairs(True) - bdc.setTypeMask('Ti', ['O'], True, others=False) + bdc.setTypeMask("Ti", ["O"], True, others=False) bdc() tps = set(zip(bdc.types0, bdc.types1)) self.assertEqual(2, len(tps)) - self.assertTrue(('Ti', 'O') in tps) - self.assertTrue(('O', 'Ti') in tps) - bdc.setTypeMask(['Ti'], self.rutile.element, 0, others=1) + self.assertTrue(("Ti", "O") in tps) + self.assertTrue(("O", "Ti") in tps) + bdc.setTypeMask(["Ti"], self.rutile.element, 0, others=1) bdc() tps = set(zip(bdc.types0, bdc.types1)) - self.assertEqual(set([('O', 'O')]), tps) + self.assertEqual(set([("O", "O")]), tps) return @@ -270,25 +251,27 @@ def test_setTypeMask(self): # ---------------------------------------------------------------------------- -@unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) + class TestBondCalculatorObjCryst(unittest.TestCase): + @pytest.fixture(autouse=True) + def _check_periodictable(self, has_pyobjcryst, _msg_nopyobjcryst): + if not has_pyobjcryst: + pytest.skip(_msg_nopyobjcryst) + def setUp(self): self.bdc = BondCalculator() - if not hasattr(self, 'rutile'): - type(self).rutile = loadObjCrystCrystal('rutile.cif') - if not hasattr(self, 'nickel'): - type(self).nickel = loadObjCrystCrystal('Ni.cif') + if not hasattr(self, "rutile"): + type(self).rutile = loadObjCrystCrystal("rutile.cif") + if not hasattr(self, "nickel"): + type(self).nickel = loadObjCrystCrystal("Ni.cif") return - def tearDown(self): return - def test___call__(self): - """check BondCalculator.__call__() - """ + """Check BondCalculator.__call__()""" bdc = self.bdc bdc.rmax = 0 self.assertEqual(0, len(bdc(self.rutile).tolist())) @@ -298,10 +281,8 @@ def test___call__(self): self.assertEqual(12, len(bdc(self.nickel))) return - def test_sites(self): - """check BondCalculator.sites - """ + """Check BondCalculator.sites.""" bdc = self.bdc dst = bdc(self.rutile) self.assertEqual(len(dst), len(bdc.sites0)) @@ -310,8 +291,10 @@ def test_sites(self): self.assertEqual(1, numpy.max(bdc.sites0)) self.assertEqual(0, numpy.min(bdc.sites1)) self.assertEqual(1, numpy.max(bdc.sites1)) - dij = [(tuple(d) + (i0, i1)) for d, i0, i1 in zip( - bdc.directions, bdc.sites0, bdc.sites1)] + dij = [ + (tuple(d) + (i0, i1)) + for d, i0, i1 in zip(bdc.directions, bdc.sites0, bdc.sites1) + ] self.assertEqual(len(dij), len(set(dij))) bdc.maskAllPairs(False) bdc(self.rutile) @@ -322,16 +305,14 @@ def test_sites(self): self.assertEqual(set([1]), set(bdc.sites0).union(bdc.sites1)) return - def test_types(self): - """check BondCalculator.types - """ + """Check BondCalculator.types.""" bdc = self.bdc dst = bdc(self.rutile) self.assertEqual(len(dst), len(bdc.types0)) self.assertEqual(len(dst), len(bdc.types1)) - self.assertEqual(set(('Ti', 'O')), set(bdc.types0)) - self.assertEqual(set(('Ti', 'O')), set(bdc.types1)) + self.assertEqual(set(("Ti", "O")), set(bdc.types0)) + self.assertEqual(set(("Ti", "O")), set(bdc.types1)) self.assertNotEqual(bdc.types0.tolist(), bdc.types1.tolist()) bdc.maskAllPairs(False) bdc(self.rutile) @@ -340,13 +321,11 @@ def test_types(self): bdc.setPairMask(1, 1, True) bdc(self.rutile) self.assertTrue(len(bdc.types0)) - self.assertEqual(set(['O']), set(bdc.types0).union(set(bdc.types1))) + self.assertEqual(set(["O"]), set(bdc.types0).union(set(bdc.types1))) return - def test_filterCone(self): - """check BondCalculator.filterCone() - """ + """Check BondCalculator.filterCone()""" bdc = self.bdc bdc.rmax = 2.5 bdc.filterCone([+0.5, +0.5, 0], 1) @@ -362,10 +341,8 @@ def test_filterCone(self): self.assertEqual(12, len(bdc(self.nickel))) return - def test_filterOff(self): - """check BondCalculator.filterOff() - """ + """Check BondCalculator.filterOff()""" bdc = self.bdc bdc.rmax = 2.5 bdc.filterCone([1, 2, 3], -1) @@ -374,11 +351,12 @@ def test_filterOff(self): self.assertEqual(12, len(bdc(self.nickel))) return + # End of class TestBondCalculatorObjCryst # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testbvscalculator.py b/tests/test_bvscalculator.py similarity index 73% rename from src/diffpy/srreal/tests/testbvscalculator.py rename to tests/test_bvscalculator.py index 488c6348..f15172f9 100644 --- a/src/diffpy/srreal/tests/testbvscalculator.py +++ b/tests/test_bvscalculator.py @@ -1,45 +1,41 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.bvscalculator -""" +"""Unit tests for diffpy.srreal.bvscalculator.""" -import unittest import pickle +import unittest + +from testutils import loadDiffPyStructure, pickle_with_attr from diffpy.srreal.bvscalculator import BVSCalculator -from diffpy.srreal.tests.testutils import loadDiffPyStructure -from diffpy.srreal.tests.testutils import pickle_with_attr + ############################################################################## class TestBVSCalculator(unittest.TestCase): def setUp(self): self.bvc = BVSCalculator() - if not hasattr(self, 'rutile'): - type(self).rutile = loadDiffPyStructure('rutile.cif') + if not hasattr(self, "rutile"): + type(self).rutile = loadDiffPyStructure("rutile.cif") # rutile.cif does not have charge data, we need to add them here - iondict = {'Ti' : 'Ti4+', 'O' : 'O2-'} - for a in self.rutile: a.element = iondict[a.element] + iondict = {"Ti": "Ti4+", "O": "O2-"} + for a in self.rutile: + a.element = iondict[a.element] return - def tearDown(self): return - def test___init__(self): - """check BVSCalculator.__init__() - """ + """Check BVSCalculator.__init__()""" self.assertEqual(1e-5, self.bvc.valenceprecision) bvc1 = BVSCalculator(valenceprecision=1e-4) self.assertEqual(1e-4, bvc1.valenceprecision) return - def test___call__(self): - """check BVSCalculator.__call__() - """ + """Check BVSCalculator.__call__()""" vcalc = self.bvc(self.rutile) self.assertEqual(len(self.rutile), len(vcalc)) self.assertEqual(tuple(self.bvc.value), tuple(vcalc)) @@ -51,10 +47,8 @@ def test___call__(self): self.assertTrue(abs((vo - vc) / vo) < 0.1) return - def test_bvdiff(self): - """check BVSCalculator.bvdiff - """ + """Check BVSCalculator.bvdiff.""" self.bvc(self.rutile) self.assertEqual(6, len(self.bvc.bvdiff)) # rutile is overbonded @@ -62,25 +56,21 @@ def test_bvdiff(self): self.assertTrue(bvd < 0) return - def test_bvmsdiff(self): - """check BVSCalculator.bvmsdiff - """ + """Check BVSCalculator.bvmsdiff.""" self.assertEqual(0, self.bvc.bvmsdiff) self.bvc(self.rutile) self.assertAlmostEqual(0.0158969, self.bvc.bvmsdiff, 6) return - def test_bvrmsdiff(self): - """check BVSCalculator.bvrmsdiff - """ + """Check BVSCalculator.bvrmsdiff.""" from math import sqrt + self.assertEqual(0, self.bvc.bvrmsdiff) self.bvc(self.rutile) self.assertTrue(self.bvc.bvrmsdiff > 0) - self.assertAlmostEqual(sqrt(self.bvc.bvmsdiff), - self.bvc.bvrmsdiff, 12) + self.assertAlmostEqual(sqrt(self.bvc.bvmsdiff), self.bvc.bvrmsdiff, 12) bvrmsd0 = self.bvc.bvrmsdiff # check mixed occupancy rutilemix = self.rutile.copy() @@ -93,34 +83,25 @@ def test_bvrmsdiff(self): self.assertAlmostEqual(bvrmsd0, self.bvc.bvrmsdiff, 12) return - def test_eval(self): - """check BVSCalculator.eval() - """ + """Check BVSCalculator.eval()""" vcalc = self.bvc.eval(self.rutile) self.assertEqual(tuple(vcalc), tuple(self.bvc.value)) return - def test_valences(self): - """check BVSCalculator.valences - """ + """Check BVSCalculator.valences.""" self.bvc(self.rutile) - self.assertEqual((4, 4, -2, -2, -2, -2), - tuple(self.bvc.valences)) + self.assertEqual((4, 4, -2, -2, -2, -2), tuple(self.bvc.valences)) return - def test_value(self): - """check BVSCalculator.value - """ + """Check BVSCalculator.value.""" self.assertEqual(0, len(self.bvc.value)) return - def test_pickling(self): - '''check pickling and unpickling of BVSCalculator. - ''' + """Check pickling and unpickling of BVSCalculator.""" bvsc = BVSCalculator() bvsc.rmin = 0.1 bvsc.rmax = 12.3 @@ -130,13 +111,11 @@ def test_pickling(self): self.assertFalse(bvsc is bvsc1) for a in bvsc._namesOfDoubleAttributes(): self.assertEqual(getattr(bvsc, a), getattr(bvsc1, a)) - self.assertRaises(RuntimeError, pickle_with_attr, bvsc, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, bvsc, foo="bar") return - def test_mask_pickling(self): - '''Check if mask gets properly pickled and restored. - ''' + """Check if mask gets properly pickled and restored.""" self.bvc.maskAllPairs(False) self.bvc.setPairMask(0, 1, True) self.assertTrue(False is self.bvc.getPairMask(0, 0)) @@ -146,13 +125,11 @@ def test_mask_pickling(self): self.assertTrue(True is bvc1.getPairMask(0, 1)) return - def test_table_pickling(self): - '''Check if bvparamtable gets correctly pickled and restored. - ''' - self.bvc.bvparamtable.setCustom('A', 1, 'B', -2, 7, 8) + """Check if bvparamtable gets correctly pickled and restored.""" + self.bvc.bvparamtable.setCustom("A", 1, "B", -2, 7, 8) bvc1 = pickle.loads(pickle.dumps(self.bvc)) - bpab = bvc1.bvparamtable.lookup('A+', 'B2-') + bpab = bvc1.bvparamtable.lookup("A+", "B2-") self.assertEqual("A", bpab.atom0) self.assertEqual(1, bpab.valence0) self.assertEqual("B", bpab.atom1) @@ -161,11 +138,11 @@ def test_table_pickling(self): self.assertEqual(8, bpab.B) return - def test_pickling_derived_structure(self): - '''check pickling of BVSCalculator with DerivedStructureAdapter. - ''' - from diffpy.srreal.tests.testutils import DerivedStructureAdapter + """Check pickling of BVSCalculator with + DerivedStructureAdapter.""" + from testutils import DerivedStructureAdapter + bvc = self.bvc stru0 = DerivedStructureAdapter() bvc.setStructure(stru0) @@ -179,15 +156,13 @@ def test_pickling_derived_structure(self): self.assertEqual(1, stru1.cpqcount) return - def test_table_atom_valence(self): - '''check calculation with defined valences in bvparamtable - ''' + """Check calculation with defined valences in bvparamtable.""" bvc = self.bvc barerutile = self.rutile.copy() for a in barerutile: - a.element = a.element.rstrip('+-012345678') - self.assertEqual({"Ti" : 2, "O" : 4}, barerutile.composition) + a.element = a.element.rstrip("+-012345678") + self.assertEqual({"Ti": 2, "O": 4}, barerutile.composition) self.assertFalse(any(bvc(barerutile))) bptb = bvc.bvparamtable bptb.setAtomValence("Ti", +4) @@ -201,9 +176,10 @@ def test_table_atom_valence(self): self.assertFalse(any(bvc(barerutile))) return + # End of class TestBVSCalculator -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testdebyepdfcalculator.py b/tests/test_debyepdfcalculator.py similarity index 68% rename from src/diffpy/srreal/tests/testdebyepdfcalculator.py rename to tests/test_debyepdfcalculator.py index 78d8b0ef..ff160f88 100644 --- a/src/diffpy/srreal/tests/testdebyepdfcalculator.py +++ b/tests/test_debyepdfcalculator.py @@ -1,17 +1,16 @@ #!/usr/bin/env python -"""Unit tests for pdfcalculator.py -""" +"""Unit tests for pdfcalculator.py.""" -import unittest import pickle +import unittest + import numpy +from testutils import _maxNormDiff, loadDiffPyStructure, pickle_with_attr from diffpy.srreal.pdfcalculator import DebyePDFCalculator, PDFCalculator -from diffpy.srreal.tests.testutils import loadDiffPyStructure -from diffpy.srreal.tests.testutils import pickle_with_attr -from diffpy.srreal.tests.testpdfcalculator import _maxNormDiff + ############################################################################## class TestDebyePDFCalculator(unittest.TestCase): @@ -22,78 +21,71 @@ class TestDebyePDFCalculator(unittest.TestCase): def setUp(self): self.dpdfc = DebyePDFCalculator() if not TestDebyePDFCalculator.bucky: - TestDebyePDFCalculator.bucky = ( - loadDiffPyStructure('C60bucky.stru')) + TestDebyePDFCalculator.bucky = loadDiffPyStructure("C60bucky.stru") if not TestDebyePDFCalculator.tio2rutile: - TestDebyePDFCalculator.tio2rutile = ( - loadDiffPyStructure('TiO2_rutile-fit.stru')) + TestDebyePDFCalculator.tio2rutile = loadDiffPyStructure( + "TiO2_rutile-fit.stru" + ) return -# def tearDown(self): -# return -# -# def test___call__(self): -# """check DebyePDFCalculator.__call__() -# """ -# return -# -# def test___init__(self): -# """check DebyePDFCalculator.__init__() -# """ -# return + # def tearDown(self): + # return + # + # def test___call__(self): + # """check DebyePDFCalculator.__call__() + # """ + # return + # + # def test___init__(self): + # """check DebyePDFCalculator.__init__() + # """ + # return def test___getattr__(self): - """check DebyePDFCalculator.__getattr__() - """ + """Check DebyePDFCalculator.__getattr__()""" self.assertEqual(0.0, self.dpdfc.qmin) - self.dpdfc._setDoubleAttr('qmin', 1.23) + self.dpdfc._setDoubleAttr("qmin", 1.23) self.assertEqual(1.23, self.dpdfc.qmin) return def test___setattr__(self): - """check DebyePDFCalculator.__setattr__() - """ - self.assertNotEqual(1.23, self.dpdfc._getDoubleAttr('rmin')) + """Check DebyePDFCalculator.__setattr__()""" + self.assertNotEqual(1.23, self.dpdfc._getDoubleAttr("rmin")) self.dpdfc.rmin = 1.23 - self.assertEqual(1.23, self.dpdfc._getDoubleAttr('rmin')) + self.assertEqual(1.23, self.dpdfc._getDoubleAttr("rmin")) return def test__getDoubleAttr(self): - """check DebyePDFCalculator._getDoubleAttr() - """ + """Check DebyePDFCalculator._getDoubleAttr()""" gdba = self.dpdfc._getDoubleAttr - self.assertEqual(1.0, gdba('scale')) - self.assertEqual(0.0, gdba('qdamp')) - self.assertRaises(Exception, gdba, 'notanattribute') + self.assertEqual(1.0, gdba("scale")) + self.assertEqual(0.0, gdba("qdamp")) + self.assertRaises(Exception, gdba, "notanattribute") return def test__hasDoubleAttr(self): - """check DebyePDFCalculator._hasDoubleAttr() - """ - self.assertTrue(self.dpdfc._hasDoubleAttr('scale')) - self.assertFalse(self.dpdfc._hasDoubleAttr('notanattribute')) + """Check DebyePDFCalculator._hasDoubleAttr()""" + self.assertTrue(self.dpdfc._hasDoubleAttr("scale")) + self.assertFalse(self.dpdfc._hasDoubleAttr("notanattribute")) return def test__namesOfDoubleAttributes(self): - """check DebyePDFCalculator._namesOfDoubleAttributes() - """ + """Check DebyePDFCalculator._namesOfDoubleAttributes()""" self.assertTrue(type(self.dpdfc._namesOfDoubleAttributes()) is set) - self.assertTrue('qmax' in self.dpdfc._namesOfDoubleAttributes()) + self.assertTrue("qmax" in self.dpdfc._namesOfDoubleAttributes()) return def test__setDoubleAttr(self): - """check DebyePDFCalculator._setDoubleAttr() - """ + """Check DebyePDFCalculator._setDoubleAttr()""" gdba = self.dpdfc._getDoubleAttr sdba = self.dpdfc._setDoubleAttr - self.assertEqual(0.0, gdba('rmin')) - sdba('rmin', 3.0) - self.assertEqual(3.0, gdba('rmin')) + self.assertEqual(0.0, gdba("rmin")) + sdba("rmin", 3.0) + self.assertEqual(3.0, gdba("rmin")) return def test_PDF_C60bucky(self): - """check DebyePDFCalculator.pdf for C60 Bucky ball. - """ + """Check DebyePDFCalculator.pdf for C60 Bucky ball.""" qmax = self.dpdfc.qmax r0, g0 = PDFCalculator(qmax=qmax)(self.bucky) r1, g1 = self.dpdfc(self.bucky) @@ -102,8 +94,7 @@ def test_PDF_C60bucky(self): return def test_partial_pdfs(self): - """Check calculation of partial PDFs. - """ + """Check calculation of partial PDFs.""" dpdfc = self.dpdfc dpdfc.qmin = 1.0 rutile = self.tio2rutile @@ -119,9 +110,9 @@ def test_partial_pdfs(self): self.assertTrue(numpy.allclose(g0, g1 + g1i)) # Ti-O dpdfc.maskAllPairs(False) - dpdfc.setTypeMask('all', 'ALL', True) - dpdfc.setTypeMask('Ti', 'Ti', False) - dpdfc.setTypeMask('O', 'O', False) + dpdfc.setTypeMask("all", "ALL", True) + dpdfc.setTypeMask("Ti", "Ti", False) + dpdfc.setTypeMask("O", "O", False) r2, g2 = dpdfc(rutile) self.assertTrue(numpy.array_equal(r0, r2)) dpdfc.invertMask() @@ -139,7 +130,7 @@ def test_partial_pdfs(self): self.assertTrue(numpy.array_equal(g2i, g2ti)) # O-O dpdfc.maskAllPairs(False) - dpdfc.setTypeMask('O', 'O', True) + dpdfc.setTypeMask("O", "O", True) r3, g3 = dpdfc(rutile) dpdfc.invertMask() r3i, g3i = dpdfc(rutile) @@ -149,12 +140,11 @@ def test_partial_pdfs(self): return def test_pickling(self): - '''check pickling and unpickling of PDFCalculator. - ''' + """Check pickling and unpickling of PDFCalculator.""" dpdfc = self.dpdfc - dpdfc.setScatteringFactorTableByType('N') - dpdfc.scatteringfactortable.setCustomAs('Na', 'Na', 7) - dpdfc.addEnvelope('sphericalshape') + dpdfc.setScatteringFactorTableByType("N") + dpdfc.scatteringfactortable.setCustomAs("Na", "Na", 7) + dpdfc.addEnvelope("sphericalshape") dpdfc.debyeprecision = 0.001 dpdfc.delta1 = 0.2 dpdfc.delta2 = 0.3 @@ -174,21 +164,19 @@ def test_pickling(self): sft = dpdfc.scatteringfactortable sft1 = dpdfc1.scatteringfactortable self.assertEqual(sft.type(), sft1.type()) - self.assertEqual(7.0, sft1.lookup('Na')) + self.assertEqual(7.0, sft1.lookup("Na")) for a in dpdfc._namesOfDoubleAttributes(): self.assertEqual(getattr(dpdfc, a), getattr(dpdfc1, a)) - self.assertEqual(13.3, - dpdfc1.getEnvelope('sphericalshape').spdiameter) - self.assertEqual(dpdfc._namesOfDoubleAttributes(), - dpdfc1._namesOfDoubleAttributes()) + self.assertEqual(13.3, dpdfc1.getEnvelope("sphericalshape").spdiameter) + self.assertEqual( + dpdfc._namesOfDoubleAttributes(), dpdfc1._namesOfDoubleAttributes() + ) self.assertEqual(dpdfc.usedenvelopetypes, dpdfc1.usedenvelopetypes) - self.assertRaises(RuntimeError, pickle_with_attr, dpdfc, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, dpdfc, foo="bar") return - def test_mask_pickling(self): - '''Check if mask gets properly pickled and restored. - ''' + """Check if mask gets properly pickled and restored.""" self.dpdfc.maskAllPairs(False) self.dpdfc.setPairMask(0, 1, True) self.dpdfc.setTypeMask("Na", "Cl", True) @@ -201,11 +189,11 @@ def test_mask_pickling(self): self.assertTrue(True is self.dpdfc.getTypeMask("Cl", "Na")) return - def test_pickling_derived_structure(self): - '''check pickling of DebyePDFCalculator with DerivedStructureAdapter. - ''' - from diffpy.srreal.tests.testutils import DerivedStructureAdapter + """Check pickling of DebyePDFCalculator with + DerivedStructureAdapter.""" + from testutils import DerivedStructureAdapter + dpdfc = self.dpdfc stru0 = DerivedStructureAdapter() dpdfc.setStructure(stru0) @@ -268,7 +256,7 @@ def test_pickling_derived_structure(self): # End of class TestDebyePDFCalculator -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testoverlapcalculator.py b/tests/test_overlapcalculator.py similarity index 70% rename from src/diffpy/srreal/tests/testoverlapcalculator.py rename to tests/test_overlapcalculator.py index 44d83312..ce27bd31 100644 --- a/src/diffpy/srreal/tests/testoverlapcalculator.py +++ b/tests/test_overlapcalculator.py @@ -1,35 +1,38 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.overlapcalculator -""" +"""Unit tests for diffpy.srreal.overlapcalculator.""" -import unittest -import pickle import copy +import pickle +import unittest + import numpy +import pytest +from testutils import ( + loadDiffPyStructure, + loadObjCrystCrystal, + pickle_with_attr, +) -from diffpy.srreal.tests.testutils import has_pyobjcryst, _msg_nopyobjcryst -from diffpy.srreal.tests.testutils import loadDiffPyStructure -from diffpy.srreal.tests.testutils import loadObjCrystCrystal -from diffpy.srreal.tests.testutils import pickle_with_attr -from diffpy.srreal.overlapcalculator import OverlapCalculator from diffpy.srreal.atomradiitable import CovalentRadiiTable +from diffpy.srreal.overlapcalculator import OverlapCalculator # ---------------------------------------------------------------------------- + class TestOverlapCalculator(unittest.TestCase): pool = None def setUp(self): self.olc = OverlapCalculator() - if not hasattr(self, 'rutile'): - type(self).rutile = loadDiffPyStructure('rutile.cif') - if not hasattr(self, 'nickel'): - type(self).nickel = loadDiffPyStructure('Ni.stru') - if not hasattr(self, 'niprim'): - type(self).niprim = loadDiffPyStructure('Ni_primitive.stru') + if not hasattr(self, "rutile"): + type(self).rutile = loadDiffPyStructure("rutile.cif") + if not hasattr(self, "nickel"): + type(self).nickel = loadDiffPyStructure("Ni.stru") + if not hasattr(self, "niprim"): + type(self).niprim = loadDiffPyStructure("Ni_primitive.stru") return def tearDown(self): @@ -40,8 +43,7 @@ def tearDown(self): return def test___init__(self): - """check OverlapCalculator.__init__() - """ + """Check OverlapCalculator.__init__()""" self.assertEqual(0, self.olc.rmin) self.assertTrue(100 <= self.olc.rmax) self.assertEqual(0, self.olc.rmaxused) @@ -49,15 +51,14 @@ def test___init__(self): return def test___call__(self): - """check OverlapCalculator.__call__() - """ + """Check OverlapCalculator.__call__()""" olc = self.olc sso1 = olc(self.rutile) self.assertEqual(6, len(sso1)) self.assertFalse(numpy.any(sso1)) self.assertEqual(0.0, olc.rmaxused) rtb = olc.atomradiitable - rtb.fromString('Ti:1.6, O:0.66') + rtb.fromString("Ti:1.6, O:0.66") sso2 = olc(self.rutile) self.assertEqual(6, len(sso2[sso2 > 0])) self.assertEqual(3.2, olc.rmaxused) @@ -67,8 +68,7 @@ def test___call__(self): return def test___getstate__(self): - """check OverlapCalculator.__getstate__() - """ + """Check OverlapCalculator.__getstate__()""" olc = self.olc self.assertIs(None, olc.__getstate__()[-1]) tb = CovalentRadiiTable() @@ -79,8 +79,7 @@ def test___getstate__(self): return def test_pickling(self): - '''check pickling and unpickling of OverlapCalculator. - ''' + """Check pickling and unpickling of OverlapCalculator.""" olc = self.olc olc.rmin = 0.1 olc.rmax = 12.3 @@ -92,14 +91,15 @@ def test_pickling(self): self.assertEqual(getattr(olc, a), getattr(olc1, a)) self.assertFalse(olc1.getPairMask(1, 2)) self.assertTrue(olc1.getPairMask(0, 0)) - self.assertTrue(numpy.array_equal( - olc.sitesquareoverlaps, olc1.sitesquareoverlaps)) - self.assertRaises(RuntimeError, pickle_with_attr, olc, foo='bar') + self.assertTrue( + numpy.array_equal(olc.sitesquareoverlaps, olc1.sitesquareoverlaps) + ) + self.assertRaises(RuntimeError, pickle_with_attr, olc, foo="bar") return def test_pickling_artb(self): - '''check pickling and unpickling of OverlapCalculator.atomradiitable. - ''' + """Check pickling and unpickling of + OverlapCalculator.atomradiitable.""" olc = self.olc olc.atomradiitable.setDefault(1.3) spkl = pickle.dumps(olc) @@ -107,19 +107,20 @@ def test_pickling_artb(self): self.assertFalse(olc is olc1) self.assertEqual(1.3, olc1.atomradiitable.getDefault()) olc.atomradiitable = CovalentRadiiTable() - olc.atomradiitable.setCustom('Na', 2) + olc.atomradiitable.setCustom("Na", 2) olc.atomradiitable.foo = 123 spkl2 = pickle.dumps(olc) olc2 = pickle.loads(spkl2) - self.assertEqual(2, olc2.atomradiitable.lookup('Na')) + self.assertEqual(2, olc2.atomradiitable.lookup("Na")) self.assertEqual(1, len(olc2.atomradiitable.getAllCustom())) self.assertEqual(123, olc2.atomradiitable.foo) return def test_pickling_derived_structure(self): - '''check pickling of OverlapCalculator with DerivedStructureAdapter. - ''' - from diffpy.srreal.tests.testutils import DerivedStructureAdapter + """Check pickling of OverlapCalculator with + DerivedStructureAdapter.""" + from testutils import DerivedStructureAdapter + olc = self.olc stru0 = DerivedStructureAdapter() olc.setStructure(stru0) @@ -134,52 +135,54 @@ def test_pickling_derived_structure(self): return def test_parallel(self): - """check parallel run of OverlapCalculator - """ + """Check parallel run of OverlapCalculator.""" import multiprocessing + from diffpy.srreal.parallel import createParallelCalculator + ncpu = 4 self.pool = multiprocessing.Pool(processes=ncpu) olc = self.olc - polc = createParallelCalculator(OverlapCalculator(), - ncpu, self.pool.imap_unordered) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + polc = createParallelCalculator( + OverlapCalculator(), ncpu, self.pool.imap_unordered + ) + olc.atomradiitable.fromString("Ti:1.6, O:0.66") polc.atomradiitable = olc.atomradiitable self.assertTrue(numpy.array_equal(olc(self.rutile), polc(self.rutile))) self.assertTrue(olc.totalsquareoverlap > 0.0) self.assertEqual(olc.totalsquareoverlap, polc.totalsquareoverlap) - self.assertEqual(sorted(zip(olc.sites0, olc.sites1)), - sorted(zip(polc.sites0, polc.sites1))) + self.assertEqual( + sorted(zip(olc.sites0, olc.sites1)), + sorted(zip(polc.sites0, polc.sites1)), + ) olc.atomradiitable.resetAll() self.assertEqual(0.0, sum(olc(self.rutile))) self.assertEqual(0.0, sum(polc(self.rutile))) return def test_distances(self): - """check OverlapCalculator.distances - """ + """Check OverlapCalculator.distances.""" olc = self.olc olc(self.nickel) self.assertEqual(0, len(olc.distances)) - olc.atomradiitable.setCustom('Ni', 1.25) + olc.atomradiitable.setCustom("Ni", 1.25) olc(self.nickel) self.assertEqual(4 * 12, len(olc.distances)) dmin = numpy.sqrt(0.5) * self.nickel.lattice.a self.assertAlmostEqual(dmin, numpy.min(olc.distances)) self.assertAlmostEqual(dmin, numpy.max(olc.distances)) olc.maskAllPairs(False) - olc.setPairMask(0, 'all', True) + olc.setPairMask(0, "all", True) olc(self.nickel) self.assertEqual(12 + 12, len(olc.distances)) return def test_directions(self): - """check OverlapCalculator.directions - """ + """Check OverlapCalculator.directions.""" olc = self.olc olc(self.nickel) self.assertEqual([], olc.directions.tolist()) - olc.atomradiitable.setCustom('Ni', 1.25) + olc.atomradiitable.setCustom("Ni", 1.25) olc.eval(self.nickel) drs = self.olc.directions nms = numpy.sqrt(numpy.sum(numpy.power(drs, 2), axis=1)) @@ -188,10 +191,9 @@ def test_directions(self): return def test_gradients(self): - """check OverlapCalculator.gradients - """ + """Check OverlapCalculator.gradients.""" olc = self.olc - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertEqual((6, 3), olc.gradients.shape) self.assertTrue(numpy.allclose([0, 0, 0], numpy.sum(olc.gradients))) @@ -199,7 +201,7 @@ def test_gradients(self): self.assertTrue(abs(g2[0]) > 0.1) tso0 = olc.totalsquareoverlap dx = 1e-8 - rutile2 = loadDiffPyStructure('rutile.cif') + rutile2 = loadDiffPyStructure("rutile.cif") rutile2[2].xyz_cartn += [dx, 0.0, 0.0] olc.eval(rutile2) g2nx = (olc.totalsquareoverlap - tso0) / dx @@ -207,63 +209,59 @@ def test_gradients(self): return def test_sitesquareoverlaps(self): - """check OverlapCalculator.sitesquareoverlaps - """ + """Check OverlapCalculator.sitesquareoverlaps.""" olc = self.olc self.assertTrue(numpy.array_equal([], olc.sitesquareoverlaps)) olc(self.rutile) self.assertTrue(numpy.array_equal(6 * [0.0], olc.sitesquareoverlaps)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") sso = olc(self.rutile) self.assertTrue(numpy.array_equal(sso, olc.sitesquareoverlaps)) self.assertTrue(numpy.all(sso)) return def test_totalsquareoverlap(self): - """check OverlapCalculator.totalsquareoverlap - """ + """Check OverlapCalculator.totalsquareoverlap.""" olc = self.olc self.assertEqual(0.0, olc.totalsquareoverlap) olc(self.rutile) self.assertEqual(0.0, olc.totalsquareoverlap) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertTrue(1.20854162728, olc.totalsquareoverlap) return def test_meansquareoverlap(self): - """check OverlapCalculator.meansquareoverlap - """ + """Check OverlapCalculator.meansquareoverlap.""" olc = self.olc self.assertEqual(0.0, olc.meansquareoverlap) olc(self.nickel) self.assertEqual(0.0, olc.meansquareoverlap) - olc.atomradiitable.setCustom('Ni', 1.25) + olc.atomradiitable.setCustom("Ni", 1.25) olc(self.nickel) mso0 = olc.meansquareoverlap self.assertTrue(mso0 > 0.0) sso1 = olc(self.niprim) self.assertEqual(1, len(sso1)) self.assertAlmostEqual(mso0, olc.meansquareoverlap) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertAlmostEqual(0.201423604547, olc.meansquareoverlap) return def test_flipDiffTotal(self): - """check OverlapCalculator.flipDiffTotal - """ + """Check OverlapCalculator.flipDiffTotal.""" olc = self.olc - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertEqual(0.0, olc.flipDiffTotal(0, 0)) self.assertEqual(0.0, olc.flipDiffTotal(0, 1)) self.assertEqual(0.0, olc.flipDiffTotal(2, 5)) tso0 = olc.totalsquareoverlap olc2 = copy.copy(olc) - rutile2 = loadDiffPyStructure('rutile.cif') - rutile2[0].element = 'O' - rutile2[2].element = 'Ti' + rutile2 = loadDiffPyStructure("rutile.cif") + rutile2[0].element = "O" + rutile2[2].element = "Ti" olc2(rutile2) fdt02 = olc2.totalsquareoverlap - tso0 self.assertTrue(fdt02 > 0.01) @@ -273,48 +271,46 @@ def test_flipDiffTotal(self): return def test_getNeighborSites(self): - """check OverlapCalculator.getNeighborSites - """ + """Check OverlapCalculator.getNeighborSites.""" olc = self.olc olc(self.rutile) self.assertEqual(set(), olc.getNeighborSites(0)) self.assertEqual(set(), olc.getNeighborSites(3)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) oxygens = list(range(2, 6)) self.assertEqual(set([0] + oxygens), olc.getNeighborSites(0)) self.assertEqual(set([1] + oxygens), olc.getNeighborSites(1)) self.assertEqual(set(range(2)), olc.getNeighborSites(2)) self.assertEqual(set(range(2)), olc.getNeighborSites(5)) - n5, = numpy.array([5], dtype=int) + (n5,) = numpy.array([5], dtype=int) self.assertEqual(set(range(2)), olc.getNeighborSites(n5)) return def test_coordinations(self): - """check OverlapCalculator.coordinations - """ + """Check OverlapCalculator.coordinations.""" olc = self.olc self.assertEqual(0, len(olc.coordinations)) olc(self.rutile) self.assertEqual(6, len(olc.coordinations)) self.assertFalse(numpy.any(olc.coordinations)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) - self.assertTrue(numpy.array_equal( - [8, 8, 3, 3, 3, 3], olc.coordinations)) + self.assertTrue( + numpy.array_equal([8, 8, 3, 3, 3, 3], olc.coordinations) + ) return def test_coordinationByTypes(self): - """check OverlapCalculator.coordinationByTypes - """ + """Check OverlapCalculator.coordinationByTypes.""" olc = self.olc olc(self.rutile) self.assertEqual({}, olc.coordinationByTypes(0)) self.assertEqual({}, olc.coordinationByTypes(5)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) - cTi = {'Ti' : 2.0, 'O' : 6.0} - cO = {'Ti' : 3.0} + cTi = {"Ti": 2.0, "O": 6.0} + cO = {"Ti": 3.0} self.assertEqual(cTi, olc.coordinationByTypes(0)) self.assertEqual(cTi, olc.coordinationByTypes(1)) self.assertEqual(cO, olc.coordinationByTypes(2)) @@ -324,91 +320,96 @@ def test_coordinationByTypes(self): return def test_neighborhoods(self): - """check OverlapCalculator.neighborhoods - """ + """Check OverlapCalculator.neighborhoods.""" olc = self.olc self.assertEqual([], olc.neighborhoods) olc(self.rutile) self.assertEqual([set((i,)) for i in range(6)], olc.neighborhoods) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertEqual([set(range(6))], olc.neighborhoods) - olc.atomradiitable.setCustom('Ti', 1.8) - olc.atomradiitable.setCustom('O', 0.1) + olc.atomradiitable.setCustom("Ti", 1.8) + olc.atomradiitable.setCustom("O", 0.1) olc(self.rutile) nghbs = [set((0, 1))] + [set((i,)) for i in range(2, 6)] self.assertEqual(nghbs, olc.neighborhoods) return + # End of class TestOverlapCalculator # ---------------------------------------------------------------------------- -@unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) + class TestOverlapCalculatorObjCryst(unittest.TestCase): + @pytest.fixture(autouse=True) + def _check_periodictable(self, has_pyobjcryst, _msg_nopyobjcryst): + if not has_pyobjcryst: + pytest.skip(_msg_nopyobjcryst) + def setUp(self): self.olc = OverlapCalculator() - if not hasattr(self, 'rutile'): - type(self).rutile = loadObjCrystCrystal('rutile.cif') - if not hasattr(self, 'nickel'): - type(self).nickel = loadObjCrystCrystal('Ni.cif') + if not hasattr(self, "rutile"): + type(self).rutile = loadObjCrystCrystal("rutile.cif") + if not hasattr(self, "nickel"): + type(self).nickel = loadObjCrystCrystal("Ni.cif") return def tearDown(self): return def test_totalsquareoverlap(self): - """check OverlapCalculator.totalsquareoverlap for ObjCryst crystal - """ + """Check OverlapCalculator.totalsquareoverlap for ObjCryst + crystal.""" olc = self.olc self.assertEqual(0.0, olc.totalsquareoverlap) olc(self.rutile) self.assertEqual(0.0, olc.totalsquareoverlap) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertTrue(1.20854162728, olc.totalsquareoverlap) return def test_meansquareoverlap(self): - """check OverlapCalculator.meansquareoverlap for ObjCryst crystal - """ + """Check OverlapCalculator.meansquareoverlap for ObjCryst + crystal.""" olc = self.olc self.assertEqual(0.0, olc.meansquareoverlap) olc(self.rutile) self.assertEqual(0.0, olc.meansquareoverlap) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertAlmostEqual(0.201423604547, olc.meansquareoverlap) return def test_flipDiffTotal(self): - """check OverlapCalculator.flipDiffTotal for an ObjCryst crystal - """ + """Check OverlapCalculator.flipDiffTotal for an ObjCryst + crystal.""" olc = self.olc olc(self.rutile) self.assertEqual(0.0, olc.flipDiffTotal(0, 1)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) tso0 = olc.totalsquareoverlap olc2 = copy.copy(olc) - olc2.atomradiitable.fromString('Ti:0.66, O:1.6') + olc2.atomradiitable.fromString("Ti:0.66, O:1.6") olc2(self.rutile) fdt01 = olc2.totalsquareoverlap - tso0 self.assertAlmostEqual(fdt01, olc.flipDiffTotal(0, 1)) return def test_flipDiffMean(self): - """check OverlapCalculator.flipDiffMean for an ObjCryst crystal - """ + """Check OverlapCalculator.flipDiffMean for an ObjCryst + crystal.""" olc = self.olc olc(self.rutile) self.assertEqual(0.0, olc.flipDiffMean(0, 1)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) mso0 = olc.meansquareoverlap olc2 = copy.copy(olc) - olc2.atomradiitable.fromString('Ti:0.66, O:1.6') + olc2.atomradiitable.fromString("Ti:0.66, O:1.6") olc2(self.rutile) fdm01 = olc2.meansquareoverlap - mso0 self.assertAlmostEqual(fdm01, olc.flipDiffMean(0, 1)) @@ -418,63 +419,64 @@ def test_flipDiffMean(self): return def test_getNeighborSites(self): - """check OverlapCalculator.getNeighborSites for an ObjCryst crystal - """ + """Check OverlapCalculator.getNeighborSites for an ObjCryst + crystal.""" olc = self.olc olc(self.rutile) self.assertEqual(set(), olc.getNeighborSites(0)) self.assertEqual(set(), olc.getNeighborSites(1)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertEqual(set([0, 1]), olc.getNeighborSites(0)) self.assertEqual(set([0]), olc.getNeighborSites(1)) return def test_coordinations(self): - """check OverlapCalculator.coordinations for an ObjCryst crystal - """ + """Check OverlapCalculator.coordinations for an ObjCryst + crystal.""" olc = self.olc self.assertEqual(0, len(olc.coordinations)) olc(self.rutile) self.assertEqual(2, len(olc.coordinations)) self.assertFalse(numpy.any(olc.coordinations)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertTrue(numpy.array_equal([8, 3], olc.coordinations)) return def test_coordinationByTypes(self): - """check OverlapCalculator.coordinationByTypes for an ObjCryst crystal - """ + """Check OverlapCalculator.coordinationByTypes for an ObjCryst + crystal.""" olc = self.olc olc(self.rutile) self.assertEqual({}, olc.coordinationByTypes(0)) self.assertEqual({}, olc.coordinationByTypes(1)) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) - cTi = {'Ti' : 2.0, 'O' : 6.0} - cO = {'Ti' : 3.0} + cTi = {"Ti": 2.0, "O": 6.0} + cO = {"Ti": 3.0} self.assertEqual(cTi, olc.coordinationByTypes(0)) self.assertEqual(cO, olc.coordinationByTypes(1)) return def test_neighborhoods(self): - """check OverlapCalculator.neighborhoods for an ObjCryst crystal - """ + """Check OverlapCalculator.neighborhoods for an ObjCryst + crystal.""" olc = self.olc self.assertEqual([], olc.neighborhoods) olc(self.rutile) self.assertEqual([set((i,)) for i in range(2)], olc.neighborhoods) - olc.atomradiitable.fromString('Ti:1.6, O:0.66') + olc.atomradiitable.fromString("Ti:1.6, O:0.66") olc(self.rutile) self.assertEqual([set((0, 1))], olc.neighborhoods) return + # End of class TestOverlapCalculatorObjCryst # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testpairquantity.py b/tests/test_pairquantity.py similarity index 73% rename from src/diffpy/srreal/tests/testpairquantity.py rename to tests/test_pairquantity.py index 2bf755ab..14335a9f 100644 --- a/src/diffpy/srreal/tests/testpairquantity.py +++ b/tests/test_pairquantity.py @@ -1,71 +1,69 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.pairquantity -""" +"""Unit tests for diffpy.srreal.pairquantity.""" -import unittest import pickle +import unittest + import numpy +from testutils import mod_structure from diffpy.srreal.pairquantity import PairQuantity -from diffpy.srreal.srreal_ext import BasePairQuantity from diffpy.srreal.pdfcalculator import PDFCalculator -from diffpy.srreal.tests.testutils import mod_structure - +from diffpy.srreal.srreal_ext import BasePairQuantity # ---------------------------------------------------------------------------- + class TestBasePairQuantity(unittest.TestCase): def setUp(self): self.bpq = BasePairQuantity() return - def test_pickling(self): "verify pickling is disabled for the C++ base class." self.assertRaises(RuntimeError, pickle.dumps, self.bpq) return + # End of class TestBasePairQuantity # ---------------------------------------------------------------------------- + class TestPairQuantity(unittest.TestCase): def setUp(self): self.pq = PairQuantity() return - def test_evaluatortype(self): - """check PairQuantity.evaluatortype property. - """ + """Check PairQuantity.evaluatortype property.""" pq = self.pq - self.assertTrue(pq.evaluatortype in ('BASIC', 'OPTIMIZED')) - pq.evaluatortype = 'BASIC' - self.assertEqual('BASIC', pq.evaluatortype) - self.assertRaises(ValueError, setattr, pq, 'evaluatortype', 'invalid') - self.assertRaises(ValueError, setattr, pq, 'evaluatortype', 'basic') - self.assertRaises(ValueError, setattr, pq, 'evaluatortype', 'BASic') + self.assertTrue(pq.evaluatortype in ("BASIC", "OPTIMIZED")) + pq.evaluatortype = "BASIC" + self.assertEqual("BASIC", pq.evaluatortype) + self.assertRaises(ValueError, setattr, pq, "evaluatortype", "invalid") + self.assertRaises(ValueError, setattr, pq, "evaluatortype", "basic") + self.assertRaises(ValueError, setattr, pq, "evaluatortype", "BASic") # check all supported evaluators in PDFCalculator pdfc = PDFCalculator() - self.assertEqual('OPTIMIZED', pdfc.evaluatortype) - pdfc.evaluatortype = 'BASIC' - self.assertEqual('BASIC', pdfc.evaluatortype) - pdfc.evaluatortype = 'CHECK' - self.assertEqual('CHECK', pdfc.evaluatortype) - pdfc.evaluatortype = 'OPTIMIZED' - self.assertEqual('OPTIMIZED', pdfc.evaluatortype) + self.assertEqual("OPTIMIZED", pdfc.evaluatortype) + pdfc.evaluatortype = "BASIC" + self.assertEqual("BASIC", pdfc.evaluatortype) + pdfc.evaluatortype = "CHECK" + self.assertEqual("CHECK", pdfc.evaluatortype) + pdfc.evaluatortype = "OPTIMIZED" + self.assertEqual("OPTIMIZED", pdfc.evaluatortype) return - def test_setStructure(self): - """check PairQuantity.setStructure() - """ + """Check PairQuantity.setStructure()""" Structure = mod_structure.Structure Atom = mod_structure.Atom from diffpy.srreal.structureadapter import EMPTY + stru = Structure([Atom("Ar", [0.1, 0.2, 0.3])]) self.pq.setStructure(stru) adpt = self.pq.getStructure() @@ -76,10 +74,8 @@ def test_setStructure(self): self.assertEqual(0, adpt.countSites()) return - def test_setPairMask_args(self): - """check argument type handling in setPairMask - """ + """Check argument type handling in setPairMask.""" spm = self.pq.setPairMask gpm = self.pq.getPairMask self.assertRaises(TypeError, spm, 0.0, 0, False) @@ -92,19 +88,16 @@ def test_setPairMask_args(self): self.assertFalse(gpm(2, 7)) return - def test_getStructure(self): - """check PairQuantity.getStructure() - """ + """Check PairQuantity.getStructure()""" adpt = self.pq.getStructure() self.assertEqual(0, adpt.countSites()) return - def test_ticker(self): - """check PairQuantity.ticker() - """ + """Check PairQuantity.ticker()""" from diffpy.srreal.eventticker import EventTicker + et0 = EventTicker(self.pq.ticker()) self.pq.rmax = 3.77 et1 = self.pq.ticker() @@ -112,10 +105,8 @@ def test_ticker(self): self.assertTrue(et0 < et1) return - def test_ticker_override(self): - """check Python override of PairQuantity.ticker. - """ + """Check Python override of PairQuantity.ticker.""" pqcnt = PQCounter() self.assertEqual(0, pqcnt.tcnt) et0 = pqcnt.ticker() @@ -130,16 +121,15 @@ def test_ticker_override(self): self.assertEqual(1, pqcnt.tcnt) # Check if ticker call from OPTIMIZED evaluator is handled # with our Python override. - pqcnt.evaluatortype = 'OPTIMIZED' + pqcnt.evaluatortype = "OPTIMIZED" self.assertEqual(1, pqcnt.tcnt) pqcnt.eval() self.assertEqual(2, pqcnt.tcnt) return - def test__addPairContribution(self): - """Check Python override of PairQuantity._addPairContribution. - """ + """Check Python override of + PairQuantity._addPairContribution.""" pqcnt = PQCounter() self.assertEqual(0, pqcnt(carbonzchain(0))) self.assertEqual(0, pqcnt(carbonzchain(1))) @@ -147,42 +137,42 @@ def test__addPairContribution(self): self.assertEqual(10, pqcnt(carbonzchain(5))) return - def test_optimized_evaluation(self): - """Check OPTIMIZED evaluation in Python-defined calculator class. - """ + """Check OPTIMIZED evaluation in Python-defined calculator + class.""" c8 = carbonzchain(8) c9 = carbonzchain(9) pqd = PQDerived() + # wrapper for evaluation using specified evaluatortype. # Use pq.eval twice to trigger optimized evaluation. - eval_as = lambda evtp, pq, stru : ( - setattr(pq, 'evaluatortype', evtp), - pq.eval(stru), pq.eval())[-1] - eval_as('BASIC', pqd, c8) - self.assertEqual('BASIC', pqd.evaluatortype) + def eval_as(evtp, pq, stru): + setattr(pq, "evaluatortype", evtp) + pq.eval(stru) + return pq.eval() + + eval_as("BASIC", pqd, c8) + self.assertEqual("BASIC", pqd.evaluatortype) # pqd does not support OPTIMIZED evaluation. Its use will # raise ValueError or RuntimeError for older libdiffpy. # Here we check for StandardError that covers them both. - self.assertRaises(Exception, - eval_as, 'OPTIMIZED', pqd, c8) + self.assertRaises(Exception, eval_as, "OPTIMIZED", pqd, c8) # PQCounter supports OPTIMIZED evaluation mode. ocnt = PQCounter() - ocnt.evaluatortype = 'OPTIMIZED' + ocnt.evaluatortype = "OPTIMIZED" self.assertEqual(28, ocnt(c8)) self.assertEqual(28, ocnt(c8)) - self.assertEqual('OPTIMIZED', ocnt.evaluatortypeused) + self.assertEqual("OPTIMIZED", ocnt.evaluatortypeused) self.assertEqual(36, ocnt(c9)) - self.assertEqual('OPTIMIZED', ocnt.evaluatortypeused) + self.assertEqual("OPTIMIZED", ocnt.evaluatortypeused) self.assertEqual(28, ocnt(c8)) - self.assertEqual('OPTIMIZED', ocnt.evaluatortypeused) + self.assertEqual("OPTIMIZED", ocnt.evaluatortypeused) return - def test_pickling(self): - '''check pickling and unpickling of PairQuantity. - ''' - from diffpy.srreal.tests.testutils import DerivedStructureAdapter + """Check pickling and unpickling of PairQuantity.""" + from testutils import DerivedStructureAdapter + stru0 = DerivedStructureAdapter() self.pq.setStructure(stru0) self.assertEqual(1, stru0.cpqcount) @@ -201,12 +191,14 @@ def test_pickling(self): self.assertEqual("asdf", pcnt2.foo) return + # End of class TestPairQuantity # ---------------------------------------------------------------------------- # helper for testing PairQuantity overrides + class PQDerived(PairQuantity): tcnt = 0 @@ -215,10 +207,12 @@ def ticker(self): self.tcnt += 1 return PairQuantity.ticker(self) + # End of class PQDerived # helper for testing support for optimized evaluation + class PQCounter(PQDerived): def __init__(self): @@ -228,7 +222,7 @@ def __init__(self): return def __call__(self, structure=None): - rv, = self.eval(structure) + (rv,) = self.eval(structure) return rv def _addPairContribution(self, bnds, sumscale): @@ -244,17 +238,19 @@ def _restorePartialValue(self): del self.__stashed_value return + # End of class PQCounter + def carbonzchain(n): "Helper function that returns a z-chain of Carbon atoms." Structure = mod_structure.Structure Atom = mod_structure.Atom - rv = Structure([Atom('C', [0, 0, z]) for z in range(n)]) + rv = Structure([Atom("C", [0, 0, z]) for z in range(n)]) return rv -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testparallel.py b/tests/test_parallel.py similarity index 71% rename from src/diffpy/srreal/tests/testparallel.py rename to tests/test_parallel.py index 9373dca6..b4b53b17 100644 --- a/src/diffpy/srreal/tests/testparallel.py +++ b/tests/test_parallel.py @@ -1,15 +1,17 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.parallel -""" +"""Unit tests for diffpy.srreal.parallel.""" -import unittest import multiprocessing +import unittest + import numpy -from diffpy.srreal.tests.testutils import loadDiffPyStructure +from testutils import loadDiffPyStructure + from diffpy.srreal.parallel import createParallelCalculator + ############################################################################## class TestRoutines(unittest.TestCase): @@ -20,11 +22,13 @@ class TestRoutines(unittest.TestCase): def setUp(self): if self.cdse is None: - type(self).cdse = loadDiffPyStructure('CdSe_cadmoselite.cif') - for a in self.cdse: a.Uisoequiv = 0.003 + type(self).cdse = loadDiffPyStructure("CdSe_cadmoselite.cif") + for a in self.cdse: + a.Uisoequiv = 0.003 if self.nickel is None: - type(self).nickel = loadDiffPyStructure('Ni.cif') - for a in self.nickel: a.Uisoequiv = 0.003 + type(self).nickel = loadDiffPyStructure("Ni.cif") + for a in self.nickel: + a.Uisoequiv = 0.003 return def tearDown(self): @@ -40,34 +44,34 @@ def pool(self): self._pool = multiprocessing.Pool(processes=self.ncpu) return self._pool - def test_parallel_evaluatortype(self): - """check handling of the evaluatortype property - """ + """Check handling of the evaluatortype property.""" from diffpy.srreal.pdfcalculator import PDFCalculator + pdfc = PDFCalculator() - self.assertEqual('OPTIMIZED', pdfc.evaluatortype) + self.assertEqual("OPTIMIZED", pdfc.evaluatortype) ppdfc = createParallelCalculator(pdfc, 2, map) - self.assertEqual('BASIC', ppdfc.evaluatortype) - self.assertEqual('BASIC', pdfc.evaluatortype) - ppdfc.evaluatortype = 'BASIC' - self.assertRaises(ValueError, - setattr, ppdfc, 'evaluatortype', 'OPTIMIZED') + self.assertEqual("BASIC", ppdfc.evaluatortype) + self.assertEqual("BASIC", pdfc.evaluatortype) + ppdfc.evaluatortype = "BASIC" + self.assertRaises( + ValueError, setattr, ppdfc, "evaluatortype", "OPTIMIZED" + ) return - def test_parallel_pdf(self): - """check parallel PDFCalculator - """ + """Check parallel PDFCalculator.""" from diffpy.srreal.pdfcalculator import PDFCalculator + pdfc = PDFCalculator() r0, g0 = pdfc(self.cdse) ppdfc1 = createParallelCalculator(PDFCalculator(), 3, map) r1, g1 = ppdfc1(self.cdse) self.assertTrue(numpy.array_equal(r0, r1)) self.assertTrue(numpy.allclose(g0, g1)) - ppdfc2 = createParallelCalculator(PDFCalculator(), - self.ncpu, self.pool.imap_unordered) + ppdfc2 = createParallelCalculator( + PDFCalculator(), self.ncpu, self.pool.imap_unordered + ) r2, g2 = ppdfc2(self.cdse) self.assertTrue(numpy.array_equal(r0, r2)) self.assertTrue(numpy.allclose(g0, g2)) @@ -84,25 +88,25 @@ def test_parallel_pdf(self): self.assertTrue(numpy.allclose(g0a, g2a)) return - def test_parallel_bonds(self): - """check parallel BondCalculator - """ + """Check parallel BondCalculator.""" from diffpy.srreal.bondcalculator import BondCalculator + nickel = self.nickel bc = BondCalculator() d0 = bc(nickel) pbc1 = createParallelCalculator(BondCalculator(), 3, map) d1 = pbc1(nickel) self.assertTrue(numpy.array_equal(d0, d1)) - pbc2 = createParallelCalculator(BondCalculator(), - self.ncpu, self.pool.imap_unordered) + pbc2 = createParallelCalculator( + BondCalculator(), self.ncpu, self.pool.imap_unordered + ) d2 = pbc2(nickel) self.assertTrue(numpy.array_equal(d0, d2)) bc.rmax = pbc1.rmax = pbc2.rmax = 2.5 for bci in (bc, pbc1, pbc2): bci.maskAllPairs(False) - bci.setPairMask(0, 'all', True) + bci.setPairMask(0, "all", True) bci.filterCone([1, 0, 0], 48) d0a = bc(nickel) self.assertEqual(8, len(d0a)) @@ -112,9 +116,10 @@ def test_parallel_bonds(self): self.assertTrue(numpy.array_equal(d0a, d2a)) return + # End of class TestRoutines -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testpdfbaseline.py b/tests/test_pdfbaseline.py similarity index 61% rename from src/diffpy/srreal/tests/testpdfbaseline.py rename to tests/test_pdfbaseline.py index 327ffded..e9aff06a 100644 --- a/src/diffpy/srreal/tests/testpdfbaseline.py +++ b/tests/test_pdfbaseline.py @@ -1,28 +1,33 @@ #!/usr/bin/env python -"""Unit tests for the PDFBaseline class from diffpy.srreal.pdfcalculator -""" +"""Unit tests for the PDFBaseline class from +diffpy.srreal.pdfcalculator.""" -import unittest import pickle -import numpy +import unittest -from diffpy.srreal.tests.testutils import pickle_with_attr -from diffpy.srreal.pdfbaseline import PDFBaseline, makePDFBaseline -from diffpy.srreal.pdfbaseline import ZeroBaseline, LinearBaseline +import numpy +from testutils import pickle_with_attr + +from diffpy.srreal.pdfbaseline import ( + LinearBaseline, + PDFBaseline, + ZeroBaseline, + makePDFBaseline, +) from diffpy.srreal.pdfcalculator import PDFCalculator # ---------------------------------------------------------------------------- + class TestPDFBaseline(unittest.TestCase): def setUp(self): - self.linear = PDFBaseline.createByType('linear') - self.zero = PDFBaseline.createByType('zero') + self.linear = PDFBaseline.createByType("linear") + self.zero = PDFBaseline.createByType("zero") return - def tearDown(self): for tp in PDFBaseline.getRegisteredTypes(): PDFBaseline._deregisterType(tp) @@ -30,19 +35,15 @@ def tearDown(self): self.zero._registerThisType() return - def test___init__(self): - """check PDFBaseline.__init__() - """ + """Check PDFBaseline.__init__()""" self.assertEqual(0.0, self.linear.slope) - self.linear._setDoubleAttr('slope', 2.0) + self.linear._setDoubleAttr("slope", 2.0) self.assertEqual(2.0, self.linear.slope) return - def test___call__(self): - """check PDFBaseline.__call__() - """ + """Check PDFBaseline.__call__()""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PDFBaseline().__call__, 37) self.assertEqual(0.0, self.zero(10)) @@ -51,7 +52,7 @@ def test___call__(self): self.assertEqual(0.0, self.linear(345)) self.linear.slope = -2 self.assertEqual(-7.0, self.linear(3.5)) - self.assertEqual(-2.0, self.linear._getDoubleAttr('slope')) + self.assertEqual(-2.0, self.linear._getDoubleAttr("slope")) x = numpy.arange(0, 10.001, 0.1) xb = numpy.array([(0.0, xi) for xi in x])[:, 1] self.assertTrue(xb.strides > x.strides) @@ -59,52 +60,46 @@ def test___call__(self): self.assertTrue(numpy.array_equal(-2 * x, self.linear(xb))) return - def test_clone(self): - """check PDFBaseline.clone - """ + """Check PDFBaseline.clone.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PDFBaseline().clone) self.linear.slope = 17 bl2 = self.linear.clone() - self.assertEqual('linear', bl2.type()) + self.assertEqual("linear", bl2.type()) self.assertEqual(17.0, bl2.slope) - self.assertEqual(17.0, bl2._getDoubleAttr('slope')) + self.assertEqual(17.0, bl2._getDoubleAttr("slope")) return - def test_create(self): - """check PDFBaseline.create - """ + """Check PDFBaseline.create.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PDFBaseline().create) - self.assertEqual('zero', self.zero.create().type()) - self.assertEqual('linear', self.linear.create().type()) + self.assertEqual("zero", self.zero.create().type()) + self.assertEqual("linear", self.linear.create().type()) self.linear.slope = 17 self.assertEqual(0.0, self.linear.create().slope) return - def test_type(self): - """check PDFBaseline.type - """ + """Check PDFBaseline.type.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PDFBaseline().type) - self.assertEqual('linear', self.linear.type()) - self.assertEqual('zero', self.zero.type()) + self.assertEqual("linear", self.linear.type()) + self.assertEqual("zero", self.zero.type()) self.assertTrue(type(self.linear) is LinearBaseline) self.assertTrue(type(self.zero) is ZeroBaseline) return - def test__aliasType(self): - """check PDFBaseline._aliasType. - """ + """Check PDFBaseline._aliasType.""" self.assertRaises(ValueError, PDFBaseline.createByType, "alias") - self.assertRaises(RuntimeError, PDFBaseline._aliasType, - "invalid", "alias") - self.assertRaises(RuntimeError, PDFBaseline._aliasType, - "linear", "zero") + self.assertRaises( + RuntimeError, PDFBaseline._aliasType, "invalid", "alias" + ) + self.assertRaises( + RuntimeError, PDFBaseline._aliasType, "linear", "zero" + ) PDFBaseline._aliasType("linear", "alias") bl = PDFBaseline.createByType("alias") self.assertEqual("linear", bl.type()) @@ -114,81 +109,72 @@ def test__aliasType(self): bl1 = PDFBaseline.createByType("alias") self.assertTrue(isinstance(bl1, LinearBaseline)) # no other type can be aliased to the existing name. - self.assertRaises(RuntimeError, PDFBaseline._aliasType, - "zero", "alias") + self.assertRaises( + RuntimeError, PDFBaseline._aliasType, "zero", "alias" + ) return - def test__deregisterType(self): - """check PDFBaseline._deregisterType. - """ + """Check PDFBaseline._deregisterType.""" self.assertEqual(0, PDFBaseline._deregisterType("nonexistent")) PDFBaseline._aliasType("linear", "alias") self.assertEqual(2, PDFBaseline._deregisterType("alias")) - self.assertFalse('linear' in PDFBaseline.getRegisteredTypes()) + self.assertFalse("linear" in PDFBaseline.getRegisteredTypes()) self.assertEqual(0, PDFBaseline._deregisterType("alias")) return - def test_createByType(self): - """check PDFBaseline.createByType() - """ - self.assertRaises(ValueError, PDFBaseline.createByType, 'notregistered') + """Check PDFBaseline.createByType()""" + self.assertRaises( + ValueError, PDFBaseline.createByType, "notregistered" + ) return - def test_isRegisteredType(self): - """check PDFBaseline.isRegisteredType() - """ + """Check PDFBaseline.isRegisteredType()""" self.assertTrue(PDFBaseline.isRegisteredType("linear")) self.assertFalse(PDFBaseline.isRegisteredType("nonexistent")) PDFBaseline._deregisterType("linear") self.assertFalse(PDFBaseline.isRegisteredType("linear")) return - def test_getAliasedTypes(self): - """check PDFBaseline.getAliasedTypes() - """ + """Check PDFBaseline.getAliasedTypes()""" self.assertEqual({}, PDFBaseline.getAliasedTypes()) PDFBaseline._aliasType("linear", "foo") PDFBaseline._aliasType("linear", "bar") PDFBaseline._aliasType("linear", "linear") PDFBaseline._aliasType("bar", "foo") - self.assertEqual({'bar' : 'linear', 'foo' : 'linear'}, - PDFBaseline.getAliasedTypes()) + self.assertEqual( + {"bar": "linear", "foo": "linear"}, PDFBaseline.getAliasedTypes() + ) return - def test_getRegisteredTypes(self): - """check PDFBaseline.getRegisteredTypes - """ + """Check PDFBaseline.getRegisteredTypes.""" regtypes = PDFBaseline.getRegisteredTypes() self.assertTrue(2 <= len(regtypes)) - self.assertTrue('linear' in regtypes) - self.assertTrue('zero' in regtypes) + self.assertTrue("linear" in regtypes) + self.assertTrue("zero" in regtypes) return - def test_pickling(self): - '''check pickling and unpickling of PDFBaseline. - ''' + """Check pickling and unpickling of PDFBaseline.""" linear = self.linear linear.slope = 11 linear2 = pickle.loads(pickle.dumps(linear)) - self.assertEqual('linear', linear2.type()) + self.assertEqual("linear", linear2.type()) self.assertEqual(11, linear2.slope) - self.assertEqual(11, linear2._getDoubleAttr('slope')) - self.assertRaises(RuntimeError, pickle_with_attr, linear, foo='bar') - self.assertRaises(RuntimeError, pickle_with_attr, self.zero, foo='bar') + self.assertEqual(11, linear2._getDoubleAttr("slope")) + self.assertRaises(RuntimeError, pickle_with_attr, linear, foo="bar") + self.assertRaises(RuntimeError, pickle_with_attr, self.zero, foo="bar") return - def test_makePDFBaseline(self): - '''check the makePDFBaseline wrapper. - ''' - pbl = makePDFBaseline('parabolabaseline', - parabola_baseline, a=1, b=2, c=3) + """Check the makePDFBaseline wrapper.""" + pbl = makePDFBaseline( + "parabolabaseline", parabola_baseline, a=1, b=2, c=3 + ) self.assertEqual(3, pbl(0)) self.assertEqual(6, pbl(1)) self.assertEqual(11, pbl(2)) @@ -199,59 +185,76 @@ def test_makePDFBaseline(self): self.assertEqual(0, pbl2.b) self.assertEqual(3, pbl2.c) self.assertEqual([7, 3, 28], [pbl2(x) for x in [-2, 0, 5]]) - pbl3 = PDFBaseline.createByType('parabolabaseline') + pbl3 = PDFBaseline.createByType("parabolabaseline") self.assertEqual(1, pbl3.a) self.assertEqual(2, pbl3.b) self.assertEqual(3, pbl3.c) - pbl.foo = 'bar' + pbl.foo = "bar" pbl4 = pickle.loads(pickle.dumps(pbl)) self.assertEqual([7, 3, 28], [pbl4(x) for x in [-2, 0, 5]]) - self.assertEqual('bar', pbl4.foo) + self.assertEqual("bar", pbl4.foo) # fail if this baseline type already exists. - self.assertRaises(RuntimeError, makePDFBaseline, 'linear', - parabola_baseline, a=1, b=2, c=3) - self.assertRaises(RuntimeError, makePDFBaseline, 'parabolabaseline', - parabola_baseline, a=1, b=2, c=3) + self.assertRaises( + RuntimeError, + makePDFBaseline, + "linear", + parabola_baseline, + a=1, + b=2, + c=3, + ) + self.assertRaises( + RuntimeError, + makePDFBaseline, + "parabolabaseline", + parabola_baseline, + a=1, + b=2, + c=3, + ) # check replacement of an existing type. - makePDFBaseline('linear', parabola_baseline, replace=True, - a=1, b=2, c=4) - pbl4 = PDFBaseline.createByType('linear') - self.assertEqual(set(('a', 'b', 'c')), pbl4._namesOfDoubleAttributes()) + makePDFBaseline( + "linear", parabola_baseline, replace=True, a=1, b=2, c=4 + ) + pbl4 = PDFBaseline.createByType("linear") + self.assertEqual(set(("a", "b", "c")), pbl4._namesOfDoubleAttributes()) self.assertEqual(4, pbl4.c) # check baseline with no attributes - pbl5 = makePDFBaseline('myzero', lambda x: 0.0) + pbl5 = makePDFBaseline("myzero", lambda x: 0.0) self.assertEqual(0, pbl5(33)) self.assertEqual(set(), pbl5._namesOfDoubleAttributes()) return - def test_picking_owned(self): - '''verify pickling of PDFBaseline owned by PDF calculators. - ''' - pbl = makePDFBaseline('parabolabaseline', - parabola_baseline, a=1, b=2, c=3) + """Verify pickling of PDFBaseline owned by PDF calculators.""" + pbl = makePDFBaseline( + "parabolabaseline", parabola_baseline, a=1, b=2, c=3 + ) pbl.a = 7 - pbl.foobar = 'asdf' + pbl.foobar = "asdf" pc = PDFCalculator() pc.baseline = pbl self.assertIs(pbl, pc.baseline) pc2 = pickle.loads(pickle.dumps(pc)) pbl2 = pc2.baseline self.assertEqual(7, pbl2.a) - self.assertEqual('asdf', pbl2.foobar) - self.assertEqual('parabolabaseline', pbl2.type()) + self.assertEqual("asdf", pbl2.foobar) + self.assertEqual("parabolabaseline", pbl2.type()) return + # End of class TestPDFBaseline # ---------------------------------------------------------------------------- # function for wrapping by makePDFBaseline + def parabola_baseline(x, a, b, c): return a * x**2 + b * x + c -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testpdfcalcobjcryst.py b/tests/test_pdfcalcobjcryst.py similarity index 54% rename from src/diffpy/srreal/tests/testpdfcalcobjcryst.py rename to tests/test_pdfcalcobjcryst.py index 2193888d..3780558f 100644 --- a/src/diffpy/srreal/tests/testpdfcalcobjcryst.py +++ b/tests/test_pdfcalcobjcryst.py @@ -1,36 +1,36 @@ #!/usr/bin/env python -"""Unit tests for pdfcalculator.py on ObjCryst crystal structures -""" +"""Unit tests for pdfcalculator.py on ObjCryst crystal structures.""" import re import unittest import numpy -from diffpy.srreal.pdfcalculator import PDFCalculator +import pytest +from testutils import _maxNormDiff, datafile, loadObjCrystCrystal -from diffpy.srreal.tests.testutils import has_pyobjcryst, _msg_nopyobjcryst -from diffpy.srreal.tests.testutils import loadObjCrystCrystal -from diffpy.srreal.tests.testutils import datafile -from diffpy.srreal.tests.testpdfcalculator import _maxNormDiff +from diffpy.srreal.pdfcalculator import PDFCalculator # helper functions + def _loadExpectedPDF(basefilename): - '''Read expected result and return a tuple of (r, g, cfgdict). - ''' - rxf = re.compile(r'[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?') + """Read expected result and return a tuple of (r, g, cfgdict).""" + rxf = re.compile(r"[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?") fullpath = datafile(basefilename) cfgdict = {} fp = open(fullpath) for line in fp: - if line[:1] != '#': break + if line[:1] != "#": + break w = line.split() - has_cfgdata = (len(w) == 4 and w[2] == '=') - if not has_cfgdata: continue + has_cfgdata = len(w) == 4 and w[2] == "=" + if not has_cfgdata: + continue cfgdict[w[1]] = w[3] - if rxf.match(w[3]): cfgdict[w[1]] = float(w[3]) + if rxf.match(w[3]): + cfgdict[w[1]] = float(w[3]) fp.close() r, g = numpy.loadtxt(fullpath, usecols=(0, 1), unpack=True) rv = (r, g, cfgdict) @@ -38,34 +38,40 @@ def _loadExpectedPDF(basefilename): def _makePDFCalculator(crst, cfgdict): - '''Return a PDFCalculator object evaluated for a pyobjcryst.Crystal crst. - ''' - pdfcargs = {k : v for k, v in cfgdict.items() - if k not in ('biso', 'type')} + """Return a PDFCalculator object evaluated for a pyobjcryst.Crystal + crst.""" + pdfcargs = {k: v for k, v in cfgdict.items() if k not in ("biso", "type")} pdfc = PDFCalculator(**pdfcargs) - if 'biso' in cfgdict: + if "biso" in cfgdict: reg = crst.GetScatteringPowerRegistry() for i in range(reg.GetNb()): sp = reg.GetObj(i) - sp.SetBiso(cfgdict['biso']) - if 'type' in cfgdict: - pdfc.scatteringfactortable = cfgdict['type'] + sp.SetBiso(cfgdict["biso"]) + if "type" in cfgdict: + pdfc.scatteringfactortable = cfgdict["type"] pdfc.eval(crst) # avoid metadata override by PDFFitStructure for k, v in pdfcargs.items(): setattr(pdfc, k, v) return pdfc + # ---------------------------------------------------------------------------- -@unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) + class TestPDFCalcObjcryst(unittest.TestCase): + @pytest.fixture(autouse=True) + def _check_periodictable(self, has_pyobjcryst, _msg_nopyobjcryst): + if not has_pyobjcryst: + pytest.skip(_msg_nopyobjcryst) + def _comparePDFs(self, nickname, pdfbasename, cifbasename): def setself(**kwtoset): for n, v in kwtoset.items(): - setattr(self, nickname + '_' + n, v) + setattr(self, nickname + "_" + n, v) return + r, gobs, cfg = _loadExpectedPDF(pdfbasename) setself(r=r, gobs=gobs, cfg=cfg) crst = loadObjCrystCrystal(cifbasename) @@ -76,38 +82,36 @@ def setself(**kwtoset): setself(gcalc=gcalc, mxnd=mxnd) return - def test_CdSeN(self): - '''check PDFCalculator on ObjCryst loaded CIF, neutrons - ''' - self._comparePDFs('cdsen', - 'CdSe_cadmoselite_N.fgr', 'CdSe_cadmoselite.cif') + """Check PDFCalculator on ObjCryst loaded CIF, neutrons.""" + self._comparePDFs( + "cdsen", "CdSe_cadmoselite_N.fgr", "CdSe_cadmoselite.cif" + ) self.assertTrue(self.cdsen_mxnd < 0.01) return - def test_CdSeX(self): - '''check PDFCalculator on ObjCryst loaded CIF, xrays - ''' - self._comparePDFs('cdsex', - 'CdSe_cadmoselite_X.fgr', 'CdSe_cadmoselite.cif') + """Check PDFCalculator on ObjCryst loaded CIF, xrays.""" + self._comparePDFs( + "cdsex", "CdSe_cadmoselite_X.fgr", "CdSe_cadmoselite.cif" + ) self.assertTrue(self.cdsex_mxnd < 0.01) return - def test_rutileaniso(self): - '''check PDFCalculator on ObjCryst loaded anisotropic rutile - ''' - self._comparePDFs('rutileaniso', - 'TiO2_rutile-fit.fgr', 'TiO2_rutile-fit.cif') + """Check PDFCalculator on ObjCryst loaded anisotropic rutile.""" + self._comparePDFs( + "rutileaniso", "TiO2_rutile-fit.fgr", "TiO2_rutile-fit.cif" + ) self.assertTrue(self.rutileaniso_mxnd < 0.057) return + # End of class TestPDFCalcObjcryst # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testpdfcalculator.py b/tests/test_pdfcalculator.py similarity index 72% rename from src/diffpy/srreal/tests/testpdfcalculator.py rename to tests/test_pdfcalculator.py index e018d325..28b31980 100644 --- a/src/diffpy/srreal/tests/testpdfcalculator.py +++ b/tests/test_pdfcalculator.py @@ -1,32 +1,27 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.pdfcalculator -""" +"""Unit tests for diffpy.srreal.pdfcalculator.""" -import unittest import pickle +import unittest import numpy -from diffpy.srreal.tests.testutils import loadDiffPyStructure -from diffpy.srreal.tests.testutils import datafile -from diffpy.srreal.tests.testutils import pickle_with_attr -from diffpy.srreal.pdfcalculator import PDFCalculator -from diffpy.srreal.pdfcalculator import fftgtof, fftftog +from testutils import ( + _maxNormDiff, + datafile, + loadDiffPyStructure, + pickle_with_attr, +) + +from diffpy.srreal.pdfcalculator import PDFCalculator, fftftog, fftgtof # helper functions -def _maxNormDiff(yobs, ycalc): - '''Returned maximum difference normalized by RMS of the yobs - ''' - yobsa = numpy.array(yobs) - obsmax = numpy.max(numpy.fabs(yobsa)) or 1 - ynmdiff = (yobsa - ycalc) / obsmax - rv = max(numpy.fabs(ynmdiff)) - return rv # ---------------------------------------------------------------------------- + class TestPDFCalculator(unittest.TestCase): nickel = None @@ -35,28 +30,25 @@ class TestPDFCalculator(unittest.TestCase): def setUp(self): self.pdfcalc = PDFCalculator() if not self.nickel: - type(self).nickel = loadDiffPyStructure('Ni.stru') + type(self).nickel = loadDiffPyStructure("Ni.stru") if not self.tio2rutile: - type(self).tio2rutile = ( - loadDiffPyStructure('TiO2_rutile-fit.stru')) + type(self).tio2rutile = loadDiffPyStructure("TiO2_rutile-fit.stru") return def tearDown(self): return def test___init__(self): - """check PDFCalculator.__init__() - """ + """Check PDFCalculator.__init__()""" pdfc = PDFCalculator(qmin=13, rmin=4, rmax=99) self.assertEqual(13, pdfc.qmin) self.assertEqual(4, pdfc.rmin) self.assertEqual(99, pdfc.rmax) - self.assertEqual(99, pdfc._getDoubleAttr('rmax')) + self.assertEqual(99, pdfc._getDoubleAttr("rmax")) return def test___call__(self): - """Check PDFCalculator.__call__() - """ + """Check PDFCalculator.__call__()""" r0, g0 = self.pdfcalc(self.tio2rutile, rmin=2) self.assertEqual(2.0, r0[0]) r1, g1 = self.pdfcalc(self.tio2rutile, scale=7) @@ -65,7 +57,7 @@ def test___call__(self): rutile2 = self.tio2rutile.copy() # work around Structure bug of shared pdffit dictionary rutile2.pdffit = dict(self.tio2rutile.pdffit) - rutile2.pdffit['spdiameter'] = 5.0 + rutile2.pdffit["spdiameter"] = 5.0 r3, g3 = self.pdfcalc(rutile2) self.assertEqual(0.0, sum(g3[r3 >= 5] ** 2)) r4, g4 = self.pdfcalc(rutile2, scale=1, spdiameter=0) @@ -74,53 +66,47 @@ def test___call__(self): return def test__getDoubleAttr(self): - """check PDFCalculator._getDoubleAttr() - """ + """Check PDFCalculator._getDoubleAttr()""" gdba = self.pdfcalc._getDoubleAttr - self.assertEqual(1.0, gdba('scale')) - self.assertEqual(0.0, gdba('qdamp')) - self.assertRaises(Exception, gdba, 'notanattribute') + self.assertEqual(1.0, gdba("scale")) + self.assertEqual(0.0, gdba("qdamp")) + self.assertRaises(Exception, gdba, "notanattribute") return def test__hasDoubleAttr(self): - """check PDFCalculator._hasDoubleAttr() - """ - self.assertTrue(self.pdfcalc._hasDoubleAttr('scale')) - self.assertFalse(self.pdfcalc._hasDoubleAttr('notanattribute')) + """Check PDFCalculator._hasDoubleAttr()""" + self.assertTrue(self.pdfcalc._hasDoubleAttr("scale")) + self.assertFalse(self.pdfcalc._hasDoubleAttr("notanattribute")) return def test__namesOfDoubleAttributes(self): - """check PDFCalculator._namesOfDoubleAttributes() - """ + """Check PDFCalculator._namesOfDoubleAttributes()""" self.assertTrue(type(self.pdfcalc._namesOfDoubleAttributes()) is set) - self.assertTrue('qmax' in self.pdfcalc._namesOfDoubleAttributes()) + self.assertTrue("qmax" in self.pdfcalc._namesOfDoubleAttributes()) return def test__setDoubleAttr(self): - """check PDFCalculator._setDoubleAttr() - """ + """Check PDFCalculator._setDoubleAttr()""" gdba = self.pdfcalc._getDoubleAttr sdba = self.pdfcalc._setDoubleAttr - self.assertEqual(0.0, gdba('rmin')) - sdba('rmin', 3.0) - self.assertEqual(3.0, gdba('rmin')) + self.assertEqual(0.0, gdba("rmin")) + sdba("rmin", 3.0) + self.assertEqual(3.0, gdba("rmin")) return def test_eval_nickel(self): - """check PDFCalculator.eval() on simple Nickel data - """ - fnipf2 = datafile('Ni-fit.fgr') + """Check PDFCalculator.eval() on simple Nickel data.""" + fnipf2 = datafile("Ni-fit.fgr") gpf2 = numpy.loadtxt(fnipf2, usecols=(1,)) - self.pdfcalc._setDoubleAttr('rmax', 10.0001) + self.pdfcalc._setDoubleAttr("rmax", 10.0001) self.pdfcalc.eval(self.nickel) gcalc = self.pdfcalc.pdf self.assertTrue(_maxNormDiff(gpf2, gcalc) < 0.0091) return def test_eval_rutile(self): - """check PDFCalculator.eval() on anisotropic rutile data - """ - frutile = datafile('TiO2_rutile-fit.fgr') + """Check PDFCalculator.eval() on anisotropic rutile data.""" + frutile = datafile("TiO2_rutile-fit.fgr") gpf2 = numpy.loadtxt(frutile, usecols=(1,)) # configure calculator according to testdata/TiO2_ruitile-fit.fgr self.pdfcalc.qmax = 26 @@ -143,8 +129,7 @@ def test_eval_rutile(self): return def test_partial_pdfs(self): - """Check calculation of partial PDFs. - """ + """Check calculation of partial PDFs.""" pdfc = self.pdfcalc pdfc.rstep = 0.1 rutile = self.tio2rutile @@ -208,8 +193,7 @@ def test_partial_pdfs(self): return def test_full_mask(self): - '''Test PDFCalculator for a fully masked structure. - ''' + """Test PDFCalculator for a fully masked structure.""" pdfc = self.pdfcalc pdfc.rstep = 0.1 rutile = self.tio2rutile @@ -225,8 +209,7 @@ def test_full_mask(self): return def test_zero_mask(self): - '''Test PDFCalculator with a totally masked out structure. - ''' + """Test PDFCalculator with a totally masked out structure.""" pdfc = self.pdfcalc pdfc.rstep = 0.1 rutile = self.tio2rutile @@ -241,12 +224,11 @@ def test_zero_mask(self): return def test_pickling(self): - '''check pickling and unpickling of PDFCalculator. - ''' + """Check pickling and unpickling of PDFCalculator.""" pdfc = self.pdfcalc - pdfc.scatteringfactortable = 'N' - pdfc.scatteringfactortable.setCustomAs('Na', 'Na', 7) - pdfc.addEnvelope('sphericalshape') + pdfc.scatteringfactortable = "N" + pdfc.scatteringfactortable.setCustomAs("Na", "Na", 7) + pdfc.addEnvelope("sphericalshape") pdfc.delta1 = 0.2 pdfc.delta2 = 0.3 pdfc.maxextension = 10.1 @@ -266,20 +248,19 @@ def test_pickling(self): sft = pdfc.scatteringfactortable sft1 = pdfc1.scatteringfactortable self.assertEqual(sft.type(), sft1.type()) - self.assertEqual(7.0, sft1.lookup('Na')) + self.assertEqual(7.0, sft1.lookup("Na")) for a in pdfc._namesOfDoubleAttributes(): self.assertEqual(getattr(pdfc, a), getattr(pdfc1, a)) - self.assertEqual(13.3, - pdfc1.getEnvelope('sphericalshape').spdiameter) - self.assertEqual(pdfc._namesOfDoubleAttributes(), - pdfc1._namesOfDoubleAttributes()) + self.assertEqual(13.3, pdfc1.getEnvelope("sphericalshape").spdiameter) + self.assertEqual( + pdfc._namesOfDoubleAttributes(), pdfc1._namesOfDoubleAttributes() + ) self.assertEqual(pdfc.usedenvelopetypes, pdfc1.usedenvelopetypes) - self.assertRaises(RuntimeError, pickle_with_attr, pdfc, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, pdfc, foo="bar") return def test_mask_pickling(self): - '''Check if mask gets properly pickled and restored. - ''' + """Check if mask gets properly pickled and restored.""" self.pdfcalc.maskAllPairs(False) self.pdfcalc.setPairMask(0, 1, True) self.assertTrue(False is self.pdfcalc.getPairMask(0, 0)) @@ -290,9 +271,10 @@ def test_mask_pickling(self): return def test_pickling_derived_structure(self): - '''check pickling of PDFCalculator with DerivedStructureAdapter. - ''' - from diffpy.srreal.tests.testutils import DerivedStructureAdapter + """Check pickling of PDFCalculator with + DerivedStructureAdapter.""" + from testutils import DerivedStructureAdapter + pdfc = self.pdfcalc stru0 = DerivedStructureAdapter() pdfc.setStructure(stru0) @@ -307,20 +289,21 @@ def test_pickling_derived_structure(self): return def test_envelopes(self): - '''Check the envelopes property. - ''' + """Check the envelopes property.""" from diffpy.srreal.pdfenvelope import PDFEnvelope + pc = self.pdfcalc self.assertTrue(len(pc.envelopes) > 0) pc.clearEnvelopes() self.assertEqual(0, len(pc.envelopes)) - pc.addEnvelope(PDFEnvelope.createByType('scale')) + pc.addEnvelope(PDFEnvelope.createByType("scale")) self.assertEqual(1, len(pc.envelopes)) - self.assertEqual('scale', pc.envelopes[0].type()) - pc.envelopes += ('qresolution',) - self.assertEqual(('qresolution', 'scale'), pc.usedenvelopetypes) - self.assertTrue(all([isinstance(e, PDFEnvelope) - for e in pc.envelopes])) + self.assertEqual("scale", pc.envelopes[0].type()) + pc.envelopes += ("qresolution",) + self.assertEqual(("qresolution", "scale"), pc.usedenvelopetypes) + self.assertTrue( + all([isinstance(e, PDFEnvelope) for e in pc.envelopes]) + ) return @@ -353,41 +336,41 @@ def test_envelopes(self): # ---------------------------------------------------------------------------- + class TestFFTRoutines(unittest.TestCase): def test_fft_conversions(self): - """Verify conversions of arguments in fftgtof function. - """ - fnipf2 = datafile('Ni-fit.fgr') + """Verify conversions of arguments in fftgtof function.""" + fnipf2 = datafile("Ni-fit.fgr") data = numpy.loadtxt(fnipf2) dr = 0.01 - fq0, dq0 = fftgtof(data[:,1], dr) - fq1, dq1 = fftgtof(data[:,1].copy(), dr) - fq2, dq2 = fftgtof(list(data[:,1]), dr) + fq0, dq0 = fftgtof(data[:, 1], dr) + fq1, dq1 = fftgtof(data[:, 1].copy(), dr) + fq2, dq2 = fftgtof(list(data[:, 1]), dr) self.assertTrue(numpy.array_equal(fq0, fq1)) self.assertTrue(numpy.array_equal(fq0, fq2)) self.assertEqual(dq0, dq1) self.assertEqual(dq0, dq2) return - def test_fft_roundtrip(self): - """Check if forward and inverse transformation recover the input. - """ - fnipf2 = datafile('Ni-fit.fgr') + """Check if forward and inverse transformation recover the + input.""" + fnipf2 = datafile("Ni-fit.fgr") g0 = numpy.loadtxt(fnipf2, usecols=(1,)) dr0 = 0.01 fq, dq = fftgtof(g0, dr0) g1, dr1 = fftftog(fq, dq) self.assertAlmostEqual(dr0, dr1, 12) - self.assertTrue(numpy.allclose(g0, g1[:g0.size])) + self.assertTrue(numpy.allclose(g0, g1[: g0.size])) return + # End of class TestFFTRoutines # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testpdfenvelope.py b/tests/test_pdfenvelope.py similarity index 67% rename from src/diffpy/srreal/tests/testpdfenvelope.py rename to tests/test_pdfenvelope.py index 48751290..ff8d95a6 100644 --- a/src/diffpy/srreal/tests/testpdfenvelope.py +++ b/tests/test_pdfenvelope.py @@ -1,47 +1,49 @@ #!/usr/bin/env python -"""Unit tests for the PDFEnvelope class from diffpy.srreal.pdfcalculator -""" +"""Unit tests for the PDFEnvelope class from +diffpy.srreal.pdfcalculator.""" -import unittest import pickle -import numpy +import unittest -from diffpy.srreal.tests.testutils import pickle_with_attr -from diffpy.srreal.pdfenvelope import PDFEnvelope, makePDFEnvelope -from diffpy.srreal.pdfenvelope import QResolutionEnvelope, ScaleEnvelope -from diffpy.srreal.pdfenvelope import SphericalShapeEnvelope, StepCutEnvelope -from diffpy.srreal.pdfcalculator import PDFCalculator, DebyePDFCalculator +import numpy +from testutils import pickle_with_attr + +from diffpy.srreal.pdfcalculator import DebyePDFCalculator, PDFCalculator +from diffpy.srreal.pdfenvelope import ( + PDFEnvelope, + QResolutionEnvelope, + ScaleEnvelope, + SphericalShapeEnvelope, + StepCutEnvelope, + makePDFEnvelope, +) # ---------------------------------------------------------------------------- + class TestPDFEnvelope(unittest.TestCase): def setUp(self): - self.fstepcut = PDFEnvelope.createByType('stepcut') + self.fstepcut = PDFEnvelope.createByType("stepcut") self.fstepcut.stepcut = 5 - self.fscale = PDFEnvelope.createByType('scale') + self.fscale = PDFEnvelope.createByType("scale") return - def tearDown(self): - PDFEnvelope._deregisterType('parabolaenvelope') + PDFEnvelope._deregisterType("parabolaenvelope") return - def test___init__(self): - """check PDFEnvelope.__init__() - """ + """Check PDFEnvelope.__init__()""" self.assertEqual(1.0, self.fscale.scale) - self.fscale._setDoubleAttr('scale', 2.0) + self.fscale._setDoubleAttr("scale", 2.0) self.assertEqual(2.0, self.fscale.scale) return - def test___call__(self): - """check PDFEnvelope.__call__() - """ + """Check PDFEnvelope.__call__()""" x = numpy.arange(0, 9.1, 0.3) xb = numpy.array([(0.0, xi) for xi in x])[:, 1] self.assertTrue(xb.strides > x.strides) @@ -59,76 +61,65 @@ def test___call__(self): self.assertEqual(-2.0, self.fscale(3.5)) return - def test_clone(self): - """check PDFEnvelope.clone - """ + """Check PDFEnvelope.clone.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PDFEnvelope().clone) self.fstepcut.stepcut = 17 e2 = self.fstepcut.clone() - self.assertEqual('stepcut', e2.type()) + self.assertEqual("stepcut", e2.type()) self.assertEqual(17.0, e2.stepcut) - self.assertEqual(17.0, e2._getDoubleAttr('stepcut')) + self.assertEqual(17.0, e2._getDoubleAttr("stepcut")) return - def test_create(self): - """check PDFEnvelope.create - """ + """Check PDFEnvelope.create.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PDFEnvelope().create) - self.assertEqual('stepcut', self.fstepcut.create().type()) - self.assertEqual('scale', self.fscale.create().type()) + self.assertEqual("stepcut", self.fstepcut.create().type()) + self.assertEqual("scale", self.fscale.create().type()) self.fstepcut.stepcut = 17 self.assertEqual(0.0, self.fstepcut.create().stepcut) return - def test_type(self): - """check PDFEnvelope.type - """ + """Check PDFEnvelope.type.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PDFEnvelope().type) - self.assertEqual('stepcut', self.fstepcut.type()) - self.assertEqual('scale', self.fscale.type()) + self.assertEqual("stepcut", self.fstepcut.type()) + self.assertEqual("scale", self.fscale.type()) return - def test_createByType(self): - """check PDFEnvelope.createByType() - """ - self.assertRaises(ValueError, PDFEnvelope.createByType, 'notregistered') + """Check PDFEnvelope.createByType()""" + self.assertRaises( + ValueError, PDFEnvelope.createByType, "notregistered" + ) return - def test_getRegisteredTypes(self): - """check PDFEnvelope.getRegisteredTypes - """ + """Check PDFEnvelope.getRegisteredTypes.""" regtypes = PDFEnvelope.getRegisteredTypes() self.assertTrue(2 <= len(regtypes)) - self.assertTrue('stepcut' in regtypes) - self.assertTrue('scale' in regtypes) + self.assertTrue("stepcut" in regtypes) + self.assertTrue("scale" in regtypes) return - def test_pickling(self): - '''check pickling and unpickling of PDFEnvelope. - ''' + """Check pickling and unpickling of PDFEnvelope.""" stp = self.fstepcut stp.stepcut = 11 stp2 = pickle.loads(pickle.dumps(stp)) - self.assertEqual('stepcut', stp2.type()) + self.assertEqual("stepcut", stp2.type()) self.assertEqual(11, stp2.stepcut) - self.assertEqual(11, stp2._getDoubleAttr('stepcut')) + self.assertEqual(11, stp2._getDoubleAttr("stepcut")) return - def test_makePDFEnvelope(self): - '''check the makePDFEnvelope wrapper. - ''' - pbl = makePDFEnvelope('parabolaenvelope', - parabola_envelope, a=1, b=2, c=3) + """Check the makePDFEnvelope wrapper.""" + pbl = makePDFEnvelope( + "parabolaenvelope", parabola_envelope, a=1, b=2, c=3 + ) self.assertEqual(3, pbl(0)) self.assertEqual(6, pbl(1)) self.assertEqual(11, pbl(2)) @@ -139,25 +130,24 @@ def test_makePDFEnvelope(self): self.assertEqual(0, pbl2.b) self.assertEqual(3, pbl2.c) self.assertEqual([7, 3, 28], [pbl2(x) for x in [-2, 0, 5]]) - pbl3 = PDFEnvelope.createByType('parabolaenvelope') + pbl3 = PDFEnvelope.createByType("parabolaenvelope") self.assertEqual(1, pbl3.a) self.assertEqual(2, pbl3.b) self.assertEqual(3, pbl3.c) pbl3.a = 0 - pbl3.foo = 'asdf' + pbl3.foo = "asdf" pbl3cp = pickle.loads(pickle.dumps(pbl3)) self.assertEqual(0, pbl3cp.a) - self.assertEqual('asdf', pbl3cp.foo) + self.assertEqual("asdf", pbl3cp.foo) return - def test_picking_owned(self): - '''verify pickling of envelopes owned by PDF calculators. - ''' - pbl = makePDFEnvelope('parabolaenvelope', - parabola_envelope, a=1, b=2, c=3) + """Verify pickling of envelopes owned by PDF calculators.""" + pbl = makePDFEnvelope( + "parabolaenvelope", parabola_envelope, a=1, b=2, c=3 + ) pbl.a = 7 - pbl.foobar = 'asdf' + pbl.foobar = "asdf" pc = PDFCalculator() pc.envelopes = (pbl,) dbpc = DebyePDFCalculator() @@ -173,117 +163,122 @@ def test_picking_owned(self): dbpc2 = pickle.loads(pickle.dumps(dbpc)) self.assertEqual(3.5, pc2.scale) self.assertEqual(3.5, dbpc2.scale) - pblcopies = [pc2.getEnvelope("parabolaenvelope"), - dbpc2.getEnvelope("parabolaenvelope")] + pblcopies = [ + pc2.getEnvelope("parabolaenvelope"), + dbpc2.getEnvelope("parabolaenvelope"), + ] for pbl2 in pblcopies: self.assertEqual(7, pbl2.a) - self.assertEqual('asdf', pbl2.foobar) - self.assertEqual('parabolaenvelope', pbl2.type()) + self.assertEqual("asdf", pbl2.foobar) + self.assertEqual("parabolaenvelope", pbl2.type()) return + # ---------------------------------------------------------------------------- + class TestQResolutionEnvelope(unittest.TestCase): def setUp(self): self.evlp = QResolutionEnvelope() return - def test_type(self): - self.assertEqual('qresolution', self.evlp.type()) - self.assertTrue(hasattr(self.evlp, 'qdamp')) + self.assertEqual("qresolution", self.evlp.type()) + self.assertTrue(hasattr(self.evlp, "qdamp")) return - def test_pickling(self): evlp = self.evlp evlp.qdamp = 3 evlp2 = pickle.loads(pickle.dumps(evlp)) self.assertEqual(QResolutionEnvelope, type(evlp2)) self.assertEqual(3, evlp2.qdamp) - self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo="bar") return + # ---------------------------------------------------------------------------- + class TestScaleEnvelope(unittest.TestCase): def setUp(self): self.evlp = ScaleEnvelope() return - def test_type(self): - self.assertEqual('scale', self.evlp.type()) - self.assertTrue(hasattr(self.evlp, 'scale')) + self.assertEqual("scale", self.evlp.type()) + self.assertTrue(hasattr(self.evlp, "scale")) return - def test_pickling(self): evlp = self.evlp evlp.scale = 3 evlp2 = pickle.loads(pickle.dumps(evlp)) self.assertEqual(ScaleEnvelope, type(evlp2)) self.assertEqual(3, evlp2.scale) - self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo="bar") return + # ---------------------------------------------------------------------------- + class TestSphericalShapeEnvelope(unittest.TestCase): def setUp(self): self.evlp = SphericalShapeEnvelope() return - def test_type(self): - self.assertEqual('sphericalshape', self.evlp.type()) - self.assertTrue(hasattr(self.evlp, 'spdiameter')) + self.assertEqual("sphericalshape", self.evlp.type()) + self.assertTrue(hasattr(self.evlp, "spdiameter")) return - def test_pickling(self): evlp = self.evlp evlp.spdiameter = 3 evlp2 = pickle.loads(pickle.dumps(evlp)) self.assertEqual(SphericalShapeEnvelope, type(evlp2)) self.assertEqual(3, evlp2.spdiameter) - self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo="bar") return + # ---------------------------------------------------------------------------- + class TestStepCutEnvelope(unittest.TestCase): def setUp(self): self.evlp = StepCutEnvelope() return - def test_type(self): - self.assertEqual('stepcut', self.evlp.type()) - self.assertTrue(hasattr(self.evlp, 'stepcut')) + self.assertEqual("stepcut", self.evlp.type()) + self.assertTrue(hasattr(self.evlp, "stepcut")) return - def test_pickling(self): evlp = self.evlp evlp.stepcut = 3 evlp2 = pickle.loads(pickle.dumps(evlp)) self.assertEqual(StepCutEnvelope, type(evlp2)) self.assertEqual(3, evlp2.stepcut) - self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, evlp, foo="bar") return + # ---------------------------------------------------------------------------- + def parabola_envelope(x, a, b, c): - 'parabola function for wrapping by makePDFEnvelope' + "parabola function for wrapping by makePDFEnvelope" return a * x**2 + b * x + c -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testpeakprofile.py b/tests/test_peakprofile.py similarity index 69% rename from src/diffpy/srreal/tests/testpeakprofile.py rename to tests/test_peakprofile.py index 50f86ae3..dfc59ed2 100644 --- a/src/diffpy/srreal/tests/testpeakprofile.py +++ b/tests/test_peakprofile.py @@ -1,79 +1,70 @@ #!/usr/bin/env python -"""Unit tests for the PeakProfile classes from diffpy.srreal.peakprofile -""" +"""Unit tests for the PeakProfile classes from +diffpy.srreal.peakprofile.""" -import unittest import pickle +import unittest + import numpy +from testutils import mod_structure, pickle_with_attr -from diffpy.srreal.tests.testutils import pickle_with_attr -from diffpy.srreal.tests.testutils import mod_structure -from diffpy.srreal.peakprofile import PeakProfile from diffpy.srreal.pdfcalculator import PDFCalculator +from diffpy.srreal.peakprofile import PeakProfile # ---------------------------------------------------------------------------- + class TestPeakProfile(unittest.TestCase): def setUp(self): - self.pkgauss = PeakProfile.createByType('gaussian') - self.pkcropped = PeakProfile.createByType('croppedgaussian') + self.pkgauss = PeakProfile.createByType("gaussian") + self.pkcropped = PeakProfile.createByType("croppedgaussian") return - def tearDown(self): return - def test___init__(self): - """check PeakProfile.__init__() - """ + """Check PeakProfile.__init__()""" self.assertNotEqual(0.0, self.pkgauss.peakprecision) - self.assertEqual(self.pkgauss.peakprecision, - self.pkcropped.peakprecision) - self.pkgauss._setDoubleAttr('peakprecision', 0.01) + self.assertEqual( + self.pkgauss.peakprecision, self.pkcropped.peakprecision + ) + self.pkgauss._setDoubleAttr("peakprecision", 0.01) self.assertEqual(0.01, self.pkgauss.peakprecision) return - def test_create(self): - """check PeakProfile.create - """ + """Check PeakProfile.create.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PeakProfile().create) - self.assertEqual('gaussian', self.pkgauss.create().type()) + self.assertEqual("gaussian", self.pkgauss.create().type()) self.pkgauss.peakprecision = 0.007 self.assertNotEqual(0.007, self.pkgauss.create().peakprecision) return - def test_clone(self): - """check PeakProfile.clone - """ + """Check PeakProfile.clone.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PeakProfile().clone) self.pkgauss.peakprecision = 0.0003 pkg2 = self.pkgauss.clone() - self.assertEqual('gaussian', pkg2.type()) + self.assertEqual("gaussian", pkg2.type()) self.assertEqual(0.0003, pkg2.peakprecision) - self.assertEqual(0.0003, pkg2._getDoubleAttr('peakprecision')) + self.assertEqual(0.0003, pkg2._getDoubleAttr("peakprecision")) return - def test_type(self): - """check PeakProfile.type - """ + """Check PeakProfile.type.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PeakProfile().type) - self.assertEqual('croppedgaussian', self.pkcropped.type()) + self.assertEqual("croppedgaussian", self.pkcropped.type()) return - def test___call__(self): - """check PeakProfile.__call__() - """ + """Check PeakProfile.__call__()""" ymx = self.pkgauss(0.0, 1) yhalflo = self.pkgauss(-0.5, 1) yhalfhi = self.pkgauss(-0.5, 1) @@ -83,11 +74,10 @@ def test___call__(self): self.assertNotEqual(0, self.pkgauss(10, 1)) return - def test_ticker(self): - """check PeakProfile.ticker() - """ + """Check PeakProfile.ticker()""" from diffpy.srreal.eventticker import EventTicker + et0 = EventTicker(self.pkgauss.ticker()) self.pkgauss.peakprecision = 0.003 et1 = self.pkgauss.ticker() @@ -95,10 +85,9 @@ def test_ticker(self): self.assertTrue(et0 < et1) return - def test_ticker_override(self): - """check method override for PeakProfile.ticker in a derived class. - """ + """Check method override for PeakProfile.ticker in a derived + class.""" pkf = MySawTooth() self.assertEqual(0, pkf.tcnt) et0 = pkf.ticker() @@ -116,33 +105,30 @@ def test_ticker_override(self): self.assertEqual(2, pkf.tcnt) return - def test_getRegisteredTypes(self): - """check PeakProfile.getRegisteredTypes - """ + """Check PeakProfile.getRegisteredTypes.""" regtypes = PeakProfile.getRegisteredTypes() self.assertTrue(2 <= len(regtypes)) - self.assertTrue(regtypes.issuperset( - ['gaussian', 'croppedgaussian'])) + self.assertTrue(regtypes.issuperset(["gaussian", "croppedgaussian"])) return - def test_pickling(self): - '''check pickling and unpickling of PeakProfile. - ''' + """Check pickling and unpickling of PeakProfile.""" pkg = self.pkgauss pkg.peakprecision = 0.0011 pkg2 = pickle.loads(pickle.dumps(pkg)) - self.assertEqual('gaussian', pkg2.type()) + self.assertEqual("gaussian", pkg2.type()) self.assertEqual(0.0011, pkg2.peakprecision) - self.assertEqual(0.0011, pkg2._getDoubleAttr('peakprecision')) - self.assertRaises(RuntimeError, pickle_with_attr, pkg, foo='bar') + self.assertEqual(0.0011, pkg2._getDoubleAttr("peakprecision")) + self.assertRaises(RuntimeError, pickle_with_attr, pkg, foo="bar") pkc = self.pkcropped - self.assertRaises(RuntimeError, pickle_with_attr, pkc, foo='bar') + self.assertRaises(RuntimeError, pickle_with_attr, pkc, foo="bar") return + # ---------------------------------------------------------------------------- + class MySawTooth(PeakProfile): "Helper class for testing PeakProfile." @@ -154,9 +140,11 @@ def create(self): def clone(self): import copy + return copy.copy(self) tcnt = 0 + def ticker(self): self.tcnt += 1 return PeakProfile.ticker(self) @@ -164,7 +152,8 @@ def ticker(self): def __call__(self, x, fwhm): w = 1.0 * fwhm rv = (1 - abs(x) / w) / (1.0 * w) - if rv < 0: rv = 0 + if rv < 0: + rv = 0 return rv def xboundlo(self, fwhm): @@ -173,67 +162,64 @@ def xboundlo(self, fwhm): def xboundhi(self, fwhm): return +fwhm + # End of class MySawTooth + class TestPeakProfileOwner(unittest.TestCase): def setUp(self): MySawTooth()._registerThisType() self.pc = PDFCalculator() - self.pc.peakprofile = 'mysawtooth' + self.pc.peakprofile = "mysawtooth" self.pkf = self.pc.peakprofile self.pkf.peakprecision = 0.0017 return - def tearDown(self): PeakProfile._deregisterType(self.pkf.type()) return - def test_pkftype(self): - '''Check type of the owned PeakProfile instance. - ''' - self.assertEqual('mysawtooth', self.pc.peakprofile.type()) + """Check type of the owned PeakProfile instance.""" + self.assertEqual("mysawtooth", self.pc.peakprofile.type()) return - def test_custom_peakprofile(self): "Check if our MySawTooth is indeed applied." - c2 = mod_structure.Structure(2 * [mod_structure.Atom('C')]) + c2 = mod_structure.Structure(2 * [mod_structure.Atom("C")]) c2.z = [0, 1] c2.Uisoequiv = 0.01 r, g = self.pc(c2) k = g.argmax() self.assertEqual(1, r[k]) - self.assertTrue(numpy.allclose(numpy.diff(g[k - 5:k], 2), 0)) - self.assertTrue(numpy.allclose(numpy.diff(g[k:k + 5], 2), 0)) + self.assertTrue(numpy.allclose(numpy.diff(g[k - 5 : k], 2), 0)) + self.assertTrue(numpy.allclose(numpy.diff(g[k : k + 5], 2), 0)) pkf2 = self.pc.peakprofile.clone() self.assertTrue(isinstance(pkf2, MySawTooth)) self.assertEqual(0.0017, pkf2.peakprecision) return - def test_pickling(self): - '''Check pickling of an owned PeakProfile instance. - ''' + """Check pickling of an owned PeakProfile instance.""" pc1 = pickle.loads(pickle.dumps(self.pc)) self.pkf.peakprecision = 0.0003 - self.pkf.foo = 'bar' + self.pkf.foo = "bar" pc2 = pickle.loads(pickle.dumps(self.pc)) - self.assertEqual('mysawtooth', pc1.peakprofile.type()) + self.assertEqual("mysawtooth", pc1.peakprofile.type()) self.assertEqual(0.0017, pc1.peakprofile.peakprecision) self.assertEqual(0.0017, pc1.peakprecision) - self.assertFalse(hasattr(pc1.peakprofile, 'foo')) - self.assertEqual('mysawtooth', pc2.peakprofile.type()) + self.assertFalse(hasattr(pc1.peakprofile, "foo")) + self.assertEqual("mysawtooth", pc2.peakprofile.type()) self.assertEqual(0.0003, pc2.peakprofile.peakprecision) self.assertEqual(0.0003, pc2.peakprecision) - self.assertEqual('bar', pc2.peakprofile.foo) + self.assertEqual("bar", pc2.peakprofile.foo) return + # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testpeakwidthmodel.py b/tests/test_peakwidthmodel.py similarity index 70% rename from src/diffpy/srreal/tests/testpeakwidthmodel.py rename to tests/test_peakwidthmodel.py index 830b3bdb..2b5bd853 100644 --- a/src/diffpy/srreal/tests/testpeakwidthmodel.py +++ b/tests/test_peakwidthmodel.py @@ -1,38 +1,41 @@ #!/usr/bin/env python -"""Unit tests for the PeakWidthModel classes from diffpy.srreal.peakwidthmodel -""" +"""Unit tests for the PeakWidthModel classes from +diffpy.srreal.peakwidthmodel.""" -import unittest import pickle +import unittest + +from testutils import loadDiffPyStructure -from diffpy.srreal.peakwidthmodel import PeakWidthModel -from diffpy.srreal.peakwidthmodel import DebyeWallerPeakWidth, JeongPeakWidth from diffpy.srreal.pdfcalculator import DebyePDFCalculator, PDFCalculator +from diffpy.srreal.peakwidthmodel import ( + DebyeWallerPeakWidth, + JeongPeakWidth, + PeakWidthModel, +) from diffpy.srreal.structureadapter import createStructureAdapter -from diffpy.srreal.tests.testutils import loadDiffPyStructure # ---------------------------------------------------------------------------- + class TestPeakWidthModel(unittest.TestCase): tio2stru = None tio2adpt = None def setUp(self): - self.pwconst = PeakWidthModel.createByType('constant') + self.pwconst = PeakWidthModel.createByType("constant") self.pwconst.width = 2 if self.tio2stru is None: - self.tio2stru = loadDiffPyStructure('rutile.cif') + self.tio2stru = loadDiffPyStructure("rutile.cif") self.tio2adpt = createStructureAdapter(self.tio2stru) return - def tearDown(self): return - def _genbonds(self, rmin, rmax): "Return ready-to-use BondGenerator for rutile." bnds = self.tio2adpt.createBondGenerator() @@ -41,63 +44,52 @@ def _genbonds(self, rmin, rmax): bnds.rewind() return bnds - def test___init__(self): - """check PeakWidthModel.__init__() - """ + """Check PeakWidthModel.__init__()""" self.assertEqual(2.0, self.pwconst.width) - self.pwconst._setDoubleAttr('width', 3.0) + self.pwconst._setDoubleAttr("width", 3.0) self.assertEqual(3.0, self.pwconst.width) return - def test_create(self): - """check PeakWidthModel.create - """ + """Check PeakWidthModel.create.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PeakWidthModel().create) - self.assertEqual('constant', self.pwconst.create().type()) + self.assertEqual("constant", self.pwconst.create().type()) self.pwconst.width = 17 self.assertEqual(0.0, self.pwconst.create().width) return - def test_clone(self): - """check PeakWidthModel.clone - """ + """Check PeakWidthModel.clone.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PeakWidthModel().clone) self.pwconst.width = 17 pwc2 = self.pwconst.clone() - self.assertEqual('constant', pwc2.type()) + self.assertEqual("constant", pwc2.type()) self.assertEqual(17.0, pwc2.width) - self.assertEqual(17.0, pwc2._getDoubleAttr('width')) + self.assertEqual(17.0, pwc2._getDoubleAttr("width")) return - def test_type(self): - """check PeakWidthModel.type - """ + """Check PeakWidthModel.type.""" # this is a virtual method in the base class self.assertRaises(RuntimeError, PeakWidthModel().type) - self.assertEqual('constant', self.pwconst.type()) + self.assertEqual("constant", self.pwconst.type()) return - def test_calculate(self): - """check PeakWidthModel.calculate() - """ + """Check PeakWidthModel.calculate()""" pwm = PeakWidthModel() bnds = self._genbonds(1, 2) self.assertRaises(RuntimeError, pwm.calculate, bnds) self.assertEqual(2.0, self.pwconst.calculate(bnds)) return - def test_isowidths(self): - """check ConstantPeakWidth properties bisowidth, uisowidth. - """ + """Check ConstantPeakWidth properties bisowidth, uisowidth.""" from numpy import pi + cnpw = self.pwconst dwpw = DebyeWallerPeakWidth() bnds = self._genbonds(1, 2) @@ -107,21 +99,19 @@ def test_isowidths(self): self.assertAlmostEqual(cnpw.bisowidth, 8 * pi**2 * cnpw.uisowidth, 12) return - def test_maxWidth(self): - """check PeakWidthModel.maxWidth() - """ - self.assertRaises(RuntimeError, PeakWidthModel().maxWidth, - self.tio2adpt, 0, 10) + """Check PeakWidthModel.maxWidth()""" + self.assertRaises( + RuntimeError, PeakWidthModel().maxWidth, self.tio2adpt, 0, 10 + ) self.assertEqual(2.0, self.pwconst.maxWidth(self.tio2adpt, 0, 10)) self.assertEqual(2.0, self.pwconst.maxWidth(self.tio2stru, 0, 10)) return - def test_ticker(self): - """check PeakWidthModel.ticker() - """ + """Check PeakWidthModel.ticker()""" from diffpy.srreal.eventticker import EventTicker + et0 = EventTicker(self.pwconst.ticker()) self.pwconst.width = 3 et1 = self.pwconst.ticker() @@ -129,10 +119,9 @@ def test_ticker(self): self.assertTrue(et0 < et1) return - def test_ticker_override(self): - """check PeakWidthModel.ticker override in a Python-derived class. - """ + """Check PeakWidthModel.ticker override in a Python-derived + class.""" pwm = MyPWM() self.assertEqual(0, pwm.tcnt) et0 = pwm.ticker() @@ -150,76 +139,69 @@ def test_ticker_override(self): self.assertEqual(2, pwm.tcnt) return - def test_getRegisteredTypes(self): - """check PeakWidthModel.getRegisteredTypes - """ + """Check PeakWidthModel.getRegisteredTypes.""" regtypes = PeakWidthModel.getRegisteredTypes() self.assertTrue(3 <= len(regtypes)) - self.assertTrue(regtypes.issuperset( - ['constant', 'debye-waller', 'jeong'])) + self.assertTrue( + regtypes.issuperset(["constant", "debye-waller", "jeong"]) + ) return - def test_pickling(self): - '''check pickling and unpickling of PeakWidthModel. - ''' + """Check pickling and unpickling of PeakWidthModel.""" pwc = self.pwconst pwc.width = 11 pwc2 = pickle.loads(pickle.dumps(pwc)) - self.assertEqual('constant', pwc2.type()) + self.assertEqual("constant", pwc2.type()) self.assertEqual(11, pwc2.width) - self.assertEqual(11, pwc2._getDoubleAttr('width')) + self.assertEqual(11, pwc2._getDoubleAttr("width")) return + # ---------------------------------------------------------------------------- + class TestDebyeWallerPeakWidth(unittest.TestCase): def setUp(self): self.pwm = DebyeWallerPeakWidth() return - def test_type(self): - """check DebyeWallerPeakWidth.type - """ - self.assertEqual('debye-waller', self.pwm.type()) + """Check DebyeWallerPeakWidth.type.""" + self.assertEqual("debye-waller", self.pwm.type()) self.assertEqual(0, len(self.pwm._namesOfDoubleAttributes())) return - def test_pickling(self): - """check pickling of DebyeWallerPeakWidth class. - """ - self.assertEqual('debye-waller', self.pwm.type()) + """Check pickling of DebyeWallerPeakWidth class.""" + self.assertEqual("debye-waller", self.pwm.type()) pwm = self.pwm pwm2 = pickle.loads(pickle.dumps(pwm)) self.assertEqual(DebyeWallerPeakWidth, type(pwm2)) return + # ---------------------------------------------------------------------------- + class TestJeongPeakWidth(unittest.TestCase): def setUp(self): self.pwm = JeongPeakWidth() return - def test_type(self): - """check JeongPeakWidth.type - """ - self.assertEqual('jeong', self.pwm.type()) - self.assertTrue(hasattr(self.pwm, 'delta1')) - self.assertTrue(hasattr(self.pwm, 'delta2')) - self.assertTrue(hasattr(self.pwm, 'qbroad')) + """Check JeongPeakWidth.type.""" + self.assertEqual("jeong", self.pwm.type()) + self.assertTrue(hasattr(self.pwm, "delta1")) + self.assertTrue(hasattr(self.pwm, "delta2")) + self.assertTrue(hasattr(self.pwm, "qbroad")) return - def test_pickling(self): - """check pickling of the DebyeWallerPeakWidth class - """ + """Check pickling of the DebyeWallerPeakWidth class.""" pwm = self.pwm pwm.delta1 = 1 pwm.delta2 = 2 @@ -231,8 +213,10 @@ def test_pickling(self): self.assertEqual(3, pwm2.qbroad) return + # ---------------------------------------------------------------------------- + class MyPWM(PeakWidthModel): "Helper class for testing PeakWidthModelOwner." @@ -240,7 +224,7 @@ class MyPWM(PeakWidthModel): def __init__(self): PeakWidthModel.__init__(self) - self._registerDoubleAttribute('pwmscale') + self._registerDoubleAttribute("pwmscale") return def type(self): @@ -251,18 +235,22 @@ def create(self): def clone(self): import copy + return copy.copy(self) def calculate(self, bnds): return self.pwmscale * bnds.msd() tcnt = 0 + def ticker(self): self.tcnt += 1 return PeakWidthModel.ticker(self) + # End of class MyPWM + class TestPeakWidthOwner(unittest.TestCase): @classmethod @@ -270,7 +258,6 @@ def tearDownClass(cls): assert "mypwm" not in PeakWidthModel.getRegisteredTypes() return - def setUp(self): self.pc = PDFCalculator() self.dbpc = DebyePDFCalculator() @@ -279,25 +266,21 @@ def setUp(self): self.dbpc.peakwidthmodel = self.pwm return - def test_pwmtype(self): - '''Check type of the owned PeakWidthModel instance. - ''' - self.assertEqual('mypwm', self.pc.peakwidthmodel.type()) - self.assertEqual('mypwm', self.dbpc.peakwidthmodel.type()) + """Check type of the owned PeakWidthModel instance.""" + self.assertEqual("mypwm", self.pc.peakwidthmodel.type()) + self.assertEqual("mypwm", self.dbpc.peakwidthmodel.type()) return - def test_pickling(self): - '''Check pickling of an owned PeakWidthModel instance. - ''' + """Check pickling of an owned PeakWidthModel instance.""" pc1 = pickle.loads(pickle.dumps(self.pc)) self.pwm.pwmscale = 3 pc2 = pickle.loads(pickle.dumps(self.pc)) - self.assertEqual('mypwm', pc1.peakwidthmodel.type()) + self.assertEqual("mypwm", pc1.peakwidthmodel.type()) self.assertEqual(1.5, pc1.peakwidthmodel.pwmscale) self.assertEqual(1.5, pc1.pwmscale) - self.assertEqual('mypwm', pc2.peakwidthmodel.type()) + self.assertEqual("mypwm", pc2.peakwidthmodel.type()) self.assertEqual(3, pc2.peakwidthmodel.pwmscale) self.assertEqual(3, pc2.pwmscale) dbpc2 = pickle.loads(pickle.dumps(self.dbpc)) @@ -305,9 +288,10 @@ def test_pickling(self): self.assertEqual(3, dbpc2.pwmscale) return + # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testscatteringfactortable.py b/tests/test_scatteringfactortable.py similarity index 53% rename from src/diffpy/srreal/tests/testscatteringfactortable.py rename to tests/test_scatteringfactortable.py index 4edd6023..097d4129 100644 --- a/src/diffpy/srreal/tests/testscatteringfactortable.py +++ b/tests/test_scatteringfactortable.py @@ -1,72 +1,89 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.scatteringfactortable -""" +"""Unit tests for diffpy.srreal.scatteringfactortable.""" -import unittest import pickle +import unittest + import numpy +from testutils import pickle_with_attr -from diffpy.srreal.tests.testutils import pickle_with_attr -from diffpy.srreal.scatteringfactortable import ScatteringFactorTable -from diffpy.srreal.scatteringfactortable import SFTXray, SFTElectron -from diffpy.srreal.scatteringfactortable import SFTNeutron, SFTElectronNumber -from diffpy.srreal.pdfcalculator import PDFCalculator, DebyePDFCalculator +from diffpy.srreal.pdfcalculator import DebyePDFCalculator, PDFCalculator +from diffpy.srreal.scatteringfactortable import ( + ScatteringFactorTable, + SFTElectron, + SFTElectronNumber, + SFTNeutron, + SFTXray, +) # ---------------------------------------------------------------------------- + class LocalTable(ScatteringFactorTable): def clone(self): import copy + return copy.copy(self) - def create(self): return LocalTable() - def _standardLookup(self, smbl, q): return q + 1 - def radiationType(self): return "LTB" - def type(self): return "localtable" + + def create(self): + return LocalTable() + + def _standardLookup(self, smbl, q): + return q + 1 + + def radiationType(self): + return "LTB" + + def type(self): + return "localtable" + def ticker(self): self.tcnt += 1 return ScatteringFactorTable.ticker(self) + tcnt = 0 + # ---------------------------------------------------------------------------- + class TestScatteringFactorTable(unittest.TestCase): def setUp(self): - self.sftx = ScatteringFactorTable.createByType('X') - self.sftn = ScatteringFactorTable.createByType('N') + self.sftx = ScatteringFactorTable.createByType("X") + self.sftn = ScatteringFactorTable.createByType("N") LocalTable()._registerThisType() return def tearDown(self): - ScatteringFactorTable._deregisterType('localtable') + ScatteringFactorTable._deregisterType("localtable") return def test_class_registry(self): - """check if instances are aliased by radiationType(). - """ - ltb = ScatteringFactorTable.createByType('LTB') + """Check if instances are aliased by radiationType().""" + ltb = ScatteringFactorTable.createByType("LTB") self.assertTrue(type(ltb) is LocalTable) - ltb2 = ScatteringFactorTable.createByType('localtable') + ltb2 = ScatteringFactorTable.createByType("localtable") self.assertTrue(type(ltb2) is LocalTable) return def test_ticker(self): - """check ScatteringFactorTable.ticker() - """ + """Check ScatteringFactorTable.ticker()""" from diffpy.srreal.eventticker import EventTicker + et0 = EventTicker(self.sftx.ticker()) - self.sftx.setCustomAs('D', 'H') + self.sftx.setCustomAs("D", "H") et1 = self.sftx.ticker() self.assertNotEqual(et0, et1) self.assertTrue(et0 < et1) return def test_ticker_override(self): - """check Python override of ScatteringFactorTable.ticker. - """ + """Check Python override of ScatteringFactorTable.ticker.""" from diffpy.srreal.pdfcalculator import PDFCalculator + lsft = LocalTable() self.assertEqual(0, lsft.tcnt) et0 = lsft.ticker() @@ -85,108 +102,112 @@ def test_ticker_override(self): return def test_pickling(self): - """check pickling of ScatteringFactorTable instances. - """ + """Check pickling of ScatteringFactorTable instances.""" self.assertEqual(0, len(self.sftx.getCustomSymbols())) - self.sftx.setCustomAs('Na', 'Na', 123) - self.sftx.setCustomAs('Calias', 'C') + self.sftx.setCustomAs("Na", "Na", 123) + self.sftx.setCustomAs("Calias", "C") self.assertEqual(2, len(self.sftx.getCustomSymbols())) sftx1 = pickle.loads(pickle.dumps(self.sftx)) self.assertEqual(2, len(sftx1.getCustomSymbols())) - self.assertAlmostEqual(123, sftx1.lookup('Na'), 12) - self.assertEqual(self.sftx.lookup('C'), sftx1.lookup('Calias')) + self.assertAlmostEqual(123, sftx1.lookup("Na"), 12) + self.assertEqual(self.sftx.lookup("C"), sftx1.lookup("Calias")) self.assertEqual(self.sftx.type(), sftx1.type()) pwa = pickle_with_attr - self.assertRaises(RuntimeError, pwa, SFTXray(), foo='bar') - self.assertRaises(RuntimeError, pwa, SFTElectron(), foo='bar') - self.assertRaises(RuntimeError, pwa, SFTNeutron(), foo='bar') - self.assertRaises(RuntimeError, pwa, SFTElectronNumber(), foo='bar') + self.assertRaises(RuntimeError, pwa, SFTXray(), foo="bar") + self.assertRaises(RuntimeError, pwa, SFTElectron(), foo="bar") + self.assertRaises(RuntimeError, pwa, SFTNeutron(), foo="bar") + self.assertRaises(RuntimeError, pwa, SFTElectronNumber(), foo="bar") return def test_picking_owned(self): - '''verify pickling of envelopes owned by PDF calculators. - ''' + """Verify pickling of envelopes owned by PDF calculators.""" pc = PDFCalculator() dbpc = DebyePDFCalculator() ltb = LocalTable() - ltb.setCustomAs('Na', 'Na', 37) - ltb.foo = 'bar' + ltb.setCustomAs("Na", "Na", 37) + ltb.foo = "bar" pc.scatteringfactortable = ltb dbpc.scatteringfactortable = ltb self.assertIs(ltb, pc.scatteringfactortable) self.assertIs(ltb, dbpc.scatteringfactortable) pc2 = pickle.loads(pickle.dumps(pc)) dbpc2 = pickle.loads(pickle.dumps(dbpc)) - self.assertEqual('localtable', pc2.scatteringfactortable.type()) - self.assertEqual('localtable', dbpc2.scatteringfactortable.type()) - self.assertEqual(37, pc2.scatteringfactortable.lookup('Na')) - self.assertEqual(37, dbpc2.scatteringfactortable.lookup('Na')) - self.assertEqual('bar', pc2.scatteringfactortable.foo) - self.assertEqual('bar', dbpc2.scatteringfactortable.foo) + self.assertEqual("localtable", pc2.scatteringfactortable.type()) + self.assertEqual("localtable", dbpc2.scatteringfactortable.type()) + self.assertEqual(37, pc2.scatteringfactortable.lookup("Na")) + self.assertEqual(37, dbpc2.scatteringfactortable.lookup("Na")) + self.assertEqual("bar", pc2.scatteringfactortable.foo) + self.assertEqual("bar", dbpc2.scatteringfactortable.foo) return def test_pickling_derived(self): - """check pickling of a derived classes. - """ + """Check pickling of a derived classes.""" lsft = LocalTable() - self.assertEqual(3, lsft._standardLookup('Na', 2)) + self.assertEqual(3, lsft._standardLookup("Na", 2)) self.assertEqual(set(), lsft.getCustomSymbols()) - lsft.foobar = 'asdf' - lsft.setCustomAs('Na', 'Na', 123) + lsft.foobar = "asdf" + lsft.setCustomAs("Na", "Na", 123) self.assertEqual(1, len(lsft.getCustomSymbols())) lsft1 = pickle.loads(pickle.dumps(lsft)) self.assertEqual(1, len(lsft1.getCustomSymbols())) - self.assertAlmostEqual(123, lsft1.lookup('Na'), 12) - self.assertEqual('asdf', lsft1.foobar) + self.assertAlmostEqual(123, lsft1.lookup("Na"), 12) + self.assertEqual("asdf", lsft1.foobar) self.assertEqual(lsft.type(), lsft1.type()) - self.assertEqual(3, lsft1._standardLookup('Cl', 2)) - self.assertEqual(1, lsft1.lookup('H')) + self.assertEqual(3, lsft1._standardLookup("Cl", 2)) + self.assertEqual(1, lsft1.lookup("H")) return def test_derived_create(self): - """Check override of ScatteringFactorTable.create in Python class. - """ + """Check override of ScatteringFactorTable.create in Python + class.""" lsft = LocalTable() - lsft.setCustomAs('Xy', 'Na') + lsft.setCustomAs("Xy", "Na") lsft2 = lsft.create() self.assertTrue(isinstance(lsft2, LocalTable)) self.assertEqual(set(), lsft2.getCustomSymbols()) return def test_derived_clone(self): - """Check override of ScatteringFactorTable.clone in Python class. - """ + """Check override of ScatteringFactorTable.clone in Python + class.""" lsft = LocalTable() - lsft.setCustomAs('Xy', 'Na') + lsft.setCustomAs("Xy", "Na") lsft2 = lsft.clone() self.assertTrue(isinstance(lsft2, LocalTable)) - self.assertEqual(set(['Xy']), lsft2.getCustomSymbols()) + self.assertEqual(set(["Xy"]), lsft2.getCustomSymbols()) return def test_lookup(self): - """Check ScatteringFactorTable.lookup handling of array arguments. - """ + """Check ScatteringFactorTable.lookup handling of array + arguments.""" qa = numpy.linspace(0, 50) - qb = numpy.array([(x, 0.0) for x in qa])[:,0] + qb = numpy.array([(x, 0.0) for x in qa])[:, 0] self.assertTrue(qb.strides > qa.strides) sftx = self.sftx - fmn0 = numpy.array([sftx.lookup('Mn', x) for x in qa]) - self.assertTrue(numpy.array_equal(fmn0, sftx.lookup('Mn', qa))) - self.assertTrue(numpy.array_equal(fmn0, sftx.lookup('Mn', qb))) - self.assertTrue(numpy.array_equal( - fmn0.reshape(5, 10), sftx.lookup('Mn', qa.reshape(5, 10)))) - self.assertTrue(numpy.array_equal( - fmn0.reshape(5, 2, 5), sftx.lookup('Mn', qa.reshape(5, 2, 5)))) - self.assertTrue(numpy.array_equal(fmn0, sftx.lookup('Mn', list(qa)))) - self.assertRaises(TypeError, sftx.lookup, 'Na', 'asdf') - self.assertRaises(TypeError, sftx.lookup, 'Na', {}) + fmn0 = numpy.array([sftx.lookup("Mn", x) for x in qa]) + self.assertTrue(numpy.array_equal(fmn0, sftx.lookup("Mn", qa))) + self.assertTrue(numpy.array_equal(fmn0, sftx.lookup("Mn", qb))) + self.assertTrue( + numpy.array_equal( + fmn0.reshape(5, 10), sftx.lookup("Mn", qa.reshape(5, 10)) + ) + ) + self.assertTrue( + numpy.array_equal( + fmn0.reshape(5, 2, 5), sftx.lookup("Mn", qa.reshape(5, 2, 5)) + ) + ) + self.assertTrue(numpy.array_equal(fmn0, sftx.lookup("Mn", list(qa)))) + self.assertRaises(TypeError, sftx.lookup, "Na", "asdf") + self.assertRaises(TypeError, sftx.lookup, "Na", {}) return + # End of class TestScatteringFactorTable # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/testsfaverage.py b/tests/test_sfaverage.py similarity index 64% rename from src/diffpy/srreal/tests/testsfaverage.py rename to tests/test_sfaverage.py index f7838405..1171b590 100644 --- a/src/diffpy/srreal/tests/testsfaverage.py +++ b/tests/test_sfaverage.py @@ -1,38 +1,36 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.sfaverage -""" +"""Unit tests for diffpy.srreal.sfaverage.""" import unittest + import numpy +import pytest +from testutils import loadDiffPyStructure, loadObjCrystCrystal -from diffpy.srreal.sfaverage import SFAverage from diffpy.srreal.scatteringfactortable import ScatteringFactorTable -from diffpy.srreal.tests.testutils import loadDiffPyStructure -from diffpy.srreal.tests.testutils import has_pyobjcryst, _msg_nopyobjcryst -from diffpy.srreal.tests.testutils import loadObjCrystCrystal +from diffpy.srreal.sfaverage import SFAverage # ---------------------------------------------------------------------------- + class TestSFAverage(unittest.TestCase): def setUp(self): - self.sftx = ScatteringFactorTable.createByType('X') - self.sftn = ScatteringFactorTable.createByType('N') + self.sftx = ScatteringFactorTable.createByType("X") + self.sftn = ScatteringFactorTable.createByType("N") return def tearDown(self): return - def test_fromStructure_CdSe(self): - """check SFAverage.fromStructure() for CdSe - """ - cdse = loadDiffPyStructure('CdSe_cadmoselite.cif') + """Check SFAverage.fromStructure() for CdSe.""" + cdse = loadDiffPyStructure("CdSe_cadmoselite.cif") sfavg = SFAverage.fromStructure(cdse, self.sftx) - fcd = self.sftx.lookup('Cd') - fse = self.sftx.lookup('Se') + fcd = self.sftx.lookup("Cd") + fse = self.sftx.lookup("Se") self.assertTrue(isinstance(sfavg.f1sum, float)) self.assertAlmostEqual(0.5 * (fcd + fse), sfavg.f1avg) self.assertAlmostEqual(0.5 * (fcd**2 + fse**2), sfavg.f2avg) @@ -46,23 +44,21 @@ def test_fromStructure_CdSe(self): self.assertEqual(sfavg.f2sum, sfavg2.f2sum[0]) sfavg3 = SFAverage.fromStructure(cdse, self.sftn, qa) self.assertEqual(sfavg3.f1sum[0], sfavg3.f1sum[-1]) - sfavg4 = SFAverage.fromStructure(cdse, 'N', qa) + sfavg4 = SFAverage.fromStructure(cdse, "N", qa) self.assertTrue(numpy.array_equal(sfavg3.f1sum, sfavg4.f1sum)) self.assertTrue(numpy.array_equal(sfavg3.f2sum, sfavg4.f2sum)) - sfavg5 = SFAverage.fromStructure(cdse, 'EN', qa) + sfavg5 = SFAverage.fromStructure(cdse, "EN", qa) self.assertFalse(numpy.array_equal(sfavg3.f1sum, sfavg5.f1sum)) - self.assertRaises(TypeError, SFAverage.fromStructure, - 'notastructure', self.sftx) - self.assertRaises(ValueError, SFAverage.fromStructure, - cdse, 'invalid') + self.assertRaises( + TypeError, SFAverage.fromStructure, "notastructure", self.sftx + ) + self.assertRaises(ValueError, SFAverage.fromStructure, cdse, "invalid") return - def test_fromComposition(self): - """check SFAverage.fromComposition() - """ - sfavg1 = SFAverage.fromComposition({'Na' : 1, 'Cl' : 1}, self.sftx) - fm = ['Na', 0.25, 'Cl', 0.75, 'Cl', 0.25, 'Na', 0.5, 'Na', 0.25] + """Check SFAverage.fromComposition()""" + sfavg1 = SFAverage.fromComposition({"Na": 1, "Cl": 1}, self.sftx) + fm = ["Na", 0.25, "Cl", 0.75, "Cl", 0.25, "Na", 0.5, "Na", 0.25] smblcnts = zip(fm[0::2], fm[1::2]) sfavg2 = SFAverage.fromComposition(smblcnts, self.sftx) self.assertEqual(sfavg1.f1sum, sfavg2.f1sum) @@ -74,39 +70,45 @@ def test_fromComposition(self): self.assertEqual(0, sfempty.count) return + # End of class TestSFAverage # ---------------------------------------------------------------------------- -@unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) + class TestSFAverageObjCryst(unittest.TestCase): + @pytest.fixture(autouse=True) + def _check_periodictable(self, has_pyobjcryst, _msg_nopyobjcryst): + if not has_pyobjcryst: + pytest.skip(_msg_nopyobjcryst) + def setUp(self): - self.sftx = ScatteringFactorTable.createByType('X') + self.sftx = ScatteringFactorTable.createByType("X") return def tearDown(self): return - def test_from_rutile(self): - """check SFAverage.fromStructure for pyobjcryst Crystal of rutile. - """ - rutile = loadObjCrystCrystal('rutile.cif') + """Check SFAverage.fromStructure for pyobjcryst Crystal of + rutile.""" + rutile = loadObjCrystCrystal("rutile.cif") qa = numpy.arange(0, 25, 0.1) sfavg = SFAverage.fromStructure(rutile, self.sftx, qa) - fti = self.sftx.lookup('Ti', qa) - fo = self.sftx.lookup('O', qa) + fti = self.sftx.lookup("Ti", qa) + fo = self.sftx.lookup("O", qa) self.assertTrue(numpy.allclose((fti + 2 * fo) / 3.0, sfavg.f1avg)) fti2, fo2 = fti**2, fo**2 self.assertTrue(numpy.allclose((fti2 + 2 * fo2) / 3.0, sfavg.f2avg)) self.assertEqual(6, sfavg.count) + # End of class TestSFAverageObjCryst # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srreal/tests/teststructureadapter.py b/tests/test_structureadapter.py similarity index 74% rename from src/diffpy/srreal/tests/teststructureadapter.py rename to tests/test_structureadapter.py index 9224ace9..186e2ba6 100644 --- a/src/diffpy/srreal/tests/teststructureadapter.py +++ b/tests/test_structureadapter.py @@ -1,34 +1,46 @@ #!/usr/bin/env python -"""Unit tests for diffpy.srreal.structureadapter -""" +"""Unit tests for diffpy.srreal.structureadapter.""" -import unittest import pickle +import unittest + import numpy +import pytest +from testutils import ( + DerivedAtomicStructureAdapter, + DerivedCrystalStructureAdapter, + DerivedPeriodicStructureAdapter, + DerivedStructureAdapter, + loadCrystalStructureAdapter, + loadDiffPyStructure, + loadObjCrystCrystal, +) + from diffpy.srreal.pdfcalculator import PDFCalculator -from diffpy.srreal.tests.testutils import has_pyobjcryst, _msg_nopyobjcryst -from diffpy.srreal.tests.testutils import loadObjCrystCrystal -from diffpy.srreal.tests.testutils import loadDiffPyStructure -from diffpy.srreal.tests.testutils import loadCrystalStructureAdapter from diffpy.srreal.structureadapter import ( - createStructureAdapter, nometa, nosymmetry, StructureAdapter, - AtomicStructureAdapter, Atom, PeriodicStructureAdapter, - CrystalStructureAdapter) -import diffpy.srreal.tests.testutils as testutils + Atom, + AtomicStructureAdapter, + CrystalStructureAdapter, + PeriodicStructureAdapter, + StructureAdapter, + createStructureAdapter, + nometa, + nosymmetry, +) # ---------------------------------------------------------------------------- + class TestRoutines(unittest.TestCase): def setUp(self): - self.nickel = loadDiffPyStructure('Ni.stru') + self.nickel = loadDiffPyStructure("Ni.stru") return def test_createStructureAdapter(self): - """check createStructureAdapter() routine. - """ + """Check createStructureAdapter() routine.""" adpt = createStructureAdapter(self.nickel) self.assertEqual(4, adpt.countSites()) self.assertTrue(False is adpt.siteAnisotropy(0)) @@ -42,11 +54,12 @@ def test_createStructureAdapter(self): return def test_createStructureAdapterTypes(self): - '''Check types returned by conversion from diffpy.structure. - ''' + """Check types returned by conversion from diffpy.structure.""" from diffpy.srreal.structureconverters import ( DiffPyStructureAtomicAdapter, - DiffPyStructurePeriodicAdapter) + DiffPyStructurePeriodicAdapter, + ) + adpt = createStructureAdapter(self.nickel) self.assertTrue(type(adpt) is DiffPyStructurePeriodicAdapter) self.nickel.pdffit = None @@ -61,8 +74,8 @@ def test_createStructureAdapterTypes(self): return def test_createStructureAdapter_int64_occupancy(self): - """Check Structure conversion when occupany is of numpy.int64 type. - """ + """Check Structure conversion when occupany is of numpy.int64 + type.""" self.nickel[0].occupancy = numpy.int64(0) self.nickel[1].occupancy = numpy.int64(1) adpt = createStructureAdapter(self.nickel) @@ -71,48 +84,54 @@ def test_createStructureAdapter_int64_occupancy(self): return def test_pickling(self): - '''check pickling of StructureAdapter instances. - ''' + """Check pickling of StructureAdapter instances.""" adpt = createStructureAdapter(self.nickel) adpt1 = pickle.loads(pickle.dumps(adpt)) self.assertFalse(adpt is adpt1) self.assertEqual(adpt.countSites(), adpt1.countSites()) self.assertEqual(adpt.totalOccupancy(), adpt1.totalOccupancy()) self.assertEqual(adpt.siteAtomType(1), adpt1.siteAtomType(1)) - self.assertTrue(numpy.array_equal( - adpt.siteCartesianPosition(1), adpt1.siteCartesianPosition(1))) + self.assertTrue( + numpy.array_equal( + adpt.siteCartesianPosition(1), adpt1.siteCartesianPosition(1) + ) + ) self.assertEqual(adpt.siteMultiplicity(1), adpt1.siteMultiplicity(1)) self.assertEqual(adpt.siteOccupancy(1), adpt1.siteOccupancy(1)) self.assertTrue(adpt.siteAnisotropy(1) is adpt1.siteAnisotropy(1)) - self.assertTrue(numpy.array_equal( - adpt.siteCartesianUij(1), adpt1.siteCartesianUij(1))) + self.assertTrue( + numpy.array_equal( + adpt.siteCartesianUij(1), adpt1.siteCartesianUij(1) + ) + ) return def test_pickle_nonwrapped(self): - '''Check if pickling works for non-wrapped C++ object. - ''' + """Check if pickling works for non-wrapped C++ object.""" from diffpy.srreal.structureadapter import EMPTY as e0 + spkl = pickle.dumps(e0) e1 = pickle.loads(spkl) self.assertEqual(0, e1.countSites()) return + # End of class TestStructureAdapter # ---------------------------------------------------------------------------- + class TestDerivedAdapter(unittest.TestCase): - 'Check functionality in a Python-derived StructureAdapter class.' + "Check functionality in a Python-derived StructureAdapter class." - DerivedCls = testutils.DerivedStructureAdapter + DerivedCls = DerivedStructureAdapter def setUp(self): self.adpt = self.DerivedCls() return def test__customPQConfig(self): - """check if DerivedCls._customPQConfig gets called. - """ + """Check if DerivedCls._customPQConfig gets called.""" self.assertEqual(0, self.adpt.cpqcount) pc = PDFCalculator() pc.setStructure(self.adpt) @@ -122,8 +141,7 @@ def test__customPQConfig(self): return def test_pickling(self): - '''check pickling of DerivedCls instances. - ''' + """Check pickling of DerivedCls instances.""" self.adpt.cpqcount = 1 adpt1 = pickle.loads(pickle.dumps(self.adpt)) self.assertTrue(self.DerivedCls is type(adpt1)) @@ -136,31 +154,36 @@ def test_pickling(self): self.assertEqual(3, adpt1.cpqcount) return + # End of class TestDerivedAdapter + class TestDerivedAtomicAdapter(TestDerivedAdapter): - DerivedCls = testutils.DerivedAtomicStructureAdapter + DerivedCls = DerivedAtomicStructureAdapter + class TestDerivedPeriodicAdapter(TestDerivedAdapter): - DerivedCls = testutils.DerivedPeriodicStructureAdapter + DerivedCls = DerivedPeriodicStructureAdapter + class TestDerivedCrystalAdapter(TestDerivedAdapter): - DerivedCls = testutils.DerivedCrystalStructureAdapter + DerivedCls = DerivedCrystalStructureAdapter + # ---------------------------------------------------------------------------- + class TestNoMeta(unittest.TestCase): def setUp(self): - self.nickel = loadDiffPyStructure('Ni.stru') + self.nickel = loadDiffPyStructure("Ni.stru") return def test_nometa(self): - '''check NoMetaStructureAdapter. - ''' + """Check NoMetaStructureAdapter.""" r0, g0 = PDFCalculator()(self.nickel) ni1 = self.nickel.copy() - ni1.pdffit['scale'] = 2.0 + ni1.pdffit["scale"] = 2.0 r1, g1 = PDFCalculator()(ni1) self.assertTrue(numpy.array_equal(r0, r1)) self.assertTrue(numpy.allclose(2 * g0, g1)) @@ -170,7 +193,7 @@ def test_nometa(self): self.assertTrue(numpy.array_equal(r0, r1nm)) self.assertTrue(numpy.allclose(g0, g1nm)) ni2 = self.nickel.copy() - ni2.pdffit['delta2'] = 4 + ni2.pdffit["delta2"] = 4 r2, g2 = PDFCalculator()(ni2) r2, g2nm = PDFCalculator()(nometa(ni2)) self.assertFalse(numpy.allclose(g0, g2)) @@ -183,11 +206,10 @@ def test_nometa(self): return def test_nometa_pickling(self): - '''check pickling of the NoMetaStructureAdapter wrapper. - ''' + """Check pickling of the NoMetaStructureAdapter wrapper.""" r0, g0 = PDFCalculator()(self.nickel) ni1 = self.nickel.copy() - ni1.pdffit['scale'] = 2.0 + ni1.pdffit["scale"] = 2.0 ni1nm = pickle.loads(pickle.dumps(nometa(ni1))) self.assertFalse(ni1nm is ni1) r1nm, g1nm = PDFCalculator()(ni1nm) @@ -196,25 +218,25 @@ def test_nometa_pickling(self): return def test_nometa_twice(self): - '''check that second call of nometa returns the same object. - ''' + """Check that second call of nometa returns the same object.""" adpt1 = nometa(self.nickel) adpt2 = nometa(adpt1) self.assertTrue(adpt1 is adpt2) + # End of class TestNoMeta # ---------------------------------------------------------------------------- + class TestNoSymmetry(unittest.TestCase): def setUp(self): - self.nickel = loadDiffPyStructure('Ni.stru') + self.nickel = loadDiffPyStructure("Ni.stru") return def test_nosymmetry(self): - '''check NoSymmetryStructureAdapter. - ''' + """Check NoSymmetryStructureAdapter.""" pdfc0 = PDFCalculator() r0, g0 = pdfc0(self.nickel) rdf0 = pdfc0.rdf @@ -224,7 +246,7 @@ def test_nosymmetry(self): r1, g1 = pdfc1(niuc) self.assertTrue(numpy.array_equal(r0, r1)) self.assertFalse(numpy.allclose(g0, g1)) - tail = (r0 > 5.0) + tail = r0 > 5.0 self.assertTrue(numpy.allclose(0.0 * g1[tail], g1[tail])) rdf0 = pdfc0.rdf rdf1 = pdfc1.rdf @@ -238,15 +260,14 @@ def test_nosymmetry(self): return def test_nosymmetry_twice(self): - '''check that second call of nosymmetry returns the same object. - ''' + """Check that second call of nosymmetry returns the same + object.""" adpt1 = nosymmetry(self.nickel) adpt2 = nosymmetry(adpt1) self.assertTrue(adpt1 is adpt2) def test_nosymmetry_pickling(self): - '''check pickling of the NoSymmetryStructureAdapter wrapper. - ''' + """Check pickling of the NoSymmetryStructureAdapter wrapper.""" ni1ns = nosymmetry(self.nickel) r1, g1 = PDFCalculator()(ni1ns) ni2ns = pickle.loads(pickle.dumps(ni1ns)) @@ -256,38 +277,48 @@ def test_nosymmetry_pickling(self): self.assertTrue(numpy.array_equal(g1, g2)) return + # End of class TestNoSymmetry # ---------------------------------------------------------------------------- -@unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) + class TestPyObjCrystAdapter(unittest.TestCase): + @pytest.fixture(autouse=True) + def _check_periodictable(self, has_pyobjcryst, _msg_nopyobjcryst): + if not has_pyobjcryst: + pytest.skip(_msg_nopyobjcryst) + def setUp(self): - rutile_crystal = loadObjCrystCrystal('TiO2_rutile-fit.cif') + rutile_crystal = loadObjCrystCrystal("TiO2_rutile-fit.cif") self.rutile = createStructureAdapter(rutile_crystal) return def test_objcryst_adapter(self): - '''check ObjCrystStructureAdapter for rutile. - ''' + """Check ObjCrystStructureAdapter for rutile.""" self.assertEqual(2, self.rutile.countSites()) self.assertEqual(6, self.rutile.totalOccupancy()) self.assertEqual("Ti", self.rutile.siteAtomType(0)) self.assertEqual("O", self.rutile.siteAtomType(1)) self.assertTrue(True is self.rutile.siteAnisotropy(0)) self.assertTrue(True is self.rutile.siteAnisotropy(1)) - self.assertTrue(numpy.allclose( - numpy.diag([0.008698, 0.008698, 0.005492]), - self.rutile.siteCartesianUij(0))) - self.assertTrue(numpy.allclose( - numpy.diag([0.021733, 0.021733, 0.007707]), - self.rutile.siteCartesianUij(1))) + self.assertTrue( + numpy.allclose( + numpy.diag([0.008698, 0.008698, 0.005492]), + self.rutile.siteCartesianUij(0), + ) + ) + self.assertTrue( + numpy.allclose( + numpy.diag([0.021733, 0.021733, 0.007707]), + self.rutile.siteCartesianUij(1), + ) + ) return def test_objcryst_pickling(self): - '''check pickling of the NoSymmetryStructureAdapter wrapper. - ''' + """Check pickling of the NoSymmetryStructureAdapter wrapper.""" r0, g0 = PDFCalculator()(self.rutile) rutile1 = pickle.loads(pickle.dumps(self.rutile)) self.assertFalse(self.rutile is rutile1) @@ -296,12 +327,14 @@ def test_objcryst_pickling(self): self.assertTrue(numpy.array_equal(g0, g1)) return + # End of class TestPyObjCrystAdapter # ---------------------------------------------------------------------------- + class IndexRangeTests(object): - 'Check error handling for site index arguments.' + "Check error handling for site index arguments." AdptClass = None @@ -310,73 +343,74 @@ def setUp(self): return def test_siteAtomTypeIndex(self): - """Check out-of-range arguments in AdptClass.siteAtomType. - """ + """Check out-of-range arguments in AdptClass.siteAtomType.""" cnt = self.adpt.countSites() self.assertRaises(IndexError, self.adpt.siteAtomType, cnt) self.assertRaises(IndexError, self.adpt.siteAtomType, -1) return def test_siteCartesianPositionIndex(self): - """Check out-of-range arguments in AdptClass.siteCartesianPosition. - """ + """Check out-of-range arguments in + AdptClass.siteCartesianPosition.""" cnt = self.adpt.countSites() self.assertRaises(IndexError, self.adpt.siteCartesianPosition, cnt) self.assertRaises(IndexError, self.adpt.siteCartesianPosition, -1) return def test_siteMultiplicityIndex(self): - """Check out-of-range arguments in AdptClass.siteMultiplicity. - """ + """Check out-of-range arguments in + AdptClass.siteMultiplicity.""" cnt = self.adpt.countSites() self.assertRaises(IndexError, self.adpt.siteMultiplicity, cnt) self.assertRaises(IndexError, self.adpt.siteMultiplicity, -1) return def test_siteOccupancyIndex(self): - """Check out-of-range arguments in AdptClass.siteOccupancy. - """ + """Check out-of-range arguments in AdptClass.siteOccupancy.""" cnt = self.adpt.countSites() self.assertRaises(IndexError, self.adpt.siteOccupancy, cnt) self.assertRaises(IndexError, self.adpt.siteOccupancy, -1) return def test_siteAnisotropyIndex(self): - """Check out-of-range arguments in AdptClass.siteAnisotropy. - """ + """Check out-of-range arguments in AdptClass.siteAnisotropy.""" cnt = self.adpt.countSites() self.assertRaises(IndexError, self.adpt.siteAnisotropy, cnt) self.assertRaises(IndexError, self.adpt.siteAnisotropy, -1) return def test_siteCartesianUijIndex(self): - """Check out-of-range arguments in AdptClass.siteCartesianUij. - """ + """Check out-of-range arguments in + AdptClass.siteCartesianUij.""" cnt = self.adpt.countSites() self.assertRaises(IndexError, self.adpt.siteCartesianUij, cnt) self.assertRaises(IndexError, self.adpt.siteCartesianUij, -1) return + # End of class IndexRangeTests TestCase = unittest.TestCase # test index bounds for C++ classes + class TestAtomicAdapterIndexRange(IndexRangeTests, TestCase): AdptClass = AtomicStructureAdapter + # No need to do index tests for PeriodicStructureAdapter as it does not # override any of AtomicStructureAdapter site-access methods. # CrystalStructureAdapter overrides siteMultiplicity so we'll # test it here as well. + class TestCrystalAdapter(IndexRangeTests, TestCase): AdptClass = CrystalStructureAdapter def test_expandLatticeAtom(self): """Check CrystalStructureAdapter.expandLatticeAtom.""" - cdse = loadCrystalStructureAdapter('CdSe_cadmoselite.cif') + cdse = loadCrystalStructureAdapter("CdSe_cadmoselite.cif") a = Atom() a.xyz_cartn = (0.1, 0.13, 0.17) asymsites = cdse.expandLatticeAtom(a) @@ -387,20 +421,22 @@ def test_expandLatticeAtom(self): def test_getEquivalentAtoms(self): """Check CrystalStructureAdapter.getEquivalentAtoms.""" - cdse = loadCrystalStructureAdapter('CdSe_cadmoselite.cif') + cdse = loadCrystalStructureAdapter("CdSe_cadmoselite.cif") eqatoms0 = cdse.getEquivalentAtoms(0) eqatoms1 = cdse.getEquivalentAtoms(1) self.assertTrue(type(eqatoms1) is list) self.assertEqual(2, len(eqatoms0)) self.assertEqual(2, len(eqatoms1)) - self.assertTrue(all(a.atomtype == "Cd" for a in eqatoms0)) - self.assertTrue(all(a.atomtype == "Se" for a in eqatoms1)) + self.assertTrue(all(a.atomtype == "Cd" for a in eqatoms0)) + self.assertTrue(all(a.atomtype == "Se" for a in eqatoms1)) return + # ---------------------------------------------------------------------------- + class TestDerivedStructureAdapter(IndexRangeTests, TestCase): - AdptClass = testutils.DerivedStructureAdapter + AdptClass = DerivedStructureAdapter def setUp(self): IndexRangeTests.setUp(self) @@ -409,121 +445,112 @@ def setUp(self): return def test_siteAtomType_valid(self): - """Check DerivedStructureAdapter.siteAtomType. - """ + """Check DerivedStructureAdapter.siteAtomType.""" adpt1 = self.adpt1 self.assertEqual("Cu", adpt1.siteAtomType(0)) self.assertEqual("", StructureAdapter.siteAtomType(adpt1, 0)) return def test_siteCartesianPosition_valid(self): - """Check DerivedStructureAdapter.siteCartesianPosition. - """ + """Check DerivedStructureAdapter.siteCartesianPosition.""" adpt1 = self.adpt1 xyz0 = adpt1.siteCartesianPosition(0) self.assertTrue(numpy.array_equal([1, 2, 3], xyz0)) return def test_siteMultiplicity_valid(self): - """Check DerivedStructureAdapter.siteMultiplicity. - """ + """Check DerivedStructureAdapter.siteMultiplicity.""" adpt1 = self.adpt1 self.assertEqual(2, adpt1.siteMultiplicity(0)) self.assertEqual(1, StructureAdapter.siteMultiplicity(adpt1, 0)) return def test_siteOccupancy_valid(self): - """Check DerivedStructureAdapter.siteOccupancy. - """ + """Check DerivedStructureAdapter.siteOccupancy.""" adpt1 = self.adpt1 self.assertEqual(0.5, adpt1.siteOccupancy(0)) self.assertEqual(1.0, StructureAdapter.siteOccupancy(adpt1, 0)) return def test_siteAnisotropy_valid(self): - """Check DerivedStructureAdapter.siteAnisotropy. - """ + """Check DerivedStructureAdapter.siteAnisotropy.""" adpt1 = self.adpt1 self.assertFalse(adpt1.siteAnisotropy(0)) return def test_siteCartesianUij_valid(self): - """Check DerivedStructureAdapter.siteCartesianUij. - """ + """Check DerivedStructureAdapter.siteCartesianUij.""" adpt1 = self.adpt1 uiso = 0.005 * numpy.identity(3) self.assertTrue(numpy.array_equal(uiso, adpt1.siteCartesianUij(0))) self.assertRaises(IndexError, adpt1.siteCartesianUij, 1) return + # End of class TestDerivedStructureAdapter # ---------------------------------------------------------------------------- + class TestStructureAdapter(unittest.TestCase): def setUp(self): self.adpt = StructureAdapter() return -# def test__customPQConfig(self): -# """check StructureAdapter._customPQConfig() -# """ -# return -# -# def test_countSites(self): -# """check StructureAdapter.countSites() -# """ -# return -# -# def test_createBondGenerator(self): -# """check StructureAdapter.createBondGenerator() -# """ -# return -# -# def test_numberDensity(self): -# """check StructureAdapter.numberDensity() -# """ -# return + # def test__customPQConfig(self): + # """check StructureAdapter._customPQConfig() + # """ + # return + # + # def test_countSites(self): + # """check StructureAdapter.countSites() + # """ + # return + # + # def test_createBondGenerator(self): + # """check StructureAdapter.createBondGenerator() + # """ + # return + # + # def test_numberDensity(self): + # """check StructureAdapter.numberDensity() + # """ + # return def test_siteAtomType(self): - """check StructureAdapter.siteAtomType() - """ + """Check StructureAdapter.siteAtomType()""" self.assertEqual("", self.adpt.siteAtomType(0)) return def test_siteCartesianPosition(self): - """check StructureAdapter.siteCartesianPosition() - """ + """Check StructureAdapter.siteCartesianPosition()""" self.assertRaises(RuntimeError, self.adpt.siteAnisotropy, 0) return def test_siteMultiplicity(self): - """check StructureAdapter.siteMultiplicity() - """ + """Check StructureAdapter.siteMultiplicity()""" self.assertEqual(1, self.adpt.siteMultiplicity(0)) return def test_siteOccupancy(self): - """check StructureAdapter.siteOccupancy() - """ - # check if we use the C++ method that alwasy return 1. + """Check StructureAdapter.siteOccupancy()""" + # check if we use the C++ method that always return 1. self.assertEqual(1.0, self.adpt.siteOccupancy(0)) self.assertEqual(1.0, self.adpt.siteOccupancy(99)) return def test_siteAnisotropy(self): - """check StructureAdapter.siteAnisotropy() - """ + """Check StructureAdapter.siteAnisotropy()""" self.assertRaises(RuntimeError, self.adpt.siteAnisotropy, 0) return def test_siteCartesianUij(self): - """check StructureAdapter.siteCartesianUij() - """ + """Check StructureAdapter.siteCartesianUij()""" self.assertRaises(RuntimeError, self.adpt.siteCartesianUij, 0) return + # def test_totalOccupancy(self): # """check StructureAdapter.totalOccupancy() # """ @@ -533,6 +560,7 @@ def test_siteCartesianUij(self): # ---------------------------------------------------------------------------- + class TestAtom(unittest.TestCase): def setUp(self): @@ -540,8 +568,7 @@ def setUp(self): return def test___init__copy(self): - '''check Atom copy constructor. - ''' + """Check Atom copy constructor.""" self.a.xyz_cartn = (1, 2, 3) a1 = Atom(self.a) self.assertEqual(self.a, a1) @@ -549,18 +576,16 @@ def test___init__copy(self): return def test_equality(self): - '''check Atom equal and not equal operators. - ''' + """Check Atom equal and not equal operators.""" self.assertEqual(self.a, Atom()) self.assertFalse(self.a != Atom()) a1 = Atom() - a1.atomtype = 'Na' + a1.atomtype = "Na" self.assertNotEqual(self.a, a1) return def test_pickling(self): - '''check pickling of Atom instances. - ''' + """Check pickling of Atom instances.""" self.a.atomtype = "Na" a1 = pickle.loads(pickle.dumps(self.a)) self.assertEqual("Na", a1.atomtype) @@ -569,8 +594,7 @@ def test_pickling(self): return def test_xyz_cartn(self): - '''check Atom.xyz_cartn. - ''' + """Check Atom.xyz_cartn.""" a = self.a a.xyz_cartn = 4, 5, 6 self.assertTrue(numpy.array_equal([4, 5, 6], a.xyz_cartn)) @@ -580,17 +604,22 @@ def test_xyz_cartn(self): return def test_uij_cartn(self): - '''check Atom.uij_cartn - ''' + """Check Atom.uij_cartn.""" a = self.a a.uij_cartn = numpy.identity(3) * 0.01 a.uc12 = 0.012 a.uc13 = 0.013 a.uc23 = 0.023 - self.assertTrue(numpy.array_equal(a.uij_cartn, [ - [0.01, 0.012, 0.013], - [0.012, 0.01, 0.023], - [0.013, 0.023, 0.01]])) + self.assertTrue( + numpy.array_equal( + a.uij_cartn, + [ + [0.01, 0.012, 0.013], + [0.012, 0.01, 0.023], + [0.013, 0.023, 0.01], + ], + ) + ) self.assertEqual(0.01, a.uc11) self.assertEqual(0.01, a.uc22) self.assertEqual(0.01, a.uc33) @@ -600,7 +629,7 @@ def test_uij_cartn(self): return def test_xc_yc_zc(self): - 'check Atom properties xc, yc, zc.' + "check Atom properties xc, yc, zc." a = self.a a.xc, a.yc, a.zc = numpy.arange(1, 4) self.assertEqual(1.0, a.xc) @@ -609,18 +638,18 @@ def test_xc_yc_zc(self): return def test_occupancy(self): - 'check Atom.occupancy' + "check Atom.occupancy" a = self.a - a.occupancy, = numpy.arange(1) + (a.occupancy,) = numpy.arange(1) self.assertEqual(0.0, a.occupancy) a.occupancy = numpy.float32(0.5) self.assertEqual(0.5, a.occupancy) return def test_anisotropy(self): - 'check Atom.anisotropy' + "check Atom.anisotropy" a = self.a - nptrue, npfalse = (numpy.arange(2) < 1) + nptrue, npfalse = numpy.arange(2) < 1 a.anisotropy = nptrue self.assertTrue(a.anisotropy) a.anisotropy = npfalse @@ -628,18 +657,19 @@ def test_anisotropy(self): return def test_ucij(self): - 'check Atom attributes u11, u22, etc.' + "check Atom attributes u11, u22, etc." a = self.a a.uc11, a.uc22, a.uc33, a.uc12, a.uc13, a.uc23 = numpy.arange(1, 7) uijexp = [[1, 4, 5], [4, 2, 6], [5, 6, 3]] self.assertTrue(numpy.array_equal(uijexp, a.uij_cartn)) return + # End of class TestAtom # ---------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 00000000..d50b30db --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,10 @@ +"""Unit tests for __version__.py.""" + +import diffpy.srreal + + +def test_package_version(): + """Ensure the package version is defined and not set to the initial + placeholder.""" + assert hasattr(diffpy.srreal, "__version__") + assert diffpy.srreal.__version__ != "0.0.0" diff --git a/src/diffpy/srreal/tests/testdata/C60bucky.stru b/tests/testdata/C60bucky.stru similarity index 100% rename from src/diffpy/srreal/tests/testdata/C60bucky.stru rename to tests/testdata/C60bucky.stru diff --git a/src/diffpy/srreal/tests/testdata/CdSe_cadmoselite.cif b/tests/testdata/CdSe_cadmoselite.cif similarity index 100% rename from src/diffpy/srreal/tests/testdata/CdSe_cadmoselite.cif rename to tests/testdata/CdSe_cadmoselite.cif diff --git a/src/diffpy/srreal/tests/testdata/CdSe_cadmoselite_N.fgr b/tests/testdata/CdSe_cadmoselite_N.fgr similarity index 100% rename from src/diffpy/srreal/tests/testdata/CdSe_cadmoselite_N.fgr rename to tests/testdata/CdSe_cadmoselite_N.fgr diff --git a/src/diffpy/srreal/tests/testdata/CdSe_cadmoselite_X.fgr b/tests/testdata/CdSe_cadmoselite_X.fgr similarity index 100% rename from src/diffpy/srreal/tests/testdata/CdSe_cadmoselite_X.fgr rename to tests/testdata/CdSe_cadmoselite_X.fgr diff --git a/src/diffpy/srreal/tests/testdata/Ni-fit.fgr b/tests/testdata/Ni-fit.fgr similarity index 100% rename from src/diffpy/srreal/tests/testdata/Ni-fit.fgr rename to tests/testdata/Ni-fit.fgr diff --git a/src/diffpy/srreal/tests/testdata/Ni.cif b/tests/testdata/Ni.cif similarity index 100% rename from src/diffpy/srreal/tests/testdata/Ni.cif rename to tests/testdata/Ni.cif diff --git a/src/diffpy/srreal/tests/testdata/Ni.stru b/tests/testdata/Ni.stru similarity index 98% rename from src/diffpy/srreal/tests/testdata/Ni.stru rename to tests/testdata/Ni.stru index 5d39b959..e13f2693 100644 --- a/src/diffpy/srreal/tests/testdata/Ni.stru +++ b/tests/testdata/Ni.stru @@ -2,11 +2,11 @@ title structure Ni FCC format pdffit scale 1.000000 sharp 0.000000, 0.000000, 1.000000, 0.000000 -spcgr Fm-3m +spcgr Fm-3m cell 3.520000, 3.520000, 3.520000, 90.000000, 90.000000, 90.000000 dcell 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000 ncell 1, 1, 1, 4 -atoms +atoms NI 0.00000000 0.00000000 0.00000000 1.0000 0.00000000 0.00000000 0.00000000 0.0000 0.00126651 0.00126651 0.00126651 diff --git a/src/diffpy/srreal/tests/testdata/Ni_primitive.stru b/tests/testdata/Ni_primitive.stru similarity index 100% rename from src/diffpy/srreal/tests/testdata/Ni_primitive.stru rename to tests/testdata/Ni_primitive.stru diff --git a/src/diffpy/srreal/tests/testdata/TiO2_rutile-fit.cif b/tests/testdata/TiO2_rutile-fit.cif similarity index 100% rename from src/diffpy/srreal/tests/testdata/TiO2_rutile-fit.cif rename to tests/testdata/TiO2_rutile-fit.cif diff --git a/src/diffpy/srreal/tests/testdata/TiO2_rutile-fit.fgr b/tests/testdata/TiO2_rutile-fit.fgr similarity index 100% rename from src/diffpy/srreal/tests/testdata/TiO2_rutile-fit.fgr rename to tests/testdata/TiO2_rutile-fit.fgr diff --git a/src/diffpy/srreal/tests/testdata/TiO2_rutile-fit.stru b/tests/testdata/TiO2_rutile-fit.stru similarity index 100% rename from src/diffpy/srreal/tests/testdata/TiO2_rutile-fit.stru rename to tests/testdata/TiO2_rutile-fit.stru diff --git a/src/diffpy/srreal/tests/testdata/rutile.cif b/tests/testdata/rutile.cif similarity index 100% rename from src/diffpy/srreal/tests/testdata/rutile.cif rename to tests/testdata/rutile.cif diff --git a/src/diffpy/srreal/tests/testdata/silicon.cif b/tests/testdata/silicon.cif similarity index 100% rename from src/diffpy/srreal/tests/testdata/silicon.cif rename to tests/testdata/silicon.cif diff --git a/src/diffpy/srreal/tests/testdata/sphalerite.cif b/tests/testdata/sphalerite.cif similarity index 100% rename from src/diffpy/srreal/tests/testdata/sphalerite.cif rename to tests/testdata/sphalerite.cif diff --git a/src/diffpy/srreal/tests/testutils.py b/tests/testutils.py similarity index 58% rename from src/diffpy/srreal/tests/testutils.py rename to tests/testutils.py index 0cead96d..c2991a33 100644 --- a/src/diffpy/srreal/tests/testutils.py +++ b/tests/testutils.py @@ -1,68 +1,35 @@ #!/usr/bin/env python -"""Helper routines for running other unit tests. -""" +"""Helper routines for running other unit tests.""" import copy -import numpy import pickle -from diffpy.srreal.structureconverters import convertObjCrystCrystal -from diffpy.srreal.tests import logger - -# Deprecated in 1.3 - import of old camel-case diffpy.Structure names. -# TODO drop this in version 1.4. - -try: - import diffpy.structure as mod_structure - from diffpy.structure.parsers import getParser -except ImportError as e: - try: - import diffpy.Structure as mod_structure - from diffpy.Structure.Parsers import getParser - except ImportError: - raise e - del e - -# Resolve availability of optional packages. - -# pyobjcryst - -_msg_nopyobjcryst = "No module named 'pyobjcryst'" -try: - import pyobjcryst.crystal - convertObjCrystCrystal(pyobjcryst.crystal.Crystal()) - has_pyobjcryst = True -except ImportError: - has_pyobjcryst = False - logger.warning('Cannot import pyobjcryst, pyobjcryst tests skipped.') -except TypeError: - has_pyobjcryst = False - logger.warning('Compiled without ObjCryst, pyobjcryst tests skipped.') - -# periodictable - -_msg_noperiodictable = "No module named 'periodictable'" -try: - import periodictable - has_periodictable = True - # silence the pyflakes syntax checker - del periodictable -except ImportError: - has_periodictable = False - logger.warning('Cannot import periodictable, periodictable tests skipped.') +import numpy + +import diffpy.structure as mod_structure +from diffpy.srreal.structureadapter import ( + AtomicStructureAdapter, + CrystalStructureAdapter, + PeriodicStructureAdapter, + StructureAdapter, +) +from diffpy.structure.parsers import getParser # helper functions + def datafile(filename): - from pkg_resources import resource_filename - rv = resource_filename(__name__, "testdata/" + filename) - return rv + from pathlib import Path + + rv = Path(__file__).parent / "testdata" / filename + return str(rv) def loadObjCrystCrystal(filename): from pyobjcryst import loadCrystal + fullpath = datafile(filename) crst = loadCrystal(fullpath) return crst @@ -76,8 +43,9 @@ def loadDiffPyStructure(filename): def loadCrystalStructureAdapter(filename): from diffpy.srreal.structureconverters import _fetchDiffPyStructureData + fullpath = datafile(filename) - pcif = getParser('cif') + pcif = getParser("cif") stru = pcif.parseFile(fullpath) asu = stru[:0] + pcif.asymmetric_unit adpt = CrystalStructureAdapter() @@ -95,12 +63,18 @@ def pickle_with_attr(obj, **attr): rv = pickle.dumps(obj) return rv + +def _maxNormDiff(yobs, ycalc): + """Returned maximum difference normalized by RMS of the yobs.""" + yobsa = numpy.array(yobs) + obsmax = numpy.max(numpy.fabs(yobsa)) or 1 + ynmdiff = (yobsa - ycalc) / obsmax + rv = max(numpy.fabs(ynmdiff)) + return rv + + # helper class for testing overloading of StructureAdapter -from diffpy.srreal.structureadapter import StructureAdapter -from diffpy.srreal.structureadapter import AtomicStructureAdapter -from diffpy.srreal.structureadapter import PeriodicStructureAdapter -from diffpy.srreal.structureadapter import CrystalStructureAdapter class HasCustomPQConfig(object): @@ -117,17 +91,15 @@ def __init__(self): StructureAdapter.__init__(self) self.positions = [] - def clone(self): rv = DerivedStructureAdapter() rv.positions[:] = copy.deepcopy(self.positions) return rv - def createBondGenerator(self): from diffpy.srreal.structureadapter import BaseBondGenerator - return BaseBondGenerator(self) + return BaseBondGenerator(self) def countSites(self): return len(self.positions) @@ -139,16 +111,13 @@ def siteAtomType(self, idx): self._checkindex(idx) return "Cu" - def siteCartesianPosition(self, idx): return self.positions[idx] - def siteMultiplicity(self, idx): self._checkindex(idx) return 2 * StructureAdapter.siteMultiplicity(self, idx) - def siteOccupancy(self, idx): self._checkindex(idx) return 0.5 * StructureAdapter.siteOccupancy(self, idx) @@ -157,28 +126,32 @@ def siteAnisotropy(self, idx): self._checkindex(idx) return False - def siteCartesianUij(self, idx): self._checkindex(idx) return numpy.identity(3, dtype=float) * 0.005 - def _checkindex(self, idx): self.positions[idx] return + # End of class DerivedStructureAdapter -class DerivedAtomicStructureAdapter( - HasCustomPQConfig, AtomicStructureAdapter): + +class DerivedAtomicStructureAdapter(HasCustomPQConfig, AtomicStructureAdapter): pass + class DerivedPeriodicStructureAdapter( - HasCustomPQConfig, PeriodicStructureAdapter): + HasCustomPQConfig, PeriodicStructureAdapter +): pass + class DerivedCrystalStructureAdapter( - HasCustomPQConfig, CrystalStructureAdapter): + HasCustomPQConfig, CrystalStructureAdapter +): pass + # End of file