diff --git a/.codecov.yml b/.codecov.yml index 70686df3..04dd6510 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -10,7 +10,7 @@ coverage: status: patch: default: - target: '80' + target: '70' if_no_uploads: error if_not_found: success if_ci_failed: failure @@ -20,12 +20,13 @@ coverage: target: auto if_no_uploads: error if_not_found: success - if_ci_failed: failure + if_ci_failed: error paths: '!*/tests/.*' tests: target: 97.9% paths: '*/tests/.*' + if_not_found: success flags: tests: diff --git a/.coveragerc b/.coveragerc index d0a0d16d..fe3daa8e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,22 +1,13 @@ -# 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] +source = + diffpy.srfit +[report] omit = - ## exclude debug.py from codecov report - */tests/debug.py + */python?.?/* + */site-packages/nose/* + # ignore _version.py and versioneer.py + .*version.* + *_version.py + +exclude_lines = + if __name__ == '__main__': diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..2d2cb168 --- /dev/null +++ b/.flake8 @@ -0,0 +1,11 @@ +[flake8] +exclude = + .git, + __pycache__, + build, + dist, + doc/source/conf.py +max-line-length = 115 +# 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 9d58a8cd..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 -.gitarchive.cfg export-subst -*.bat text eol=crlf diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 00000000..0f560278 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,35 @@ +--- +name: Release +about: Checklist and communication channel for PyPI and GitHub release +title: "Ready for PyPI/GitHub release" +labels: "release" +assignees: "" +--- + +### PyPI/GitHub release 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 on the website (e.g., diffpy.org) are updated. +- [ ] Successfully run any tutorial examples or do functional testing with the latest Python version. +- [ ] Grammar and writing quality are checked (no typos). + +Please mention @sbillinge here when you are ready for PyPI/GitHub release. Include any additional comments necessary, such as +version information and details about the pre-release here: + +### conda-forge release checklist: + + + +- [ ] New package dependencies listed in `conda.txt` and `test.txt` are added to `meta.yaml` in the feedstock. +- [ ] All relevant issues in the feedstock are addressed in the release PR. + +### 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/workflows/check-news-item.yml b/.github/workflows/check-news-item.yml new file mode 100644 index 00000000..9d696ef9 --- /dev/null +++ b/.github/workflows/check-news-item.yml @@ -0,0 +1,12 @@ +name: Check for News + +on: + pull_request_target: + branches: + - main + +jobs: + build: + uses: Billingegroup/release-scripts/.github/workflows/_check-news-item.yml@v0 + with: + project: diffpy.srfit 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..01dcfbb5 --- /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: + coverage: + uses: Billingegroup/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 + with: + project: diffpy.srfit + c_extension: false + 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..993d589b --- /dev/null +++ b/.github/workflows/publish-docs-on-release.yml @@ -0,0 +1,14 @@ +name: Build and Deploy Docs + +on: + release: + types: + - published + workflow_dispatch: + +jobs: + docs: + uses: Billingegroup/release-scripts/.github/workflows/_publish-docs-on-release.yml@v0 + with: + project: diffpy.srfit + c_extension: false diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml new file mode 100644 index 00000000..3402e468 --- /dev/null +++ b/.github/workflows/tests-on-pr.yml @@ -0,0 +1,16 @@ +name: Tests on PR + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + validate: + uses: Billingegroup/release-scripts/.github/workflows/_tests-on-pr.yml@v0 + with: + project: diffpy.srfit + c_extension: false + headless: false diff --git a/.gitignore b/.gitignore index 9f5ecbf0..a25212ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,44 +1,99 @@ +# 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/ +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 + +# 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 # version information setup.cfg /src/diffpy/*/version.cfg + +# Rever +rever/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..e0926f42 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,4 @@ +[settings] +line_length = 115 +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..3070e199 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,46 @@ +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] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a3469af5..00000000 --- a/.travis.yml +++ /dev/null @@ -1,123 +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.srfit - - 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 - - $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 - python-dev python-setuptools python-numpy - build-essential - - - $NOBREW || test "${TRAVIS_OS_NAME}" = "osx" || exit $? - - $NOBREW || brew update - - $NOBREW || brew upgrade python - - $NOBREW || brew install gcc || brew link --overwrite gcc - - - $NOSYS || devutils/makesdist - - $NOSYS || MYTARBUNDLE="$(ls -t "${PWD}"/dist/*.tar.gz | head -1)" - - -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 - - $NOMC || conda install --yes - diffpy.structure pyobjcryst "diffpy.srreal>=1.3.0" - # TODO - always install srfit-sasview when ready for Python 3. - - if $MYUSEMC && [[ "$MYPYTHON_VERSION" == 2.7 ]]; then - conda install --yes srfit-sasview; - fi - - - $NOSYS || $MYPIP install $MYPIPFLAGS coverage - - $NOSYS || $MYPIP install $MYPIPFLAGS pycifrw - - $NOSYS || $MYPIP install $MYPIPFLAGS diffpy.structure - - $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: - # do not post coverage reports when testing with system Python. - - $NOMC || $MYPIP install $MYPIPFLAGS codecov - - $NOMC || codecov diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 00000000..7b2a5956 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,13 @@ +Authors +======= + +Chris Farrow +Pavol Juhas +Simon J.L. Billinge +Billinge Group and community contributors. + +Contributors +------------ + +For a list of contributors, visit +https://github.com/diffpy/diffpy.srfit/graphs/contributors diff --git a/AUTHORS.txt b/AUTHORS.txt deleted file mode 100644 index 2e7b1714..00000000 --- a/AUTHORS.txt +++ /dev/null @@ -1,9 +0,0 @@ -Authors: - -Chris Farrow -Pavol Juhas -Simon J.L. Billinge - -Contributors: - -https://github.com/diffpy/diffpy.srfit/graphs/contributors diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 7b90ae3f..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,33 +0,0 @@ -# Release notes - -## Version 3.0.0 – 2019-03-14 - -Differences from version 1.3. - -### Added - -- Support for Python 3.7, 3.6, 3.5 in addition to 2.7. - -### Changed - -- Always use lower-case imports from `diffpy.structure`. -- Use numeric-value sort to order variables in `PrintFitHook`. - -### Deprecated - -- Variable `__gitsha__` in the `version` module renamed to `__git_commit__`. - -### Removed - -- Optional upper and lower-bound arguments in `Parameter.setValue`. - The bounds can be set with `Parameter.boundRange` instead. -- Unused classes `ListOperator`, `SetOperator`. - -### Fixed - -- Metadata retrieval from `PDFContribution` hierarchy. -- Refresh `PDFGenerator` when its `rgrid` is changed in-place. -- Zero division in the `nppdfsas.py` example. -- Crash in `ellipsoidsas.py` example because of bug in `Parameter.setValue`. -- Pickling of `ProfileGenerator` objects and of bound class methods. -- Invalid escape sequences in string values. diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 00000000..f9c8f1cd --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,34 @@ +============= +Release Notes +============= + +3.0.0 +==== + +**Added** + +* Support for Python 3.7, 3.6, 3.5 in addition to 2.7. + +**Changed** + +* Always use lower-case imports from `diffpy.structure`. +* Use numeric-value sort to order variables in `PrintFitHook`. + +**Deprecated** + +* Variable `__gitsha__` in the `version` module renamed to `__git_commit__`. + +**Removed** + +* Optional upper and lower-bound arguments in `Parameter.setValue`. + The bounds can be set with `Parameter.boundRange` instead. +* Unused classes `ListOperator`, `SetOperator`. + +**Fixed** + +* Metadata retrieval from `PDFContribution` hierarchy. +* Refresh `PDFGenerator` when its `rgrid` is changed in-place. +* Zero division in the `nppdfsas.py` example. +* Crash in `ellipsoidsas.py` example because of bug in `Parameter.setValue`. +* Pickling of `ProfileGenerator` objects and of bound class methods. +* Invalid escape sequences in string values. diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst new file mode 100644 index 00000000..ff9c3561 --- /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, socio-economic 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.txt b/LICENSE.rst similarity index 100% rename from LICENSE.txt rename to LICENSE.rst diff --git a/LICENSE_DANSE.rst b/LICENSE_DANSE.rst new file mode 100644 index 00000000..d56af619 --- /dev/null +++ b/LICENSE_DANSE.rst @@ -0,0 +1,50 @@ +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 2006-2007, Board of Trustees of Michigan State University, + +Copyright 2008-2012, The Trustees of Columbia University in the +City of New York. (Copyright holder indicated in each source file). + +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: + + 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 + notice, this list of conditions and the following disclaimer. + +- 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 + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS". COPYRIGHT HOLDER +EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES AND CONDITIONS, EITHER +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, TITLE, FITNESS, ADEQUACY OR SUITABILITY +FOR A PARTICULAR PURPOSE, AND ANY WARRANTIES OF FREEDOM FROM +INFRINGEMENT OF ANY DOMESTIC OR FOREIGN PATENT, COPYRIGHTS, TRADE +SECRETS OR OTHER PROPRIETARY RIGHTS OF ANY PARTY. IN NO EVENT SHALL +COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE OR RELATING TO THIS AGREEMENT, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE_DANSE.txt b/LICENSE_DANSE.txt deleted file mode 100644 index c92ca2d5..00000000 --- a/LICENSE_DANSE.txt +++ /dev/null @@ -1,34 +0,0 @@ -This program is part of the DiffPy and DANSE open-source projects at Columbia -University and is available subject to the conditions and terms laid out below. - -Copyright (c) 2008-2011, The Trustees of Columbia University in -the City of New York. All rights reserved. - -For more information please visit the diffpy 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 notice, this - list of conditions and the following disclaimer. - - * 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 names of COLUMBIA UNIVERSITY, MICHIGAN STATE UNIVERSITY nor the - names of their contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in index 3894e104..f1a78eec 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,16 +1,12 @@ -recursive-include src * -include AUTHORS.txt LICENSE*.txt README.rst -recursive-exclude src *.pyc -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 .codecov.yml -exclude .coveragerc -exclude .travis* -prune conda-recipe -prune devutils -prune doc +# 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 59fefa83..b4ae141f 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,41 @@ -.. image:: https://travis-ci.org/diffpy/diffpy.srfit.svg?branch=master - :target: https://travis-ci.org/diffpy/diffpy.srfit +|Icon| |title|_ +=============== -.. image:: https://codecov.io/gh/diffpy/diffpy.srfit/branch/master/graph/badge.svg - :target: https://codecov.io/gh/diffpy/diffpy.srfit +.. |title| replace:: diffpy.srfit +.. _title: https://diffpy.github.io/diffpy.srfit +.. |Icon| image:: https://avatars.githubusercontent.com/diffpy + :target: https://diffpy.github.io/diffpy.srfit + :height: 100px -diffpy.srfit -======================================================================== +|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.srfit/actions/workflows/matrix-and-codecov-on-merge-to-main.yml/badge.svg + :target: https://github.com/diffpy/diffpy.srfit/actions/workflows/matrix-and-codecov-on-merge-to-main.yml + +.. |Codecov| image:: https://codecov.io/gh/diffpy/diffpy.srfit/branch/main/graph/badge.svg + :target: https://codecov.io/gh/diffpy/diffpy.srfit + +.. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/diffpy.srfit + :target: https://anaconda.org/conda-forge/diffpy.srfit + +.. |PR| image:: https://img.shields.io/badge/PR-Welcome-29ab47ff + +.. |PyPi| image:: https://img.shields.io/pypi/v/diffpy.srfit + :target: https://pypi.org/project/diffpy.srfit/ + +.. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/diffpy.srfit + :target: https://pypi.org/project/diffpy.srfit/ + +.. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue + :target: https://github.com/diffpy/diffpy.srfit/issues + +Python package for structure refinement from diffraction data. Configurable code for solving atomic structures. @@ -35,7 +64,21 @@ co-refinements with other techniques. For more information about the diffpy.srfit library, see the users manual at http://diffpy.github.io/diffpy.srfit. -REQUIREMENTS +Citation +-------- + +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: + + P. Juhás, C. L. Farrow, X. Yang, K. R. Knox and S. J. L. Billinge, + `Complex modeling: a strategy and software program for combining + multiple information sources to solve ill posed structure and + nanostructure inverse problems + `__, + *Acta Crystallogr. A* **71**, 562-568 (2015). + +Requirements ------------------------------------------------------------------------ The diffpy.srfit package requires Python 3.5 or later or 2.7 and @@ -79,68 +122,79 @@ Linux some of the required software can be installed using :: For other required packages see their respective web pages for installation instructions. +Installation +------------ -INSTALLATION ------------------------------------------------------------------------- +The preferred method is to use `Miniconda Python +`_ +and install from the "conda-forge" channel of Conda packages. -The preferred method is to use Anaconda Python and install from the -"diffpy" channel of Anaconda packages :: +To add "conda-forge" to the conda channels, run the following in a terminal. :: - conda config --add channels diffpy - conda install diffpy.srfit + conda config --add channels conda-forge -diffpy.srfit is also included in the "diffpy-cmi" collection -of packages for structure analysis :: +We want to install our packages in a suitable conda environment. +The following creates and activates a new environment named ``diffpy.srfit_env`` :: - conda install diffpy-cmi + conda create -n diffpy.srfit_env python=3 + conda activate diffpy.srfit_env -Another option is to use ``easy_install`` to download and install the -latest release from `Python Package Index `_ :: +Then, to fully install ``diffpy.srfit`` in our active environment, run :: - easy_install diffpy.srfit + conda install diffpy.srfit -If you prefer to install from sources, make sure all required software -packages are in place and then run :: +Another option is to use ``pip`` to download and install the latest release from +`Python Package Index `_. +To install using ``pip`` into your ``diffpy.srfit_env`` environment, type :: - python setup.py install + pip install diffpy.srfit -You may need to use ``sudo`` with system Python so the process is -allowed to put files to the system directories. If administrator (root) -access is not available, consult the output from -``python setup.py install --help`` for options to install to a -user-writable locations. The installation integrity can be verified by -changing to the HOME directory and running :: +If you prefer to install from sources, after installing the dependencies, obtain the source archive from +`GitHub `_. Once installed, ``cd`` into your ``diffpy.srfit`` directory +and run the following :: - python -m diffpy.srfit.tests.run + pip install . +Support and Contribute +---------------------- -DEVELOPMENT ------------------------------------------------------------------------- +`Diffpy user group `_ is the discussion forum for general questions and discussions about the use of diffpy.srfit. Please join the diffpy.srfit users community by joining the Google group. The diffpy.srfit project welcomes your expertise and enthusiasm! + +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 `_. diffpy.srfit is an open-source software developed as a part of the DiffPy-CMI -complex modeling initiative at the Brookhaven National Laboratory. The -diffpy.srfit sources are hosted at -https://github.com/diffpy/diffpy.srfit. +complex modeling initiative at Columbia University. -Feel free to fork the project and contribute. To install diffpy.srfit +Feel free to fork the project and contribute. To install diffpy.srfit in a development mode, with its sources being directly used by Python -rather than copied to a package directory, use :: +rather than copied to a package directory, use the following in the root +directory :: - python setup.py develop --user + pip install -e . +To ensure code quality and to prevent accidental commits into the default branch, please set up the use of our pre-commit +hooks. -ACKNOWLEDGEMENT ------------------------------------------------------------------------- +1. Install pre-commit in your working environment by running ``conda install pre-commit``. -The source code in *observable.py* was derived from the 1.0 version -of the Caltech "Pyre" project. +2. Initialize pre-commit (one time only) ``pre-commit install``. + +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. + +Improvements and fixes are always appreciated. +Before contribuing, please read our `Code of Conduct `_. -CONTACTS +Acknowledgement ------------------------------------------------------------------------ -For more information on diffpy.srfit please visit the project web-page +The source code in *observable.py* was derived from the 1.0 version +of the Caltech "Pyre" project. -http://www.diffpy.org +Contact +------- -or email Prof. Simon Billinge at sb2896@columbia.edu. +For more information on diffpy.srfit please visit the project `web-page `_ or email Prof. Simon Billinge at sb2896@columbia.edu. diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.txt similarity index 100% rename from conda-recipe/meta.yaml rename to conda-recipe/meta.txt diff --git a/conda-recipe/run_test.py b/conda-recipe/run_test.txt similarity index 98% rename from conda-recipe/run_test.py rename to conda-recipe/run_test.txt index 545d32fa..6905f7d4 100644 --- a/conda-recipe/run_test.py +++ b/conda-recipe/run_test.txt @@ -1,4 +1,5 @@ #!/usr/bin/env python import diffpy.srfit.tests + assert diffpy.srfit.tests.test().wasSuccessful() diff --git a/devutils/makesdist b/devutils/makesdist deleted file mode 100755 index 6aaae616..00000000 --- a/devutils/makesdist +++ /dev/null @@ -1,54 +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] + '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/Makefile b/doc/Makefile index b9c30f1e..3720ec8e 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,34 +1,194 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build +BASENAME = $(subst .,,$(subst $() $(),,diffpy.srfit)) + +# 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 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# 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 + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @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)" -all: doc examples upload - -RELEASE = alpha9 -TARGET = farrowch@login.cacr.caltech.edu -DOCROOT = ~/docroot/diffraction/ -PKGROOT = ~/dev_danse_us/ - -.PHONY : doc -doc: - epydoc diffpy.srfit --html -vvv -o diffpy.srfitapi -n diffpy.srfit \ ---include-log --exclude diffpy.srfit.structure.cctbxstructure $@ - $(MAKE) -C devmanual $@ - -.PHONY : upload -upload: - rsync -ruvz --delete diffpy.srfitapi $(TARGET):$(DOCROOT) - ssh $(TARGET) "rm -rf $(DOCROOT)/diffpy.srfitapi-$(RELEASE)" - ssh $(TARGET) "cp -r $(DOCROOT)/diffpy.srfitapi $(DOCROOT)/diffpy.srfitapi-$(RELEASE)" - rsync -ruv srfit_examples.zip $(TARGET):$(PKGROOT) - ssh $(TARGET) "rm -rf $(PKGROOT)/srfit_examples-$(RELEASE).zip" - ssh $(TARGET) "cp -r $(PKGROOT)/srfit_examples.zip $(PKGROOT)/srfit_examples-$(RELEASE).zip" - $(MAKE) -C devmanual $@ - -.PHONY : examples -examples: - zip -r srfit_examples.zip ./examples/*.py ./examples/data -x \*svn\* -x \*threedoublepeaks\* -x \*temp\* -x \*test\* - -.PHONY : clean clean: - rm -rf diffpy.srfitapi - rm -f srfit_examples.zip - $(MAKE) -C devmanual $@ + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/$(BASENAME).qhcp" + @echo "To view the help file:" + @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/$(BASENAME)" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/$(BASENAME)" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(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 + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +# Manual publishing to the gh-pages branch + +GITREPOPATH = $(shell cd $(CURDIR) && git rev-parse --git-dir) +GITREMOTE = origin +GITREMOTEURL = $(shell git config --get remote.$(GITREMOTE).url) +GITLASTCOMMIT = $(shell git rev-parse --short HEAD) +publish: + @test -d build/html || \ + ( echo >&2 "Run 'make html' first!"; false ) + git show-ref --verify --quiet refs/heads/gh-pages || \ + git branch --track gh-pages $(GITREMOTE)/gh-pages + test -d build/gh-pages || \ + git clone -s -b gh-pages $(GITREPOPATH) build/gh-pages + cd build/gh-pages && \ + git pull $(GITREMOTEURL) gh-pages + rsync -acv --delete --exclude=.git --exclude=.rsync-exclude \ + --exclude-from=build/gh-pages/.rsync-exclude \ + --link-dest=$(CURDIR)/build/html build/html/ build/gh-pages/ + cd build/gh-pages && \ + git add --all . && \ + git diff --cached --quiet || \ + git commit -m "Sync with the source at $(GITLASTCOMMIT)." + cd build/gh-pages && \ + git push origin gh-pages diff --git a/doc/examples/coreshellnp.py b/doc/examples/coreshellnp.py index 2f1da057..4228baa0 100644 --- a/doc/examples/coreshellnp.py +++ b/doc/examples/coreshellnp.py @@ -22,14 +22,11 @@ """ import numpy -from scipy.optimize import leastsq - from pyobjcryst import loadCrystal +from scipy.optimize import leastsq +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults # Example Code @@ -73,7 +70,8 @@ def makeRecipe(stru1, stru2, datname): # and a spherical shell CF for the shell. Since this is set up as two # phases, we implicitly assume that the core-shell correlations contribute # very little to the PDF. - from diffpy.srfit.pdf.characteristicfunctions import sphericalCF, shellCF + from diffpy.srfit.pdf.characteristicfunctions import shellCF, sphericalCF + contribution.registerFunction(sphericalCF, name="f_CdS") contribution.registerFunction(shellCF, name="f_ZnS") @@ -140,10 +138,11 @@ def plotResults(recipe): diff = g - gcalc + diffzero import pylab - pylab.plot(r, g, 'bo', label="G(r) Data") - pylab.plot(r, gcalc, 'r-', label="G(r) Fit") - pylab.plot(r, diff, 'g-', label="G(r) diff") - pylab.plot(r, diffzero, 'k-') + + pylab.plot(r, g, "bo", label="G(r) Data") + pylab.plot(r, gcalc, "r-", label="G(r) Fit") + pylab.plot(r, diff, "g-", label="G(r) diff") + pylab.plot(r, diffzero, "k-") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) @@ -165,6 +164,7 @@ def main(): stru2 = loadCrystal(znsciffile) recipe = makeRecipe(stru1, stru2, data) from diffpy.srfit.fitbase.fithook import PlotFitHook + recipe.pushFitHook(PlotFitHook()) recipe.fithooks[0].verbose = 3 diff --git a/doc/examples/crystalpdf.py b/doc/examples/crystalpdf.py index ced49e9a..cd6e8138 100644 --- a/doc/examples/crystalpdf.py +++ b/doc/examples/crystalpdf.py @@ -26,21 +26,19 @@ """ import numpy +from gaussianrecipe import scipyOptimize -from diffpy.structure import Structure +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults +from diffpy.structure import Structure -from gaussianrecipe import scipyOptimize +# Example Code -####### Example Code def makeRecipe(ciffile, datname): """Create a fitting recipe for crystalline PDF data.""" - ## The Profile + # The Profile # This will be used to store the observed and calculated PDF profile. profile = Profile() @@ -53,9 +51,9 @@ def makeRecipe(ciffile, datname): parser = PDFParser() parser.parseFile(datname) profile.loadParsedData(parser) - profile.setCalculationRange(xmax = 20) + profile.setCalculationRange(xmax=20) - ## The ProfileGenerator + # The ProfileGenerator # The PDFGenerator is for configuring and calculating a PDF profile. Here, # we want to refine a Structure object from diffpy.structure. We tell the # PDFGenerator that with the 'setStructure' method. All other configuration @@ -67,18 +65,18 @@ def makeRecipe(ciffile, datname): stru.read(ciffile) generator.setStructure(stru) - ## The FitContribution + # The FitContribution # Here we associate the Profile and ProfileGenerator, as has been done # before. contribution = FitContribution("nickel") contribution.addProfileGenerator(generator) - contribution.setProfile(profile, xname = "r") + contribution.setProfile(profile, xname="r") - ## Make the FitRecipe and add the FitContribution. + # Make the FitRecipe and add the FitContribution. recipe = FitRecipe() recipe.addContribution(contribution) - ## Configure the fit variables + # Configure the fit variables # The PDFGenerator class holds the ParameterSet associated with the # Structure passed above in a data member named "phase". (We could have @@ -95,6 +93,7 @@ def makeRecipe(ciffile, datname): # documentation for more details. The 'constrainAsSpaceGroup' method may # create new Parameters, which it returns in a SpaceGroupParameters object. from diffpy.srfit.structure import constrainAsSpaceGroup + sgpars = constrainAsSpaceGroup(phase, "Fm-3m") # The SpaceGroupParameters object returned by 'constrainAsSpaceGroup' holds @@ -124,6 +123,7 @@ def makeRecipe(ciffile, datname): # Give the recipe away so it can be used! return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -135,10 +135,11 @@ def plotResults(recipe): diff = g - gcalc + diffzero import pylab - pylab.plot(r,g,'bo',label="G(r) Data") - pylab.plot(r, gcalc,'r-',label="G(r) Fit") - pylab.plot(r,diff,'g-',label="G(r) diff") - pylab.plot(r,diffzero,'k-') + + pylab.plot(r, g, "bo", label="G(r) Data") + pylab.plot(r, gcalc, "r-", label="G(r) Fit") + pylab.plot(r, diff, "g-", label="G(r) diff") + pylab.plot(r, diffzero, "k-") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) @@ -146,8 +147,8 @@ def plotResults(recipe): pylab.show() return -if __name__ == "__main__": +if __name__ == "__main__": # Make the data and the recipe ciffile = "data/ni.cif" data = "data/ni-q27r100-neutron.gr" diff --git a/doc/examples/crystalpdfall.py b/doc/examples/crystalpdfall.py index c79cf6c5..2383f503 100644 --- a/doc/examples/crystalpdfall.py +++ b/doc/examples/crystalpdfall.py @@ -20,17 +20,14 @@ """ import numpy - +from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults -from gaussianrecipe import scipyOptimize +# Example Code -####### Example Code def makeProfile(datafile): """Make an place data within a Profile.""" @@ -38,28 +35,29 @@ def makeProfile(datafile): parser = PDFParser() parser.parseFile(datafile) profile.loadParsedData(parser) - profile.setCalculationRange(xmax = 20) + profile.setCalculationRange(xmax=20) return profile + def makeContribution(name, generator, profile): """Make a FitContribution and add a generator and profile.""" contribution = FitContribution(name) contribution.addProfileGenerator(generator) - contribution.setProfile(profile, xname = "r") + contribution.setProfile(profile, xname="r") return contribution -def makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si, - xdata_sini): + +def makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si, xdata_sini): """Create a fitting recipe for crystalline PDF data.""" - ## The Profiles + # The Profiles # We need a profile for each data set. xprofile_ni = makeProfile(xdata_ni) xprofile_si = makeProfile(xdata_si) nprofile_ni = makeProfile(ndata_ni) xprofile_sini = makeProfile(xdata_sini) - ## The ProfileGenerators + # The ProfileGenerators # We create one for each phase and share the phases. xgenerator_ni = PDFGenerator("xG_ni") stru = loadCrystal(ciffile_ni) @@ -80,13 +78,12 @@ def makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si, xgenerator_sini_si = PDFGenerator("xG_sini_si") xgenerator_sini_si.setPhase(phase_si) - ## The FitContributions + # The FitContributions # We one of these for each data set. xcontribution_ni = makeContribution("xnickel", xgenerator_ni, xprofile_ni) xcontribution_si = makeContribution("xsilicon", xgenerator_si, xprofile_si) ncontribution_ni = makeContribution("nnickel", ngenerator_ni, nprofile_ni) - xcontribution_sini = makeContribution("xsini", xgenerator_sini_ni, - xprofile_sini) + xcontribution_sini = makeContribution("xsini", xgenerator_sini_ni, xprofile_sini) xcontribution_sini.addProfileGenerator(xgenerator_sini_si) xcontribution_sini.setEquation("scale * (xG_sini_ni + xG_sini_si)") @@ -105,22 +102,22 @@ def makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si, # Now we vary and constrain Parameters as before. for par in phase_ni.sgpars: - recipe.addVar(par, name = par.name + "_ni") + recipe.addVar(par, name=par.name + "_ni") delta2_ni = recipe.newVar("delta2_ni", 2.5) recipe.constrain(xgenerator_ni.delta2, delta2_ni) recipe.constrain(ngenerator_ni.delta2, delta2_ni) recipe.constrain(xgenerator_sini_ni.delta2, delta2_ni) for par in phase_si.sgpars: - recipe.addVar(par, name = par.name + "_si") + recipe.addVar(par, name=par.name + "_si") delta2_si = recipe.newVar("delta2_si", 2.5) recipe.constrain(xgenerator_si.delta2, delta2_si) recipe.constrain(xgenerator_sini_si.delta2, delta2_si) # Now the experimental parameters - recipe.addVar(xgenerator_ni.scale, name = "xscale_ni") - recipe.addVar(xgenerator_si.scale, name = "xscale_si") - recipe.addVar(ngenerator_ni.scale, name = "nscale_ni") + recipe.addVar(xgenerator_ni.scale, name="xscale_ni") + recipe.addVar(xgenerator_si.scale, name="xscale_si") + recipe.addVar(ngenerator_ni.scale, name="nscale_ni") recipe.addVar(xcontribution_sini.scale, 1.0, "xscale_sini") recipe.newVar("pscale_sini_ni", 0.8) recipe.constrain(xgenerator_sini_ni.scale, "pscale_sini_ni") @@ -137,6 +134,7 @@ def makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si, # Give the recipe away so it can be used! return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -145,67 +143,67 @@ def plotResults(recipe): xr_ni = xnickel.profile.x xg_ni = xnickel.profile.y xgcalc_ni = xnickel.profile.ycalc - xdiffzero_ni = -0.8 * max(xg_ni) * numpy.ones_like(xg_ni) + xdiffzero_ni = -0.8 * max(xg_ni) * numpy.ones_like(xg_ni) xdiff_ni = xg_ni - xgcalc_ni + xdiffzero_ni xsilicon = recipe.xsilicon xr_si = xsilicon.profile.x xg_si = xsilicon.profile.y xgcalc_si = xsilicon.profile.ycalc - xdiffzero_si = -0.8 * max(xg_si) * numpy.ones_like(xg_si) + xdiffzero_si = -0.8 * max(xg_si) * numpy.ones_like(xg_si) xdiff_si = xg_si - xgcalc_si + xdiffzero_si nnickel = recipe.nnickel nr_ni = nnickel.profile.x ng_ni = nnickel.profile.y ngcalc_ni = nnickel.profile.ycalc - ndiffzero_ni = -0.8 * max(ng_ni) * numpy.ones_like(ng_ni) + ndiffzero_ni = -0.8 * max(ng_ni) * numpy.ones_like(ng_ni) ndiff_ni = ng_ni - ngcalc_ni + ndiffzero_ni xsini = recipe.xsini xr_sini = xsini.profile.x xg_sini = xsini.profile.y xgcalc_sini = xsini.profile.ycalc - xdiffzero_sini = -0.8 * max(xg_sini) * numpy.ones_like(xg_sini) + xdiffzero_sini = -0.8 * max(xg_sini) * numpy.ones_like(xg_sini) xdiff_sini = xg_sini - xgcalc_sini + xdiffzero_sini - import pylab + pylab.subplot(2, 2, 1) - pylab.plot(xr_ni,xg_ni,'bo',label="G(r) x-ray nickel Data") - pylab.plot(xr_ni,xgcalc_ni,'r-',label="G(r) x-ray nickel Fit") - pylab.plot(xr_ni,xdiff_ni,'g-',label="G(r) x-ray nickel diff") - pylab.plot(xr_ni,xdiffzero_ni,'k-') + pylab.plot(xr_ni, xg_ni, "bo", label="G(r) x-ray nickel Data") + pylab.plot(xr_ni, xgcalc_ni, "r-", label="G(r) x-ray nickel Fit") + pylab.plot(xr_ni, xdiff_ni, "g-", label="G(r) x-ray nickel diff") + pylab.plot(xr_ni, xdiffzero_ni, "k-") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) pylab.subplot(2, 2, 2) - pylab.plot(xr_si,xg_si,'bo',label="G(r) x-ray silicon Data") - pylab.plot(xr_si,xgcalc_si,'r-',label="G(r) x-ray silicon Fit") - pylab.plot(xr_si,xdiff_si,'g-',label="G(r) x-ray silicon diff") - pylab.plot(xr_si,xdiffzero_si,'k-') + pylab.plot(xr_si, xg_si, "bo", label="G(r) x-ray silicon Data") + pylab.plot(xr_si, xgcalc_si, "r-", label="G(r) x-ray silicon Fit") + pylab.plot(xr_si, xdiff_si, "g-", label="G(r) x-ray silicon diff") + pylab.plot(xr_si, xdiffzero_si, "k-") pylab.legend(loc=1) pylab.subplot(2, 2, 3) - pylab.plot(nr_ni,ng_ni,'bo',label="G(r) neutron nickel Data") - pylab.plot(nr_ni,ngcalc_ni,'r-',label="G(r) neutron nickel Fit") - pylab.plot(nr_ni,ndiff_ni,'g-',label="G(r) neutron nickel diff") - pylab.plot(nr_ni,ndiffzero_ni,'k-') + pylab.plot(nr_ni, ng_ni, "bo", label="G(r) neutron nickel Data") + pylab.plot(nr_ni, ngcalc_ni, "r-", label="G(r) neutron nickel Fit") + pylab.plot(nr_ni, ndiff_ni, "g-", label="G(r) neutron nickel diff") + pylab.plot(nr_ni, ndiffzero_ni, "k-") pylab.legend(loc=1) pylab.subplot(2, 2, 4) - pylab.plot(xr_sini,xg_sini,'bo',label="G(r) x-ray sini Data") - pylab.plot(xr_sini,xgcalc_sini,'r-',label="G(r) x-ray sini Fit") - pylab.plot(xr_sini,xdiff_sini,'g-',label="G(r) x-ray sini diff") - pylab.plot(xr_sini,xdiffzero_sini,'k-') + pylab.plot(xr_sini, xg_sini, "bo", label="G(r) x-ray sini Data") + pylab.plot(xr_sini, xgcalc_sini, "r-", label="G(r) x-ray sini Fit") + pylab.plot(xr_sini, xdiff_sini, "g-", label="G(r) x-ray sini diff") + pylab.plot(xr_sini, xdiffzero_sini, "k-") pylab.legend(loc=1) pylab.show() return -if __name__ == "__main__": +if __name__ == "__main__": # Make the data and the recipe ciffile_ni = "data/ni.cif" ciffile_si = "data/si.cif" @@ -215,8 +213,7 @@ def plotResults(recipe): xdata_sini = "data/si90ni10-q27r60-xray.gr" # Make the recipe - recipe = makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si, - xdata_sini) + recipe = makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si, xdata_sini) # Optimize scipyOptimize(recipe) diff --git a/doc/examples/crystalpdfobjcryst.py b/doc/examples/crystalpdfobjcryst.py index c7201493..07c2f454 100644 --- a/doc/examples/crystalpdfobjcryst.py +++ b/doc/examples/crystalpdfobjcryst.py @@ -20,22 +20,20 @@ by the ObjCrystCrystalParSet structure adapter. """ +from crystalpdf import plotResults +from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults -from gaussianrecipe import scipyOptimize -from crystalpdf import plotResults +# Example Code -####### Example Code def makeRecipe(ciffile, datname): """Create a fitting recipe for crystalline PDF data.""" - ## The Profile + # The Profile # This will be used to store the observed and calculated PDF profile. profile = Profile() @@ -46,9 +44,9 @@ def makeRecipe(ciffile, datname): parser = PDFParser() parser.parseFile(datname) profile.loadParsedData(parser) - profile.setCalculationRange(xmax = 20) + profile.setCalculationRange(xmax=20) - ## The ProfileGenerator + # The ProfileGenerator # This time we use the CreateCrystalFromCIF method of pyobjcryst.crystal to # create a Crystal object. That object is passed to the PDFGenerator as in # the previous example. @@ -57,16 +55,16 @@ def makeRecipe(ciffile, datname): generator.setStructure(stru) generator.setQmax(40.0) - ## The FitContribution + # The FitContribution contribution = FitContribution("nickel") contribution.addProfileGenerator(generator) - contribution.setProfile(profile, xname = "r") + contribution.setProfile(profile, xname="r") # Make the FitRecipe and add the FitContribution. recipe = FitRecipe() recipe.addContribution(contribution) - ## Configure the fit variables + # Configure the fit variables # As before, we get a handle to the structure parameter set. In this case, # it is a ObjCrystCrystalParSet instance that was created when we called @@ -89,7 +87,7 @@ def makeRecipe(ciffile, datname): for par in phase.sgpars: recipe.addVar(par) # set the initial thermal factor to a non-zero value - assert hasattr(recipe, 'B11_0') + assert hasattr(recipe, "B11_0") recipe.B11_0 = 0.1 # We now select non-structural parameters to refine. @@ -103,8 +101,8 @@ def makeRecipe(ciffile, datname): # Give the recipe away so it can be used! return recipe -if __name__ == "__main__": +if __name__ == "__main__": # Make the data and the recipe ciffile = "data/si.cif" data = "data/si-q27r60-xray.gr" diff --git a/doc/examples/crystalpdftwodata.py b/doc/examples/crystalpdftwodata.py index a43da784..a076e68f 100644 --- a/doc/examples/crystalpdftwodata.py +++ b/doc/examples/crystalpdftwodata.py @@ -22,22 +22,19 @@ """ import numpy - +from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults -from gaussianrecipe import scipyOptimize +# Example Code -####### Example Code def makeRecipe(ciffile, xdatname, ndatname): """Create a fitting recipe for crystalline PDF data.""" - ## The Profiles + # The Profiles # We need a profile for each data set. This means that we will need two # FitContributions as well. xprofile = Profile() @@ -47,14 +44,14 @@ def makeRecipe(ciffile, xdatname, ndatname): parser = PDFParser() parser.parseFile(xdatname) xprofile.loadParsedData(parser) - xprofile.setCalculationRange(xmax = 20) + xprofile.setCalculationRange(xmax=20) parser = PDFParser() parser.parseFile(ndatname) nprofile.loadParsedData(parser) - nprofile.setCalculationRange(xmax = 20) + nprofile.setCalculationRange(xmax=20) - ## The ProfileGenerators + # The ProfileGenerators # We need one of these for the x-ray data. xgenerator = PDFGenerator("G") stru = loadCrystal(ciffile) @@ -79,15 +76,15 @@ def makeRecipe(ciffile, xdatname, ndatname): ngenerator = PDFGenerator("G") ngenerator.setPhase(xgenerator.phase) - ## The FitContributions + # The FitContributions # We associate the x-ray PDFGenerator and Profile in one FitContribution... xcontribution = FitContribution("xnickel") xcontribution.addProfileGenerator(xgenerator) - xcontribution.setProfile(xprofile, xname = "r") + xcontribution.setProfile(xprofile, xname="r") # and the neutron objects in another. ncontribution = FitContribution("nnickel") ncontribution.addProfileGenerator(ngenerator) - ncontribution.setProfile(nprofile, xname = "r") + ncontribution.setProfile(nprofile, xname="r") # This example is different than the previous ones in that we are composing # a residual function from other residuals (one for the x-ray contribution @@ -132,6 +129,7 @@ def makeRecipe(ciffile, xdatname, ndatname): # Give the recipe away so it can be used! return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -149,18 +147,19 @@ def plotResults(recipe): ndiff = ng - ngcalc + ndiffzero import pylab + pylab.subplot(2, 1, 1) - pylab.plot(xr,xg,'bo',label="G(r) x-ray Data") - pylab.plot(xr,xgcalc,'r-',label="G(r) x-ray Fit") - pylab.plot(xr,xdiff,'g-',label="G(r) x-ray diff") - pylab.plot(xr,xdiffzero,'k-') + pylab.plot(xr, xg, "bo", label="G(r) x-ray Data") + pylab.plot(xr, xgcalc, "r-", label="G(r) x-ray Fit") + pylab.plot(xr, xdiff, "g-", label="G(r) x-ray diff") + pylab.plot(xr, xdiffzero, "k-") pylab.legend(loc=1) pylab.subplot(2, 1, 2) - pylab.plot(nr,ng,'bo',label="G(r) neutron Data") - pylab.plot(nr,ngcalc,'r-',label="G(r) neutron Fit") - pylab.plot(nr,ndiff,'g-',label="G(r) neutron diff") - pylab.plot(nr,ndiffzero,'k-') + pylab.plot(nr, ng, "bo", label="G(r) neutron Data") + pylab.plot(nr, ngcalc, "r-", label="G(r) neutron Fit") + pylab.plot(nr, ndiff, "g-", label="G(r) neutron diff") + pylab.plot(nr, ndiffzero, "k-") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) @@ -168,8 +167,8 @@ def plotResults(recipe): pylab.show() return -if __name__ == "__main__": +if __name__ == "__main__": # Make the data and the recipe ciffile = "data/ni.cif" xdata = "data/ni-q27r60nodg-xray.gr" diff --git a/doc/examples/crystalpdftwophase.py b/doc/examples/crystalpdftwophase.py index df7cce77..447dcc0a 100644 --- a/doc/examples/crystalpdftwophase.py +++ b/doc/examples/crystalpdftwophase.py @@ -22,31 +22,28 @@ """ import numpy - +from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults -from gaussianrecipe import scipyOptimize +# Example Code -####### Example Code def makeRecipe(niciffile, siciffile, datname): """Create a fitting recipe for crystalline PDF data.""" - ## The Profile + # The Profile profile = Profile() # Load data and add it to the profile parser = PDFParser() parser.parseFile(datname) profile.loadParsedData(parser) - profile.setCalculationRange(xmax = 20) + profile.setCalculationRange(xmax=20) - ## The ProfileGenerator + # The ProfileGenerator # In order to fit two phases simultaneously, we must use two PDFGenerators. # PDFGenerator is designed to take care of as little information as it # must. (Don't do too much, and do it well.) A PDFGenerator can generate @@ -66,13 +63,13 @@ def makeRecipe(niciffile, siciffile, datname): stru = loadCrystal(siciffile) generator_si.setStructure(stru) - ## The FitContribution + # The FitContribution # Add both generators to the FitContribution. Add the Profile. This will # send the metadata to the generators. contribution = FitContribution("nisi") contribution.addProfileGenerator(generator_ni) contribution.addProfileGenerator(generator_si) - contribution.setProfile(profile, xname = "r") + contribution.setProfile(profile, xname="r") # Write the fitting equation. We want to sum the PDFs from each phase and # multiply it by a scaling factor. We also want a certain phase scaling @@ -84,7 +81,7 @@ def makeRecipe(niciffile, siciffile, datname): recipe = FitRecipe() recipe.addContribution(contribution) - ## Configure the fit variables + # Configure the fit variables # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.newVar("scale_ni", 0.1) @@ -105,13 +102,13 @@ def makeRecipe(niciffile, siciffile, datname): # First the nickel parameters phase_ni = generator_ni.phase for par in phase_ni.sgpars: - recipe.addVar(par, name = par.name + "_ni") - recipe.addVar(generator_ni.delta2, name = "delta2_ni") + recipe.addVar(par, name=par.name + "_ni") + recipe.addVar(generator_ni.delta2, name="delta2_ni") # Next the silicon parameters phase_si = generator_si.phase for par in phase_si.sgpars: - recipe.addVar(par, name = par.name + "_si") - recipe.addVar(generator_si.delta2, name = "delta2_si") + recipe.addVar(par, name=par.name + "_si") + recipe.addVar(generator_si.delta2, name="delta2_si") # We have prior information from the earlier examples so we'll use it here # in the form of restraints. @@ -121,22 +118,23 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.restrain("a_ni", lb = 3.527, ub = 3.527, scaled = True) + recipe.restrain("a_ni", lb=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.restrain("delta2_ni", lb = 2.22, ub = 2.22, scaled = True) - recipe.restrain("Biso_0_ni", lb = 0.454, ub = 0.454, scaled = True) + recipe.restrain("delta2_ni", lb=2.22, ub=2.22, scaled=True) + recipe.restrain("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.restrain("a_si", lb = 5.430, ub = 5.430, scaled = True) - recipe.restrain("delta2_si", lb = 3.54, ub = 3.54, scaled = True) - recipe.restrain("Biso_0_si", lb = 0.645, ub = 0.645, scaled = True) + recipe.restrain("a_si", lb=5.430, ub=5.430, scaled=True) + recipe.restrain("delta2_si", lb=3.54, ub=3.54, scaled=True) + recipe.restrain("Biso_0_si", lb=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -148,10 +146,11 @@ def plotResults(recipe): diff = g - gcalc + diffzero import pylab - pylab.plot(r,g,'bo',label="G(r) Data") - pylab.plot(r, gcalc,'r-',label="G(r) Fit") - pylab.plot(r,diff,'g-',label="G(r) diff") - pylab.plot(r,diffzero,'k-') + + pylab.plot(r, g, "bo", label="G(r) Data") + pylab.plot(r, gcalc, "r-", label="G(r) Fit") + pylab.plot(r, diff, "g-", label="G(r) diff") + pylab.plot(r, diffzero, "k-") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) @@ -161,7 +160,6 @@ def plotResults(recipe): if __name__ == "__main__": - # Make the data and the recipe niciffile = "data/ni.cif" siciffile = "data/si.cif" diff --git a/doc/examples/data/ni.iq b/doc/examples/data/ni.iq index 917301a1..56594ba3 100644 --- a/doc/examples/data/ni.iq +++ b/doc/examples/data/ni.iq @@ -2462,4 +2462,3 @@ 26.123511 0 26.133371 0 26.143228 0 - diff --git a/doc/examples/debyemodel.py b/doc/examples/debyemodel.py index 79bf7add..c5ef7f45 100644 --- a/doc/examples/debyemodel.py +++ b/doc/examples/debyemodel.py @@ -34,11 +34,10 @@ """ import numpy - -from diffpy.srfit.fitbase import FitContribution, FitRecipe, Profile, FitResults - from gaussianrecipe import scipyOptimize +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile + # The data data = """\ 015.0 0.00334 0.00013 @@ -54,7 +53,8 @@ 500.0 0.03946 0.00250 """ -####### Example Code +# Example Code + def makeRecipe(): """Make the recipe for the fit. @@ -80,17 +80,17 @@ def makeRecipe(): """ - ## The Profile + # The Profile # Create a Profile to hold the experimental and calculated signal. profile = Profile() # Load data and add it to the profile. It is our responsibility to get our # data into the profile. - xydy = numpy.array(map(float, data.split()), dtype=float).reshape(-1,3) + xydy = numpy.array(map(float, data.split()), dtype=float).reshape(-1, 3) x, y, dy = numpy.hsplit(xydy, 3) profile.setObservedProfile(x, y, dy) - ## The FitContribution + # The FitContribution # The FitContribution associates the profile with the Debye function. contribution = FitContribution("pb") # Tell the contribution about the Profile. We will need to use the @@ -123,7 +123,7 @@ def makeRecipe(): # can specify that as well. contribution.setEquation("debye(T, 207.2, abs(thetaD)) + offset") - ## The FitRecipe + # The FitRecipe # The FitRecipe lets us define what we want to fit. It is where we can # create variables, constraints and restraints. If we had multiple profiles # to fit simultaneously, the contribution from each could be added to the @@ -146,11 +146,12 @@ def makeRecipe(): # breaking the restraint by the point-average chi^2 value so that the # restraint is roughly as significant as any other data point throughout # the fit. - recipe.restrain(recipe.offset, lb = 0, scaled = True) + recipe.restrain(recipe.offset, lb=0, scaled=True) # We're done setting up the recipe. We can now do other things with it. return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -163,15 +164,17 @@ def plotResults(recipe): Ucalc = recipe.pb.profile.ycalc import pylab - pylab.plot(T,U,'o',label="Pb $U_{iso}$ Data") - pylab.plot(T,Ucalc) + + pylab.plot(T, U, "o", label="Pb $U_{iso}$ Data") + pylab.plot(T, Ucalc) pylab.xlabel("T (K)") pylab.ylabel(r"$U_{iso} (\AA^2)$") - pylab.legend(loc = (0.0,0.8)) + pylab.legend(loc=(0.0, 0.8)) pylab.show() return + def main(): """The workflow of creating, running and inspecting a fit.""" @@ -197,12 +200,14 @@ def main(): # as we treat them as if existing in some external library that we cannot # modify. + def debye(T, m, thetaD): """A wrapped version of 'adps' that can handle an array of T-values.""" y = numpy.array([adps(m, thetaD, x) for x in T]) return y -def adps(m,thetaD,T): + +def adps(m, thetaD, T): """Calculates atomic displacement factors within the Debye model = (3h^2/4 pi^2 m kB thetaD)(phi(thetaD/T)/(ThetaD/T) + 1/4) @@ -216,9 +221,9 @@ def adps(m,thetaD,T): Uiso -- float -- the thermal factor from the Debye recipe at temp T """ - h = 6.6260755e-34 # Planck's constant. J.s of m^2.kg/s + h = 6.6260755e-34 # Planck's constant. J.s of m^2.kg/s kB = 1.3806503e-23 # Boltzmann's constant. J/K - amu = 1.66053886e-27 # Atomic mass unit. kg + amu = 1.66053886e-27 # Atomic mass unit. kg def __phi(x): """evaluates the phi integral needed in Debye calculation @@ -232,29 +237,26 @@ def __phi(x): phi -- float -- value of the phi function """ - def __debyeKernel(xi): - """function needed by debye calculators - """ - y = xi/(numpy.exp(xi)-1) + def __debyeKernel(xi): + """function needed by debye calculators""" + y = xi / (numpy.exp(xi) - 1) return y import scipy.integrate int = scipy.integrate.quad(__debyeKernel, 0, x) - phi = (1/x) * int[0] + phi = (1 / x) * int[0] return phi - m = m * amu - u2 = (3*h**2 / (4 * numpy.pi**2 *m *kB *thetaD))*\ - (__phi(thetaD/T)/(thetaD/T) + 1./4.) + u2 = (3 * h**2 / (4 * numpy.pi**2 * m * kB * thetaD)) * (__phi(thetaD / T) / (thetaD / T) + 1.0 / 4.0) - return u2*1e20 + return u2 * 1e20 -if __name__ == "__main__": +if __name__ == "__main__": main() # End of file diff --git a/doc/examples/debyemodelII.py b/doc/examples/debyemodelII.py index 0e5e3542..e8858b84 100644 --- a/doc/examples/debyemodelII.py +++ b/doc/examples/debyemodelII.py @@ -34,11 +34,12 @@ done. """ +from debyemodel import makeRecipe, scipyOptimize + from diffpy.srfit.fitbase import FitRecipe, FitResults -from debyemodel import makeRecipe, scipyOptimize +# Example Code -####### Example Code def makeRecipeII(): """Make a recipe for fitting low and high temperature regions. @@ -81,8 +82,8 @@ def makeRecipeII(): # Vary the offset from each FitContribution separately, while keeping the # Debye temperatures the same. We give each offset variable a different # name in the recipe so it retains its identity. - recipe.addVar(recipe.lowT.offset, name = "lowToffset") - recipe.addVar(recipe.highT.offset, name = "highToffset") + recipe.addVar(recipe.lowT.offset, name="lowToffset") + recipe.addVar(recipe.highT.offset, name="highToffset") # We create a new Variable and use the recipe's "constrain" method to # associate the Debye temperature parameters with that variable. recipe.newVar("thetaD", 100) @@ -90,6 +91,7 @@ def makeRecipeII(): recipe.constrain(recipe.highT.thetaD, "thetaD") return recipe + def plotResults(recipe): """Display the results contained within a refined FitRecipe.""" @@ -99,8 +101,8 @@ def plotResults(recipe): # We want to extend the fitting range to its full extent so we can get a # nice full plot. - recipe.lowT.profile.setCalculationRange(xmin='obs', xmax='obs') - recipe.highT.profile.setCalculationRange(xmin='obs', xmax='obs') + recipe.lowT.profile.setCalculationRange(xmin="obs", xmax="obs") + recipe.highT.profile.setCalculationRange(xmin="obs", xmax="obs") T = recipe.lowT.profile.x U = recipe.lowT.profile.y # We can use a FitContribution's 'evaluateEquation' method to evaluate @@ -114,20 +116,21 @@ def plotResults(recipe): # Now we can plot this. import pylab - pylab.plot(T,U,'o',label="Pb $U_{iso}$ Data") - lbl1 = r"$T_d$=%3.1f K, lowToff=%1.5f $\AA^2$"% (abs(thetaD),lowToffset) - lbl2 = r"$T_d$=%3.1f K, highToff=%1.5f $\AA^2$"% (abs(thetaD),highToffset) - pylab.plot(T,lowU,label=lbl1) - pylab.plot(T,highU,label=lbl2) + + pylab.plot(T, U, "o", label="Pb $U_{iso}$ Data") + lbl1 = r"$T_d$=%3.1f K, lowToff=%1.5f $\AA^2$" % (abs(thetaD), lowToffset) + lbl2 = r"$T_d$=%3.1f K, highToff=%1.5f $\AA^2$" % (abs(thetaD), highToffset) + pylab.plot(T, lowU, label=lbl1) + pylab.plot(T, highU, label=lbl2) pylab.xlabel("T (K)") pylab.ylabel(r"$U_{iso} (\AA^2)$") - pylab.legend(loc = (0.0,0.8)) + pylab.legend(loc=(0.0, 0.8)) pylab.show() return -def main(): +def main(): # Create the recipe recipe = makeRecipeII() @@ -147,7 +150,6 @@ def main(): if __name__ == "__main__": - main() # End of file diff --git a/doc/examples/ellipsoidsas.py b/doc/examples/ellipsoidsas.py index 87c4d016..6cf5ea24 100644 --- a/doc/examples/ellipsoidsas.py +++ b/doc/examples/ellipsoidsas.py @@ -16,18 +16,18 @@ """Example of a refinement of SAS I(Q) data to an ellipsoidal model. """ +from gaussianrecipe import scipyOptimize + +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.sas import SASGenerator, SASParser -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults, Profile -from gaussianrecipe import scipyOptimize +# Example Code -####### Example Code def makeRecipe(datname): """Create a fitting recipe for ellipsoidal SAS data.""" - ## The Profile + # The Profile # This will be used to store the observed and calculated I(Q) data. profile = Profile() @@ -37,7 +37,7 @@ def makeRecipe(datname): parser.parseFile(datname) profile.loadParsedData(parser) - ## The ProfileGenerator + # The ProfileGenerator # The SASGenerator is for configuring and calculating a SAS profile. We use # a sas model to configure and serve as the calculation engine of the # generator. This allows us to use the full sas model creation @@ -45,15 +45,16 @@ def makeRecipe(datname): # data. The documentation for the various sas models can be found at # http://www.sasview.org. from sas.models.EllipsoidModel import EllipsoidModel + model = EllipsoidModel() generator = SASGenerator("generator", model) - ## The FitContribution + # The FitContribution # Here we associate the Profile and ProfileGenerator, as has been done # before. contribution = FitContribution("ellipsoid") contribution.addProfileGenerator(generator) - contribution.setProfile(profile, xname = "q") + contribution.setProfile(profile, xname="q") # We want to fit the log of the signal to the log of the data so that the # higher-Q information remains significant. There are no I(Q) uncertainty @@ -61,11 +62,11 @@ def makeRecipe(datname): # will have on the estimated parameter uncertainties. contribution.setResidualEquation("log(eq) - log(y)") - ## Make the FitRecipe and add the FitContribution. + # Make the FitRecipe and add the FitContribution. recipe = FitRecipe() recipe.addContribution(contribution) - ## Configure the fit variables + # Configure the fit variables # The SASGenerator uses the parameters from the params and dispersion # attribues of the model. These vary from model to model, but are adopted # as SrFit Parameters within the generator. Whereas the dispersion @@ -81,6 +82,7 @@ def makeRecipe(datname): # Give the recipe away so it can be used! return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -91,9 +93,10 @@ def plotResults(recipe): diff = y - ycalc + min(y) import pylab - pylab.loglog(r,y,'bo',label="I(Q) Data") - pylab.loglog(r, ycalc,'r-',label="I(Q) Fit") - pylab.loglog(r,diff,'g-',label="I(Q) diff") + + pylab.loglog(r, y, "bo", label="I(Q) Data") + pylab.loglog(r, ycalc, "r-", label="I(Q) Fit") + pylab.loglog(r, diff, "g-", label="I(Q) diff") pylab.xlabel(r"$Q (\AA^{-1})$") pylab.ylabel("$I (arb. units)$") pylab.legend(loc=1) @@ -101,8 +104,8 @@ def plotResults(recipe): pylab.show() return -if __name__ == "__main__": +if __name__ == "__main__": # Make the data and the recipe data = "data/sas_ellipsoid_testdata.txt" diff --git a/doc/examples/gaussiangenerator.py b/doc/examples/gaussiangenerator.py index 7b0818b8..e0d13eec 100644 --- a/doc/examples/gaussiangenerator.py +++ b/doc/examples/gaussiangenerator.py @@ -41,10 +41,10 @@ from numpy import exp -from diffpy.srfit.fitbase import ProfileGenerator, Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe +from diffpy.srfit.fitbase import FitContribution, FitRecipe, Profile, ProfileGenerator + +# Example Code -####### Example Code class GaussianGenerator(ProfileGenerator): """A class for calculating a Gaussian profile. @@ -86,9 +86,9 @@ def __init__(self, name): # ProfileGenerator. The signature is # _newParameter(name, value). # See the API for full details. - self._newParameter('A', 1.0) - self._newParameter('x0', 0.0) - self._newParameter('sigma', 1.0) + self._newParameter("A", 1.0) + self._newParameter("x0", 0.0) + self._newParameter("sigma", 1.0) return def __call__(self, x): @@ -107,13 +107,15 @@ def __call__(self, x): # Now we can use them. Note that we imported exp from numpy at the top # of the module. - y = A * exp(-0.5*(x-x0)**2/sigma**2) + y = A * exp(-0.5 * (x - x0) ** 2 / sigma**2) # Now return the value. return y + # End class GaussianGenerator + def makeRecipe(): """Create a recipe that uses the GaussianGenerator. @@ -122,7 +124,7 @@ def makeRecipe(): """ - ## The Profile + # The Profile # Create a Profile to hold the experimental and calculated signal. profile = Profile() @@ -130,12 +132,12 @@ def makeRecipe(): # numpy. profile.loadtxt("data/gaussian.dat") - ## The ProfileGenerator + # The ProfileGenerator # Create a GaussianGenerator named "g". This will be the name we use to # refer to the generator from within the FitContribution equation. generator = GaussianGenerator("g") - ## The FitContribution + # The FitContribution # Create a FitContribution that will associate the Profile with the # GaussianGenerator. The GaussianGenerator will be accessible as an # attribute of the FitContribution by its name ("g"). Note that this will @@ -144,7 +146,7 @@ def makeRecipe(): contribution.addProfileGenerator(generator) contribution.setProfile(profile) - ## The FitRecipe + # The FitRecipe # Now we create the FitRecipe and add the FitContribution. recipe = FitRecipe() recipe.addContribution(contribution) @@ -159,7 +161,7 @@ def makeRecipe(): # gaussianrecipe.py so we can expect the same output. recipe.addVar(generator.A, 1) recipe.addVar(generator.x0, 5) - recipe.addVar(generator.sigma, name = "sig") + recipe.addVar(generator.sigma, name="sig") recipe.sig.value = 1 # Give the recipe away so it can be used! @@ -167,10 +169,10 @@ def makeRecipe(): if __name__ == "__main__": - # We can use main from gaussianrecipe.py, since this doesn't care if we use # a ProfileGenerator or not. from gaussianrecipe import main + main() # End of file diff --git a/doc/examples/gaussianrecipe.py b/doc/examples/gaussianrecipe.py index b4bb404d..1a5938ce 100644 --- a/doc/examples/gaussianrecipe.py +++ b/doc/examples/gaussianrecipe.py @@ -46,9 +46,10 @@ from __future__ import print_function -from diffpy.srfit.fitbase import FitContribution, FitRecipe, Profile, FitResults +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile + +# Example Code -####### Example Code def main(): """The workflow of creating, running and inspecting a fit.""" @@ -91,7 +92,7 @@ def makeRecipe(): """ - ## The Profile + # The Profile # Create a Profile to hold the experimental and calculated signal. profile = Profile() @@ -99,7 +100,7 @@ def makeRecipe(): # numpy. profile.loadtxt("data/gaussian.dat") - ## The FitContribution + # The FitContribution # The FitContribution associates the Profile with a fitting equation. The # FitContribution also stores the parameters of the fitting equation. We # give our FitContribution then name "g1". We will be able to access the @@ -127,7 +128,7 @@ def makeRecipe(): # attribute. Parameters also have a 'name' attribute. contribution.A.value = 1.0 - ## The FitRecipe + # The FitRecipe # The FitRecipe lets us define what we want to fit. It is where we can # create variables, constraints and restraints. recipe = FitRecipe() @@ -150,11 +151,12 @@ def makeRecipe(): # Here we create a Variable named 'sig', which is tied to the 'sigma' # Parameter of our FitContribution. We give it an initial value through the # FitRecipe instance. - recipe.addVar(contribution.sigma, name = "sig") + recipe.addVar(contribution.sigma, name="sig") recipe.sig.value = 1 return recipe + def scipyOptimize(recipe): """Optimize the recipe created above using scipy. @@ -169,11 +171,13 @@ def scipyOptimize(recipe): # (recipe.residual) and the starting values of the Variables # (recipe.getValues()). from scipy.optimize.minpack import leastsq + print("Fit using scipy's LM optimizer") leastsq(recipe.residual, recipe.getValues()) return + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -189,17 +193,18 @@ def plotResults(recipe): # This stuff is specific to pylab from the matplotlib distribution. import pylab - pylab.plot(x, y, 'b.', label = "observed Gaussian") - pylab.plot(x, ycalc, 'g-', label = "calculated Gaussian") - pylab.legend(loc = (0.0,0.8)) + + pylab.plot(x, y, "b.", label="observed Gaussian") + pylab.plot(x, ycalc, "g-", label="calculated Gaussian") + pylab.legend(loc=(0.0, 0.8)) pylab.xlabel("x") pylab.ylabel("y") pylab.show() return -if __name__ == "__main__": +if __name__ == "__main__": main() # End of file diff --git a/doc/examples/interface.py b/doc/examples/interface.py index 106af7a2..97a8ae23 100644 --- a/doc/examples/interface.py +++ b/doc/examples/interface.py @@ -19,12 +19,12 @@ the diffpy.srfit.interface.interface.py module. """ -from diffpy.srfit.fitbase import FitContribution, FitRecipe, Profile, FitResults +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile -####### Example Code +# Example Code -def main(): +def main(): p = Profile() p.loadtxt("data/gaussian.dat") @@ -47,11 +47,12 @@ def main(): # loosely tying parameters to a value. r = FitRecipe() r |= c - r += (c.A, 0.5), (c.x0, 5), 'sig' - r *= c.sigma, 'sig' + r += (c.A, 0.5), (c.x0, 5), "sig" + r *= c.sigma, "sig" r %= c.A, 0.5, 0.5 from gaussianrecipe import scipyOptimize + scipyOptimize(r) res = FitResults(r) @@ -59,6 +60,7 @@ def main(): res.printResults() # Plot the results. from gaussianrecipe import plotResults + plotResults(r) return diff --git a/doc/examples/npintensity.py b/doc/examples/npintensity.py index 91a2b55d..41a41d08 100644 --- a/doc/examples/npintensity.py +++ b/doc/examples/npintensity.py @@ -45,15 +45,13 @@ from __future__ import print_function import numpy +from gaussianrecipe import scipyOptimize -from diffpy.srfit.fitbase import ProfileGenerator, Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile, ProfileGenerator from diffpy.srfit.structure.diffpyparset import DiffpyStructureParSet -from gaussianrecipe import scipyOptimize +# Example Code -####### Example Code class IntensityGenerator(ProfileGenerator): """A class for calculating intensity using the Debye equation. @@ -136,6 +134,7 @@ def setStructure(self, strufile): """ # Load the structure from file from diffpy.structure import Structure + stru = Structure() stru.read(strufile) @@ -167,6 +166,7 @@ def __call__(self, q): print("iofq called", self.count) return iofq(self.phase.stru, q) + # End class IntensityGenerator @@ -178,21 +178,21 @@ def makeRecipe(strufile, datname): """ - ## The Profile + # The Profile # Create a Profile. This will hold the experimental and calculated signal. profile = Profile() # Load data and add it to the profile x, y, u = profile.loadtxt(datname) - ## The ProfileGenerator + # The ProfileGenerator # Create an IntensityGenerator named "I". This will be the name we use to # refer to the generator from within the FitContribution equation. We also # need to load the model structure we're using. generator = IntensityGenerator("I") generator.setStructure(strufile) - ## The FitContribution + # The FitContribution # Create a FitContribution, that will associate the Profile with the # ProfileGenerator. The ProfileGenerator will be accessible as an # attribute of the FitContribution by its name ("I"). We also want to tell @@ -200,7 +200,7 @@ def makeRecipe(strufile, datname): # use it in equations with this name. contribution = FitContribution("bucky") contribution.addProfileGenerator(generator) - contribution.setProfile(profile, xname = "q") + contribution.setProfile(profile, xname="q") # Now we're ready to define the fitting equation for the FitContribution. # We need to modify the intensity calculation, and we'll do that from @@ -233,8 +233,9 @@ def makeRecipe(strufile, datname): # function and registering it with the FitContribution. pi = numpy.pi exp = numpy.exp + def gaussian(q, q0, width): - return 1/(2*pi*width**2)**0.5 * exp(-0.5 * ((q-q0)/width)**2) + return 1 / (2 * pi * width**2) ** 0.5 * exp(-0.5 * ((q - q0) / width) ** 2) # This registers the python function and extracts the name and creates # Parameters from the arguments. @@ -294,8 +295,8 @@ def gaussian(q, q0, width): # Give the recipe away so it can be used! return recipe -def main(): +def main(): # Make the data and the recipe strufile = "data/C60.stru" q = numpy.arange(1, 20, 0.05) @@ -315,30 +316,32 @@ def main(): # 'iofq' from the IntensityGenerator. rescount = recipe.fithooks[0].count calcount = recipe.bucky.I.count - footer = "iofq called %i%% of the time"%int(100.0*calcount/rescount) - res.printResults(footer = footer) + footer = "iofq called %i%% of the time" % int(100.0 * calcount / rescount) + res.printResults(footer=footer) # Plot! plotResults(recipe) return + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" # All this should be pretty familiar by now. q = recipe.bucky.profile.x - I = recipe.bucky.profile.y - Icalc = recipe.bucky.profile.ycalc + intensity = recipe.bucky.profile.y + intensity_calc = recipe.bucky.profile.ycalc bkgd = recipe.bucky.evaluateEquation("bkgd") - diff = I - Icalc + diff = intensity - intensity_calc import pylab - pylab.plot(q,I,'ob',label="I(Q) Data") - pylab.plot(q,Icalc,'r-',label="I(Q) Fit") - pylab.plot(q,diff,'g-',label="I(Q) diff") - pylab.plot(q,bkgd,'c-',label="Bkgd. Fit") + + pylab.plot(q, intensity, "ob", label="I(Q) Data") + pylab.plot(q, intensity_calc, "r-", label="I(Q) Fit") + pylab.plot(q, diff, "g-", label="I(Q) diff") + pylab.plot(q, bkgd, "c-", label="Bkgd. Fit") pylab.xlabel(r"$Q (\AA^{-1})$") pylab.ylabel("Intensity (arb. units)") pylab.legend(loc=1) @@ -371,22 +374,20 @@ def iofq(S, q): # The precision of distance measurements deltad = 1e-6 - dmult = int(1/deltad) + dmult = int(1 / deltad) deltau = deltad**2 - umult = int(1/deltau) + umult = int(1 / deltau) pairdict = {} elcount = {} n = len(S) for i in range(n): - # count the number of each element eli = S[i].element m = elcount.get(eli, 0) elcount[eli] = m + 1 for j in range(i + 1, n): - elj = S[j].element # Get the pair @@ -395,11 +396,11 @@ def iofq(S, q): # Get the distance to the desired precision d = S.distance(i, j) - D = int(d*dmult) + D = int(d * dmult) # Get the DW factor to the same precision ss = S[i].Uisoequiv + S[j].Uisoequiv - SS = int(ss*umult) + SS = int(ss * umult) # Record the multiplicity of this pair key = (els[0], els[1], D, SS) @@ -439,6 +440,7 @@ def iofq(S, q): return y + def getXScatteringFactor(el, q): """Get the x-ray scattering factor for an element over the q range. @@ -447,15 +449,17 @@ def getXScatteringFactor(el, q): """ try: import cctbx.eltbx.xray_scattering as xray + wk1995 = xray.wk1995(el) g = wk1995.fetch() # at_stol - at sin(theta)/lambda = Q/(4*pi) - f = numpy.asarray( map( g.at_stol, q/(4*numpy.pi) ) ) + f = numpy.asarray(map(g.at_stol, q / (4 * numpy.pi))) return f except ImportError: return 1 -def makeData(strufile, q, datname, scale, a, Uiso, sig, bkgc, nl = 1): + +def makeData(strufile, q, datname, scale, a, Uiso, sig, bkgc, nl=1): """Make some fake data and save it to file. Make some data to fit. This uses iofq to calculate an intensity curve, and @@ -474,6 +478,7 @@ def makeData(strufile, q, datname, scale, a, Uiso, sig, bkgc, nl = 1): """ from diffpy.structure import Structure + S = Structure() S.read(strufile) @@ -487,11 +492,11 @@ def makeData(strufile, q, datname, scale, a, Uiso, sig, bkgc, nl = 1): # We want to broaden the peaks as well. This simulates instrument effects. q0 = q[len(q) // 2] - g = numpy.exp(-0.5*((q-q0)/sig)**2) - y = numpy.convolve(y, g, mode='same')/sum(g) + g = numpy.exp(-0.5 * ((q - q0) / sig) ** 2) + y = numpy.convolve(y, g, mode="same") / sum(g) # Add a polynomial background. - bkgd = (q + bkgc)**2 * (1.5*max(q) - q)**5 + bkgd = (q + bkgc) ** 2 * (1.5 * max(q) - q) ** 5 bkgd *= 0.2 * max(y) / max(bkgd) y += bkgd @@ -500,11 +505,11 @@ def makeData(strufile, q, datname, scale, a, Uiso, sig, bkgc, nl = 1): y *= scale # Calculate the uncertainty - u = (y/nl)**0.5 + u = (y / nl) ** 0.5 # And apply the noise if nl > 0: - y = numpy.random.poisson(y*nl) / nl + y = numpy.random.poisson(y * nl) / nl # Now save it numpy.savetxt(datname, numpy.transpose([q, y, u])) @@ -512,7 +517,6 @@ def makeData(strufile, q, datname, scale, a, Uiso, sig, bkgc, nl = 1): if __name__ == "__main__": - main() # End of file diff --git a/doc/examples/npintensityII.py b/doc/examples/npintensityII.py index 7ab98dce..b5f27fb4 100644 --- a/doc/examples/npintensityII.py +++ b/doc/examples/npintensityII.py @@ -35,14 +35,13 @@ """ import numpy +from gaussianrecipe import scipyOptimize +from npintensity import IntensityGenerator, makeData -from diffpy.srfit.fitbase import FitContribution, FitRecipe, Profile, FitResults -from npintensity import IntensityGenerator -from npintensity import makeData +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile -from gaussianrecipe import scipyOptimize +# Example Code -####### Example Code def makeRecipe(strufile, datname1, datname2): """Create a recipe that uses the IntensityGenerator. @@ -59,7 +58,7 @@ def makeRecipe(strufile, datname1, datname2): """ - ## The Profiles + # The Profiles # Create two Profiles for the two FitContributions. profile1 = Profile() profile2 = Profile() @@ -68,7 +67,7 @@ def makeRecipe(strufile, datname1, datname2): profile1.loadtxt(datname1) x, y, u = profile2.loadtxt(datname2) - ## The ProfileGenerators + # The ProfileGenerators # Create two IntensityGenerators named "I". There will not be a name # conflict, since the name is only meaningful within the FitContribution # that holds the ProfileGenerator. Load the structure into one and make @@ -81,14 +80,14 @@ def makeRecipe(strufile, datname1, datname2): generator2 = IntensityGenerator("I") generator2.addParameterSet(generator1.phase) - ## The FitContributions + # The FitContributions # Create the FitContributions. contribution1 = FitContribution("bucky1") contribution1.addProfileGenerator(generator1) - contribution1.setProfile(profile1, xname = "q") + contribution1.setProfile(profile1, xname="q") contribution2 = FitContribution("bucky2") contribution2.addProfileGenerator(generator2) - contribution2.setProfile(profile2, xname = "q") + contribution2.setProfile(profile2, xname="q") # Now we're ready to define the fitting equation for each FitContribution. # The functions registered below will be independent, even though they take @@ -105,8 +104,9 @@ def makeRecipe(strufile, datname1, datname2): # We will create the broadening function by registering a python function. pi = numpy.pi exp = numpy.exp + def gaussian(q, q0, width): - return 1/(2*pi*width**2)**0.5 * exp(-0.5 * ((q-q0)/width)**2) + return 1 / (2 * pi * width**2) ** 0.5 * exp(-0.5 * ((q - q0) / width) ** 2) contribution1.registerFunction(gaussian) contribution2.registerFunction(gaussian) @@ -128,32 +128,32 @@ def gaussian(q, q0, width): # background that we just defined in the FitContributions. We have to do # this separately for each FitContribution. We tag the variables so it is # easy to retrieve the background variables. - recipe.addVar(contribution1.b0, 0, name = "b1_0", tag = "bcoeffs1") - recipe.addVar(contribution1.b1, 0, name = "b1_1", tag = "bcoeffs1") - recipe.addVar(contribution1.b2, 0, name = "b1_2", tag = "bcoeffs1") - recipe.addVar(contribution1.b3, 0, name = "b1_3", tag = "bcoeffs1") - recipe.addVar(contribution1.b4, 0, name = "b1_4", tag = "bcoeffs1") - recipe.addVar(contribution1.b5, 0, name = "b1_5", tag = "bcoeffs1") - recipe.addVar(contribution1.b6, 0, name = "b1_6", tag = "bcoeffs1") - recipe.addVar(contribution1.b7, 0, name = "b1_7", tag = "bcoeffs1") - recipe.addVar(contribution1.b8, 0, name = "b1_8", tag = "bcoeffs1") - recipe.addVar(contribution1.b9, 0, name = "b1_9", tag = "bcoeffs1") - recipe.addVar(contribution2.b0, 0, name = "b2_0", tag = "bcoeffs2") - recipe.addVar(contribution2.b1, 0, name = "b2_1", tag = "bcoeffs2") - recipe.addVar(contribution2.b2, 0, name = "b2_2", tag = "bcoeffs2") - recipe.addVar(contribution2.b3, 0, name = "b2_3", tag = "bcoeffs2") - recipe.addVar(contribution2.b4, 0, name = "b2_4", tag = "bcoeffs2") - recipe.addVar(contribution2.b5, 0, name = "b2_5", tag = "bcoeffs2") - recipe.addVar(contribution2.b6, 0, name = "b2_6", tag = "bcoeffs2") - recipe.addVar(contribution2.b7, 0, name = "b2_7", tag = "bcoeffs2") - recipe.addVar(contribution2.b8, 0, name = "b2_8", tag = "bcoeffs2") - recipe.addVar(contribution2.b9, 0, name = "b2_9", tag = "bcoeffs2") + recipe.addVar(contribution1.b0, 0, name="b1_0", tag="bcoeffs1") + recipe.addVar(contribution1.b1, 0, name="b1_1", tag="bcoeffs1") + recipe.addVar(contribution1.b2, 0, name="b1_2", tag="bcoeffs1") + recipe.addVar(contribution1.b3, 0, name="b1_3", tag="bcoeffs1") + recipe.addVar(contribution1.b4, 0, name="b1_4", tag="bcoeffs1") + recipe.addVar(contribution1.b5, 0, name="b1_5", tag="bcoeffs1") + recipe.addVar(contribution1.b6, 0, name="b1_6", tag="bcoeffs1") + recipe.addVar(contribution1.b7, 0, name="b1_7", tag="bcoeffs1") + recipe.addVar(contribution1.b8, 0, name="b1_8", tag="bcoeffs1") + recipe.addVar(contribution1.b9, 0, name="b1_9", tag="bcoeffs1") + recipe.addVar(contribution2.b0, 0, name="b2_0", tag="bcoeffs2") + recipe.addVar(contribution2.b1, 0, name="b2_1", tag="bcoeffs2") + recipe.addVar(contribution2.b2, 0, name="b2_2", tag="bcoeffs2") + recipe.addVar(contribution2.b3, 0, name="b2_3", tag="bcoeffs2") + recipe.addVar(contribution2.b4, 0, name="b2_4", tag="bcoeffs2") + recipe.addVar(contribution2.b5, 0, name="b2_5", tag="bcoeffs2") + recipe.addVar(contribution2.b6, 0, name="b2_6", tag="bcoeffs2") + recipe.addVar(contribution2.b7, 0, name="b2_7", tag="bcoeffs2") + recipe.addVar(contribution2.b8, 0, name="b2_8", tag="bcoeffs2") + recipe.addVar(contribution2.b9, 0, name="b2_9", tag="bcoeffs2") # We also want to adjust the scale and the convolution width - recipe.addVar(contribution1.scale, 1, name = "scale1") - recipe.addVar(contribution1.width, 0.1, name = "width1") - recipe.addVar(contribution2.scale, 1, name = "scale2") - recipe.addVar(contribution2.width, 0.1, name = "width2") + recipe.addVar(contribution1.scale, 1, name="scale1") + recipe.addVar(contribution1.width, 0.1, name="width1") + recipe.addVar(contribution2.scale, 1, name="scale2") + recipe.addVar(contribution2.width, 0.1, name="width2") # We can also refine structural parameters. We only have to do this once, # since each generator holds the same DiffpyStructureParSet. @@ -175,6 +175,7 @@ def gaussian(q, q0, width): # Give the recipe away so it can be used! return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -197,18 +198,19 @@ def plotResults(recipe): diff1 += offset import pylab + pylab.subplot(2, 1, 1) - pylab.plot(q,I1,'bo',label="I1(Q) Data") - pylab.plot(q,Icalc1,'r-',label="I1(Q) Fit") - pylab.plot(q,diff1,'g-',label="I1(Q) diff") - pylab.plot(q,bkgd1,'c-',label="Bkgd1 Fit") + pylab.plot(q, I1, "bo", label="I1(Q) Data") + pylab.plot(q, Icalc1, "r-", label="I1(Q) Fit") + pylab.plot(q, diff1, "g-", label="I1(Q) diff") + pylab.plot(q, bkgd1, "c-", label="Bkgd1 Fit") pylab.legend(loc=1) pylab.subplot(2, 1, 2) - pylab.plot(q,I2,'bo',label="I2(Q) Data") - pylab.plot(q,Icalc2,'r-',label="I2(Q) Fit") - pylab.plot(q,diff2,'g-',label="I2(Q) diff") - pylab.plot(q,bkgd2,'c-',label="Bkgd2 Fit") + pylab.plot(q, I2, "bo", label="I2(Q) Data") + pylab.plot(q, Icalc2, "r-", label="I2(Q) Fit") + pylab.plot(q, diff2, "g-", label="I2(Q) diff") + pylab.plot(q, bkgd2, "c-", label="Bkgd2 Fit") pylab.xlabel(r"$Q (\AA^{-1})$") pylab.ylabel("Intensity (arb. units)") pylab.legend(loc=1) @@ -216,8 +218,8 @@ def plotResults(recipe): pylab.show() return -def main(): +def main(): # Make two different data sets, each from the same structure, but with # different scale, noise, broadening and background. strufile = "data/C60.stru" @@ -258,8 +260,8 @@ def main(): return -if __name__ == "__main__": +if __name__ == "__main__": main() # End of file diff --git a/doc/examples/nppdfcrystal.py b/doc/examples/nppdfcrystal.py index 46ffb848..1d7dd7d2 100644 --- a/doc/examples/nppdfcrystal.py +++ b/doc/examples/nppdfcrystal.py @@ -26,15 +26,12 @@ """ import numpy - +from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults -from gaussianrecipe import scipyOptimize def makeRecipe(ciffile, grdata): """Make a recipe to model a crystal-like nanoparticle PDF.""" @@ -45,10 +42,10 @@ def makeRecipe(ciffile, grdata): pdfparser = PDFParser() pdfparser.parseFile(grdata) pdfprofile.loadParsedData(pdfparser) - pdfprofile.setCalculationRange(xmin = 0.1, xmax = 20) + pdfprofile.setCalculationRange(xmin=0.1, xmax=20) pdfcontribution = FitContribution("pdf") - pdfcontribution.setProfile(pdfprofile, xname = "r") + pdfcontribution.setProfile(pdfprofile, xname="r") pdfgenerator = PDFGenerator("G") pdfgenerator.setQmax(30.0) @@ -58,7 +55,8 @@ def makeRecipe(ciffile, grdata): # Register the nanoparticle shape factor. from diffpy.srfit.pdf.characteristicfunctions import sphericalCF - pdfcontribution.registerFunction(sphericalCF, name = "f") + + pdfcontribution.registerFunction(sphericalCF, name="f") # Now we set up the fitting equation. pdfcontribution.setEquation("f * G") @@ -79,6 +77,7 @@ def makeRecipe(ciffile, grdata): return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -96,12 +95,13 @@ def plotResults(recipe): fr *= max(g) / fr[0] import pylab - pylab.plot(r,g,'bo',label="G(r) Data") - pylab.plot(r, gcryst,'y--',label="G(r) Crystal") - pylab.plot(r, fr,'k--',label="f(r) calculated (scaled)") - pylab.plot(r, gcalc,'r-',label="G(r) Fit") - pylab.plot(r,diff,'g-',label="G(r) diff") - pylab.plot(r, diffzero,'k-') + + pylab.plot(r, g, "bo", label="G(r) Data") + pylab.plot(r, gcryst, "y--", label="G(r) Crystal") + pylab.plot(r, fr, "k--", label="f(r) calculated (scaled)") + pylab.plot(r, gcalc, "r-", label="G(r) Fit") + pylab.plot(r, diff, "g-", label="G(r) diff") + pylab.plot(r, diffzero, "k-") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) @@ -109,8 +109,8 @@ def plotResults(recipe): pylab.show() return -if __name__ == "__main__": +if __name__ == "__main__": ciffile = "data/pb.cif" grdata = "data/pb_100_qmin1.gr" diff --git a/doc/examples/nppdfobjcryst.py b/doc/examples/nppdfobjcryst.py index bab460fc..6558f0ad 100644 --- a/doc/examples/nppdfobjcryst.py +++ b/doc/examples/nppdfobjcryst.py @@ -21,24 +21,23 @@ import numpy -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import DebyePDFGenerator -####### Example Code +# Example Code + def makeRecipe(molecule, datname): """Create a recipe that uses the DebyePDFGenerator.""" - ## The Profile + # The Profile profile = Profile() # Load data and add it to the profile profile.loadtxt(datname) profile.setCalculationRange(xmin=1.2, xmax=8) - ## The ProfileGenerator + # The ProfileGenerator # Create a DebyePDFGenerator named "G". generator = DebyePDFGenerator("G") generator.setStructure(molecule) @@ -46,10 +45,10 @@ def makeRecipe(molecule, datname): generator.setQmin(0.68) generator.setQmax(22) - ## The FitContribution + # The FitContribution contribution = FitContribution("bucky") contribution.addProfileGenerator(generator) - contribution.setProfile(profile, xname = "r") + contribution.setProfile(profile, xname="r") # Make a FitRecipe. recipe = FitRecipe() @@ -64,7 +63,6 @@ def makeRecipe(molecule, datname): # First, the isotropic thermal displacement factor. Biso = recipe.newVar("Biso") for atom in c60.getScatterers(): - # We have defined a 'center' atom that is a dummy, which means that it # has no scattering power. It is only used as a reference point for # our bond length. We don't want to constrain it. @@ -86,13 +84,12 @@ def makeRecipe(molecule, datname): # from the constraint. radius = recipe.newVar("radius") for i, atom in enumerate(c60.getScatterers()): - if atom.isDummy(): continue # This creates a Parameter that moves the second atom according to the # bond length. Note that each Parameter needs a unique name. - par = c60.addBondLengthParameter("rad%i"%i, center, atom) + par = c60.addBondLengthParameter("rad%i" % i, center, atom) recipe.constrain(par, radius) # Add the correlation term, scale. The scale is too short to effectively @@ -103,6 +100,7 @@ def makeRecipe(molecule, datname): # Give the recipe away so it can be used! return recipe + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -110,14 +108,15 @@ def plotResults(recipe): r = recipe.bucky.profile.x g = recipe.bucky.profile.y gcalc = recipe.bucky.profile.ycalc - diffzero = -0.8 * max(g) * numpy.ones_like(g) + diffzero = -0.8 * max(g) * numpy.ones_like(g) diff = g - gcalc + diffzero import pylab - pylab.plot(r,g,'ob',label="G(r) Data") - pylab.plot(r,gcalc,'-r',label="G(r) Fit") - pylab.plot(r,diff,'-g',label="G(r) diff") - pylab.plot(r,diffzero,'-k') + + pylab.plot(r, g, "ob", label="G(r) Data") + pylab.plot(r, gcalc, "-r", label="G(r) Fit") + pylab.plot(r, diff, "-g", label="G(r) diff") + pylab.plot(r, diffzero, "-k") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) @@ -125,8 +124,8 @@ def plotResults(recipe): pylab.show() return -def main(): +def main(): molecule = makeC60() # Make the data and the recipe recipe = makeRecipe(molecule, "data/C60.gr") @@ -135,6 +134,7 @@ def main(): # Optimize from scipy.optimize import leastsq + leastsq(recipe.residual, recipe.getValues()) # Print results @@ -146,8 +146,8 @@ def main(): return -c60xyz = \ -""" + +c60xyz = """ 3.451266498 0.685000000 0.000000000 3.451266498 -0.685000000 0.000000000 -3.451266498 0.685000000 0.000000000 @@ -210,6 +210,7 @@ def main(): -2.279809890 -2.580456608 -0.724000000 """ + def makeC60(): """Make the C60 molecule using pyobjcryst.""" @@ -236,13 +237,12 @@ def makeC60(): # Add the other atoms. They will be named C1, C2, ..., C60. for i, l in enumerate(c60xyz.strip().splitlines()): x, y, z = map(float, l.split()) - m.AddAtom(x, y, z, sp, "C%i"%(i+1)) + m.AddAtom(x, y, z, sp, "C%i" % (i + 1)) return m if __name__ == "__main__": - main() # End of file diff --git a/doc/examples/nppdfsas.py b/doc/examples/nppdfsas.py index b5975345..ab74efb2 100644 --- a/doc/examples/nppdfsas.py +++ b/doc/examples/nppdfsas.py @@ -24,17 +24,14 @@ """ import numpy - +from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile from diffpy.srfit.pdf import PDFGenerator, PDFParser from diffpy.srfit.pdf.characteristicfunctions import SASCF -from diffpy.srfit.sas import SASParser, SASGenerator -from diffpy.srfit.fitbase import Profile -from diffpy.srfit.fitbase import FitContribution, FitRecipe -from diffpy.srfit.fitbase import FitResults +from diffpy.srfit.sas import SASGenerator, SASParser -from gaussianrecipe import scipyOptimize def makeRecipe(ciffile, grdata, iqdata): """Make complex-modeling recipe where I(q) and G(r) are fit @@ -50,10 +47,10 @@ def makeRecipe(ciffile, grdata, iqdata): pdfparser = PDFParser() pdfparser.parseFile(grdata) pdfprofile.loadParsedData(pdfparser) - pdfprofile.setCalculationRange(xmin = 0.1, xmax = 20) + pdfprofile.setCalculationRange(xmin=0.1, xmax=20) pdfcontribution = FitContribution("pdf") - pdfcontribution.setProfile(pdfprofile, xname = "r") + pdfcontribution.setProfile(pdfprofile, xname="r") pdfgenerator = PDFGenerator("G") pdfgenerator.setQmax(30.0) @@ -75,6 +72,7 @@ def makeRecipe(ciffile, grdata, iqdata): sascontribution.setProfile(sasprofile) from sas.models.EllipsoidModel import EllipsoidModel + model = EllipsoidModel() sasgenerator = SASGenerator("generator", model) sascontribution.addProfileGenerator(sasgenerator) @@ -105,7 +103,7 @@ def makeRecipe(ciffile, grdata, iqdata): recipe.addVar(pdfgenerator.delta2, 0) # SAS - recipe.addVar(sasgenerator.scale, 1, name = "iqscale") + recipe.addVar(sasgenerator.scale, 1, name="iqscale") recipe.addVar(sasgenerator.radius_a, 10) recipe.addVar(sasgenerator.radius_b, 10) @@ -117,16 +115,17 @@ def makeRecipe(ciffile, grdata, iqdata): return recipe + def fitRecipe(recipe): """We refine in stages to help the refinement converge.""" # Tune SAS. recipe.setWeight(recipe.pdf, 0) recipe.fix("all") - recipe.free("radius_a", "radius_b", iqscale = 1e8) - recipe.constrain('radius_b', 'radius_a') + recipe.free("radius_a", "radius_b", iqscale=1e8) + recipe.constrain("radius_b", "radius_a") scipyOptimize(recipe) - recipe.unconstrain('radius_b') + recipe.unconstrain("radius_b") # Tune PDF recipe.setWeight(recipe.pdf, 1) @@ -143,6 +142,7 @@ def fitRecipe(recipe): return + def plotResults(recipe): """Plot the results contained within a refined FitRecipe.""" @@ -160,12 +160,13 @@ def plotResults(recipe): fr *= max(g) / fr[0] import pylab - pylab.plot(r,g,'bo',label="G(r) Data") - pylab.plot(r, gcryst,'y--',label="G(r) Crystal") - pylab.plot(r, fr,'k--',label="f(r) calculated (scaled)") - pylab.plot(r, gcalc,'r-',label="G(r) Fit") - pylab.plot(r, diff,'g-',label="G(r) diff") - pylab.plot(r, diffzero,'k-') + + pylab.plot(r, g, "bo", label="G(r) Data") + pylab.plot(r, gcryst, "y--", label="G(r) Crystal") + pylab.plot(r, fr, "k--", label="f(r) calculated (scaled)") + pylab.plot(r, gcalc, "r-", label="G(r) Fit") + pylab.plot(r, diff, "g-", label="G(r) diff") + pylab.plot(r, diffzero, "k-") pylab.xlabel(r"$r (\AA)$") pylab.ylabel(r"$G (\AA^{-2})$") pylab.legend(loc=1) @@ -175,7 +176,6 @@ def plotResults(recipe): if __name__ == "__main__": - ciffile = "data/pb.cif" grdata = "data/pb_100_qmin1.gr" iqdata = "data/pb_100_qmax1.iq" diff --git a/doc/examples/simplepdf.py b/doc/examples/simplepdf.py index df95bbd2..1fbc6699 100644 --- a/doc/examples/simplepdf.py +++ b/doc/examples/simplepdf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -######################################################################## +####################################################################### # # diffpy.srfit by DANSE Diffraction group # Simon J. L. Billinge @@ -19,14 +19,15 @@ It uses the PDFContribution class to simplify fit setup. """ -from diffpy.structure import Structure -from diffpy.srfit.pdf import PDFContribution +from crystalpdf import plotResults +from gaussianrecipe import scipyOptimize + from diffpy.srfit.fitbase import FitRecipe, FitResults +from diffpy.srfit.pdf import PDFContribution +from diffpy.structure import Structure -from gaussianrecipe import scipyOptimize -from crystalpdf import plotResults +# Example Code -####### Example Code def makeRecipe(ciffile, datname): """Create a fitting recipe for crystalline PDF data.""" @@ -34,21 +35,22 @@ def makeRecipe(ciffile, datname): # Work directly with a custom PDFContribution to load the data contribution = PDFContribution("nickel") contribution.loadData(datname) - contribution.setCalculationRange(xmin = 1, xmax = 20, dx = 0.1) + contribution.setCalculationRange(xmin=1, xmax=20, dx=0.1) # and the phase stru = Structure() stru.read(ciffile) contribution.addStructure("nickel", stru) - ## Make the FitRecipe and add the FitContribution. + # Make the FitRecipe and add the FitContribution. recipe = FitRecipe() recipe.addContribution(contribution) - ## Configure the fit variables + # Configure the fit variables phase = contribution.nickel.phase from diffpy.srfit.structure import constrainAsSpaceGroup + sgpars = constrainAsSpaceGroup(phase, "Fm-3m") for par in sgpars.latpars: @@ -57,14 +59,14 @@ def makeRecipe(ciffile, datname): recipe.addVar(par, 0.005) recipe.addVar(contribution.scale, 1) - recipe.addVar(contribution.qdamp, 0.03, fixed = True) + recipe.addVar(contribution.qdamp, 0.03, fixed=True) recipe.addVar(contribution.nickel.delta2, 5) # Give the recipe away so it can be used! return recipe -if __name__ == "__main__": +if __name__ == "__main__": # Make the data and the recipe ciffile = "data/ni.cif" data = "data/ni-q27r100-neutron.gr" diff --git a/doc/examples/simplepdftwophase.py b/doc/examples/simplepdftwophase.py index f000926a..d91eb5d9 100644 --- a/doc/examples/simplepdftwophase.py +++ b/doc/examples/simplepdftwophase.py @@ -15,15 +15,15 @@ """Example of a simplified PDF refinement of two-phase structure.""" +from crystalpdftwophase import plotResults +from gaussianrecipe import scipyOptimize from pyobjcryst import loadCrystal -from diffpy.srfit.pdf import PDFContribution from diffpy.srfit.fitbase import FitRecipe, FitResults +from diffpy.srfit.pdf import PDFContribution -from gaussianrecipe import scipyOptimize -from crystalpdftwophase import plotResults +# Example Code -####### Example Code def makeRecipe(niciffile, siciffile, datname): """Create a fitting recipe for crystalline PDF data.""" @@ -31,7 +31,7 @@ def makeRecipe(niciffile, siciffile, datname): # Load data and add it to the profile contribution = PDFContribution("nisi") contribution.loadData(datname) - contribution.setCalculationRange(xmax = 20) + contribution.setCalculationRange(xmax=20) stru = loadCrystal(niciffile) contribution.addStructure("ni", stru) @@ -43,7 +43,7 @@ def makeRecipe(niciffile, siciffile, datname): recipe = FitRecipe() recipe.addContribution(contribution) - ## Configure the fit variables + # Configure the fit variables # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.newVar("scale_ni", 0.1) @@ -66,13 +66,13 @@ def makeRecipe(niciffile, siciffile, datname): # above. phase_ni = contribution.ni.phase for par in phase_ni.sgpars: - recipe.addVar(par, name = par.name + "_ni") - recipe.addVar(contribution.ni.delta2, name = "delta2_ni") + recipe.addVar(par, name=par.name + "_ni") + recipe.addVar(contribution.ni.delta2, name="delta2_ni") # Next the silicon parameters phase_si = contribution.si.phase for par in phase_si.sgpars: - recipe.addVar(par, name = par.name + "_si") - recipe.addVar(contribution.si.delta2, name = "delta2_si") + recipe.addVar(par, name=par.name + "_si") + recipe.addVar(contribution.si.delta2, name="delta2_si") # We have prior information from the earlier examples so we'll use it here # in the form of restraints. @@ -82,25 +82,24 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.restrain("a_ni", lb = 3.527, ub = 3.527, scaled = True) + recipe.restrain("a_ni", lb=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.restrain("delta2_ni", lb = 2.22, ub = 2.22, scaled = True) - recipe.restrain("Biso_0_ni", lb = 0.454, ub = 0.454, scaled = True) + recipe.restrain("delta2_ni", lb=2.22, ub=2.22, scaled=True) + recipe.restrain("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.restrain("a_si", lb = 5.430, ub = 5.430, scaled = True) - recipe.restrain("delta2_si", lb = 3.54, ub = 3.54, scaled = True) - recipe.restrain("Biso_0_si", lb = 0.645, ub = 0.645, scaled = True) + recipe.restrain("a_si", lb=5.430, ub=5.430, scaled=True) + recipe.restrain("delta2_si", lb=3.54, ub=3.54, scaled=True) + recipe.restrain("Biso_0_si", lb=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe if __name__ == "__main__": - # Make the data and the recipe niciffile = "data/ni.cif" siciffile = "data/si.cif" diff --git a/doc/examples/simplerecipe.py b/doc/examples/simplerecipe.py index 45b8785f..3bd0e292 100644 --- a/doc/examples/simplerecipe.py +++ b/doc/examples/simplerecipe.py @@ -22,7 +22,8 @@ from diffpy.srfit.fitbase import SimpleRecipe -####### Example Code +# Example Code + def main(): """Set up a simple recipe in a few lines.""" @@ -46,6 +47,7 @@ def main(): # We explicitly optimize the residual method of the SimpleRecipe from scipy.optimize import leastsq + leastsq(recipe.residual, recipe.values) # Print the results @@ -53,8 +55,8 @@ def main(): return -if __name__ == "__main__": +if __name__ == "__main__": main() # End of file diff --git a/doc/examples/threedoublepeaks.py b/doc/examples/threedoublepeaks.py index 35b7597b..fb8f2c0b 100644 --- a/doc/examples/threedoublepeaks.py +++ b/doc/examples/threedoublepeaks.py @@ -20,9 +20,10 @@ import numpy -from diffpy.srfit.fitbase import FitContribution, FitRecipe, Profile, FitResults +from diffpy.srfit.fitbase import FitContribution, FitRecipe, FitResults, Profile + +# Example Code -####### Example Code def makeRecipe(): """Make a FitRecipe for fitting three double-gaussian curves to data. @@ -41,22 +42,22 @@ def makeRecipe(): """ - ## The Profile + # The Profile # Create a Profile to hold the experimental and calculated signal. profile = Profile() x, y, dy = profile.loadtxt("data/threedoublepeaks.dat") # Create the contribution contribution = FitContribution("peaks") - contribution.setProfile(profile, xname = "t") + contribution.setProfile(profile, xname="t") pi = numpy.pi exp = numpy.exp # This is a building-block of our profile function def gaussian(t, mu, sig): - return 1/(2*pi*sig**2)**0.5 * exp(-0.5 * ((t-mu)/sig)**2) + return 1 / (2 * pi * sig**2) ** 0.5 * exp(-0.5 * ((t - mu) / sig) ** 2) - contribution.registerFunction(gaussian, name = "peakshape") + contribution.registerFunction(gaussian, name="peakshape") def delta(t, mu): """Calculate a delta-function. @@ -83,12 +84,13 @@ def delta(t, mu): + 0.23*convolve( delta(t, mu22), peakshape(t, c, sig22) ) ) + \ A3 * ( convolve( delta(t, mu31), peakshape(t, c, sig31) ) \ + 0.23*convolve( delta(t, mu32), peakshape(t, c, sig32) ) ) + \ - bkgd") + bkgd" + ) # c is the center of the gaussian. - contribution.c.value = x[len(x) // 2] + contribution.c.value = x[len(x) // 2] - ## The FitRecipe + # The FitRecipe # The FitRecipe lets us define what we want to fit. It is where we can # create variables, constraints and restraints. recipe = FitRecipe() @@ -109,12 +111,13 @@ def delta(t, mu): recipe.addVar(contribution.mu31, 33.0) # Constrain the position of the second double peak - from numpy import sin, arcsin + from numpy import arcsin, sin + def peakloc(mu): """Calculate the location of the second peak given the first.""" l1 = 1.012 l2 = 1.0 - return 180 / pi * arcsin( pi / 180 * l2 * sin(mu) / l1 ) + return 180 / pi * arcsin(pi / 180 * l2 * sin(mu) / l1) recipe.registerFunction(peakloc) recipe.constrain(contribution.mu12, "peakloc(mu11)") @@ -128,7 +131,7 @@ def peakloc(mu): def sig(sig0, dsig, mu): """Calculate the peak broadening with respect to position.""" - return sig0 * (1 - dsig * mu**2); + return sig0 * (1 - dsig * mu**2) recipe.registerFunction(sig) recipe.fix("mu") @@ -136,25 +139,23 @@ def sig(sig0, dsig, mu): recipe.sig0.value = 0.001 recipe.dsig.value = 4.0 recipe.constrain(contribution.sig11, "sig(sig0, dsig, mu11)") - recipe.constrain(contribution.sig12, "sig(sig0, dsig, mu12)", - ns = {"mu12" : contribution.mu12} ) + recipe.constrain(contribution.sig12, "sig(sig0, dsig, mu12)", ns={"mu12": contribution.mu12}) recipe.constrain(contribution.sig21, "sig(sig0, dsig, mu21)") - recipe.constrain(contribution.sig22, "sig(sig0, dsig, mu22)", - ns = {"mu22" : contribution.mu22} ) + recipe.constrain(contribution.sig22, "sig(sig0, dsig, mu22)", ns={"mu22": contribution.mu22}) recipe.constrain(contribution.sig31, "sig(sig0, dsig, mu31)") - recipe.constrain(contribution.sig32, "sig(sig0, dsig, mu32)", - ns = {"mu32" : contribution.mu32} ) + recipe.constrain(contribution.sig32, "sig(sig0, dsig, mu32)", ns={"mu32": contribution.mu32}) # Also the background - recipe.addVar(contribution.b0, 0, tag = "bkgd") - recipe.addVar(contribution.b1, 0, tag = "bkgd") - recipe.addVar(contribution.b2, 0, tag = "bkgd") - recipe.addVar(contribution.b3, 0, tag = "bkgd") - recipe.addVar(contribution.b4, 0, tag = "bkgd") - recipe.addVar(contribution.b5, 0, tag = "bkgd") - recipe.addVar(contribution.b6, 0, tag = "bkgd") + recipe.addVar(contribution.b0, 0, tag="bkgd") + recipe.addVar(contribution.b1, 0, tag="bkgd") + recipe.addVar(contribution.b2, 0, tag="bkgd") + recipe.addVar(contribution.b3, 0, tag="bkgd") + recipe.addVar(contribution.b4, 0, tag="bkgd") + recipe.addVar(contribution.b5, 0, tag="bkgd") + recipe.addVar(contribution.b6, 0, tag="bkgd") return recipe + def scipyOptimize(recipe): """Optimize the recipe created above using scipy. @@ -169,6 +170,7 @@ def scipyOptimize(recipe): # (recipe.residual) and the starting values of the Variables # (recipe.getValues()). from scipy.optimize.minpack import leastsq + print("Fit using scipy's LM optimizer") leastsq(recipe.residual, recipe.getValues()) @@ -190,16 +192,18 @@ def plotResults(recipe): # This stuff is specific to pylab from the matplotlib distribution. import pylab - pylab.plot(x, y, 'b.', label = "observed profile") - pylab.plot(x, ycalc, 'r-', label = "calculated profile") - pylab.plot(x, y - ycalc - 0.1 * max(y), 'g-', label = "difference") - pylab.legend(loc = (0.0,0.8)) + + pylab.plot(x, y, "b.", label="observed profile") + pylab.plot(x, ycalc, "r-", label="calculated profile") + pylab.plot(x, y - ycalc - 0.1 * max(y), "g-", label="difference") + pylab.legend(loc=(0.0, 0.8)) pylab.xlabel("x") pylab.ylabel("y") pylab.show() return + def steerFit(recipe): """Steer the fit for this problem. @@ -219,8 +223,8 @@ def steerFit(recipe): return -if __name__ == "__main__": +if __name__ == "__main__": # Create the recipe recipe = makeRecipe() diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 00000000..2be83069 --- /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/conf.py b/doc/manual/source/conf.py index 53398e50..c3cbddc0 100644 --- a/doc/manual/source/conf.py +++ b/doc/manual/source/conf.py @@ -12,9 +12,12 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import os +import sys import time +from setup import versiondata + # 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. @@ -22,7 +25,7 @@ # sys.path.insert(0, os.path.abspath('../../..')) # abbreviations -ab_authors = u'Christopher L. Farrow, Pavol Juhás, Simon J.L. Billinge group' +ab_authors = "Christopher L. Farrow, Pavol Juhás, Simon J.L. Billinge group" # -- General configuration ----------------------------------------------------- @@ -32,38 +35,39 @@ # 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.napoleon', - 'sphinx.ext.intersphinx', - 'm2r', + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.intersphinx", + "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 = u'diffpy.srfit' -copyright = u'%Y, Brookhaven National Laboratory' +project = "diffpy.srfit" +copyright = "%Y, Brookhaven National Laboratory" # 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. -sys.path.insert(0, os.path.abspath('../../..')) -from setup import versiondata -fullversion = versiondata.get('DEFAULT', 'version') +sys.path.insert(0, os.path.abspath("../../..")) + + +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 @@ -74,13 +78,13 @@ # 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_seconds = versiondata.getint("DEFAULT", "timestamp") +today = time.strftime("%B %d, %Y", time.localtime(today_seconds)) 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) # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -101,10 +105,10 @@ # 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.srfit.'] +modindex_common_prefix = ["diffpy.srfit."] # Display all warnings for missing links. nitpicky = True @@ -113,14 +117,14 @@ # 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_py3doc_enhanced_theme" # 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', + "collapsiblesidebar": "true", + "navigation_with_keys": "true", } # Add any paths that contain custom themes here, relative to this directory. @@ -189,27 +193,24 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'srfitdoc' +htmlhelp_basename = "srfitdoc" # -- 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]). latex_documents = [ - ('index', 'srfit_manual.tex', u'diffpy.srfit documentation', - ab_authors, 'manual'), + ("index", "srfit_manual.tex", "diffpy.srfit documentation", ab_authors, "manual"), ] # The name of an image file (relative to this directory) to place at the top of @@ -237,10 +238,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'diffpy.srfit', u'diffpy.srfit documentation', - ab_authors, 1) -] +man_pages = [("index", "diffpy.srfit", "diffpy.srfit documentation", ab_authors, 1)] # If true, show URL addresses after external links. # man_show_urls = False @@ -252,9 +250,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'diffpy.srfit', u'diffpy.srfit documentation', - ab_authors, 'diffpy.srfit', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "diffpy.srfit", + "diffpy.srfit documentation", + ab_authors, + "diffpy.srfit", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. @@ -269,6 +273,6 @@ # 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://docs.scipy.org/doc/numpy", None), + "python": ("https://docs.python.org/3.7", None), } 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.srfit.equation.literals.rst b/doc/source/api/diffpy.srfit.equation.literals.rst new file mode 100644 index 00000000..20679c3e --- /dev/null +++ b/doc/source/api/diffpy.srfit.equation.literals.rst @@ -0,0 +1,44 @@ +:tocdepth: -1 + +diffpy.srfit.equation.literals package +====================================== + +.. automodule:: diffpy.srfit.equation.literals + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.equation.literals.argument module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.literals.argument + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.literals.abcs module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.literals.abcs + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.literals.literal module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.literals.literal + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.literals.operators module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.literals.operators + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.equation.rst b/doc/source/api/diffpy.srfit.equation.rst new file mode 100644 index 00000000..ef74cf23 --- /dev/null +++ b/doc/source/api/diffpy.srfit.equation.rst @@ -0,0 +1,37 @@ +:tocdepth: -1 + +diffpy.srfit.equation package +============================= + +.. automodule:: diffpy.srfit.equation + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :titlesonly: + + diffpy.srfit.equation.visitors + diffpy.srfit.equation.literals + +Submodules +---------- + +diffpy.srfit.equation.builder module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.builder + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.equationmod module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.equationmod + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.equation.visitors.rst b/doc/source/api/diffpy.srfit.equation.visitors.rst new file mode 100644 index 00000000..88c35374 --- /dev/null +++ b/doc/source/api/diffpy.srfit.equation.visitors.rst @@ -0,0 +1,52 @@ +:tocdepth: -1 + +diffpy.srfit.equation.visitors package +====================================== + +.. automodule:: diffpy.srfit.equation.visitors + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.equation.visitors.validator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.visitors.validator + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.visitors.swapper module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.visitors.swapper + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.visitors.argfinder module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.visitors.argfinder + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.visitors.visitor module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.visitors.visitor + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.equation.visitors.printer module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.equation.visitors.printer + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.fitbase.rst b/doc/source/api/diffpy.srfit.fitbase.rst new file mode 100644 index 00000000..19bc255e --- /dev/null +++ b/doc/source/api/diffpy.srfit.fitbase.rst @@ -0,0 +1,140 @@ +:tocdepth: -1 + +diffpy.srfit.fitbase package +============================ + +.. automodule:: diffpy.srfit.fitbase + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.fitbase.simplerecipe module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.simplerecipe + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.constraint module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.constraint + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.fithook module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.fithook + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.fitresults module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.fitresults + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.profilegenerator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.profilegenerator + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.validatable module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.validatable + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.configurable module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.configurable + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.profile module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.profile + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.restraint module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.restraint + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.fitcontribution module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.fitcontribution + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.recipeorganizer module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.recipeorganizer + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.fitrecipe module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.fitrecipe + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.parameterset module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.parameterset + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.calculator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.calculator + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.parameter module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.parameter + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.fitbase.profileparser module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.fitbase.profileparser + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.interface.rst b/doc/source/api/diffpy.srfit.interface.rst new file mode 100644 index 00000000..92497fea --- /dev/null +++ b/doc/source/api/diffpy.srfit.interface.rst @@ -0,0 +1,20 @@ +:tocdepth: -1 + +diffpy.srfit.interface package +============================== + +.. automodule:: diffpy.srfit.interface + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.interface.interface module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.interface.interface + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.pdf.rst b/doc/source/api/diffpy.srfit.pdf.rst new file mode 100644 index 00000000..424c7f16 --- /dev/null +++ b/doc/source/api/diffpy.srfit.pdf.rst @@ -0,0 +1,60 @@ +:tocdepth: -1 + +diffpy.srfit.pdf package +======================== + +.. automodule:: diffpy.srfit.pdf + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.pdf.debyepdfgenerator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.pdf.debyepdfgenerator + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.pdf.characteristicfunctions module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.pdf.characteristicfunctions + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.pdf.pdfparser module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.pdf.pdfparser + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.pdf.pdfgenerator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.pdf.pdfgenerator + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.pdf.basepdfgenerator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.pdf.basepdfgenerator + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.pdf.pdfcontribution module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.pdf.pdfcontribution + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.rst b/doc/source/api/diffpy.srfit.rst new file mode 100644 index 00000000..8f840d0c --- /dev/null +++ b/doc/source/api/diffpy.srfit.rst @@ -0,0 +1,34 @@ +:tocdepth: -1 + +diffpy.srfit package +==================== + +.. automodule:: diffpy.srfit + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :titlesonly: + + diffpy.srfit.interface + diffpy.srfit.equation + diffpy.srfit.util + diffpy.srfit.pdf + diffpy.srfit.sas + diffpy.srfit.fitbase + diffpy.srfit.structure + +Submodules +---------- + +diffpy.srfit.exceptions module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.exceptions + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.sas.rst b/doc/source/api/diffpy.srfit.sas.rst new file mode 100644 index 00000000..8ad88d91 --- /dev/null +++ b/doc/source/api/diffpy.srfit.sas.rst @@ -0,0 +1,60 @@ +:tocdepth: -1 + +diffpy.srfit.sas package +======================== + +.. automodule:: diffpy.srfit.sas + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.sas.sasparser module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.sas.sasparser + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.sas.prcalculator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.sas.prcalculator + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.sas.sasprofile module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.sas.sasprofile + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.sas.sasparameter module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.sas.sasparameter + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.sas.sasimport module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.sas.sasimport + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.sas.sasgenerator module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.sas.sasgenerator + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.structure.rst b/doc/source/api/diffpy.srfit.structure.rst new file mode 100644 index 00000000..59063abd --- /dev/null +++ b/doc/source/api/diffpy.srfit.structure.rst @@ -0,0 +1,68 @@ +:tocdepth: -1 + +diffpy.srfit.structure package +============================== + +.. automodule:: diffpy.srfit.structure + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.structure.objcrystparset module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.structure.objcrystparset + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.structure.basestructureparset module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.structure.basestructureparset + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.structure.srrealparset module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.structure.srrealparset + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.structure.diffpyparset module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.structure.diffpyparset + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.structure.cctbxparset module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.structure.cctbxparset + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.structure.bvsrestraint module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.structure.bvsrestraint + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.structure.sgconstraints module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.structure.sgconstraints + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.srfit.util.rst b/doc/source/api/diffpy.srfit.util.rst new file mode 100644 index 00000000..2471e5ad --- /dev/null +++ b/doc/source/api/diffpy.srfit.util.rst @@ -0,0 +1,60 @@ +:tocdepth: -1 + +diffpy.srfit.util package +========================= + +.. automodule:: diffpy.srfit.util + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.srfit.util.inpututils module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.util.inpututils + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.util.observable module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.util.observable + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.util.tagmanager module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.util.tagmanager + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.util.argbinders module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.util.argbinders + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.util.nameutils module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.util.nameutils + :members: + :undoc-members: + :show-inheritance: + +diffpy.srfit.util.weakrefcallable module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.srfit.util.weakrefcallable + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 00000000..e7654b23 --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# diffpy.srfit documentation build configuration file, created by +# sphinx-quickstart on Thu Jan 30 15:49:41 2014. +# +# 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. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import time +from importlib.metadata import version +from pathlib import Path + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use Path().resolve() to make it absolute, like shown here. +# 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 = "Billinge Group members and community contributors" + +# -- 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. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "sphinx_rtd_theme", + "m2r", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = [".rst", ".md"] + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = "diffpy.srfit" +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. + +fullversion = version(project) +# The short X.Y version. +version = "".join(fullversion.split(".post")[:1]) +# The full version, including alpha/beta/rc tags. +release = fullversion + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +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) + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ["build"] + +# 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. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# A list of ignored prefixes for module index sorting. +modindex_common_prefix = ["diffpy.srfit"] + +# Display all warnings for missing links. +nitpicky = True + +# -- 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_rtd_theme" + +# 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 = { + "navigation_with_keys": "true", +} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# 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' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +basename = "diffpy.srfit".replace(" ", "").replace(".", "") +htmlhelp_basename = basename + "doc" + + +# -- 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': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ("index", "diffpy.srfit.tex", "diffpy.srfit Documentation", ab_authors, "manual"), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- 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.srfit", "diffpy.srfit Documentation", ab_authors, 1)] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- 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.srfit", + "diffpy.srfit Documentation", + ab_authors, + "diffpy.srfit", + "One line description of project.", + "Miscellaneous", + ), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# 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 = {'http://docs.python.org/': None} diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 00000000..d7ca0de9 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,44 @@ +####### +|title| +####### + +.. |title| replace:: diffpy.srfit documentation + +diffpy.srfit - Python package for structure refinement from diffraction data.. + +| Software version |release|. +| Last updated |today|. + +======= +Authors +======= + +diffpy.srfit is developed by Billinge Group +and its community contributors. + +For a detailed list of contributors see +https://github.com/diffpy/diffpy.srfit/graphs/contributors. + +============ +Installation +============ + +See the `README `_ +file included with the distribution. + +================= +Table of contents +================= +.. toctree:: + :titlesonly: + + license + release + Package API + +======= +Indices +======= + +* :ref:`genindex` +* :ref:`search` diff --git a/doc/source/license.rst b/doc/source/license.rst new file mode 100644 index 00000000..72aaee94 --- /dev/null +++ b/doc/source/license.rst @@ -0,0 +1,144 @@ +: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 + + +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/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/environment.yml b/environment.yml new file mode 100644 index 00000000..0e279fbf --- /dev/null +++ b/environment.yml @@ -0,0 +1,6 @@ +name: diffpy.srfit +channels: + - conda-forge +dependencies: + - python=3 + - pip 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/cookie.rst b/news/cookie.rst new file mode 100644 index 00000000..7a814233 --- /dev/null +++ b/news/cookie.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* cookiecut to group's Python package standard + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..fe07f87d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,75 @@ +[build-system] +requires = ["setuptools>=62.0", "setuptools-git-versioning>=2.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "diffpy.srfit" +dynamic=['version', 'dependencies'] +authors = [ + { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, +] +maintainers = [ + { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, +] +description = "Python package for structure refinement from diffraction data." +keywords = ['optimization', 'constraints', 'restraints', 'structure refinement', 'complex modeling'] +readme = "README.rst" +requires-python = ">=3.10" +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: Free To Use But Restricted', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Topic :: Scientific/Engineering :: Physics', + 'Topic :: Scientific/Engineering :: Chemistry', +] + +[project.urls] +Homepage = "https://github.com/diffpy/diffpy.srfit/" +Issues = "https://github.com/diffpy/diffpy.srfit/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/run.txt"]} + +[tool.black] +line-length = 115 +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..e69de29b diff --git a/requirements/docs.txt b/requirements/docs.txt new file mode 100644 index 00000000..ab17b1c8 --- /dev/null +++ b/requirements/docs.txt @@ -0,0 +1,4 @@ +sphinx +sphinx_rtd_theme +doctr +m2r diff --git a/requirements/pip.txt b/requirements/pip.txt new file mode 100644 index 00000000..e69de29b diff --git a/requirements/run-ext.txt b/requirements/run-ext.txt new file mode 100644 index 00000000..9cc306b0 --- /dev/null +++ b/requirements/run-ext.txt @@ -0,0 +1 @@ +diffpy.structure diff --git a/requirements/run.txt b/requirements/run.txt new file mode 100644 index 00000000..59149ac8 --- /dev/null +++ b/requirements/run.txt @@ -0,0 +1,3 @@ +scipy +numpy +matplotlib 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 deleted file mode 100755 index d3577dd2..00000000 --- a/setup.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python - -# Installation script for diffpy.srfit - -"""diffpy.srfit - framework for setting up complex modeling refinements. - -Packages: diffpy.srfit -""" - -import os -import re -import sys -from setuptools import setup, find_packages - -# Use this version when git data are not available, like in git zip archive. -# Update when tagging a new release. -FALLBACK_VERSION = '3.0.0.post0' - -# determine if we run with Python 3. -PY3 = (sys.version_info[0] == 3) - -# 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/srfit/version.cfg') -gitarchivecfgfile = os.path.join(MYDIR, '.gitarchive.cfg') - -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 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 -setup_args = dict( - name = "diffpy.srfit", - version = versiondata.get('DEFAULT', 'version'), - packages = find_packages(os.path.join(MYDIR, 'src')), - package_dir = {'' : 'src'}, - test_suite = 'diffpy.srfit.tests', - include_package_data = True, - install_requires = ['six'], - zip_safe = False, - author = "Simon J.L. Billinge", - author_email = "sb2896@columbia.edu", - maintainer = "Pavol Juhas", - maintainer_email = "pavol.juhas@gmail.com", - description = "SrFit - Structure refinement from diffraction data", - long_description = long_description, - long_description_content_type = 'text/x-rst', - license = 'BSD-style license', - url = "https://github.com/diffpy/diffpy.srfit", - keywords = "optimization constraints restraints structure refinement complex modeling", - 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', - 'Operating System :: POSIX', - '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', - ], -) - -if __name__ == '__main__': - setup(**setup_args) - -# End of file diff --git a/src/diffpy/__init__.py b/src/diffpy/__init__.py index 11098213..903acd5f 100644 --- a/src/diffpy/__init__.py +++ b/src/diffpy/__init__.py @@ -1,26 +1,23 @@ #!/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) 2024 The Trustees of Columbia University in the City of New York. +# All rights reserved. # -# File coded by: Chris Farrow +# 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.srfit/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/srfit/.DS_Store b/src/diffpy/srfit/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/src/diffpy/srfit/.DS_Store differ diff --git a/src/diffpy/srfit/__init__.py b/src/diffpy/srfit/__init__.py index 76d70de4..5b0425f6 100644 --- a/src/diffpy/srfit/__init__.py +++ b/src/diffpy/srfit/__init__.py @@ -1,18 +1,19 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.srfit 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) 2024 The Trustees of Columbia University in the City of New York. +# All rights reserved. # -# File coded by: Chris Farrow +# 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.srfit/graphs/contributors +# +# See LICENSE.rst for license information. # ############################################################################## + """Complex modeling framework for structure refinement and solution. SrFit is a tool for coherently combining known information about a material to @@ -31,9 +32,10 @@ learn how to use and customize the various parts of SrFit. """ -__all__ = ["__version__"] - # package version from diffpy.srfit.version import __version__ +# silence the pyflakes syntax checker +assert __version__ or True + # End of file diff --git a/src/diffpy/srfit/equation/__init__.py b/src/diffpy/srfit/equation/__init__.py index 135a6af2..6a248b32 100644 --- a/src/diffpy/srfit/equation/__init__.py +++ b/src/diffpy/srfit/equation/__init__.py @@ -29,5 +29,4 @@ from diffpy.srfit.equation.equationmod import Equation - # End of file diff --git a/src/diffpy/srfit/equation/builder.py b/src/diffpy/srfit/equation/builder.py index e35e9e11..01db6667 100644 --- a/src/diffpy/srfit/equation/builder.py +++ b/src/diffpy/srfit/equation/builder.py @@ -77,27 +77,33 @@ > eq = beq.makeEquation() """ -__all__ = ["EquationFactory", "BaseBuilder", "ArgumentBuilder", - "OperatorBuilder", "wrapArgument", "wrapOperator", "wrapFunction", - "getBuilder"] +__all__ = [ + "EquationFactory", + "BaseBuilder", + "ArgumentBuilder", + "OperatorBuilder", + "wrapArgument", + "wrapOperator", + "wrapFunction", + "getBuilder", +] # NOTE - the builder cannot handle numpy arrays on the left of a binary # operation because the array will automatically loop the operator of the # right-side over its arguments. This results in an array of BaseBuilder # instances, not an BaseBuilder that contains an array. -_builders = {} - - import inspect import numbers -import numpy +import numpy import six import diffpy.srfit.equation.literals as literals -from diffpy.srfit.equation.literals.literal import Literal from diffpy.srfit.equation.equationmod import Equation +from diffpy.srfit.equation.literals.literal import Literal + +_builders = {} class EquationFactory(object): @@ -125,8 +131,7 @@ def __init__(self): self.registerConstant("e", numpy.e) return - def makeEquation(self, eqstr, buildargs = True, argclass = - literals.Argument, argkw = {}): + def makeEquation(self, eqstr, buildargs=True, argclass=literals.Argument, argkw={}): """Make an equation from an equation string. Arguments @@ -152,7 +157,7 @@ def makeEquation(self, eqstr, buildargs = True, argclass = # handle scalar numbers or numpy arrays if isinstance(beq, (numbers.Number, numpy.ndarray)): lit = literals.Argument(value=beq, const=True) - eq = Equation(name='', root=lit) + eq = Equation(name="", root=lit) else: eq = beq.getEquation() self.equations.add(eq) @@ -207,8 +212,7 @@ def registerFunction(self, name, func, argnames): opbuilder = wrapFunction(name, func, len(argnames)) for n in argnames: b = self.builders[n] - l = b.literal - opbuilder.literal.addLiteral(l) + opbuilder.literal.addLiteral(b.literal) return self.registerBuilder(name, opbuilder) @@ -255,7 +259,6 @@ def deRegisterBuilder(self, name): del self.builders[name] return - def wipeout(self, eq): """Invalidate the specified equation and remove it from the factory. @@ -272,11 +275,10 @@ def wipeout(self, eq): self.equations.discard(eq) # invalidate this equation to clean up any observer relations of # objects in the factory towards its literals tree. - nan = literals.Argument('nan', value=numpy.nan, const=True) + nan = literals.Argument("nan", value=numpy.nan, const=True) eq.setRoot(nan) return - def _prepareBuilders(self, eqstr, buildargs, argclass, argkw): """Prepare builders so that equation string can be evaluated. @@ -309,14 +311,14 @@ def _prepareBuilders(self, eqstr, buildargs, argclass, argkw): # this is disallowed. if not buildargs and eqargs: eqargsstr = ", ".join(eqargs) - msg = "The equation contains undefined arguments: %s"%eqargsstr + msg = "The equation contains undefined arguments: %s" % eqargsstr raise ValueError(msg) # Make the arguments newargs = set() for argname in eqargs: - arg = argclass(name = argname, **argkw) - argbuilder = ArgumentBuilder(name = argname, arg = arg) + arg = argclass(name=argname, **argkw) + argbuilder = ArgumentBuilder(name=argname, arg=arg) newargs.add(arg) self.registerBuilder(argname, argbuilder) @@ -333,8 +335,8 @@ def _getUndefinedArgs(self, eqstr): Raises SyntaxError if the equation string uses invalid syntax. """ - import tokenize import token + import tokenize interface = six.StringIO(eqstr).readline # output is an iterator. Each entry (token) is a 5-tuple @@ -353,7 +355,7 @@ def _getUndefinedArgs(self, eqstr): if tok[0] in (token.NAME, token.OP): args.add(tok[1]) except tokenize.TokenError: - m = "invalid syntax: '%s'"%eqstr + m = "invalid syntax: '%s'" % eqstr raise SyntaxError(m) # Scan the tokens for names that do not correspond to registered @@ -362,19 +364,18 @@ def _getUndefinedArgs(self, eqstr): for tok in set(args): # Move genuine varibles to the eqargs dictionary if ( - # Check registered builders - tok in self.builders or - # Check symbols - tok in EquationFactory.symbols or - # Check ignored characters - tok in EquationFactory.ignore - ): + tok in self.builders # Check registered builders + or tok in EquationFactory.symbols # Check symbols + or tok in EquationFactory.ignore # Check ignored characters + ): args.remove(tok) return args + # End class EquationFactory + class BaseBuilder(object): """Class for building equations. @@ -392,11 +393,9 @@ def __init__(self): def __call__(self, *args): """Raises exception for easier debugging.""" - m = "%s (%s) cannot accept arguments"%\ - (self.literal.name, self.__class__.__name__) + m = "%s (%s) cannot accept arguments" % (self.literal.name, self.__class__.__name__) raise TypeError(m) - def getEquation(self): """Get the equation built by this object. @@ -404,11 +403,11 @@ def getEquation(self): name of the root node. """ # We need to make a name for this, so we name it after its root - name = "_eq_%s"%self.literal.name + name = "_eq_%s" % self.literal.name eq = Equation(name, self.literal) return eq - def __evalBinary(self, other, OperatorClass, onleft = True): + def __evalBinary(self, other, OperatorClass, onleft=True): """Evaluate a binary function. Other can be an BaseBuilder or a constant. @@ -498,7 +497,9 @@ def __rmod__(self, other): def __neg__(self): return self.__evalUnary(literals.NegationOperator) -## These are used by the class. + +# These are used by the class. + class ArgumentBuilder(BaseBuilder): """BaseBuilder wrapper around an Argument literal. @@ -510,7 +511,7 @@ class ArgumentBuilder(BaseBuilder): literal -- The Argument wrapped by this instance. """ - def __init__(self, value = None, name = None, const = False, arg = None): + def __init__(self, value=None, name=None, const=False, arg=None): """Create an ArgumentBuilder instance, containing a new Argument. Arguments @@ -524,14 +525,15 @@ def __init__(self, value = None, name = None, const = False, arg = None): """ BaseBuilder.__init__(self) if arg is None: - self.literal = literals.Argument(value=value, name=name, - const=const) + self.literal = literals.Argument(value=value, name=name, const=const) else: self.literal = arg return + # end class ArgumentBuilder + class OperatorBuilder(BaseBuilder): """BaseBuilder wrapper around an Operator literal. @@ -540,7 +542,7 @@ class OperatorBuilder(BaseBuilder): name -- The name of the operator to be wrapped """ - def __init__(self, name, op = None): + def __init__(self, name, op=None): """Wrap an Operator or a function by name. Arguments @@ -573,43 +575,48 @@ def __call__(self, *args): self.literal = literals.UFuncOperator(ufunc) # Here the Operator is already specified. We can copy its attributes # to a new Operator inside of the new OperatorBuilder. - op = literals.makeOperator(name=self.literal.name, - symbol=self.literal.symbol, - nin=self.literal.nin, - nout=self.literal.nout, - operation=self.literal.operation) + op = literals.makeOperator( + name=self.literal.name, + symbol=self.literal.symbol, + nin=self.literal.nin, + nout=self.literal.nout, + operation=self.literal.operation, + ) newobj.literal = op # Now that we have a literal, let's check our inputs literal = newobj.literal if literal.nin >= 0 and len(args) != literal.nin: - raise ValueError("%s takes %i arguments (%i given)"%\ - (self.literal, self.literal.nin, len(args))) + raise ValueError("%s takes %i arguments (%i given)" % (self.literal, self.literal.nin, len(args))) # Wrap scalar arguments for i, arg in enumerate(args): # Wrap the argument if it is not already if not isinstance(arg, BaseBuilder): - name = self.name + "_%i"%i - arg = ArgumentBuilder(value = arg, name = name, const = True) + name = self.name + "_%i" % i + arg = ArgumentBuilder(value=arg, name=name, const=True) newobj.literal.addLiteral(arg.literal) return newobj + # end class OperatorBuilder # Utility functions + def wrapArgument(name, arg): """Wrap an Argument as a builder.""" - argbuilder = ArgumentBuilder(arg = arg) + argbuilder = ArgumentBuilder(arg=arg) return argbuilder + def wrapOperator(name, op): """Wrap an Operator as a builder.""" opbuilder = OperatorBuilder(name, op) return opbuilder + def wrapFunction(name, func, nin=2, nout=1): """Wrap a function in an OperatorBuilder instance. @@ -620,19 +627,19 @@ def wrapFunction(name, func, nin=2, nout=1): Returns the OperatorBuilder instance that wraps the function. """ - op = literals.makeOperator(name=name, symbol=name, - nin=nin, nout=nout, - operation=func) + op = literals.makeOperator(name=name, symbol=name, nin=nin, nout=nout, operation=func) # Create the OperatorBuilder opbuilder = OperatorBuilder(name, op) return opbuilder + def getBuilder(name): """Get an operator from the global builders dictionary.""" return _builders[name] + def __wrapNumpyOperators(): """Export all numpy operators as OperatorBuilder instances in the module namespace. @@ -642,8 +649,11 @@ def __wrapNumpyOperators(): if isinstance(op, numpy.ufunc): _builders[name] = OperatorBuilder(name) return + + __wrapNumpyOperators() + # Register other functions as well def __wrapSrFitOperators(): """Export all non-base operators from the @@ -652,17 +662,24 @@ def __wrapSrFitOperators(): """ opmod = literals.operators excluded_types = set((opmod.CustomOperator, opmod.UFuncOperator)) - # check if opmod member should be wrapped as OperatorBuilder - is_exported_type = lambda cls : ( - inspect.isclass(cls) and issubclass(cls, opmod.Operator) and - not inspect.isabstract(cls) and - not cls in excluded_types) + + def is_exported_type(cls): + """Check if the class should be wrapped as OperatorBuilder.""" + return ( + inspect.isclass(cls) + and issubclass(cls, opmod.Operator) + and not inspect.isabstract(cls) + and cls not in excluded_types + ) + # create OperatorBuilder objects for nm, opclass in inspect.getmembers(opmod, is_exported_type): op = opclass() assert op.name, "Unnamed Operator should never appear here." _builders[op.name] = OperatorBuilder(op.name, op) return + + __wrapSrFitOperators() # End of file diff --git a/src/diffpy/srfit/equation/equationmod.py b/src/diffpy/srfit/equation/equationmod.py index a48d6dad..e82c51c4 100644 --- a/src/diffpy/srfit/equation/equationmod.py +++ b/src/diffpy/srfit/equation/equationmod.py @@ -42,9 +42,10 @@ from collections import OrderedDict -from diffpy.srfit.equation.visitors import validate, getArgs, swap -from diffpy.srfit.equation.literals.operators import Operator from diffpy.srfit.equation.literals.literal import Literal +from diffpy.srfit.equation.literals.operators import Operator +from diffpy.srfit.equation.visitors import getArgs, swap, validate + class Equation(Operator): """Class for holding and evaluating a Literal tree. @@ -81,7 +82,7 @@ class Equation(Operator): nin = None nout = 1 - def __init__(self, name = None, root = None): + def __init__(self, name=None, root=None): """Initialize. name -- A name for this Equation. @@ -93,7 +94,7 @@ def __init__(self, name = None, root = None): # Operator stuff. We circumvent Operator.__init__ since we're using # args as a property. We cannot set it, as the Operator tries to do. if name is None and root is not None: - name = "eq_%s"%root.name + name = "eq_%s" % root.name Literal.__init__(self, name) self.root = None self.argdict = OrderedDict() @@ -101,12 +102,10 @@ def __init__(self, name = None, root = None): self.setRoot(root) return - @property def symbol(self): return self.name - def operation(self, *args, **kw): """Evaluate this Equation object. @@ -117,7 +116,6 @@ def operation(self, *args, **kw): """ return self.__call__(*args, **kw) - def _getArgs(self): return list(self.argdict.values()) @@ -126,16 +124,13 @@ def _getArgs(self): def __getattr__(self, name): """Gives access to the Arguments as attributes.""" # Avoid infinite loop on argdict lookup. - argdict = object.__getattribute__(self, 'argdict') - if not name in argdict: + argdict = object.__getattribute__(self, "argdict") + if name not in argdict: raise AttributeError("No argument named '%s' here" % name) return argdict[name] - # Ensure there is no __dir__ override in the base class. - assert (getattr(Operator, '__dir__', None) is - getattr(object, '__dir__', None)) - + assert getattr(Operator, "__dir__", None) is getattr(object, "__dir__", None) def __dir__(self): "Return sorted list of attributes for this object." @@ -144,7 +139,6 @@ def __dir__(self): rv = sorted(rv) return rv - def setRoot(self, root): """Set the root of the Literal tree. @@ -167,14 +161,13 @@ def setRoot(self, root): # Get the args args = getArgs(root, getconsts=False) - self.argdict = OrderedDict( [(arg.name, arg) for arg in args] ) + self.argdict = OrderedDict([(arg.name, arg) for arg in args]) # Set Operator attributes self.nin = len(self.args) return - def __call__(self, *args, **kw): """Call the equation. @@ -196,7 +189,7 @@ def __call__(self, *args, **kw): for name, val in kw.items(): arg = self.argdict.get(name) if arg is None: - raise ValueError("No argument named '%s' here"%name) + raise ValueError("No argument named '%s' here" % name) arg.setValue(val) self._value = self.root.getValue() @@ -224,4 +217,5 @@ def identify(self, visitor): """Identify self to a visitor.""" return visitor.onEquation(self) + # End of file diff --git a/src/diffpy/srfit/equation/literals/__init__.py b/src/diffpy/srfit/equation/literals/__init__.py index 34c38cef..1632316c 100644 --- a/src/diffpy/srfit/equation/literals/__init__.py +++ b/src/diffpy/srfit/equation/literals/__init__.py @@ -25,32 +25,47 @@ (http://en.wikipedia.org/wiki/Visitor_pattern). """ -__all__ = ["Argument", "Operator", "BinaryOperator", "CustomOperator", - "AdditionOperator", "SubtractionOperator", - "MultiplicationOperator", "DivisionOperator", "ExponentiationOperator", - "RemainderOperator", "NegationOperator", "ConvolutionOperator", - "SumOperator", "UFuncOperator", "ArrayOperator", "PolyvalOperator", - "makeOperator"] +__all__ = [ + "Argument", + "Operator", + "BinaryOperator", + "CustomOperator", + "AdditionOperator", + "SubtractionOperator", + "MultiplicationOperator", + "DivisionOperator", + "ExponentiationOperator", + "RemainderOperator", + "NegationOperator", + "ConvolutionOperator", + "SumOperator", + "UFuncOperator", + "ArrayOperator", + "PolyvalOperator", + "makeOperator", +] # Import the operators from diffpy.srfit.equation.literals.argument import Argument -from diffpy.srfit.equation.literals.operators import Operator -from diffpy.srfit.equation.literals.operators import BinaryOperator -from diffpy.srfit.equation.literals.operators import CustomOperator -from diffpy.srfit.equation.literals.operators import AdditionOperator -from diffpy.srfit.equation.literals.operators import SubtractionOperator -from diffpy.srfit.equation.literals.operators import MultiplicationOperator -from diffpy.srfit.equation.literals.operators import DivisionOperator -from diffpy.srfit.equation.literals.operators import ExponentiationOperator -from diffpy.srfit.equation.literals.operators import RemainderOperator -from diffpy.srfit.equation.literals.operators import NegationOperator -from diffpy.srfit.equation.literals.operators import ConvolutionOperator -from diffpy.srfit.equation.literals.operators import UFuncOperator -from diffpy.srfit.equation.literals.operators import SumOperator -from diffpy.srfit.equation.literals.operators import ArrayOperator -from diffpy.srfit.equation.literals.operators import PolyvalOperator -from diffpy.srfit.equation.literals.operators import makeOperator +from diffpy.srfit.equation.literals.operators import ( + AdditionOperator, + ArrayOperator, + BinaryOperator, + ConvolutionOperator, + CustomOperator, + DivisionOperator, + ExponentiationOperator, + MultiplicationOperator, + NegationOperator, + Operator, + PolyvalOperator, + RemainderOperator, + SubtractionOperator, + SumOperator, + UFuncOperator, + makeOperator, +) # End of file diff --git a/src/diffpy/srfit/equation/literals/abcs.py b/src/diffpy/srfit/equation/literals/abcs.py index 05099162..2186b9bb 100644 --- a/src/diffpy/srfit/equation/literals/abcs.py +++ b/src/diffpy/srfit/equation/literals/abcs.py @@ -28,13 +28,16 @@ class LiteralABC(object): """Abstract Base Class for Literal. See Literal for usage.""" @abstractmethod - def identify(self, visitor): pass + def identify(self, visitor): + pass @abstractmethod - def getValue(self): pass + def getValue(self): + pass name = abstractproperty(None, None) + # End class LiteralABC @@ -42,11 +45,13 @@ class ArgumentABC(LiteralABC): """Abstract Base Class for Argument. See Argument for usage.""" @abstractmethod - def setValue(self, value): pass + def setValue(self, value): + pass const = abstractproperty(None, None) value = abstractproperty(None, None) + # End class ArgumentABC @@ -54,7 +59,8 @@ class OperatorABC(LiteralABC): """Abstract Base Class for Operator. See Operator for usage.""" @abstractmethod - def addLiteral(self, literal): pass + def addLiteral(self, literal): + pass args = abstractproperty(None, None) nin = abstractproperty(None, None) @@ -63,4 +69,5 @@ def addLiteral(self, literal): pass symbol = abstractproperty(None, None) value = abstractproperty(None, None) + # End class OperatorABC diff --git a/src/diffpy/srfit/equation/literals/argument.py b/src/diffpy/srfit/equation/literals/argument.py index ccd9a124..0843e7c5 100644 --- a/src/diffpy/srfit/equation/literals/argument.py +++ b/src/diffpy/srfit/equation/literals/argument.py @@ -21,10 +21,12 @@ __all__ = ["Argument"] -from numpy import ndarray, array_equal +from numpy import array_equal, ndarray + from diffpy.srfit.equation.literals.abcs import ArgumentABC from diffpy.srfit.equation.literals.literal import Literal + class Argument(Literal, ArgumentABC): """Argument class. @@ -39,7 +41,7 @@ class Argument(Literal, ArgumentABC): const = None - def __init__(self, name = None, value = None, const = False): + def __init__(self, name=None, value=None, const=False): """Initialization.""" Literal.__init__(self, name) self.const = const @@ -69,7 +71,7 @@ def setValue(self, val): self.notify() return - value = property( lambda self: self.getValue(), - lambda self, val: self.setValue(val)) + value = property(lambda self: self.getValue(), lambda self, val: self.setValue(val)) + # End of file diff --git a/src/diffpy/srfit/equation/literals/literal.py b/src/diffpy/srfit/equation/literals/literal.py index f1f460f4..26523ac0 100644 --- a/src/diffpy/srfit/equation/literals/literal.py +++ b/src/diffpy/srfit/equation/literals/literal.py @@ -25,7 +25,8 @@ from diffpy.srfit.equation.literals.abcs import LiteralABC from diffpy.srfit.util.observable import Observable -class Literal(Observable,LiteralABC): + +class Literal(Observable, LiteralABC): """Abstract class for equation pieces, such as operators and arguments. Literal derives from Observable. See diffpy.srfit.util.observable. @@ -64,6 +65,7 @@ def _flush(self, other): return def __str__(self): - return "%s(%s)"%(self.__class__.__name__, self.name) + return "%s(%s)" % (self.__class__.__name__, self.name) + # End of file diff --git a/src/diffpy/srfit/equation/literals/operators.py b/src/diffpy/srfit/equation/literals/operators.py index 0a3f679e..68e49fb3 100644 --- a/src/diffpy/srfit/equation/literals/operators.py +++ b/src/diffpy/srfit/equation/literals/operators.py @@ -25,10 +25,21 @@ but they all identify themselves with the Visitor.onOperator method. """ -__all__ = ["Operator", "AdditionOperator", "SubtractionOperator", - "MultiplicationOperator", "DivisionOperator", "ExponentiationOperator", - "RemainderOperator", "NegationOperator", "ConvolutionOperator", - "SumOperator", "UFuncOperator", "ArrayOperator", "PolyvalOperator"] +__all__ = [ + "Operator", + "AdditionOperator", + "SubtractionOperator", + "MultiplicationOperator", + "DivisionOperator", + "ExponentiationOperator", + "RemainderOperator", + "NegationOperator", + "ConvolutionOperator", + "SumOperator", + "UFuncOperator", + "ArrayOperator", + "PolyvalOperator", +] import numpy @@ -71,13 +82,11 @@ class Operator(Literal, OperatorABC): # _value : float, numpy.ndarray or None # The last value of the operator or None. - # We must declare the abstract `args` here. args = None # default for the value _value = None - def __init__(self, name=None): """Initialize the operator object with the specified name. @@ -91,7 +100,6 @@ def __init__(self, name=None): self.args = [] return - def identify(self, visitor): """Identify self to a visitor.""" return visitor.onOperator(self) @@ -115,7 +123,7 @@ def addLiteral(self, literal): def getValue(self): """Get or evaluate the value of the operator.""" if self._value is None: - vals = [l.value for l in self.args] + vals = [arg.value for arg in self.args] self._value = self.operation(*vals) return self._value @@ -124,12 +132,12 @@ def getValue(self): def _loopCheck(self, literal): """Check if a literal causes self-reference.""" if literal is self: - raise ValueError("'%s' causes self-reference"%literal) + raise ValueError("'%s' causes self-reference" % literal) # Check to see if I am a dependency of the literal. if hasattr(literal, "args"): - for l in literal.args: - self._loopCheck(l) + for arg in literal.args: + self._loopCheck(arg) return @@ -206,6 +214,7 @@ def makeOperator(name, symbol, operation, nin, nout): op.nout = nout return op + # Some specified operators @@ -278,10 +287,10 @@ def _conv(v1, v2): # Find the centroid of the first signal s1 = sum(v1) x1 = numpy.arange(len(v1), dtype=float) - c1idx = numpy.sum(v1 * x1)/s1 + c1idx = numpy.sum(v1 * x1) / s1 # Find the centroid of the convolution xc = numpy.arange(len(c), dtype=float) - ccidx = numpy.sum(c * xc)/sum(c) + ccidx = numpy.sum(c * xc) / sum(c) # Interpolate the convolution such that the centroids line up. This # uses linear interpolation. shift = ccidx - c1idx @@ -291,7 +300,7 @@ def _conv(v1, v2): # Normalize sc = sum(c) if sc > 0: - c *= s1/sc + c *= s1 / sc return c @@ -370,4 +379,5 @@ class PolyvalOperator(BinaryOperator): operation = staticmethod(numpy.polyval) pass + # End of file diff --git a/src/diffpy/srfit/equation/visitors/__init__.py b/src/diffpy/srfit/equation/visitors/__init__.py index b4014c3e..c329a324 100644 --- a/src/diffpy/srfit/equation/visitors/__init__.py +++ b/src/diffpy/srfit/equation/visitors/__init__.py @@ -27,11 +27,11 @@ from diffpy.srfit.equation.visitors.argfinder import ArgFinder from diffpy.srfit.equation.visitors.printer import Printer -from diffpy.srfit.equation.visitors.validator import Validator from diffpy.srfit.equation.visitors.swapper import Swapper +from diffpy.srfit.equation.visitors.validator import Validator -def getArgs(literal, getconsts = True): +def getArgs(literal, getconsts=True): """Get the Arguments of a Literal tree. getconsts -- If True (default), then Arguments designated as constant @@ -72,7 +72,7 @@ def validate(literal): v = Validator() errors = literal.identify(v) if errors: - m = "Errors found in Literal tree '%s'\n"%literal + m = "Errors found in Literal tree '%s'\n" % literal m += "\n".join(errors) raise ValueError(m) return diff --git a/src/diffpy/srfit/equation/visitors/argfinder.py b/src/diffpy/srfit/equation/visitors/argfinder.py index ab26a695..3a45460c 100644 --- a/src/diffpy/srfit/equation/visitors/argfinder.py +++ b/src/diffpy/srfit/equation/visitors/argfinder.py @@ -32,7 +32,7 @@ class ArgFinder(Visitor): """ - def __init__(self, getconsts = True): + def __init__(self, getconsts=True): """Initialize. Arguments @@ -61,4 +61,5 @@ def onOperator(self, op): arg.identify(self) return self.args + # End of file diff --git a/src/diffpy/srfit/equation/visitors/printer.py b/src/diffpy/srfit/equation/visitors/printer.py index 9b98414c..c48e8c9c 100644 --- a/src/diffpy/srfit/equation/visitors/printer.py +++ b/src/diffpy/srfit/equation/visitors/printer.py @@ -25,6 +25,7 @@ from diffpy.srfit.equation.visitors.visitor import Visitor + class Printer(Visitor): """Printer for printing a Literal tree. @@ -44,13 +45,11 @@ def __init__(self): self.reset() return - def reset(self): """Reset the out put string.""" self.output = "" return - @property def eqskip(self): """Pattern for equation objects to be skipped. @@ -70,7 +69,6 @@ def eqskip(self, value): self._eqpat = re.compile(value) return - def onArgument(self, arg): """Process an Argument node. @@ -82,7 +80,6 @@ def onArgument(self, arg): self.output += str(arg.name) return self.output - def onOperator(self, op): """Process an Operator node.""" # We have to deal with infix operators @@ -93,31 +90,30 @@ def onOperator(self, op): self.output += str(op.name) + "(" for idx, literal in enumerate(op.args): - if idx != 0: self.output += ", " + if idx != 0: + self.output += ", " literal.identify(self) self.output += ")" return self.output - def onEquation(self, eq): """Process an Equation node.""" - skipthis = (self._eqpat is not None and eq.name and - self._eqpat.match(eq.name)) + skipthis = self._eqpat is not None and eq.name and self._eqpat.match(eq.name) if skipthis: self.onArgument(eq) else: eq.root.identify(self) return self.output - def _onInfix(self, op): """Process infix operators.""" self.output += "(" op.args[0].identify(self) - self.output += " %s "%op.symbol + self.output += " %s " % op.symbol op.args[1].identify(self) self.output += ")" return + # End of file diff --git a/src/diffpy/srfit/equation/visitors/swapper.py b/src/diffpy/srfit/equation/visitors/swapper.py index 0b52a558..7b194aec 100644 --- a/src/diffpy/srfit/equation/visitors/swapper.py +++ b/src/diffpy/srfit/equation/visitors/swapper.py @@ -20,6 +20,7 @@ from diffpy.srfit.equation.visitors.visitor import Visitor + class Swapper(Visitor): """Swapper for swapping out one literal for another in a literal tree. @@ -80,12 +81,10 @@ def onOperator(self, op): # If we've been told to swap out a literal, then we must do it in-place # because the order of op.args matters. if self._swap: - oldlit = self.oldlit newlit = self.newlit while oldlit in op.args: - # Record the index idx = op.args.index(oldlit) # Remove the literal @@ -113,7 +112,6 @@ def onOperator(self, op): newlit.addObserver(op._flush) op._flush(other=()) - self._swap = False return @@ -143,4 +141,5 @@ def onEquation(self, eq): return + # End of file diff --git a/src/diffpy/srfit/equation/visitors/validator.py b/src/diffpy/srfit/equation/visitors/validator.py index c99b6abe..1ccbdbab 100644 --- a/src/diffpy/srfit/equation/visitors/validator.py +++ b/src/diffpy/srfit/equation/visitors/validator.py @@ -61,7 +61,7 @@ def onArgument(self, arg): """ if not isinstance(arg, ArgumentABC): - m = msg%(arg, ArgumentABC.__name__) + m = msg % (arg, ArgumentABC.__name__) self.errors.append(m) self._nin = 1 return self.errors @@ -75,24 +75,24 @@ def onOperator(self, op): """ if not isinstance(op, OperatorABC): - m = msg%(op, OperatorABC.__name__) + m = msg % (op, OperatorABC.__name__) self.errors.append(m) # Can only process single-valued functions if op.nout != 1: - m = "'%s' is not single-valued (nout != 1)"%op + m = "'%s' is not single-valued (nout != 1)" % op self.errors.append(m) # Check name if op.name is None: - m = "'%s' does not have a name"%op + m = "'%s' does not have a name" % op self.errors.append(m) # Check symbol if op.symbol is None: - m = "'%s' does not have a symbol"%op + m = "'%s' does not have a symbol" % op self.errors.append(m) # Check operation without evaluating it if op.operation is None: - m = "'%s' does not define and operation"%op + m = "'%s' does not define and operation" % op self.errors.append(m) localnin = 0 @@ -103,10 +103,11 @@ def onOperator(self, op): # Check the input/output balance if op.nin >= 0 and localnin != op.nin: - m = "'%s' requires %i inputs but receives %i"%(op, op.nin, localnin) + m = "'%s' requires %i inputs but receives %i" % (op, op.nin, localnin) self.errors.append(m) self._nin = op.nout return self.errors + # End of file diff --git a/src/diffpy/srfit/equation/visitors/visitor.py b/src/diffpy/srfit/equation/visitors/visitor.py index ef4eaa65..75f0efa9 100644 --- a/src/diffpy/srfit/equation/visitors/visitor.py +++ b/src/diffpy/srfit/equation/visitors/visitor.py @@ -29,6 +29,7 @@ __all__ = ["Visitor"] + class Visitor(object): """Abstract class for all visitors to a Literal tree. @@ -55,8 +56,7 @@ def onEquation(self, eq): # throw an exception def _abstract(self, method): - raise NotImplementedError( - "class '%s' should override method '%s'" % (self.__class__.__name__, method)) + raise NotImplementedError("class '%s' should override method '%s'" % (self.__class__.__name__, method)) # End of file diff --git a/src/diffpy/srfit/exceptions.py b/src/diffpy/srfit/exceptions.py index eed5a572..7d93fbb2 100644 --- a/src/diffpy/srfit/exceptions.py +++ b/src/diffpy/srfit/exceptions.py @@ -20,9 +20,11 @@ class SrFitError(Exception): """Generic error in SrFit expressions or recipe.""" + pass class ParseError(Exception): """Exception used by ProfileParsers.""" + pass diff --git a/src/diffpy/srfit/fitbase/__init__.py b/src/diffpy/srfit/fitbase/__init__.py index 48b96c79..f07cfd6c 100644 --- a/src/diffpy/srfit/fitbase/__init__.py +++ b/src/diffpy/srfit/fitbase/__init__.py @@ -28,17 +28,26 @@ http://www.reflectometry.org/danse/park.html """ -__all__ = ['Calculator', 'FitContribution', 'FitHook', 'FitRecipe', - 'FitResults', 'initializeRecipe', 'PlotFitHook', 'Profile', - 'ProfileGenerator', 'SimpleRecipe'] +__all__ = [ + "Calculator", + "FitContribution", + "FitHook", + "FitRecipe", + "FitResults", + "initializeRecipe", + "PlotFitHook", + "Profile", + "ProfileGenerator", + "SimpleRecipe", +] from diffpy.srfit.fitbase.calculator import Calculator from diffpy.srfit.fitbase.fitcontribution import FitContribution from diffpy.srfit.fitbase.fithook import FitHook, PlotFitHook from diffpy.srfit.fitbase.fitrecipe import FitRecipe -from diffpy.srfit.fitbase.simplerecipe import SimpleRecipe from diffpy.srfit.fitbase.fitresults import FitResults, initializeRecipe from diffpy.srfit.fitbase.profile import Profile from diffpy.srfit.fitbase.profilegenerator import ProfileGenerator +from diffpy.srfit.fitbase.simplerecipe import SimpleRecipe # End of file diff --git a/src/diffpy/srfit/fitbase/calculator.py b/src/diffpy/srfit/fitbase/calculator.py index 6bbdeb56..ee36f4af 100644 --- a/src/diffpy/srfit/fitbase/calculator.py +++ b/src/diffpy/srfit/fitbase/calculator.py @@ -26,8 +26,8 @@ __all__ = ["Calculator"] -from diffpy.srfit.fitbase.parameterset import ParameterSet from diffpy.srfit.equation.literals.operators import Operator +from diffpy.srfit.fitbase.parameterset import ParameterSet class Calculator(Operator, ParameterSet): @@ -109,4 +109,5 @@ def _validate(self): return + # End class Calculator diff --git a/src/diffpy/srfit/fitbase/configurable.py b/src/diffpy/srfit/fitbase/configurable.py index 652cad0d..dab99dda 100644 --- a/src/diffpy/srfit/fitbase/configurable.py +++ b/src/diffpy/srfit/fitbase/configurable.py @@ -54,6 +54,7 @@ def _storeConfigurable(self, obj): self._configobjs.add(obj) return + # End class Configurable # End of file diff --git a/src/diffpy/srfit/fitbase/constraint.py b/src/diffpy/srfit/fitbase/constraint.py index 82c54897..2c0ca8f9 100644 --- a/src/diffpy/srfit/fitbase/constraint.py +++ b/src/diffpy/srfit/fitbase/constraint.py @@ -41,7 +41,7 @@ class Constraint(Validatable): """ def __init__(self): - """Initialization. """ + """Initialization.""" self.par = None self.eq = None return @@ -57,10 +57,10 @@ def constrain(self, par, eq): """ if par.const: - raise ValueError("The parameter '%s' is constant"%par) + raise ValueError("The parameter '%s' is constant" % par) if par.constrained: - raise ValueError("The parameter '%s' is already constrained"%par) + raise ValueError("The parameter '%s' is already constrained" % par) par.constrained = True @@ -100,6 +100,7 @@ def _validate(self): raise SrFitError("eq is None") self.par._validate() from diffpy.srfit.equation.visitors import validate + try: validate(self.eq) except ValueError as e: @@ -117,4 +118,5 @@ def _validate(self): return + # End of file diff --git a/src/diffpy/srfit/fitbase/fitcontribution.py b/src/diffpy/srfit/fitbase/fitcontribution.py index 1e80ce0e..d2982fd9 100644 --- a/src/diffpy/srfit/fitbase/fitcontribution.py +++ b/src/diffpy/srfit/fitbase/fitcontribution.py @@ -25,11 +25,12 @@ __all__ = ["FitContribution"] -from diffpy.srfit.fitbase.parameterset import ParameterSet -from diffpy.srfit.fitbase.recipeorganizer import equationFromString +from diffpy.srfit.exceptions import SrFitError from diffpy.srfit.fitbase.parameter import ParameterProxy +from diffpy.srfit.fitbase.parameterset import ParameterSet from diffpy.srfit.fitbase.profile import Profile -from diffpy.srfit.exceptions import SrFitError +from diffpy.srfit.fitbase.recipeorganizer import equationFromString + class FitContribution(ParameterSet): """FitContribution class. @@ -80,7 +81,7 @@ def __init__(self, name): self._manage(self._generators) return - def setProfile(self, profile, xname = None, yname = None, dyname = None): + def setProfile(self, profile, xname=None, yname=None, dyname=None): """Assign the Profile for this FitContribution. profile -- A Profile that specifies the calculation points and that @@ -121,9 +122,9 @@ def setProfile(self, profile, xname = None, yname = None, dyname = None): xpar = ParameterProxy(xname, self.profile.xpar) ypar = ParameterProxy(yname, self.profile.ypar) dypar = ParameterProxy(dyname, self.profile.dypar) - self.addParameter(xpar, check = False) - self.addParameter(ypar, check = False) - self.addParameter(dypar, check = False) + self.addParameter(xpar, check=False) + self.addParameter(ypar, check=False) + self.addParameter(dypar, check=False) # If we have ProfileGenerators, set their Profiles. for gen in self._generators.values(): @@ -131,12 +132,11 @@ def setProfile(self, profile, xname = None, yname = None, dyname = None): # If we have _eq, but not _reseq, set the residual if self._eq is not None and self._reseq is None: - self.setResidualEquation('chiv') + self.setResidualEquation("chiv") return - - def addProfileGenerator(self, gen, name = None): + def addProfileGenerator(self, gen, name=None): """Add a ProfileGenerator to be used by this FitContribution. The ProfileGenerator is given a name so that it can be used as part of @@ -176,7 +176,7 @@ def addProfileGenerator(self, gen, name = None): return - def setEquation(self, eqstr, ns = {}): + def setEquation(self, eqstr, ns={}): """Set the profile equation for the FitContribution. This sets the equation that will be used when generating the residual @@ -197,8 +197,7 @@ def setEquation(self, eqstr, ns = {}): """ # Build the equation instance. - eq = equationFromString(eqstr, self._eqfactory, - buildargs=True, ns=ns) + eq = equationFromString(eqstr, self._eqfactory, buildargs=True, ns=ns) eq.name = "eq" # Register any new Parameters. @@ -212,11 +211,10 @@ def setEquation(self, eqstr, ns = {}): # Set the residual if we need to if self.profile is not None and self._reseq is None: - self.setResidualEquation('chiv') + self.setResidualEquation("chiv") return - def getEquation(self): """Get math expression string for the active profile equation. @@ -224,12 +222,12 @@ def getEquation(self): equation has not been set yet. """ from diffpy.srfit.equation.visitors import getExpression + rv = "" if self._eq is not None: rv = getExpression(self._eq) return rv - def setResidualEquation(self, eqstr): """Set the residual equation for the FitContribution. @@ -271,7 +269,6 @@ def setResidualEquation(self, eqstr): return - def getResidualEquation(self): """Get math expression string for the active residual equation. @@ -279,12 +276,12 @@ def getResidualEquation(self): equation has not been configured yet. """ from diffpy.srfit.equation.visitors import getExpression + rv = "" if self._reseq is not None: - rv = getExpression(self._reseq, eqskip='eq$') + rv = getExpression(self._reseq, eqskip="eq$") return rv - def residual(self): """Calculate the residual for this fitcontribution. @@ -306,16 +303,13 @@ def residual(self): # the following will not recompute the equation. return self._reseq() - def evaluate(self): - """Evaluate the contribution equation and update profile.ycalc. - """ + """Evaluate the contribution equation and update profile.ycalc.""" yc = self._eq() if self.profile is not None: self.profile.ycalc = yc return yc - def _validate(self): """Validate my state. @@ -332,6 +326,7 @@ def _validate(self): # Try to get the value of eq. from diffpy.srfit.equation.visitors import validate + try: validate(self._eq) except ValueError as e: @@ -343,7 +338,7 @@ def _validate(self): try: val = self._eq() except TypeError as e: - raise SrFitError("_eq cannot be evaluated: %s"%e) + raise SrFitError("_eq cannot be evaluated: %s" % e) if val is None: raise SrFitError("_eq evaluates to None") @@ -361,4 +356,5 @@ def _validate(self): raise SrFitError("residual evaluates to None") return + # End of file diff --git a/src/diffpy/srfit/fitbase/fithook.py b/src/diffpy/srfit/fitbase/fithook.py index 7dc2de96..cf36ba38 100644 --- a/src/diffpy/srfit/fitbase/fithook.py +++ b/src/diffpy/srfit/fitbase/fithook.py @@ -71,8 +71,10 @@ def postcall(self, recipe, chiv): """ return + # End class FitHook + class PrintFitHook(FitHook): """Base class for inspecting the progress of a FitRecipe refinement. @@ -148,14 +150,15 @@ def postcall(self, recipe, chiv): print("Variables") vnames = recipe.getNames() vals = recipe.getValues() - byname = lambda nv : sortKeyForNumericString(nv[0]) - items = sorted(zip(vnames, vals), key=byname) + items = sorted(zip(vnames, vals), key=lambda nv: sortKeyForNumericString(nv[0])) for name, val in items: print(" %s = %f" % (name, val)) return + # End class PrintFitHook + # TODO - Display the chi^2 on the plot during refinement. class PlotFitHook(FitHook): """This FitHook has live plotting of whatever is being refined.""" @@ -177,7 +180,6 @@ def reset(self, recipe): nrows = (nc + 1) / 2 for idx, c in enumerate(recipe._contributions.values()): - name = c.name xname = c._xname yname = c._yname @@ -185,20 +187,20 @@ def reset(self, recipe): # Create a subplot if nc > 1: - pylab.subplot(nrows, ncols, idx+1) - pdata = pylab.plot(p.x, p.y, 'bo')[0] - pfit = pylab.plot(p.x, p.y, 'r-')[0] + pylab.subplot(nrows, ncols, idx + 1) + pdata = pylab.plot(p.x, p.y, "bo")[0] + pfit = pylab.plot(p.x, p.y, "r-")[0] self._plots.append((pdata, pfit)) pylab.xlabel(xname) pylab.ylabel(yname) pylab.title(name) # Set up some event handling, so things behave nicely. - #def redraw(event): + # def redraw(event): # canvas = event.canvas # canvas.draw() # return - #pylab.connect('resize_event', redraw) + # pylab.connect('resize_event', redraw) return @@ -215,7 +217,6 @@ def postcall(self, recipe, chiv): import pylab for c, plottup in zip(recipe._contributions.values(), self._plots): - p = c.profile pdata = plottup[0] pfit = plottup[1] diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index c367f78b..e79563f0 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -35,14 +35,15 @@ __all__ = ["FitRecipe"] from collections import OrderedDict -from numpy import array, concatenate, sqrt, dot + import six +from numpy import array, concatenate, dot, sqrt -from diffpy.srfit.interface import _fitrecipe_interface -from diffpy.srfit.util.tagmanager import TagManager +from diffpy.srfit.fitbase.fithook import PrintFitHook from diffpy.srfit.fitbase.parameter import ParameterProxy from diffpy.srfit.fitbase.recipeorganizer import RecipeOrganizer -from diffpy.srfit.fitbase.fithook import PrintFitHook +from diffpy.srfit.interface import _fitrecipe_interface +from diffpy.srfit.util.tagmanager import TagManager class FitRecipe(_fitrecipe_interface, RecipeOrganizer): @@ -87,18 +88,20 @@ class FitRecipe(_fitrecipe_interface, RecipeOrganizer): bounds2 -- Bounds on parameters (read only). See getBounds2. """ - fixednames = property(lambda self: - [v.name for v in self._parameters.values() - if not (self.isFree(v) or self.isConstrained(v))], - doc='names of the fixed refinable variables') - fixedvalues = property(lambda self: - array([v.value for v in self._parameters.values() - if not (self.isFree(v) or self.isConstrained(v))]), - doc='values of the fixed refinable variables') + fixednames = property( + lambda self: [v.name for v in self._parameters.values() if not (self.isFree(v) or self.isConstrained(v))], + doc="names of the fixed refinable variables", + ) + fixedvalues = property( + lambda self: array( + [v.value for v in self._parameters.values() if not (self.isFree(v) or self.isConstrained(v))] + ), + doc="values of the fixed refinable variables", + ) bounds = property(lambda self: self.getBounds()) bounds2 = property(lambda self: self.getBounds2()) - def __init__(self, name = "fit"): + def __init__(self, name="fit"): """Initialization.""" RecipeOrganizer.__init__(self, name) self.fithooks = [] @@ -119,7 +122,7 @@ def __init__(self, name = "fit"): return - def pushFitHook(self, fithook, index = None): + def pushFitHook(self, fithook, index=None): """Add a FitHook to be called within the residual method. The hook is an object for reporting updates, or more fundamentally, @@ -138,7 +141,7 @@ def pushFitHook(self, fithook, index = None): self._updateConfiguration() return - def popFitHook(self, fithook = None, index = -1): + def popFitHook(self, fithook=None, index=-1): """Remove a FitHook by index or reference. fithook -- FitHook instance to remove from the sequence. If this is @@ -164,7 +167,7 @@ def clearFitHooks(self): del self.fithooks[:] return - def addContribution(self, con, weight = 1.0): + def addContribution(self, con, weight=1.0): """Add a FitContribution to the FitRecipe. con -- The FitContribution to be stored. @@ -203,7 +206,7 @@ def removeParameterSet(self, parset): self._removeObject(parset, self._parsets) return - def residual(self, p = []): + def residual(self, p=[]): """Calculate the vector residual to be optimized. Arguments @@ -234,22 +237,22 @@ def residual(self, p = []): con.update() # Calculate the bare chiv - chiv = concatenate([ - wi * ci.residual().flatten() - for wi, ci in zip(self._weights, self._contributions.values())]) + chiv = concatenate( + [wi * ci.residual().flatten() for wi, ci in zip(self._weights, self._contributions.values())] + ) # Calculate the point-average chi^2 - w = dot(chiv, chiv)/len(chiv) + w = dot(chiv, chiv) / len(chiv) # Now we must append the restraints - penalties = [ sqrt(res.penalty(w)) for res in self._restraintlist ] - chiv = concatenate( [ chiv, penalties ] ) + penalties = [sqrt(res.penalty(w)) for res in self._restraintlist] + chiv = concatenate([chiv, penalties]) for fithook in self.fithooks: fithook.postcall(self, chiv) return chiv - def scalarResidual(self, p = []): + def scalarResidual(self, p=[]): """Calculate the scalar residual to be optimized. Arguments @@ -267,7 +270,7 @@ def scalarResidual(self, p = []): chiv = self.residual(p) return dot(chiv, chiv) - def __call__(self, p = []): + def __call__(self, p=[]): """Same as scalarResidual method.""" return self.scalarResidual(p) @@ -318,14 +321,11 @@ def __verifyProfiles(self): # Check for profile values for con in self._contributions.values(): if con.profile is None: - m = "FitContribution '%s' does not have a Profile"%con.name + m = "FitContribution '%s' does not have a Profile" % con.name + raise AttributeError(m) + if con.profile.x is None or con.profile.y is None or con.profile.dy is None: + m = "Profile for '%s' is missing data" % con.name raise AttributeError(m) - if con.profile.x is None or\ - con.profile.y is None or\ - con.profile.dy is None: - - m = "Profile for '%s' is missing data"%con.name - raise AttributeError(m) return def __verifyParameters(self): @@ -344,7 +344,7 @@ def __verifyParameters(self): for par in badpars: objlist = self._locateManagedObject(par) names = [obj.name for obj in objlist] - badnames.append( ".".join(names) ) + badnames.append(".".join(names)) # Construct an error message, if necessary m = "" @@ -362,14 +362,15 @@ def __verifyParameters(self): def __collectConstraintsAndRestraints(self): """Collect the Constraints and Restraints from subobjects.""" - from itertools import chain from functools import cmp_to_key + from itertools import chain + rset = set(self._restraints) cdict = {} for org in chain(self._contributions.values(), self._parsets.values()): - rset.update( org._getRestraints() ) - cdict.update( org._getConstraints() ) + rset.update(org._getRestraints()) + cdict.update(org._getConstraints()) cdict.update(self._constraints) # The order of the restraint list does not matter @@ -386,7 +387,7 @@ def __collectConstraintsAndRestraints(self): # Now check the constraint's equation for constrained arguments for arg in con.eq.args: if arg in cdict: - depmap[con].add( cdict[arg] ) + depmap[con].add(cdict[arg]) # Turn the dependency map into multi-level map. def _extendDeps(con): @@ -422,8 +423,7 @@ def cmp(x, y): # Variable manipulation - def addVar(self, par, value = None, name = None, fixed = False, tag = None, - tags = []): + def addVar(self, par, value=None, name=None, fixed=False, tag=None, tags=[]): """Add a variable to be refined. par -- A Parameter that will be varied during a fit. @@ -448,10 +448,10 @@ def addVar(self, par, value = None, name = None, fixed = False, tag = None, name = name or par.name if par.const: - raise ValueError("The parameter '%s' is constant"%par) + raise ValueError("The parameter '%s' is constant" % par) if par.constrained: - raise ValueError("The parameter '%s' is constrained"%par) + raise ValueError("The parameter '%s' is constrained" % par) var = ParameterProxy(name, par) if value is not None: @@ -487,13 +487,12 @@ def delVar(self, var): def __delattr__(self, name): if name in self._parameters: - self.delVar( self._parameters[name] ) + self.delVar(self._parameters[name]) return super(FitRecipe, self).__delattr__(name) return - - def newVar(self, name, value = None, fixed = False, tag = None, tags = []): + def newVar(self, name, value=None, fixed=False, tag=None, tags=[]): """Create a new variable of the fit. This method lets new variables be created that are not tied to a @@ -543,7 +542,6 @@ def _newParameter(self, name, value, check=True): self.fix(par.name) return par - def __getVarAndCheck(self, var): """Get the actual variable from var @@ -579,14 +577,14 @@ def __getVarsFromArgs(self, *args, **kw): badtags = strargs - alltags if badtags: names = ",".join(badtags) - raise ValueError("Variables or tags cannot be found (%s)"% names) + raise ValueError("Variables or tags cannot be found (%s)" % names) # Check that variables are valid allvars = set(self._parameters.values()) badvars = varargs - allvars if badvars: names = ",".join(v.name for v in badvars) - raise ValueError("Variables cannot be found (%s)"% names) + raise ValueError("Variables cannot be found (%s)" % names) # Make sure that we only have parameters in kw kwnames = set(kw.keys()) @@ -594,7 +592,7 @@ def __getVarsFromArgs(self, *args, **kw): badkw = kwnames - allnames if badkw: names = ",".join(badkw) - raise ValueError("Tags cannot be passed as keywords (%s)"% names) + raise ValueError("Tags cannot be passed as keywords (%s)" % names) # Now get all the objects referred to in the arguments. varargs |= self._tagmanager.union(*strargs) @@ -616,7 +614,6 @@ def fix(self, *args, **kw): # Check the inputs and get the variables from them varargs = self.__getVarsFromArgs(*args, **kw) - # Fix all of these for var in varargs: self._tagmanager.tag(var, self._fixedtag) @@ -656,7 +653,7 @@ def free(self, *args, **kw): def isFree(self, var): """Check if a variable is fixed.""" - return (not self._tagmanager.hasTags(var, self._fixedtag)) + return not self._tagmanager.hasTags(var, self._fixedtag) def unconstrain(self, *pars): """Unconstrain a Parameter. @@ -692,7 +689,7 @@ def unconstrain(self, *pars): return - def constrain(self, par, con, ns = {}): + def constrain(self, par, con, ns={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. @@ -722,13 +719,13 @@ def constrain(self, par, con, ns = {}): if par is None: par = ns.get(name) if par is None: - raise ValueError("The parameter '%s' cannot be found"%name) + raise ValueError("The parameter '%s' cannot be found" % name) if con in self._parameters.keys(): con = self._parameters[con] if par.const: - raise ValueError("The parameter '%s' is constant"%par) + raise ValueError("The parameter '%s' is constant" % par) # This will pass the value of a constrained parameter to the initial # value of a parameter constraint. @@ -744,11 +741,9 @@ def constrain(self, par, con, ns = {}): RecipeOrganizer.constrain(self, par, con, ns) return - def getValues(self): """Get the current values of the variables in a list.""" - return array([v.value for v in self._parameters.values() if - self.isFree(v)]) + return array([v.value for v in self._parameters.values() if self.isFree(v)]) def getNames(self): """Get the names of the variables in a list.""" @@ -772,7 +767,7 @@ def getBounds2(self): ub = array([b[1] for b in bounds]) return lb, ub - def boundsToRestraints(self, sig = 1, scaled = False): + def boundsToRestraints(self, sig=1, scaled=False): """Turn all bounded parameters into restraints. The bounds become limits on the restraint. @@ -785,13 +780,13 @@ def boundsToRestraints(self, sig = 1, scaled = False): if not hasattr(sig, "__iter__"): sig = [sig] * len(pars) for par, x in zip(pars, sig): - self.restrain(par, par.bounds[0], par.bounds[1], sig = x, - scaled = scaled) + self.restrain(par, par.bounds[0], par.bounds[1], sig=x, scaled=scaled) return def _applyValues(self, p): """Apply variable values to the variables.""" - if len(p) == 0: return + if len(p) == 0: + return vargen = (v for v in self._parameters.values() if self.isFree(v)) for var, pval in zip(vargen, p): var.setValue(pval) @@ -802,4 +797,5 @@ def _updateConfiguration(self): self._ready = False return + # End of file diff --git a/src/diffpy/srfit/fitbase/fitresults.py b/src/diffpy/srfit/fitbase/fitresults.py index 6cde340d..4c35abb3 100644 --- a/src/diffpy/srfit/fitbase/fitresults.py +++ b/src/diffpy/srfit/fitbase/fitresults.py @@ -25,12 +25,13 @@ __all__ = ["FitResults", "ContributionResults", "initializeRecipe"] import re -import numpy from collections import OrderedDict -from diffpy.srfit.util.inpututils import inputToString +import numpy + from diffpy.srfit.util import _DASHEDLINE from diffpy.srfit.util import sortKeyForNumericString as numstr +from diffpy.srfit.util.inpututils import inputToString class FitResults(object): @@ -70,8 +71,7 @@ class FitResults(object): """ - def __init__(self, recipe, update = True, showfixed = True, showcon = - False): + def __init__(self, recipe, update=True, showfixed=True, showcon=False): """Initialize the attributes. recipe -- The recipe containing the results @@ -111,8 +111,8 @@ def __init__(self, recipe, update = True, showfixed = True, showcon = def update(self): """Update the results according to the current state of the recipe.""" - ## Note that the order of these operations are chosen to reduce - ## computation time. + # Note that the order of these operations are chosen to reduce + # computation time. recipe = self.recipe @@ -139,8 +139,7 @@ def update(self): self._calculateCovariance() # Get the variable uncertainties - self.varunc = [self.cov[i,i]**0.5 for i in \ - range(len(self.varnames))] + self.varunc = [self.cov[i, i] ** 0.5 for i in range(len(self.varnames))] # Get the constraint uncertainties self._calculateConstraintUncertainties() @@ -170,12 +169,12 @@ def _calculateCovariance(self): """ try: J = self._calculateJacobian() - u,s,vh = numpy.linalg.svd(J,0) - self.cov = numpy.dot(vh.T.conj()/s**2,vh) + u, s, vh = numpy.linalg.svd(J, 0) + self.cov = numpy.dot(vh.T.conj() / s**2, vh) except numpy.linalg.LinAlgError: self.messages.append("Cannot compute covariance matrix.") - l = len(self.varvals) - self.cov = numpy.zeros((l, l), dtype=float) + matrix_size = len(self.varvals) + self.cov = numpy.zeros((matrix_size, matrix_size), dtype=float) return def _calculateJacobian(self): @@ -207,7 +206,7 @@ def _calculateJacobian(self): # The list of constraint derivatives with respect to variables # The forward difference would be faster, but perhaps not as accurate. conr = [] - for k,v in enumerate(pvals): + for k, v in enumerate(pvals): h = delta[k] pvals[k] = v + h rk = self.recipe.residual(pvals) @@ -227,14 +226,14 @@ def _calculateJacobian(self): val = con.par.getValue() if numpy.isscalar(val): cond[i] -= con.par.getValue() - cond[i] /= 2*h + cond[i] /= 2 * h else: cond[i] = 0.0 conr.append(cond) pvals[k] = v - r.append(rk/(2*h)) + r.append(rk / (2 * h)) # Reset the constrained parameters to their original values for con in recipe._oconstraints: @@ -282,7 +281,6 @@ def _calculateConstraintUncertainties(self): # sig^2(c) = sum_i sum_j u_i u_j self.conunc = [] for dci in self._dcon: - # Create sig(v_i) (dc/dv_i) array. u = dci * vu # The outer product is all possible pairings of u_i and u_j @@ -294,7 +292,7 @@ def _calculateConstraintUncertainties(self): self.conunc.append(sig2c**0.5) return - def formatResults(self, header = "", footer = "", update = False): + def formatResults(self, header="", footer="", update=False): """Format the results and return them in a string. This function is called by printResults and saveResults. Overloading @@ -313,7 +311,7 @@ def formatResults(self, header = "", footer = "", update = False): lines = [] corrmin = 0.25 p = self.precision - pe = "%-" + "%i.%ie" % (p+6, p) + pe = "%-" + "%i.%ie" % (p + 6, p) pet = "%" + ".%ie" % (p,) # Check to see if the uncertainty values are reliable. certain = True @@ -327,56 +325,56 @@ def formatResults(self, header = "", footer = "", update = False): lines.append(header) if not certain: - l = "Some quantities invalid due to missing profile uncertainty" - if not l in self.messages: - self.messages.append(l) + error_message = "Some quantities invalid due to missing profile uncertainty" + if error_message not in self.messages: + self.messages.append(error_message) lines.extend(self.messages) - ## Overall results - l = "Overall" + # Overall results + error_message = "Overall" if not certain: - l += " (Chi2 and Reduced Chi2 invalid)" - lines.append(l) + error_message += " (Chi2 and Reduced Chi2 invalid)" + lines.append(error_message) lines.append(_DASHEDLINE) formatstr = "%-14s %.8f" - lines.append(formatstr%("Residual",self.residual)) - lines.append(formatstr%("Contributions", self.residual - self.penalty)) - lines.append(formatstr%("Restraints", self.penalty)) - lines.append(formatstr%("Chi2",self.chi2)) - lines.append(formatstr%("Reduced Chi2",self.rchi2)) - lines.append(formatstr%("Rw",self.rw)) - - ## Per-FitContribution results + lines.append(formatstr % ("Residual", self.residual)) + lines.append(formatstr % ("Contributions", self.residual - self.penalty)) + lines.append(formatstr % ("Restraints", self.penalty)) + lines.append(formatstr % ("Chi2", self.chi2)) + lines.append(formatstr % ("Reduced Chi2", self.rchi2)) + lines.append(formatstr % ("Rw", self.rw)) + + # Per-FitContribution results if len(self.conresults) > 1: keys = list(self.conresults.keys()) keys.sort(key=numstr) lines.append("") - l = "Contributions" + error_message = "Contributions" if not certain: - l += " (Chi2 and Reduced Chi2 invalid)" - lines.append(l) + error_message += " (Chi2 and Reduced Chi2 invalid)" + lines.append(error_message) lines.append(_DASHEDLINE) formatstr = "%-10s %-42.8f" for name in keys: res = self.conresults[name] lines.append("") - namestr = name + " (%f)"%res.weight + namestr = name + " (%f)" % res.weight lines.append(namestr) - lines.append("-"*len(namestr)) - lines.append(formatstr%("Residual",res.residual)) - lines.append(formatstr%("Chi2",res.chi2)) - lines.append(formatstr%("Rw",res.rw)) + lines.append("-" * len(namestr)) + lines.append(formatstr % ("Residual", res.residual)) + lines.append(formatstr % ("Chi2", res.chi2)) + lines.append(formatstr % ("Rw", res.rw)) - ## The variables + # The variables if self.varnames: lines.append("") - l = "Variables" + error_message = "Variables" if not certain: m = "Uncertainties invalid" - l += " (%s)"%m - lines.append(l) + error_message += " (%s)" % m + lines.append(error_message) lines.append(_DASHEDLINE) varnames = self.varnames @@ -385,11 +383,11 @@ def formatResults(self, header = "", footer = "", update = False): varlines = [] w = max(map(len, varnames)) - w = str(w+1) + w = str(w + 1) # Format the lines - formatstr = "%-"+w+"s " + pe + " +/- " + pet + formatstr = "%-" + w + "s " + pe + " +/- " + pet for name, val, unc in zip(varnames, varvals, varunc): - varlines.append(formatstr%(name, val, unc)) + varlines.append(formatstr % (name, val, unc)) varlines.sort() lines.extend(varlines) @@ -401,21 +399,20 @@ def formatResults(self, header = "", footer = "", update = False): lines.append("Fixed Variables") lines.append(_DASHEDLINE) w = max(map(len, self.fixednames)) - w = str(w+1) - formatstr = "%-"+w+"s " + pet + w = str(w + 1) + formatstr = "%-" + w + "s " + pet for name, val in zip(self.fixednames, self.fixedvals): - varlines.append(formatstr%(name, val)) + varlines.append(formatstr % (name, val)) varlines.sort() lines.extend(varlines) - - ## The constraints + # The constraints if self.connames and self.showcon: lines.append("") - l = "Constrained Parameters" + error_message = "Constrained Parameters" if not certain: - l += " (Uncertainties invalid)" - lines.append(l) + error_message += " (Uncertainties invalid)" + lines.append(error_message) lines.append(_DASHEDLINE) w = 0 @@ -432,52 +429,51 @@ def formatResults(self, header = "", footer = "", update = False): vals[name] = (val, unc) keys.sort(key=numstr) - w = str(w+1) - formatstr = "%-"+w+"s %- 15f +/- %-15f" + w = str(w + 1) + formatstr = "%-" + w + "s %- 15f +/- %-15f" for name in keys: val, unc = vals[name] - lines.append(formatstr%(name, val, unc)) + lines.append(formatstr % (name, val, unc)) - ## Variable correlations + # Variable correlations lines.append("") - corint = int(corrmin*100) - l = "Variable Correlations greater than %i%%"%corint + corint = int(corrmin * 100) + error_message = "Variable Correlations greater than %i%%" % corint if not certain: - l += " (Correlations invalid)" - lines.append(l) + error_message += " (Correlations invalid)" + lines.append(error_message) lines.append(_DASHEDLINE) tup = [] cornames = [] n = len(self.varnames) for i in range(n): for j in range(i + 1, n): - name = "corr(%s, %s)"%(varnames[i], varnames[j]) - val = (self.cov[i,j]/(self.cov[i,i] * self.cov[j,j])**0.5) + name = "corr(%s, %s)" % (varnames[i], varnames[j]) + val = self.cov[i, j] / (self.cov[i, i] * self.cov[j, j]) ** 0.5 if abs(val) > corrmin: cornames.append(name) tup.append((val, name)) - tup.sort(key=lambda vn : abs(vn[0])) + tup.sort(key=lambda vn: abs(vn[0])) tup.reverse() if cornames: w = max(map(len, cornames)) w = str(w + 1) - formatstr = "%-"+w+"s %.4f" + formatstr = "%-" + w + "s %.4f" for val, name in tup: - lines.append(formatstr%(name, val)) + lines.append(formatstr % (name, val)) else: - lines.append("No correlations greater than %i%%"%corint) - + lines.append("No correlations greater than %i%%" % corint) # User-defined footer if footer: lines.append(footer) - out = "\n".join(lines) + '\n' + out = "\n".join(lines) + "\n" return out - def printResults(self, header = "", footer = "", update = False): + def printResults(self, header="", footer="", update=False): """Format and print the results. header -- A header to add to the output (default "") @@ -491,8 +487,7 @@ def printResults(self, header = "", footer = "", update = False): def __str__(self): return self.formatResults() - - def saveResults(self, filename, header = "", footer = "", update = False): + def saveResults(self, filename, header="", footer="", update=False): """Format and save the results. filename - Name of the save file. @@ -502,20 +497,23 @@ def saveResults(self, filename, header = "", footer = "", update = False): """ # Save the time and user - from time import ctime from getpass import getuser + from time import ctime + myheader = "Results written: " + ctime() + "\n" myheader += "produced by " + getuser() + "\n" header = myheader + header res = self.formatResults(header, footer, update) - f = open(filename, 'w') + f = open(filename, "w") f.write(res) f.close() return + # End class FitResults + class ContributionResults(object): """Class for processing, storing FitContribution results. @@ -568,8 +566,8 @@ def __init__(self, con, weight, fitres): def _init(self, con, weight, fitres): """Initialize the attributes, for real.""" - ## Note that the order of these operations is chosen to reduce - ## computation time. + # Note that the order of these operations is chosen to reduce + # computation time. if con.profile is None: return @@ -609,13 +607,14 @@ def _calculateMetrics(self): # We take absolute values in case the signal is complex num = numpy.abs(self.y - self.ycalc) y = numpy.abs(self.y) - chiv = num/self.dy + chiv = num / self.dy self.cumchi2 = numpy.cumsum(chiv**2) # avoid index error for empty array self.chi2 = self.cumchi2[-1:].sum() yw = y / self.dy yw2tot = numpy.dot(yw, yw) - if yw2tot == 0.0: yw2tot = 1.0 + if yw2tot == 0.0: + yw2tot = 1.0 self.cumrw = numpy.sqrt(self.cumchi2 / yw2tot) # avoid index error for empty array self.rw = self.cumrw[-1:].sum() @@ -624,6 +623,7 @@ def _calculateMetrics(self): # End class ContributionResults + def resultsDictionary(results): """Get dictionary of results from file. @@ -636,8 +636,7 @@ def resultsDictionary(results): """ resstr = inputToString(results) - rx = {'f' : r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?", - 'n' : r'[a-zA-Z_]\w*'} + rx = {"f": r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?", "n": r"[a-zA-Z_]\w*"} pat = r"(%(n)s)\s+(%(f)s)" % rx matches = re.findall(pat, resstr) @@ -646,6 +645,7 @@ def resultsDictionary(results): mpairs = dict(matches) return mpairs + def initializeRecipe(recipe, results): """Initialize the variables of a recipe from a results file. diff --git a/src/diffpy/srfit/fitbase/parameter.py b/src/diffpy/srfit/fitbase/parameter.py index 277bfc52..8ef3b083 100644 --- a/src/diffpy/srfit/fitbase/parameter.py +++ b/src/diffpy/srfit/fitbase/parameter.py @@ -29,12 +29,12 @@ import numpy -from diffpy.srfit.exceptions import SrFitError from diffpy.srfit.equation.literals import Argument -from diffpy.srfit.util.nameutils import validateName -from diffpy.srfit.util.argbinders import bind2nd -from diffpy.srfit.interface import _parameter_interface +from diffpy.srfit.exceptions import SrFitError from diffpy.srfit.fitbase.validatable import Validatable +from diffpy.srfit.interface import _parameter_interface +from diffpy.srfit.util.argbinders import bind2nd +from diffpy.srfit.util.nameutils import validateName class Parameter(_parameter_interface, Argument, Validatable): @@ -53,7 +53,7 @@ class Parameter(_parameter_interface, Argument, Validatable): """ - def __init__(self, name, value = None, const = False): + def __init__(self, name, value=None, const=False): """Initialization. name -- The name of this Parameter (must be a valid attribute @@ -86,7 +86,7 @@ def setValue(self, val): Argument.setValue(self, val) return self - def setConst(self, const = True, value = None): + def setConst(self, const=True, value=None): """Toggle the Parameter as constant. const -- Flag indicating if the parameter is constant (default @@ -103,7 +103,6 @@ def setConst(self, const = True, value = None): self.setValue(value) return self - def boundRange(self, lb=None, ub=None): """Set lower and upper bound of the Parameter. @@ -118,8 +117,7 @@ def boundRange(self, lb=None, ub=None): self.bounds[1] = ub return self - - def boundWindow(self, lr = 0, ur = None): + def boundWindow(self, lr=0, ur=None): """Create bounds centered on the current value of the Parameter. lr -- The radius of the lower bound (default 0). The lower bound is @@ -148,11 +146,13 @@ def _validate(self): """ if self.value is None: - raise SrFitError("value of '%s' is None"%self.name) + raise SrFitError("value of '%s' is None" % self.name) return + # End class Parameter + class ParameterProxy(Parameter): """A Parameter proxy for another parameter. @@ -165,7 +165,6 @@ class ParameterProxy(Parameter): """ - def __init__(self, name, par): """Initialization. @@ -185,8 +184,7 @@ def __init__(self, name, par): @property def constrained(self): - """A flag indicating if the proxied Parameter is constrained. - """ + """A flag indicating if the proxied Parameter is constrained.""" return self.par.constrained @constrained.setter @@ -194,7 +192,6 @@ def constrained(self, value): self.par.constrained = bool(value) return - @property def bounds(self): """List of lower and upper bounds of the proxied Parameter. @@ -209,7 +206,6 @@ def bounds(self, value): self.par.bounds = value return - @property def _observers(self): return self.par._observers @@ -220,27 +216,22 @@ def _observers(self): def setValue(self, val): return self.par.setValue(val) - @wraps(Parameter.getValue) def getValue(self): return self.par.getValue() - @wraps(Parameter.setConst) def setConst(self, const=True, value=None): return self.par.setConst(const, value) - @wraps(Parameter.boundRange) def boundRange(self, lb=None, ub=None): return self.par.boundRange(lb, ub) - @wraps(Parameter.boundWindow) def boundWindow(self, lr=0, ur=None): return self.par.boundWindow(lr, ur) - def _validate(self): """Validate my state. @@ -254,6 +245,7 @@ def _validate(self): self.par._validate() return + # End class ParameterProxy @@ -265,7 +257,7 @@ class ParameterAdapter(Parameter): """ - def __init__(self, name, obj, getter = None, setter = None, attr = None): + def __init__(self, name, obj, getter=None, setter=None, attr=None): """Wrap an object as a Parameter. name -- The name of this Parameter. @@ -326,6 +318,7 @@ def setValue(self, value): self.notify() return self + # End class ParameterAdapter # End of file diff --git a/src/diffpy/srfit/fitbase/parameterset.py b/src/diffpy/srfit/fitbase/parameterset.py index a13d7ea5..ef64c8e9 100644 --- a/src/diffpy/srfit/fitbase/parameterset.py +++ b/src/diffpy/srfit/fitbase/parameterset.py @@ -98,7 +98,7 @@ def removeParameterSet(self, parset): self._removeObject(parset, self._parsets) return - def setConst(self, const = True): + def setConst(self, const=True): """Set every parameter within the set to a constant. const -- Flag indicating if the parameter is constant (default @@ -110,6 +110,7 @@ def setConst(self, const = True): return + # End class ParameterSet # End of file diff --git a/src/diffpy/srfit/fitbase/profile.py b/src/diffpy/srfit/fitbase/profile.py index 292fdb89..02145e93 100644 --- a/src/diffpy/srfit/fitbase/profile.py +++ b/src/diffpy/srfit/fitbase/profile.py @@ -23,13 +23,13 @@ __all__ = ["Parameter", "Profile"] -import six import numpy +import six -from diffpy.srfit.util.observable import Observable +from diffpy.srfit.exceptions import SrFitError from diffpy.srfit.fitbase.parameter import Parameter from diffpy.srfit.fitbase.validatable import Validatable -from diffpy.srfit.exceptions import SrFitError +from diffpy.srfit.util.observable import Observable # This is the roundoff tolerance for selecting bounds on arrays. epsilon = 1e-8 @@ -88,19 +88,15 @@ def __init__(self): return # We want x, y, ycalc and dy to stay in-sync with xpar, ypar and dypar - x = property( lambda self : self.xpar.getValue(), - lambda self, val : self.xpar.setValue(val) ) - y = property( lambda self : self.ypar.getValue(), - lambda self, val : self.ypar.setValue(val) ) - dy = property( lambda self : self.dypar.getValue(), - lambda self, val : self.dypar.setValue(val) ) - ycalc = property( lambda self : self.ycpar.getValue(), - lambda self, val : self.ycpar.setValue(val) ) + x = property(lambda self: self.xpar.getValue(), lambda self, val: self.xpar.setValue(val)) + y = property(lambda self: self.ypar.getValue(), lambda self, val: self.ypar.setValue(val)) + dy = property(lambda self: self.dypar.getValue(), lambda self, val: self.dypar.setValue(val)) + ycalc = property(lambda self: self.ycpar.getValue(), lambda self, val: self.ycpar.setValue(val)) # We want xobs, yobs and dyobs to be read-only - xobs = property( lambda self: self._xobs ) - yobs = property( lambda self: self._yobs ) - dyobs = property( lambda self: self._dyobs ) + xobs = property(lambda self: self._xobs) + yobs = property(lambda self: self._yobs) + dyobs = property(lambda self: self._dyobs) def loadParsedData(self, parser): """Load parsed data from a ProfileParser. @@ -113,7 +109,7 @@ def loadParsedData(self, parser): self.setObservedProfile(x, y, dy) return - def setObservedProfile(self, xobs, yobs, dyobs = None): + def setObservedProfile(self, xobs, yobs, dyobs=None): """Set the observed profile. Arguments @@ -182,29 +178,27 @@ def setCalculationRange(self, xmin=None, xmax=None, dx=None): """ if self.xobs is None: raise AttributeError("No observed profile") + # local helper function def _isobs(a): if not isinstance(a, six.string_types): return False - if a != 'obs': + if a != "obs": raise ValueError('Must be either float or "obs".') return True + # resolve new low and high bounds for x - lo = (self.x[0] if xmin is None else - self.xobs[0] if _isobs(xmin) else float(xmin)) + lo = self.x[0] if xmin is None else self.xobs[0] if _isobs(xmin) else float(xmin) lo = max(lo, self.xobs[0]) - hi = (self.x[-1] if xmax is None else - self.xobs[-1] if _isobs(xmax) else float(xmax)) + hi = self.x[-1] if xmax is None else self.xobs[-1] if _isobs(xmax) else float(xmax) hi = min(hi, self.xobs[-1]) # determine if we need to clip the original grid clip = True step = None ncur = len(self.x) - stepcur = (1 if ncur < 2 - else (self.x[-1] - self.x[0]) / (ncur - 1.0)) + stepcur = 1 if ncur < 2 else (self.x[-1] - self.x[0]) / (ncur - 1.0) nobs = len(self.xobs) - stepobs = (1 if nobs < 2 - else (self.xobs[-1] - self.xobs[0]) / (nobs - 1.0)) + stepobs = 1 if nobs < 2 else (self.xobs[-1] - self.xobs[0]) / (nobs - 1.0) if dx is None: # check if xobs overlaps with x i0 = numpy.fabs(self.xobs - self.x[0]).argmin() @@ -244,7 +238,6 @@ def _isobs(a): self.setCalculationPoints(x1) return - def setCalculationPoints(self, x): """Set the calculation points. @@ -258,8 +251,8 @@ def setCalculationPoints(self, x): """ x = numpy.asarray(x) if self.xobs is not None: - x = x[ x >= self.xobs[0] - epsilon ] - x = x[ x <= self.xobs[-1] + epsilon ] + x = x[x >= self.xobs[0] - epsilon] + x = x[x <= self.xobs[-1] + epsilon] self.x = x if self.yobs is not None: self.y = rebinArray(self.yobs, self.xobs, self.x) @@ -268,8 +261,8 @@ def setCalculationPoints(self, x): if (self.dyobs == 1).all(): self.dy = numpy.ones_like(self.x) else: - # FIXME - This does not follow error propogation rules and it - # introduces (more) correlation between the data points. + # FIXME - This does not follow error propogation rules and it + # introduces (more) correlation between the data points. self.dy = rebinArray(self.dyobs, self.xobs, self.x) return @@ -309,7 +302,6 @@ def loadtxt(self, *args, **kw): self.setObservedProfile(x, y, dy) return x, y, dy - def savetxt(self, fname, **kwargs): """Call `numpy.savetxt` with x, ycalc, y, dy. @@ -337,12 +329,11 @@ def savetxt(self, fname, **kwargs): raise SrFitError("ycalc is None") y = self.y dy = self.dy - kwargs.setdefault('header', 'x ycalc y dy') + kwargs.setdefault("header", "x ycalc y dy") data = numpy.transpose([x, ycalc, y, dy]) numpy.savetxt(fname, data, **kwargs) return - def _flush(self, other): """Invalidate cached state. @@ -362,8 +353,7 @@ def _validate(self): Raises SrFitError if validation fails. """ - datanotset = any(v is None for v in - [self.x, self.y, self.dy, self.xobs, self.yobs, self.dyobs]) + datanotset = any(v is None for v in [self.x, self.y, self.dy, self.xobs, self.yobs, self.dyobs]) if datanotset: raise SrFitError("Missing data") if len(self.x) != len(self.y) or len(self.x) != len(self.dy): @@ -373,6 +363,7 @@ def _validate(self): # End class Profile + def rebinArray(A, xold, xnew): """Rebin the an array by interpolating over the new x range. diff --git a/src/diffpy/srfit/fitbase/profilegenerator.py b/src/diffpy/srfit/fitbase/profilegenerator.py index 5d0fed84..67b37f59 100644 --- a/src/diffpy/srfit/fitbase/profilegenerator.py +++ b/src/diffpy/srfit/fitbase/profilegenerator.py @@ -45,8 +45,8 @@ from diffpy.srfit.equation.literals.operators import Operator -from diffpy.srfit.fitbase.parameterset import ParameterSet from diffpy.srfit.exceptions import SrFitError +from diffpy.srfit.fitbase.parameterset import ParameterSet class ProfileGenerator(Operator, ParameterSet): @@ -95,7 +95,6 @@ class ProfileGenerator(Operator, ParameterSet): nin = 0 nout = 1 - def __init__(self, name): """Initialize the attributes.""" Operator.__init__(self) @@ -104,7 +103,6 @@ def __init__(self, name): self.meta = {} return - @property def symbol(self): return self.name @@ -121,7 +119,7 @@ def __call__(self, x): """ return x - ## No need to overload anything below here + # No need to overload anything below here def operation(self): """Evaluate the profile. @@ -131,7 +129,6 @@ def operation(self): y = self.__call__(self.profile.x) return y - def setProfile(self, profile): """Assign the profile. @@ -147,7 +144,7 @@ def setProfile(self, profile): self._flush(other=(self,)) # Merge the profiles metadata with our own - self.meta.update( self.profile.meta ) + self.meta.update(self.profile.meta) self.processMetaData() return diff --git a/src/diffpy/srfit/fitbase/profileparser.py b/src/diffpy/srfit/fitbase/profileparser.py index 8dff0cf2..40812224 100644 --- a/src/diffpy/srfit/fitbase/profileparser.py +++ b/src/diffpy/srfit/fitbase/profileparser.py @@ -105,7 +105,7 @@ def parseFile(self, filename): Raises ParseError if the file cannot be parsed """ - infile = open(filename, 'r') + infile = open(filename, "r") self._banks = [] self._meta = {} filestring = infile.read() @@ -155,7 +155,7 @@ def selectBank(self, index): self._x, self._y, self._dx, self._dy = self._banks[index] return - def getData(self, index = None): + def getData(self, index=None): """Get the data. This method should only be called after the data has been parsed. The @@ -179,4 +179,5 @@ def getMetaData(self): """Get the parsed metadata.""" return self._meta + # End of ProfileParser diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index e3c95267..6b245de1 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -23,26 +23,25 @@ __all__ = ["RecipeContainer", "RecipeOrganizer", "equationFromString"] -from numpy import inf +import re from collections import OrderedDict from itertools import chain, groupby -import re import six +from numpy import inf +from diffpy.srfit.equation import Equation +from diffpy.srfit.equation.builder import EquationFactory +from diffpy.srfit.fitbase.configurable import Configurable from diffpy.srfit.fitbase.constraint import Constraint -from diffpy.srfit.fitbase.restraint import Restraint from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.fitbase.configurable import Configurable +from diffpy.srfit.fitbase.restraint import Restraint from diffpy.srfit.fitbase.validatable import Validatable - -from diffpy.srfit.util.observable import Observable -from diffpy.srfit.equation import Equation -from diffpy.srfit.equation.builder import EquationFactory -from diffpy.srfit.util.nameutils import validateName from diffpy.srfit.interface import _recipeorganizer_interface from diffpy.srfit.util import _DASHEDLINE from diffpy.srfit.util import sortKeyForNumericString as numstr +from diffpy.srfit.util.nameutils import validateName +from diffpy.srfit.util.observable import Observable class RecipeContainer(Observable, Configurable, Validatable): @@ -109,7 +108,6 @@ def _iterManaged(self): """Get iterator over managed objects.""" return chain(*(d.values() for d in self.__managed)) - def iterPars(self, pattern="", recurse=True): """Iterate over the Parameters contained in this object. @@ -137,7 +135,6 @@ def iterPars(self, pattern="", recurse=True): yield par return - def __iter__(self): """Iterate over top-level parameters.""" return iter(self._parameters.values()) @@ -158,12 +155,13 @@ def __getattr__(self, name): raise AttributeError(name) return arg - # Ensure there is no __dir__ override in the base class. - assert (getattr(Observable, '__dir__', None) is - getattr(Configurable, '__dir__', None) is - getattr(Validatable, '__dir__', None) is - getattr(object, '__dir__', None)) + assert ( + getattr(Observable, "__dir__", None) + is getattr(Configurable, "__dir__", None) + is getattr(Validatable, "__dir__", None) + is getattr(object, "__dir__", None) + ) def __dir__(self): "Return sorted list of attributes for this object." @@ -175,7 +173,6 @@ def __dir__(self): rv = sorted(rv) return rv - # Needed by __setattr__ _parameters = OrderedDict() __managed = [] @@ -192,12 +189,11 @@ def __setattr__(self, name, value): m = self.get(name) if m is not None: - raise AttributeError("Cannot set '%s'"%name) + raise AttributeError("Cannot set '%s'" % name) super(RecipeContainer, self).__setattr__(name, value) return - def __delattr__(self, name): """Delete parameters with del. @@ -205,17 +201,17 @@ def __delattr__(self, name): configuration changes that are not yet handled in a general way. """ if name in self._parameters: - self._removeParameter( self._parameters[name] ) + self._removeParameter(self._parameters[name]) return m = self.get(name) if m is not None: - raise AttributeError("Cannot delete '%s'"%name) + raise AttributeError("Cannot delete '%s'" % name) super(RecipeContainer, self).__delattr__(name) return - def get(self, name, default = None): + def get(self, name, default=None): """Get a managed object.""" for d in self.__managed: arg = d.get(name) @@ -232,7 +228,7 @@ def getValues(self): """Get the values of managed parameters.""" return [p.value for p in self._parameters.values()] - def _addObject(self, obj, d, check = True): + def _addObject(self, obj, d, check=True): """Add an object to a managed dictionary. obj -- The object to be stored. @@ -253,14 +249,12 @@ def _addObject(self, obj, d, check = True): # Check for extant object in d with same name oldobj = d.get(obj.name) if check and oldobj is not None: - message = "%s with name '%s' already exists"%\ - (obj.__class__.__name__, obj.name) + message = "%s with name '%s' already exists" % (obj.__class__.__name__, obj.name) raise ValueError(message) # Check for object with same name in other dictionary. if oldobj is None and self.get(obj.name) is not None: - message = "Non-%s with name '%s' already exists"%\ - (obj.__class__.__name__, obj.name) + message = "Non-%s with name '%s' already exists" % (obj.__class__.__name__, obj.name) raise ValueError(message) # Detach the old object, if there is one @@ -308,7 +302,6 @@ def _locateManagedObject(self, obj): return loc for m in self._iterManaged(): - # Check locally for the object if m is obj: loc.append(obj) @@ -316,7 +309,6 @@ def _locateManagedObject(self, obj): # Check within managed objects if hasattr(m, "_locateManagedObject"): - subloc = m._locateManagedObject(obj) if subloc: return loc + subloc @@ -346,6 +338,7 @@ def _validate(self): # End class RecipeContainer + class RecipeOrganizer(_recipeorganizer_interface, RecipeContainer): """Extended base class for organizing pieces of a FitRecipe. @@ -436,7 +429,7 @@ def _removeParameter(self, par): self._eqfactory.deRegisterBuilder(par.name) return - def registerCalculator(self, f, argnames = None): + def registerCalculator(self, f, argnames=None): """Register a Calculator so it can be used within equation strings. A Calculator is an elaborate function that can organize Parameters. @@ -456,7 +449,7 @@ def registerCalculator(self, f, argnames = None): if argnames is None: fncode = f.__call__.__func__.__code__ argnames = list(fncode.co_varnames) - argnames = argnames[1:fncode.co_argcount] + argnames = argnames[1 : fncode.co_argcount] for pname in argnames: if pname not in self._eqfactory.builders: @@ -469,7 +462,7 @@ def registerCalculator(self, f, argnames = None): eq = self._eqfactory.makeEquation(f.name) return eq - def registerFunction(self, f, name = None, argnames = None): + def registerFunction(self, f, name=None, argnames=None): """Register a function so it can be used within equation strings. This creates a function with this class that can be used within string @@ -508,9 +501,8 @@ def registerFunction(self, f, name = None, argnames = None): self._eqfactory.registerOperator(name, f) return f - #### Introspection code + # Introspection code if name is None or argnames is None: - import inspect fncode = None @@ -523,12 +515,12 @@ def registerFunction(self, f, name = None, argnames = None): fncode = f.__code__ # check class method elif inspect.ismethod(f): - fncode = f.__func__.__code__ - offset = 1 + fncode = f.__func__.__code__ + offset = 1 # check functor - elif hasattr(f, "__call__") and hasattr(f.__call__, '__func__'): - fncode = f.__call__.__func__.__code__ - offset = 1 + elif hasattr(f, "__call__") and hasattr(f.__call__, "__func__"): + fncode = f.__call__.__func__.__code__ + offset = 1 else: m = "Cannot extract name or argnames" raise ValueError(m) @@ -536,16 +528,16 @@ def registerFunction(self, f, name = None, argnames = None): # Extract the name if name is None: name = fncode.co_name - if name == '': + if name == "": m = "You must supply a name name for a lambda function" raise ValueError(m) # Extract the arguments if argnames is None: argnames = list(fncode.co_varnames) - argnames = argnames[offset:fncode.co_argcount] + argnames = argnames[offset : fncode.co_argcount] - #### End introspection code + # End introspection code # Make missing Parameters for pname in argnames: @@ -554,6 +546,7 @@ def registerFunction(self, f, name = None, argnames = None): # Initialize and register from diffpy.srfit.fitbase.calculator import Calculator + if isinstance(f, Calculator): for pname in argnames: par = self.get(pname) @@ -567,7 +560,7 @@ def registerFunction(self, f, name = None, argnames = None): return eq - def registerStringFunction(self, fstr, name, ns = {}): + def registerStringFunction(self, fstr, name, ns={}): """Register a string function. This creates a function with this class that can be used within string @@ -589,8 +582,7 @@ def registerStringFunction(self, fstr, name, ns = {}): """ # Build the equation instance. - eq = equationFromString(fstr, self._eqfactory, ns = ns, buildargs = - True) + eq = equationFromString(fstr, self._eqfactory, ns=ns, buildargs=True) eq.name = name # Register any new Parameters. @@ -601,8 +593,7 @@ def registerStringFunction(self, fstr, name, ns = {}): argnames = eq.argdict.keys() return self.registerFunction(eq, name, argnames) - - def evaluateEquation(self, eqstr, ns = {}): + def evaluateEquation(self, eqstr, ns={}): """Evaluate a string equation. eqstr -- A string equation to evaluate. The equation is evaluated at @@ -620,8 +611,7 @@ def evaluateEquation(self, eqstr, ns = {}): self._eqfactory.wipeout(eq) return rv - - def constrain(self, par, con, ns = {}): + def constrain(self, par, con, ns={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. @@ -651,16 +641,16 @@ def constrain(self, par, con, ns = {}): raise ValueError("The parameter cannot be found") if par.const: - raise ValueError("The parameter '%s' is constant"%par) + raise ValueError("The parameter '%s' is constant" % par) if isinstance(con, six.string_types): eqstr = con eq = equationFromString(con, self._eqfactory, ns) else: - eq = Equation(root = con) + eq = Equation(root=con) eqstr = con.name - eq.name = "_constraint_%s"%par.name + eq.name = "_constraint_%s" % par.name # Make and store the constraint con = Constraint() @@ -683,7 +673,7 @@ def isConstrained(self, par): name = par par = self.get(name) - return (par in self._constraints) + return par in self._constraints def unconstrain(self, *pars): """Unconstrain a Parameter. @@ -714,12 +704,11 @@ def unconstrain(self, *pars): self._updateConfiguration() else: - raise ValueError("The parameter is not constrained") return - def getConstrainedPars(self, recurse = False): + def getConstrainedPars(self, recurse=False): """Get a list of constrained managed Parameters in this object. recurse -- Recurse into managed objects and retrive their constrained @@ -728,7 +717,7 @@ def getConstrainedPars(self, recurse = False): const = self._getConstraints(recurse) return const.keys() - def clearConstraints(self, recurse = False): + def clearConstraints(self, recurse=False): """Clear all constraints managed by this organizer. recurse -- Recurse into managed objects and clear all constraints @@ -741,13 +730,11 @@ def clearConstraints(self, recurse = False): self.unconstrain(*self._constraints) if recurse: - f = lambda m : hasattr(m, "clearConstraints") - for m in filter(f, self._iterManaged()): + for m in filter(lambda m: hasattr(m, "clearConstraints"), self._iterManaged()): m.clearConstraints(recurse) return - def restrain(self, res, lb = -inf, ub = inf, sig = 1, scaled = False, ns = - {}): + def restrain(self, res, lb=-inf, ub=inf, sig=1, scaled=False, ns={}): """Restrain an expression to specified bounds res -- An equation string or Parameter to restrain. @@ -778,7 +765,7 @@ def restrain(self, res, lb = -inf, ub = inf, sig = 1, scaled = False, ns = eqstr = res eq = equationFromString(res, self._eqfactory, ns) else: - eq = Equation(root = res) + eq = Equation(root=res) eqstr = res.name # Make and store the restraint @@ -816,7 +803,7 @@ def unrestrain(self, *ress): return - def clearRestraints(self, recurse = False): + def clearRestraints(self, recurse=False): """Clear all restraints. recurse -- Recurse into managed objects and clear all restraints @@ -825,33 +812,30 @@ def clearRestraints(self, recurse = False): self.unrestrain(*self._restraints) if recurse: - f = lambda m : hasattr(m, "clearRestraints") - for m in filter(f, self._iterManaged()): + for m in filter(lambda m: hasattr(m, "clearRestraints"), self._iterManaged()): m.clearRestraints(recurse) return - def _getConstraints(self, recurse = True): + def _getConstraints(self, recurse=True): """Get the constrained Parameters for this and managed sub-objects.""" constraints = {} if recurse: - f = lambda m : hasattr(m, "_getConstraints") - for m in filter(f, self._iterManaged()): - constraints.update( m._getConstraints(recurse) ) + for m in filter(lambda m: hasattr(m, "_getConstraints"), self._iterManaged()): + constraints.update(m._getConstraints(recurse)) - constraints.update( self._constraints) + constraints.update(self._constraints) return constraints - def _getRestraints(self, recurse = True): + def _getRestraints(self, recurse=True): """Get the Restraints for this and embedded ParameterSets. This returns a set of Restraint objects. """ restraints = set(self._restraints) if recurse: - f = lambda m : hasattr(m, "_getRestraints") - for m in filter(f, self._iterManaged()): - restraints.update( m._getRestraints(recurse) ) + for m in filter(lambda m: hasattr(m, "_getRestraints"), self._iterManaged()): + restraints.update(m._getRestraints(recurse)) return restraints @@ -889,9 +873,8 @@ def _formatManaged(self, prefix=""): if self._parameters: w0 = max(len(n) for n in self._parameters) w1 = ((w0 + len(prefix) + 1) // 4 + 1) * 4 - fmt = formatstr.replace('W', str(w1)) - lines.extend(fmt.format(prefix + n, p.value) - for n, p in self._parameters.items()) + fmt = formatstr.replace("W", str(w1)) + lines.extend(fmt.format(prefix + n, p.value) for n, p in self._parameters.items()) # Recurse into managed objects. for obj in self._iterManaged(): if hasattr(obj, "_formatManaged"): @@ -901,7 +884,6 @@ def _formatManaged(self, prefix=""): lines.extend(tlines) return lines - def _formatConstraints(self): """Format constraints for showing. @@ -927,7 +909,6 @@ def _formatConstraints(self): clines.sort(key=numstr) return clines - def _formatRestraints(self): """Format restraints for showing. @@ -943,13 +924,11 @@ def _formatRestraints(self): rset = self._getRestraints() rlines = [] for res in rset: - line = "%s: lb = %f, ub = %f, sig = %f, scaled = %s"%\ - (res.eqstr, res.lb, res.ub, res.sig, res.scaled) + line = "%s: lb = %f, ub = %f, sig = %f, scaled = %s" % (res.eqstr, res.lb, res.ub, res.sig, res.scaled) rlines.append(line) rlines.sort(key=numstr) return rlines - def show(self, pattern="", textwidth=78): """Show the configuration hierarchy on the screen. @@ -965,8 +944,7 @@ def show(self, pattern="", textwidth=78): the screen width. Do not trim when negative or 0. """ regexp = re.compile(pattern) - pmatch = lambda s : (len(s.split(None, 1)) < 2 or - regexp.search(s.split(None, 1)[0])) + pmatch = lambda s: (len(s.split(None, 1)) < 2 or regexp.search(s.split(None, 1)[0])) # noqa: E731 # Show sub objects and their parameters lines = [] tlines = self._formatManaged() @@ -1007,10 +985,11 @@ def show(self, pattern="", textwidth=78): print("\n".join(s[:tw] for s in lines)) return + # End RecipeOrganizer -def equationFromString(eqstr, factory, ns = {}, buildargs = False, - argclass = Parameter, argkw = {}): + +def equationFromString(eqstr, factory, ns={}, buildargs=False, argclass=Parameter, argkw={}): """Make an equation from a string. eqstr -- A string representation of the equation. The equation must diff --git a/src/diffpy/srfit/fitbase/restraint.py b/src/diffpy/srfit/fitbase/restraint.py index 4ff2c1fa..3488e50f 100644 --- a/src/diffpy/srfit/fitbase/restraint.py +++ b/src/diffpy/srfit/fitbase/restraint.py @@ -25,8 +25,8 @@ from numpy import inf -from diffpy.srfit.fitbase.validatable import Validatable from diffpy.srfit.exceptions import SrFitError +from diffpy.srfit.fitbase.validatable import Validatable class Restraint(Validatable): @@ -49,7 +49,7 @@ class Restraint(Validatable): """ - def __init__(self, eq, lb = -inf, ub = inf, sig = 1, scaled = False): + def __init__(self, eq, lb=-inf, ub=inf, sig=1, scaled=False): """Restrain an equation to specified bounds. eq -- An equation whose evaluation is compared against the @@ -71,7 +71,7 @@ def __init__(self, eq, lb = -inf, ub = inf, sig = 1, scaled = False): self.scaled = bool(scaled) return - def penalty(self, w = 1.0): + def penalty(self, w=1.0): """Calculate the penalty of the restraint. w -- The point-average chi^2 which is optionally used to scale the @@ -81,7 +81,7 @@ def penalty(self, w = 1.0): """ val = self.eq() - penalty = (max(0, self.lb - val, val - self.ub) / self.sig)**2 + penalty = (max(0, self.lb - val, val - self.ub) / self.sig) ** 2 if self.scaled: penalty *= w @@ -99,6 +99,7 @@ def _validate(self): if self.eq is None: raise SrFitError("eq is None") from diffpy.srfit.equation.visitors import validate + try: validate(self.eq) except ValueError as e: @@ -115,6 +116,7 @@ def _validate(self): return + # End class Restraint # End of file diff --git a/src/diffpy/srfit/fitbase/simplerecipe.py b/src/diffpy/srfit/fitbase/simplerecipe.py index 9cb5cfe2..da9809c1 100644 --- a/src/diffpy/srfit/fitbase/simplerecipe.py +++ b/src/diffpy/srfit/fitbase/simplerecipe.py @@ -16,8 +16,8 @@ """Simple FitRecipe class that includes a FitContribution and Profile. """ -from diffpy.srfit.fitbase.fitrecipe import FitRecipe from diffpy.srfit.fitbase.fitcontribution import FitContribution +from diffpy.srfit.fitbase.fitrecipe import FitRecipe from diffpy.srfit.fitbase.fitresults import FitResults from diffpy.srfit.fitbase.profile import Profile @@ -68,7 +68,7 @@ class SimpleRecipe(FitRecipe): """ - def __init__(self, name = "fit", conclass = FitContribution): + def __init__(self, name="fit", conclass=FitContribution): """Initialization.""" FitRecipe.__init__(self, name) self.fithooks[0].verbose = 3 @@ -76,11 +76,10 @@ def __init__(self, name = "fit", conclass = FitContribution): self.profile = Profile() contribution.setProfile(self.profile) self.addContribution(contribution) - self.results = FitResults(self, update = False) + self.results = FitResults(self, update=False) # Adopt all the FitContribution methods - public = [aname for aname in dir(contribution) if aname not in - dir(self) and not aname.startswith("_")] + public = [aname for aname in dir(contribution) if aname not in dir(self) and not aname.startswith("_")] for mname in public: method = getattr(contribution, mname) setattr(self, mname, method) @@ -95,7 +94,7 @@ def loadParsedData(self, parser): """ return self.profile.loadParsedData(parser) - def setObservedProfile(self, xobs, yobs, dyobs = None): + def setObservedProfile(self, xobs, yobs, dyobs=None): """Set the observed profile. Arguments @@ -111,7 +110,6 @@ def setObservedProfile(self, xobs, yobs, dyobs = None): """ return self.profile.setObservedProfile(xobs, yobs, dyobs) - def setCalculationRange(self, xmin=None, xmax=None, dx=None): """Set epsilon-inclusive calculation range. @@ -146,7 +144,6 @@ def setCalculationRange(self, xmin=None, xmax=None, dx=None): """ return self.profile.setCalculationRange(xmin, xmax, dx) - def setCalculationPoints(self, x): """Set the calculation points. @@ -178,7 +175,7 @@ def loadtxt(self, *args, **kw): return self.profile.loadtxt(*args, **kw) # FitContribution - def setEquation(self, eqstr, ns = {}): + def setEquation(self, eqstr, ns={}): """Set the profile equation for the FitContribution. This sets the equation that will be used when generating the residual. @@ -195,11 +192,12 @@ def setEquation(self, eqstr, ns = {}): variable. """ - self.contribution.setEquation(eqstr, ns = {}) + self.contribution.setEquation(eqstr, ns={}) # Extract variables for par in self.contribution: # Skip Profile Parameters - if par.name in ("x", "y", "dy"): continue + if par.name in ("x", "y", "dy"): + continue if par.value is None: par.value = 0 if par.name not in self._parameters: @@ -212,7 +210,7 @@ def __call__(self): # FitResults methods - def printResults(self, header = "", footer = ""): + def printResults(self, header="", footer=""): """Format and print the results. header -- A header to add to the output (default "") @@ -222,7 +220,7 @@ def printResults(self, header = "", footer = ""): self.results.printResults(header, footer, True) return - def saveResults(self, filename, header = "", footer = ""): + def saveResults(self, filename, header="", footer=""): """Format and save the results. filename - Name of the save file. @@ -232,6 +230,7 @@ def saveResults(self, filename, header = "", footer = ""): """ self.results.saveResults(filename, header, footer, True) + # End class SimpleRecipe # End of file diff --git a/src/diffpy/srfit/fitbase/validatable.py b/src/diffpy/srfit/fitbase/validatable.py index c4db72c7..110dbbe0 100644 --- a/src/diffpy/srfit/fitbase/validatable.py +++ b/src/diffpy/srfit/fitbase/validatable.py @@ -38,7 +38,8 @@ def _validateOthers(self, iterable): """ for obj in iterable: - if obj is self: continue + if obj is self: + continue if isinstance(obj, Validatable): obj._validate() @@ -56,6 +57,7 @@ def _validate(self): # Then validate others. return + # End class Validatable # End of file diff --git a/src/diffpy/srfit/interface/__init__.py b/src/diffpy/srfit/interface/__init__.py index 8f6bd3a7..416b3354 100644 --- a/src/diffpy/srfit/interface/__init__.py +++ b/src/diffpy/srfit/interface/__init__.py @@ -21,13 +21,12 @@ """ -from diffpy.srfit.interface.interface import ParameterInterface +from diffpy.srfit.interface.interface import FitRecipeInterface, ParameterInterface, RecipeOrganizerInterface + _parameter_interface = ParameterInterface -from diffpy.srfit.interface.interface import RecipeOrganizerInterface _recipeorganizer_interface = RecipeOrganizerInterface -from diffpy.srfit.interface.interface import FitRecipeInterface _fitrecipe_interface = FitRecipeInterface # End of file diff --git a/src/diffpy/srfit/interface/interface.py b/src/diffpy/srfit/interface/interface.py index 94c5fb6d..31d9b869 100644 --- a/src/diffpy/srfit/interface/interface.py +++ b/src/diffpy/srfit/interface/interface.py @@ -21,8 +21,7 @@ objects. See individual interface classes for specifics. """ -__all__ = ["ParameterInterface", "FitRecipeInterface", - "RecipeOrganizerInterface"] +__all__ = ["ParameterInterface", "FitRecipeInterface", "RecipeOrganizerInterface"] import six @@ -45,10 +44,12 @@ def __lshift__(self, v): self.value = v return self + # End class ParameterInterface # ---------------------------------------------------------------------------- + class RecipeOrganizerInterface(object): """Mix-in class for enhancing the RecipeOrganizer interface.""" @@ -79,6 +80,7 @@ def __iadd__(self, args): This accepts arguments for a single function call. """ + # Want to detect _addParameter or _newParameter def f(*args): if isinstance(args[0], six.string_types): @@ -90,10 +92,12 @@ def f(*args): _applyargs(args, f) return self + # End class RecipeOrganizerInterface # ---------------------------------------------------------------------------- + class FitRecipeInterface(object): """Mix-in class for enhancing the FitRecipe interface.""" @@ -115,6 +119,7 @@ def __iadd__(self, args): This accepts a single argument or an iterable of single arguments or argument tuples. """ + # Want to detect addVar or newVar def f(*args): if isinstance(args[0], six.string_types): @@ -126,10 +131,12 @@ def f(*args): _applymanyargs(args, f) return self + # End class FitRecipeInterface # Local helper functions ----------------------------------------------------- + def _applymanyargs(args, f): """Apply arguments to a function. @@ -138,18 +145,19 @@ def _applymanyargs(args, f): (arg1, arg2, ...) ((arg1a, arg1b, ...), ...) """ - if not hasattr(args, '__iter__'): + if not hasattr(args, "__iter__"): f(args) return for arg in args: - if hasattr(arg, '__iter__'): + if hasattr(arg, "__iter__"): f(*arg) else: f(arg) return + def _applyargs(args, f): """Apply arguments to a function. @@ -158,10 +166,11 @@ def _applyargs(args, f): (arg1, arg2, ...) ((arg1a, arg1b, ...), ...) """ - if not hasattr(args, '__iter__'): + if not hasattr(args, "__iter__"): f(args) else: f(*args) return + # End of file diff --git a/src/diffpy/srfit/pdf/__init__.py b/src/diffpy/srfit/pdf/__init__.py index 956264e0..d0e7db8d 100644 --- a/src/diffpy/srfit/pdf/__init__.py +++ b/src/diffpy/srfit/pdf/__init__.py @@ -18,9 +18,9 @@ __all__ = ["PDFGenerator", "DebyePDFGenerator", "PDFContribution", "PDFParser"] -from diffpy.srfit.pdf.pdfgenerator import PDFGenerator from diffpy.srfit.pdf.debyepdfgenerator import DebyePDFGenerator from diffpy.srfit.pdf.pdfcontribution import PDFContribution +from diffpy.srfit.pdf.pdfgenerator import PDFGenerator from diffpy.srfit.pdf.pdfparser import PDFParser # End of file diff --git a/src/diffpy/srfit/pdf/basepdfgenerator.py b/src/diffpy/srfit/pdf/basepdfgenerator.py index f0f2747f..e6f6af7b 100644 --- a/src/diffpy/srfit/pdf/basepdfgenerator.py +++ b/src/diffpy/srfit/pdf/basepdfgenerator.py @@ -23,15 +23,15 @@ import numpy +from diffpy.srfit.exceptions import SrFitError from diffpy.srfit.fitbase import ProfileGenerator from diffpy.srfit.fitbase.parameter import ParameterAdapter from diffpy.srfit.structure import struToParameterSet -from diffpy.srfit.exceptions import SrFitError - # FIXME - Parameter creation will have to be smarter once deeper calculator # configuration is enabled. + class BasePDFGenerator(ProfileGenerator): """Base class for calculating PDF profiles using SrReal. @@ -74,7 +74,7 @@ class BasePDFGenerator(ProfileGenerator): """ - def __init__(self, name = "pdf"): + def __init__(self, name="pdf"): """Initialize the generator.""" ProfileGenerator.__init__(self, name) @@ -88,7 +88,7 @@ def __init__(self, name = "pdf"): return - _parnames = ['delta1', 'delta2', 'qbroad', 'scale', 'qdamp'] + _parnames = ["delta1", "delta2", "qbroad", "scale", "qdamp"] def _setCalculator(self, calc): """Set the SrReal calulator instance. @@ -99,13 +99,11 @@ def _setCalculator(self, calc): """ self._calc = calc for pname in self.__class__._parnames: - self.addParameter( - ParameterAdapter(pname, self._calc, attr = pname) - ) + self.addParameter(ParameterAdapter(pname, self._calc, attr=pname)) self.processMetaData() return - def parallel(self, ncpu, mapfunc = None): + def parallel(self, ncpu, mapfunc=None): """Run calculation in parallel. ncpu -- Number of parallel processes. Revert to serial mode when 1. @@ -115,8 +113,9 @@ def parallel(self, ncpu, mapfunc = None): No return value. """ from diffpy.srreal.parallel import createParallelCalculator + calc_serial = self._calc - if hasattr(calc_serial, 'pqobj'): + if hasattr(calc_serial, "pqobj"): calc_serial = calc_serial.pqobj # revert to serial calculator for ncpu <= 1 if ncpu <= 1: @@ -127,6 +126,7 @@ def parallel(self, ncpu, mapfunc = None): # ncpu = min(ncpu, multiprocessing.cpu_count()) if mapfunc is None: import multiprocessing + self._pool = multiprocessing.Pool(ncpu) mapfunc = self._pool.imap_unordered @@ -157,7 +157,7 @@ def processMetaData(self): return - def setScatteringType(self, stype = "X"): + def setScatteringType(self, stype="X"): """Set the scattering type. stype -- "X" for x-ray, "N" for neutron, "E" for electrons, @@ -186,8 +186,7 @@ def getQmax(self): return self._calc.qmax def setQmin(self, qmin): - """Set the qmin value. - """ + """Set the qmin value.""" self._calc.qmin = qmin self.meta["qmin"] = self.getQmin() return @@ -196,7 +195,7 @@ def getQmin(self): """Get the qmin value.""" return self._calc.qmin - def setStructure(self, stru, name = "phase", periodic = True): + def setStructure(self, stru, name="phase", periodic=True): """Set the structure that will be used to calculate the PDF. This creates a DiffpyStructureParSet, ObjCrystCrystalParSet or @@ -222,8 +221,7 @@ def setStructure(self, stru, name = "phase", periodic = True): self.setPhase(parset, periodic) return - - def setPhase(self, parset, periodic = True): + def setPhase(self, parset, periodic=True): """Set the phase that will be used to calculate the PDF. Set the phase directly with a DiffpyStructureParSet, @@ -258,7 +256,7 @@ def _prepare(self, r): ndiv = max(len(r) - 1, 1) self._calc.rstep = (hi - lo) / ndiv self._calc.rmin = lo - self._calc.rmax = hi + 0.5*self._calc.rstep + self._calc.rmax = hi + 0.5 * self._calc.rstep return def _validate(self): @@ -298,4 +296,5 @@ def __call__(self, r): y = numpy.interp(r, rcalc, y) return y + # End class BasePDFGenerator diff --git a/src/diffpy/srfit/pdf/characteristicfunctions.py b/src/diffpy/srfit/pdf/characteristicfunctions.py index 0d38a2a6..7fa418af 100644 --- a/src/diffpy/srfit/pdf/characteristicfunctions.py +++ b/src/diffpy/srfit/pdf/characteristicfunctions.py @@ -25,15 +25,23 @@ the 'registerFunction' method of that class. """ -__all__ = ["sphericalCF", "spheroidalCF", "spheroidalCF2", - "lognormalSphericalCF", "sheetCF", "shellCF", "shellCF2", "SASCF"] +__all__ = [ + "sphericalCF", + "spheroidalCF", + "spheroidalCF2", + "lognormalSphericalCF", + "sheetCF", + "shellCF", + "shellCF2", + "SASCF", +] import numpy -from numpy import pi, sqrt, log, exp, log2, ceil, sign from numpy import arctan as atan from numpy import arctanh as atanh -from numpy.fft import ifft, fftfreq -from scipy.special import erf +from numpy import ceil, exp, log, log2, pi, sign, sqrt +from numpy.fft import fftfreq, ifft +from scipy.special import erfc from diffpy.srfit.fitbase.calculator import Calculator @@ -51,11 +59,12 @@ def sphericalCF(r, psize): f = numpy.zeros(numpy.shape(r), dtype=float) if psize > 0: x = numpy.array(r, dtype=float) / psize - inside = (x < 1.0) + inside = x < 1.0 xin = x[inside] - f[inside] = 1.0 - 1.5*xin + 0.5*xin*xin*xin + f[inside] = 1.0 - 1.5 * xin + 0.5 * xin * xin * xin return f + def spheroidalCF(r, erad, prad): """Spheroidal characteristic function specified using radii. @@ -73,6 +82,7 @@ def spheroidalCF(r, erad, prad): pelpt = 1.0 * prad / erad return spheroidalCF2(r, psize, pelpt) + def spheroidalCF2(r, psize, axrat): """Spheroidal nanoparticle characteristic function. @@ -93,49 +103,66 @@ def spheroidalCF2(r, psize, axrat): # to simplify the equations v = pelpt d = 1.0 * psize - d2 = d*d - v2 = v*v + d2 = d * d + v2 = v * v if v == 1: return sphericalCF(r, psize) rx = r if v < 1: - - r = rx[rx <= v*psize] - r2 = r*r - f1 = 1 - 3*r/(4*d*v)*(1-r2/(4*d2)*(1+2.0/(3*v2))) \ - - 3*r/(4*d)*(1-r2/(4*d2))*v/sqrt(1-v2)*atanh(sqrt(1-v2)) - - r = rx[numpy.logical_and(rx > v*psize, rx <= psize)] - r2 = r*r - f2 = (3*d/(8*r)*(1+r2/(2*d2))*sqrt(1-r2/d2) \ - - 3*r/(4*d)*(1-r2/(4*d2))*atanh(sqrt(1-r2/d2)) \ - ) * v/sqrt(1-v2) + r = rx[rx <= v * psize] + r2 = r * r + f1 = ( + 1 + - 3 * r / (4 * d * v) * (1 - r2 / (4 * d2) * (1 + 2.0 / (3 * v2))) + - 3 * r / (4 * d) * (1 - r2 / (4 * d2)) * v / sqrt(1 - v2) * atanh(sqrt(1 - v2)) + ) + + r = rx[numpy.logical_and(rx > v * psize, rx <= psize)] + r2 = r * r + f2 = ( + ( + 3 * d / (8 * r) * (1 + r2 / (2 * d2)) * sqrt(1 - r2 / d2) + - 3 * r / (4 * d) * (1 - r2 / (4 * d2)) * atanh(sqrt(1 - r2 / d2)) + ) + * v + / sqrt(1 - v2) + ) r = rx[rx > psize] f3 = numpy.zeros_like(r) - f = numpy.concatenate((f1,f2,f3)) + f = numpy.concatenate((f1, f2, f3)) elif v > 1: - r = rx[rx <= psize] - r2 = r*r - f1 = 1 - 3*r/(4*d*v)*(1-r2/(4*d2)*(1+2.0/(3*v2))) \ - - 3*r/(4*d)*(1-r2/(4*d2))*v/sqrt(v2-1)*atan(sqrt(v2-1)) - - r = rx[numpy.logical_and(rx > psize, rx <= v*psize)] - r2 = r*r - f2 = 1 - 3*r/(4*d*v)*(1-r2/(4*d2)*(1+2.0/(3*v2))) \ - - 3.0/8*(1+r2/(2*d2))*sqrt(1-d2/r2)*v/sqrt(v2-1) \ - - 3*r/(4*d)*(1-r2/(4*d2))*v/sqrt(v2-1) \ - * (atan(sqrt(v2-1)) - atan(sqrt(r2/d2-1))) - - r = rx[rx > v*psize] + r2 = r * r + f1 = ( + 1 + - 3 * r / (4 * d * v) * (1 - r2 / (4 * d2) * (1 + 2.0 / (3 * v2))) + - 3 * r / (4 * d) * (1 - r2 / (4 * d2)) * v / sqrt(v2 - 1) * atan(sqrt(v2 - 1)) + ) + + r = rx[numpy.logical_and(rx > psize, rx <= v * psize)] + r2 = r * r + f2 = ( + 1 + - 3 * r / (4 * d * v) * (1 - r2 / (4 * d2) * (1 + 2.0 / (3 * v2))) + - 3.0 / 8 * (1 + r2 / (2 * d2)) * sqrt(1 - d2 / r2) * v / sqrt(v2 - 1) + - 3 + * r + / (4 * d) + * (1 - r2 / (4 * d2)) + * v + / sqrt(v2 - 1) + * (atan(sqrt(v2 - 1)) - atan(sqrt(r2 / d2 - 1))) + ) + + r = rx[rx > v * psize] f3 = numpy.zeros_like(r) - f = numpy.concatenate((f1,f2,f3)) + f = numpy.concatenate((f1, f2, f3)) return f @@ -166,19 +193,23 @@ def lognormalSphericalCF(r, psize, psig): Source unknown """ - if psize <= 0: return numpy.zeros_like(r) - if psig <= 0: return sphericalCF(r, psize) - - erfc = lambda x: 1.0-erf(x) + if psize <= 0: + return numpy.zeros_like(r) + if psig <= 0: + return sphericalCF(r, psize) sqrt2 = sqrt(2.0) - s = sqrt(log(psig*psig/(1.0*psize*psize) + 1)) - mu = log(psize) - s*s/2; - if mu < 0: return numpy.zeros_like(r) + s = sqrt(log(psig * psig / (1.0 * psize * psize) + 1)) + mu = log(psize) - s * s / 2 + if mu < 0: + return numpy.zeros_like(r) + + return ( + 0.5 * erfc((-mu - 3 * s * s + log(r)) / (sqrt2 * s)) + + 0.25 * r * r * r * erfc((-mu + log(r)) / (sqrt2 * s)) * exp(-3 * mu - 4.5 * s * s) + - 0.75 * r * erfc((-mu - 2 * s * s + log(r)) / (sqrt2 * s)) * exp(-mu - 2.5 * s * s) + ) - return 0.5*erfc((-mu-3*s*s+log(r))/(sqrt2*s)) \ - + 0.25*r*r*r*erfc((-mu+log(r))/(sqrt2*s))*exp(-3*mu-4.5*s*s) \ - - 0.75*r*erfc((-mu-2*s*s+log(r))/(sqrt2*s))*exp(-mu-2.5*s*s) def sheetCF(r, sthick): """Nanosheet characteristic function. @@ -217,10 +248,11 @@ def shellCF(r, radius, thickness): From Lei et al., Phys. Rev. B, 80, 024118 (2009) """ - d = 1.0*thickness - a = 1.0*radius + d/2.0 + d = 1.0 * thickness + a = 1.0 * radius + d / 2.0 return shellCF2(r, a, d) + def shellCF2(r, a, delta): """Spherical shell characteristic function. @@ -232,22 +264,24 @@ def shellCF2(r, a, delta): From Lei et al., Phys. Rev. B, 80, 024118 (2009) """ - a = 1.0*a - d = 1.0*delta + a = 1.0 * a + d = 1.0 * delta a2 = a**2 d2 = d**2 - dmr = d-r + dmr = d - r dmr2 = dmr**2 - f = r * (16*a*a2 + 12*a*d*dmr + 36*a2*(2*d-r) + 3*dmr2*(2*d+r)) \ - + 2*dmr2 * (r*(2*d+r)-12*a2) * sign(dmr) \ - - 2*(2*a-r)**2 * (r*(4*a+r)-3*d2) * sign(2*a-r) \ - + r*(4*a-2*d+r)*(2*a-d-r)**2*sign(2*a-d-r) + f = ( + r * (16 * a * a2 + 12 * a * d * dmr + 36 * a2 * (2 * d - r) + 3 * dmr2 * (2 * d + r)) + + 2 * dmr2 * (r * (2 * d + r) - 12 * a2) * sign(dmr) + - 2 * (2 * a - r) ** 2 * (r * (4 * a + r) - 3 * d2) * sign(2 * a - r) + + r * (4 * a - 2 * d + r) * (2 * a - d - r) ** 2 * sign(2 * a - d - r) + ) - f[r > 2*a+d] = 0 + f[r > 2 * a + d] = 0 - den = 8.0*r*d*(12*a2+d2) - zmask = (den == 0.0) + den = 8.0 * r * d * (12 * a2 + d2) + zmask = den == 0.0 vmask = ~zmask f[vmask] /= den[vmask] f[zmask] = 1 @@ -286,6 +320,7 @@ def __init__(self, name, model): self._model = model from diffpy.srfit.sas.sasparameter import SASParameter + # Wrap normal parameters for parname in model.params: par = SASParameter(parname, model) @@ -327,11 +362,11 @@ def __call__(self, r): rmax = max(ed, 2 * r[-1]) dq = pi / rmax qmax = pi / dr - numpoints = int(2**(ceil(log2(qmax/dq)))) + numpoints = int(2 ** (ceil(log2(qmax / dq)))) qmax = dq * numpoints # Calculate F(q) = q * I(q) from model - q = fftfreq(int(qmax/dq)) * qmax + q = fftfreq(int(qmax / dq)) * qmax fq = q * self._model.evalDistribution(q) # Calculate g(r) and the effective r-points @@ -340,20 +375,20 @@ def __call__(self, r): gr = ifft(fq).imag # Calculate full-fr for normalization - assert (rp[0] == 0.0) + assert rp[0] == 0.0 frp = numpy.zeros_like(gr) frp[1:] = gr[1:] / rp[1:] # Inerpolate onto requested grid, do not use data after jump in rp - assert (numpoints % 2 == 0) + assert numpoints % 2 == 0 nhalf = numpoints / 2 fr = numpy.interp(r, rp[:nhalf], gr[:nhalf]) - vmask = (r != 0) + vmask = r != 0 fr[vmask] /= r[vmask] # Normalize. We approximate fr[0] by using the fact that f(r) is linear # at low r. By definition, fr[0] should equal 1. - fr0 = 2*frp[2] - frp[1] + fr0 = 2 * frp[2] - frp[1] fr /= fr0 # Fix potential divide-by-zero issue, fr is 1 at r == 0 diff --git a/src/diffpy/srfit/pdf/debyepdfgenerator.py b/src/diffpy/srfit/pdf/debyepdfgenerator.py index dd70d055..fcfaa577 100644 --- a/src/diffpy/srfit/pdf/debyepdfgenerator.py +++ b/src/diffpy/srfit/pdf/debyepdfgenerator.py @@ -66,7 +66,7 @@ class DebyePDFGenerator(BasePDFGenerator): """ - def setStructure(self, stru, name = "phase", periodic = False): + def setStructure(self, stru, name="phase", periodic=False): """Set the structure that will be used to calculate the PDF. This creates a DiffpyStructureParSet, ObjCrystCrystalParSet or @@ -86,8 +86,7 @@ def setStructure(self, stru, name = "phase", periodic = False): """ return BasePDFGenerator.setStructure(self, stru, name, periodic) - - def setPhase(self, parset, periodic = False): + def setPhase(self, parset, periodic=False): """Set the phase that will be used to calculate the PDF. Set the phase directly with a DiffpyStructureParSet, @@ -106,15 +105,15 @@ def setPhase(self, parset, periodic = False): """ return BasePDFGenerator.setPhase(self, parset, periodic) - - def __init__(self, name = "pdf"): - """Initialize the generator. - """ + def __init__(self, name="pdf"): + """Initialize the generator.""" from diffpy.srreal.pdfcalculator import DebyePDFCalculator + BasePDFGenerator.__init__(self, name) self._setCalculator(DebyePDFCalculator()) return + # End class DebyePDFGenerator # End of file diff --git a/src/diffpy/srfit/pdf/pdfcontribution.py b/src/diffpy/srfit/pdf/pdfcontribution.py index 1700eb5a..de485428 100644 --- a/src/diffpy/srfit/pdf/pdfcontribution.py +++ b/src/diffpy/srfit/pdf/pdfcontribution.py @@ -20,8 +20,8 @@ __all__ = ["PDFContribution"] -from diffpy.srfit.fitbase import FitContribution -from diffpy.srfit.fitbase import Profile +from diffpy.srfit.fitbase import FitContribution, Profile + class PDFContribution(FitContribution): """PDFContribution class. @@ -72,7 +72,7 @@ def __init__(self, name): self._meta = {} # Add the profile profile = Profile() - self.setProfile(profile, xname = "r") + self.setProfile(profile, xname="r") # Need a parameter for the overall scale, in the case that this is a # multi-phase fit. @@ -96,10 +96,12 @@ def loadData(self, data): """ # Get the data into a string from diffpy.srfit.util.inpututils import inputToString + datstr = inputToString(data) # Load data with a PDFParser from diffpy.srfit.pdf.pdfparser import PDFParser + parser = PDFParser() parser.parseString(datstr) @@ -107,7 +109,6 @@ def loadData(self, data): self.profile.loadParsedData(parser) return - def setCalculationRange(self, xmin=None, xmax=None, dx=None): """Set epsilon-inclusive calculation range. @@ -142,7 +143,6 @@ def setCalculationRange(self, xmin=None, xmax=None, dx=None): """ return self.profile.setCalculationRange(xmin, xmax, dx) - def savetxt(self, fname, **kwargs): """Call numpy.savetxt with x, ycalc, y, dy @@ -154,7 +154,7 @@ def savetxt(self, fname, **kwargs): # Phase methods - def addStructure(self, name, stru, periodic = True): + def addStructure(self, name, stru, periodic=True): """Add a phase that goes into the PDF calculation. name -- A name to give the generator that will manage the PDF @@ -180,9 +180,11 @@ def addStructure(self, name, stru, periodic = True): # Based on periodic, create the proper generator. if periodic: from diffpy.srfit.pdf.pdfgenerator import PDFGenerator + gen = PDFGenerator(name) else: from diffpy.srfit.pdf.debyepdfgenerator import DebyePDFGenerator + gen = DebyePDFGenerator(name) # Set up the generator @@ -191,7 +193,7 @@ def addStructure(self, name, stru, periodic = True): return gen.phase - def addPhase(self, name, parset, periodic = True): + def addPhase(self, name, parset, periodic=True): """Add a phase that goes into the PDF calculation. name -- A name to give the generator that will manage the PDF @@ -218,9 +220,11 @@ def addPhase(self, name, parset, periodic = True): # Based on periodic, create the proper generator. if periodic: from diffpy.srfit.pdf.pdfgenerator import PDFGenerator + gen = PDFGenerator(name) else: from diffpy.srfit.pdf.debyepdfgenerator import DebyePDFGenerator + gen = DebyePDFGenerator(name) # Set up the generator @@ -268,8 +272,7 @@ def _getMetaValue(self, kwd): val = self.profile.meta.get(kwd) return val - - def setScatteringType(self, type = "X"): + def setScatteringType(self, type="X"): """Set the scattering type. type -- "X" for x-ray or "N" for neutron @@ -298,8 +301,7 @@ def getQmax(self): return self._getMetaValue("qmax") def setQmin(self, qmin): - """Set the qmin value. - """ + """Set the qmin value.""" self._meta["qmin"] = qmin for gen in self._generators.values(): gen.setQmin(qmin) @@ -309,4 +311,5 @@ def getQmin(self): """Get the qmin value.""" return self._getMetaValue("qmin") + # End of file diff --git a/src/diffpy/srfit/pdf/pdfgenerator.py b/src/diffpy/srfit/pdf/pdfgenerator.py index 5a118ceb..d7ae7851 100644 --- a/src/diffpy/srfit/pdf/pdfgenerator.py +++ b/src/diffpy/srfit/pdf/pdfgenerator.py @@ -66,12 +66,13 @@ class PDFGenerator(BasePDFGenerator): """ - def __init__(self, name = "pdf"): - """Initialize the generator. - """ + def __init__(self, name="pdf"): + """Initialize the generator.""" from diffpy.srreal.pdfcalculator import PDFCalculator + BasePDFGenerator.__init__(self, name) self._setCalculator(PDFCalculator()) return + # End class PDFGenerator diff --git a/src/diffpy/srfit/pdf/pdfparser.py b/src/diffpy/srfit/pdf/pdfparser.py index 945c5184..84d9d060 100644 --- a/src/diffpy/srfit/pdf/pdfparser.py +++ b/src/diffpy/srfit/pdf/pdfparser.py @@ -23,11 +23,13 @@ __all__ = ["PDFParser"] import re + import numpy from diffpy.srfit.exceptions import ParseError from diffpy.srfit.fitbase.profileparser import ProfileParser + class PDFParser(ProfileParser): """Class for holding a diffraction pattern. @@ -91,15 +93,15 @@ def parseString(self, patstring): """ # useful regex patterns: - rx = { 'f' : r'[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?' } + rx = {"f": r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?"} # find where does the data start - res = re.search(r'^#+ start data\s*(?:#.*\s+)*', patstring, re.M) + res = re.search(r"^#+ start data\s*(?:#.*\s+)*", patstring, re.M) # start_data is position where the first data line starts if res: start_data = res.end() else: # find line that starts with a floating point number - regexp = r'^\s*%(f)s' % rx + regexp = r"^\s*%(f)s" % rx res = re.search(regexp, patstring, re.M) if res: start_data = res.start() @@ -109,19 +111,19 @@ def parseString(self, patstring): databody = patstring[start_data:].strip() # find where the metadata starts - metadata = '' - res = re.search(r'^#+\ +metadata\b\n', header, re.M) + metadata = "" + res = re.search(r"^#+\ +metadata\b\n", header, re.M) if res: - metadata = header[res.end():] - header = header[:res.start()] + metadata = header[res.end() :] + header = header[: res.start()] # parse header meta = self._meta # stype - if re.search('(x-?ray|PDFgetX)', header, re.I): - meta["stype"] = 'X' - elif re.search('(neutron|PDFgetN)', header, re.I): - meta["stype"] = 'N' + if re.search("(x-?ray|PDFgetX)", header, re.I): + meta["stype"] = "X" + elif re.search("(neutron|PDFgetN)", header, re.I): + meta["stype"] = "N" # qmin regexp = r"\bqmin *= *(%(f)s)\b" % rx res = re.search(regexp, header, re.I) @@ -156,12 +158,12 @@ def parseString(self, patstring): regexp = r"\b(?:temp|temperature|T)\ *=\ *(%(f)s)\b" % rx res = re.search(regexp, header) if res: - meta['temperature'] = float(res.groups()[0]) + meta["temperature"] = float(res.groups()[0]) # doping regexp = r"\b(?:x|doping)\ *=\ *(%(f)s)\b" % rx res = re.search(regexp, header) if res: - meta['doping'] = float(res.groups()[0]) + meta["doping"] = float(res.groups()[0]) # parsing gerneral metadata if metadata: @@ -170,12 +172,12 @@ def parseString(self, patstring): res = re.search(regexp, metadata, re.M) if res: meta[res.groups()[0]] = float(res.groups()[1]) - metadata = metadata[res.end():] + metadata = metadata[res.end() :] else: break # read actual data - robs, Gobs, drobs, dGobs - inf_or_nan = re.compile('(?i)^[+-]?(NaN|Inf)\\b') + inf_or_nan = re.compile("(?i)^[+-]?(NaN|Inf)\\b") has_drobs = True has_dGobs = True # raise ParseError if something goes wrong @@ -190,15 +192,13 @@ def parseString(self, patstring): robs.append(float(v[0])) Gobs.append(float(v[1])) # drobs is valid if all values are defined and positive - has_drobs = (has_drobs and - len(v) > 2 and not inf_or_nan.match(v[2])) + has_drobs = has_drobs and len(v) > 2 and not inf_or_nan.match(v[2]) if has_drobs: v2 = float(v[2]) has_drobs = v2 > 0.0 drobs.append(v2) # dGobs is valid if all values are defined and positive - has_dGobs = (has_dGobs and - len(v) > 3 and not inf_or_nan.match(v[3])) + has_dGobs = has_dGobs and len(v) > 3 and not inf_or_nan.match(v[3]) if has_dGobs: v3 = float(v[3]) has_dGobs = v3 > 0.0 @@ -220,4 +220,5 @@ def parseString(self, patstring): self._banks.append([robs, Gobs, drobs, dGobs]) return + # End of PDFParser diff --git a/src/diffpy/srfit/sas/__init__.py b/src/diffpy/srfit/sas/__init__.py index c454a86e..d414207d 100644 --- a/src/diffpy/srfit/sas/__init__.py +++ b/src/diffpy/srfit/sas/__init__.py @@ -16,12 +16,11 @@ """SAS calculation tools. """ -__all__ = ["SASGenerator", "SASParser", "SASProfile", "PrCalculator", - "CFCalculator"] +__all__ = ["SASGenerator", "SASParser", "SASProfile", "PrCalculator", "CFCalculator"] +from .prcalculator import CFCalculator, PrCalculator from .sasgenerator import SASGenerator from .sasparser import SASParser from .sasprofile import SASProfile -from .prcalculator import PrCalculator, CFCalculator # End of file diff --git a/src/diffpy/srfit/sas/prcalculator.py b/src/diffpy/srfit/sas/prcalculator.py index 0ce1ad41..bb7c1db8 100644 --- a/src/diffpy/srfit/sas/prcalculator.py +++ b/src/diffpy/srfit/sas/prcalculator.py @@ -67,7 +67,8 @@ def __init__(self, name): global Invertor if Invertor is None: from diffpy.srfit.sas.sasimport import sasimport - Invertor = sasimport('sas.pr.invertor').Invertor + + Invertor = sasimport("sas.pr.invertor").Invertor self._invertor = Invertor() @@ -91,14 +92,14 @@ def __call__(self, r): self._invertor.y = iq self._invertor.err = diq c, c_cov = self._invertor.invert_optimize() - l = lambda x: self._invertor.pr(c, x) - pr = map(l, r) - + pr = map(lambda x: self._invertor.pr(c, x), r) pr = numpy.array(pr) return self.scale.value * pr + # End class PrCalculator + class CFCalculator(PrCalculator): """A class for calculating the characteristic function (CF) from data. @@ -130,4 +131,5 @@ def __call__(self, r): fr[0] = 1 return fr + # End class CFCalculator diff --git a/src/diffpy/srfit/sas/sasgenerator.py b/src/diffpy/srfit/sas/sasgenerator.py index 74a60594..dbf3f4a1 100644 --- a/src/diffpy/srfit/sas/sasgenerator.py +++ b/src/diffpy/srfit/sas/sasgenerator.py @@ -69,4 +69,5 @@ def __call__(self, q): """Calculate I(Q) for the BaseModel.""" return self._model.evalDistribution(q) + # End class SASGenerator diff --git a/src/diffpy/srfit/sas/sasimport.py b/src/diffpy/srfit/sas/sasimport.py index ba39fb4a..e3e15100 100644 --- a/src/diffpy/srfit/sas/sasimport.py +++ b/src/diffpy/srfit/sas/sasimport.py @@ -17,6 +17,7 @@ Universal import functions for volatile SasView/SansViews API-s. """ + def sasimport(modname): """Import specified module from the SasView sas package. @@ -25,41 +26,44 @@ def sasimport(modname): When specified import does not work directly, try older API-s and raise DeprecationWarning. Raise ImportError if nothing works. """ - if not modname.startswith('sas.'): + if not modname.startswith("sas."): emsg = 'Module name must start with "sas."' raise ValueError(emsg) mobj = None # standard import try: - exec('import %s as mobj' % modname) + exec("import %s as mobj" % modname) except ImportError: pass else: return mobj # revert to the old sans namespace, sas --> sans - modsans = 'sans' + modname[3:] + modsans = "sans" + modname[3:] import warnings - wfmt = ("Using obsolete package %r instead of %r. Please install " - "SasView 3.1 or the srfit-sasview package from Anaconda.") + + wfmt = ( + "Using obsolete package %r instead of %r. Please install " + "SasView 3.1 or the srfit-sasview package from Anaconda." + ) wmsg = wfmt % (modsans, modname) try: - exec('import %s as mobj' % modsans) + exec("import %s as mobj" % modsans) warnings.warn(wmsg, DeprecationWarning) except ImportError: pass else: return mobj # finally check the oldest DataLoader API for sas.dataloader - if modname.startswith('sas.dataloader'): - modloader = 'DataLoader' + modname[14:] + if modname.startswith("sas.dataloader"): + modloader = "DataLoader" + modname[14:] wmsg = wfmt % (modloader, modname) try: - exec('import %s as mobj' % modloader) + exec("import %s as mobj" % modloader) warnings.warn(wmsg, DeprecationWarning) except ImportError: pass else: return mobj # Obsolete API-s failed here. Import again and let it raise ImportError. - exec('import %s as mobj' % modname) + exec("import %s as mobj" % modname) raise AssertionError("The import above was supposed to fail.") diff --git a/src/diffpy/srfit/sas/sasparameter.py b/src/diffpy/srfit/sas/sasparameter.py index 7f3886d4..fbf841aa 100644 --- a/src/diffpy/srfit/sas/sasparameter.py +++ b/src/diffpy/srfit/sas/sasparameter.py @@ -43,7 +43,7 @@ class SASParameter(Parameter): """ - def __init__(self, name, model, parname = None): + def __init__(self, name, model, parname=None): """Create the Parameter. name -- Name of the Parameter @@ -60,7 +60,7 @@ def __init__(self, name, model, parname = None): def getValue(self): """Get the value of the Parameter.""" - value = self._model.getParam(self._parname) + value = self._model.getParam(self._parname) return value def setValue(self, value): @@ -70,4 +70,5 @@ def setValue(self, value): self.notify() return self + # End of class SASParameter diff --git a/src/diffpy/srfit/sas/sasparser.py b/src/diffpy/srfit/sas/sasparser.py index 3177954a..bf3cec74 100644 --- a/src/diffpy/srfit/sas/sasparser.py +++ b/src/diffpy/srfit/sas/sasparser.py @@ -83,7 +83,7 @@ def parseFile(self, filename): """ - Loader = sasimport('sas.dataloader.loader').Loader + Loader = sasimport("sas.dataloader.loader").Loader loader = Loader() try: @@ -117,8 +117,9 @@ def parseString(self, patstring): """ # This calls on parseFile, as that is how the sas data loader works. import tempfile + fh, fn = tempfile.mkstemp() - outfile = open(fn, 'w') + outfile = open(fn, "w") fn.write(patstring) outfile.close() self.parseFile(fn) @@ -127,6 +128,7 @@ def parseString(self, patstring): # Close the temporary file and delete it import os + os.close(fh) os.remove(fn) return diff --git a/src/diffpy/srfit/sas/sasprofile.py b/src/diffpy/srfit/sas/sasprofile.py index ef0bff3c..e35adfb1 100644 --- a/src/diffpy/srfit/sas/sasprofile.py +++ b/src/diffpy/srfit/sas/sasprofile.py @@ -75,7 +75,7 @@ def __init__(self, datainfo): self._dyobs = self._datainfo.dy return - def setObservedProfile(self, xobs, yobs, dyobs = None): + def setObservedProfile(self, xobs, yobs, dyobs=None): """Set the observed profile. This is overloaded to change the value within the datainfo object. diff --git a/src/diffpy/srfit/structure/__init__.py b/src/diffpy/srfit/structure/__init__.py index 54618c4c..cf24d47c 100644 --- a/src/diffpy/srfit/structure/__init__.py +++ b/src/diffpy/srfit/structure/__init__.py @@ -17,6 +17,10 @@ interface and automatic structure constraint generation from space group information. """ +from diffpy.srfit.structure.sgconstraints import constrainAsSpaceGroup + +# silence pyflakes checker +assert constrainAsSpaceGroup def struToParameterSet(name, stru): @@ -32,28 +36,26 @@ def struToParameterSet(name, stru): """ from diffpy.srfit.structure.diffpyparset import DiffpyStructureParSet + if DiffpyStructureParSet.canAdapt(stru): return DiffpyStructureParSet(name, stru) from diffpy.srfit.structure.objcrystparset import ObjCrystCrystalParSet + if ObjCrystCrystalParSet.canAdapt(stru): return ObjCrystCrystalParSet(name, stru) from diffpy.srfit.structure.objcrystparset import ObjCrystMoleculeParSet + if ObjCrystMoleculeParSet.canAdapt(stru): return ObjCrystMoleculeParSet(name, stru) from diffpy.srfit.structure.cctbxparset import CCTBXCrystalParSet + if CCTBXCrystalParSet.canAdapt(stru): return CCTBXCrystalParSet(name, stru) raise TypeError("Unadaptable structure format") -from diffpy.srfit.structure.sgconstraints import constrainAsSpaceGroup - -# silence pyflakes checker -assert constrainAsSpaceGroup - - # End of file diff --git a/src/diffpy/srfit/structure/bvsrestraint.py b/src/diffpy/srfit/structure/bvsrestraint.py index 38a5ea7a..43d9a6ba 100644 --- a/src/diffpy/srfit/structure/bvsrestraint.py +++ b/src/diffpy/srfit/structure/bvsrestraint.py @@ -21,8 +21,8 @@ __all__ = ["BVSRestraint"] -from diffpy.srfit.fitbase.restraint import Restraint from diffpy.srfit.exceptions import SrFitError +from diffpy.srfit.fitbase.restraint import Restraint class BVSRestraint(Restraint): @@ -41,7 +41,7 @@ class BVSRestraint(Restraint): """ - def __init__(self, parset, sig = 1, scaled = False): + def __init__(self, parset, sig=1, scaled=False): """Initialize the Restraint. parset -- SrRealParSet that creates this BVSRestraint. @@ -52,13 +52,14 @@ def __init__(self, parset, sig = 1, scaled = False): """ from diffpy.srreal.bvscalculator import BVSCalculator + self._calc = BVSCalculator() self._parset = parset self.sig = float(sig) self.scaled = bool(scaled) return - def penalty(self, w = 1.0): + def penalty(self, w=1.0): """Calculate the penalty of the restraint. w -- The point-average chi^2 which is optionally used to scale the @@ -74,7 +75,8 @@ def penalty(self, w = 1.0): penalty /= self.sig**2 # Optionally scale by w - if self.scaled: penalty *= w + if self.scaled: + penalty *= w return penalty @@ -85,16 +87,20 @@ def _validate(self): """ from numpy import nan + p = self.penalty() if p is None or p is nan: raise SrFitError("Cannot evaluate penalty") v = self._calc.value if len(v) > 1 and not v.any(): - emsg = ("Bond valence sums are all zero. Check atom symbols in " - "the structure or define custom bond-valence parameters.") + emsg = ( + "Bond valence sums are all zero. Check atom symbols in " + "the structure or define custom bond-valence parameters." + ) raise SrFitError(emsg) return # End of class BVSRestraint + # End of file diff --git a/src/diffpy/srfit/structure/cctbxparset.py b/src/diffpy/srfit/structure/cctbxparset.py index f3b04d8b..ab3b9497 100644 --- a/src/diffpy/srfit/structure/cctbxparset.py +++ b/src/diffpy/srfit/structure/cctbxparset.py @@ -32,8 +32,7 @@ from diffpy.srfit.fitbase.parameterset import ParameterSet from diffpy.srfit.structure.basestructureparset import BaseStructureParSet -__all__ = ["CCTBXScattererParSet", "CCTBXUnitCellParSet", - "CCTBXCrystalParSet"] +__all__ = ["CCTBXScattererParSet", "CCTBXUnitCellParSet", "CCTBXCrystalParSet"] class CCTBXScattererParSet(ParameterSet): @@ -65,29 +64,22 @@ def __init__(self, name, strups, idx): self.idx = idx # x, y, z, occupancy - self.addParameter(ParameterAdapter("x", None, self._xyzgetter(0), - self._xyzsetter(0))) - self.addParameter(ParameterAdapter("y", None, self._xyzgetter(1), - self._xyzsetter(1))) - self.addParameter(ParameterAdapter("z", None, self._xyzgetter(2), - self._xyzsetter(2))) - self.addParameter(ParameterAdapter("occupancy", None, self._getocc, - self._setocc)) - self.addParameter(ParameterAdapter("Uiso", None, self._getuiso, - self._setuiso)) + self.addParameter(ParameterAdapter("x", None, self._xyzgetter(0), self._xyzsetter(0))) + self.addParameter(ParameterAdapter("y", None, self._xyzgetter(1), self._xyzsetter(1))) + self.addParameter(ParameterAdapter("z", None, self._xyzgetter(2), self._xyzsetter(2))) + self.addParameter(ParameterAdapter("occupancy", None, self._getocc, self._setocc)) + self.addParameter(ParameterAdapter("Uiso", None, self._getuiso, self._setuiso)) return # Getters and setters def _xyzgetter(self, i): - def f(dummy): return self.strups.stru.scatterers()[self.idx].site[i] return f def _xyzsetter(self, i): - def f(dummy, value): xyz = list(self.strups.stru.scatterers()[self.idx].site) xyz[i] = value @@ -115,8 +107,10 @@ def _getElem(self): element = property(_getElem) + # End class CCTBXScattererParSet + class CCTBXUnitCellParSet(ParameterSet): """A wrapper for cctbx unit_cell object. @@ -137,30 +131,22 @@ def __init__(self, strups): self.strups = strups self._latpars = list(self.strups.stru.unit_cell().parameters()) - self.addParameter(ParameterAdapter("a", None, self._latgetter(0), - self._latsetter(0))) - self.addParameter(ParameterAdapter("b", None, self._latgetter(1), - self._latsetter(1))) - self.addParameter(ParameterAdapter("c", None, self._latgetter(2), - self._latsetter(2))) - self.addParameter(ParameterAdapter("alpha", None, self._latgetter(3), - self._latsetter(3))) - self.addParameter(ParameterAdapter("beta", None, self._latgetter(4), - self._latsetter(4))) - self.addParameter(ParameterAdapter("gamma", None, self._latgetter(5), - self._latsetter(5))) + self.addParameter(ParameterAdapter("a", None, self._latgetter(0), self._latsetter(0))) + self.addParameter(ParameterAdapter("b", None, self._latgetter(1), self._latsetter(1))) + self.addParameter(ParameterAdapter("c", None, self._latgetter(2), self._latsetter(2))) + self.addParameter(ParameterAdapter("alpha", None, self._latgetter(3), self._latsetter(3))) + self.addParameter(ParameterAdapter("beta", None, self._latgetter(4), self._latsetter(4))) + self.addParameter(ParameterAdapter("gamma", None, self._latgetter(5), self._latsetter(5))) return def _latgetter(self, i): - def f(dummy): return self._latpars[i] return f def _latsetter(self, i): - def f(dummy, value): self._latpars[i] = value self.strups._update = True @@ -173,6 +159,7 @@ def f(dummy, value): # FIXME - Special positions should be constant. + class CCTBXCrystalParSet(BaseStructureParSet): """A wrapper for CCTBX structure. @@ -201,14 +188,15 @@ def __init__(self, name, stru): for s in stru.scatterers(): el = s.element_symbol() i = cdict.get(el, 0) - sname = "%s%i"%(el,i) - cdict[el] = i+1 + sname = "%s%i" % (el, i) + cdict[el] = i + 1 scatterer = CCTBXScattererParSet(sname, self, i) self.addParameterSet(scatterer) self.scatterers.append(scatterer) # Constrain the lattice from diffpy.srfit.structure.sgconstraints import _constrainSpaceGroup + symbol = self.getSpaceGroup() _constrainSpaceGroup(self, symbol) @@ -231,16 +219,11 @@ def update(self): # Create the symmetry object from cctbx.crystal import symmetry - symm = symmetry( - unit_cell = self.unitcell._latpars, - space_group_symbol = sgn - ) + + symm = symmetry(unit_cell=self.unitcell._latpars, space_group_symbol=sgn) # Now the new structure - newstru = stru.__class__( - crystal_symmetry = symm, - scatterers = stru.scatterers() - ) + newstru = stru.__class__(crystal_symmetry=symm, scatterers=stru.scatterers()) self.unitcell._latpars = list(newstru.unit_cell().parameters()) @@ -278,5 +261,4 @@ def getSpaceGroup(self): return t.lookup_symbol() - # End class CCTBXCrystalParSet diff --git a/src/diffpy/srfit/structure/diffpyparset.py b/src/diffpy/srfit/structure/diffpyparset.py index 60773f69..5eef8d2a 100644 --- a/src/diffpy/srfit/structure/diffpyparset.py +++ b/src/diffpy/srfit/structure/diffpyparset.py @@ -30,8 +30,7 @@ __all__ = ["DiffpyStructureParSet"] -from diffpy.srfit.fitbase.parameter import ParameterProxy -from diffpy.srfit.fitbase.parameter import ParameterAdapter +from diffpy.srfit.fitbase.parameter import ParameterAdapter, ParameterProxy from diffpy.srfit.fitbase.parameterset import ParameterSet from diffpy.srfit.structure.srrealparset import SrRealParSet from diffpy.srfit.util.argbinders import bind2nd @@ -39,7 +38,6 @@ # Accessor for xyz of atoms class _xyzgetter(object): - def __init__(self, i): self.i = i @@ -48,7 +46,6 @@ def __call__(self, atom): class _xyzsetter(object): - def __init__(self, i): self.i = i @@ -94,12 +91,9 @@ def __init__(self, name, atom): self.atom = atom a = atom # x, y, z, occupancy - self.addParameter(ParameterAdapter("x", a, - _xyzgetter(0), _xyzsetter(0))) - self.addParameter(ParameterAdapter("y", a, - _xyzgetter(1), _xyzsetter(1))) - self.addParameter(ParameterAdapter("z", a, - _xyzgetter(2), _xyzsetter(2))) + self.addParameter(ParameterAdapter("x", a, _xyzgetter(0), _xyzsetter(0))) + self.addParameter(ParameterAdapter("y", a, _xyzgetter(1), _xyzsetter(1))) + self.addParameter(ParameterAdapter("z", a, _xyzgetter(2), _xyzsetter(2))) occupancy = ParameterAdapter("occupancy", a, attr="occupancy") self.addParameter(occupancy) self.addParameter(ParameterProxy("occ", occupancy)) @@ -150,6 +144,7 @@ def _setElem(self, el): element = property(_getElem, _setElem, "type of atom") + # End class DiffpyAtomParSet @@ -187,23 +182,18 @@ def __init__(self, lattice): self.angunits = "deg" self.lattice = lattice lat = lattice - self.addParameter(ParameterAdapter("a", lat, - _latgetter("a"), _latsetter("a"))) - self.addParameter(ParameterAdapter("b", lat, - _latgetter("b"), _latsetter("b"))) - self.addParameter(ParameterAdapter("c", lat, - _latgetter("c"), _latsetter("c"))) - self.addParameter(ParameterAdapter("alpha", lat, _latgetter("alpha"), - _latsetter("alpha"))) - self.addParameter(ParameterAdapter("beta", lat, _latgetter("beta"), - _latsetter("beta"))) - self.addParameter(ParameterAdapter("gamma", lat, _latgetter("gamma"), - _latsetter("gamma"))) + self.addParameter(ParameterAdapter("a", lat, _latgetter("a"), _latsetter("a"))) + self.addParameter(ParameterAdapter("b", lat, _latgetter("b"), _latsetter("b"))) + self.addParameter(ParameterAdapter("c", lat, _latgetter("c"), _latsetter("c"))) + self.addParameter(ParameterAdapter("alpha", lat, _latgetter("alpha"), _latsetter("alpha"))) + self.addParameter(ParameterAdapter("beta", lat, _latgetter("beta"), _latsetter("beta"))) + self.addParameter(ParameterAdapter("gamma", lat, _latgetter("gamma"), _latsetter("gamma"))) return def __repr__(self): return repr(self.lattice) + # End class DiffpyLatticeParSet @@ -247,7 +237,7 @@ def __init__(self, name, stru): el = el.replace("-", "m") i = cdict.get(el, 0) aname = "%s%i" % (el, i) - cdict[el] = i+1 + cdict[el] = i + 1 atom = DiffpyAtomParSet(aname, a) self.addParameterSet(atom) self.atoms.append(atom) @@ -265,6 +255,7 @@ def getLattice(self): def canAdapt(self, stru): """Return whether the structure can be adapted by this class.""" from diffpy.structure import Structure + return isinstance(stru, Structure) def getScatterers(self): @@ -287,7 +278,9 @@ def _getSrRealStructure(self): """ from diffpy.srreal.structureadapter import nometa + stru = SrRealParSet._getSrRealStructure(self) return nometa(stru) + # End class DiffpyStructureParSet diff --git a/src/diffpy/srfit/structure/objcrystparset.py b/src/diffpy/srfit/structure/objcrystparset.py index ac4f3b43..c31473df 100644 --- a/src/diffpy/srfit/structure/objcrystparset.py +++ b/src/diffpy/srfit/structure/objcrystparset.py @@ -40,13 +40,16 @@ __all__ = ["ObjCrystMoleculeParSet", "ObjCrystCrystalParSet"] import numpy - -from pyobjcryst.molecule import GetBondLength, GetBondAngle, GetDihedralAngle -from pyobjcryst.molecule import StretchModeBondLength, StretchModeBondAngle -from pyobjcryst.molecule import StretchModeTorsion - -from diffpy.srfit.fitbase.parameter import Parameter, ParameterAdapter -from diffpy.srfit.fitbase.parameter import ParameterProxy +from pyobjcryst.molecule import ( + GetBondAngle, + GetBondLength, + GetDihedralAngle, + StretchModeBondAngle, + StretchModeBondLength, + StretchModeTorsion, +) + +from diffpy.srfit.fitbase.parameter import Parameter, ParameterAdapter, ParameterProxy from diffpy.srfit.fitbase.parameterset import ParameterSet from diffpy.srfit.structure.srrealparset import SrRealParSet @@ -83,11 +86,10 @@ def __init__(self, name, scat, parent): self.parent = parent # x, y, z, occ - self.addParameter(ParameterAdapter("x", self.scat, attr = "X")) - self.addParameter(ParameterAdapter("y", self.scat, attr = "Y")) - self.addParameter(ParameterAdapter("z", self.scat, attr = "Z")) - self.addParameter(ParameterAdapter("occ", self.scat, attr = - "Occupancy")) + self.addParameter(ParameterAdapter("x", self.scat, attr="X")) + self.addParameter(ParameterAdapter("y", self.scat, attr="Y")) + self.addParameter(ParameterAdapter("z", self.scat, attr="Z")) + self.addParameter(ParameterAdapter("occ", self.scat, attr="Occupancy")) return def isDummy(self): @@ -101,6 +103,7 @@ def hasScatterers(self): # End class ObjCrystScattererParSet + class ObjCrystAtomParSet(ObjCrystScattererParSet): """A adaptor for a pyobjcryst.Atom. @@ -135,15 +138,15 @@ def __init__(self, name, atom, parent): sp = atom.GetScatteringPower() # The B-parameters - self.addParameter(ParameterAdapter("Biso", sp, attr = "Biso")) - self.addParameter(ParameterAdapter("B11", sp, attr = "B11")) - self.addParameter(ParameterAdapter("B22", sp, attr = "B22")) - self.addParameter(ParameterAdapter("B33", sp, attr = "B33")) - B12 = ParameterAdapter("B12", sp, attr = "B12") + self.addParameter(ParameterAdapter("Biso", sp, attr="Biso")) + self.addParameter(ParameterAdapter("B11", sp, attr="B11")) + self.addParameter(ParameterAdapter("B22", sp, attr="B22")) + self.addParameter(ParameterAdapter("B33", sp, attr="B33")) + B12 = ParameterAdapter("B12", sp, attr="B12") B21 = ParameterProxy("B21", B12) - B13 = ParameterAdapter("B13", sp, attr = "B13") + B13 = ParameterAdapter("B13", sp, attr="B13") B31 = ParameterProxy("B31", B13) - B23 = ParameterAdapter("B23", sp, attr = "B23") + B23 = ParameterAdapter("B23", sp, attr="B23") B32 = ParameterProxy("B32", B23) self.addParameter(B12) self.addParameter(B21) @@ -163,8 +166,10 @@ def _getElem(self): element = property(_getElem) + # End class ObjCrystAtomParSet + class ObjCrystMoleculeParSet(ObjCrystScattererParSet): """A adaptor for a pyobjcryst.Molecule. @@ -188,7 +193,7 @@ class ObjCrystMoleculeParSet(ObjCrystScattererParSet): """ - def __init__(self, name, molecule, parent = None): + def __init__(self, name, molecule, parent=None): """Initialize name -- The name of the scatterer @@ -200,22 +205,21 @@ def __init__(self, name, molecule, parent = None): self.stru = molecule # Add orientiation quaternion - self.addParameter(ParameterAdapter("q0", self.scat, attr = "Q0")) - self.addParameter(ParameterAdapter("q1", self.scat, attr = "Q1")) - self.addParameter(ParameterAdapter("q2", self.scat, attr = "Q2")) - self.addParameter(ParameterAdapter("q3", self.scat, attr = "Q3")) + self.addParameter(ParameterAdapter("q0", self.scat, attr="Q0")) + self.addParameter(ParameterAdapter("q1", self.scat, attr="Q1")) + self.addParameter(ParameterAdapter("q2", self.scat, attr="Q2")) + self.addParameter(ParameterAdapter("q3", self.scat, attr="Q3")) # Wrap the MolAtoms within the molecule self.atoms = [] anames = [] for a in molecule: - name = a.GetName() if not name: raise AttributeError("Each MolAtom must have a name") if name in anames: - raise AttributeError("MolAtom name '%s' is duplicated"%name) + raise AttributeError("MolAtom name '%s' is duplicated" % name) atom = ObjCrystMolAtomParSet(name, a, self) atom.molecule = self @@ -229,10 +233,11 @@ def __init__(self, name, molecule, parent = None): def canAdapt(self, stru): """Return whether the structure can be adapted by this class.""" from pyobjcryst.molecule import Molecule + return isinstance(stru, Molecule) # Part of SrRealParSet interface - def useSymmetry(self, use = True): + def useSymmetry(self, use=True): """Set this structure to use symmetry. This structure object does not support symmetry. @@ -327,18 +332,17 @@ def wrapStretchModeParameters(self): atom1 = getattr(self, name1) atom2 = getattr(self, name2) - par = ObjCrystBondLengthParameter(name, atom1, atom2, mode = mode) + par = ObjCrystBondLengthParameter(name, atom1, atom2, mode=mode) atoms = [] for a in mode.GetAtoms(): name = a.GetName() - atoms.append( getattr(self, name) ) + atoms.append(getattr(self, name)) par.AddAtoms(atoms) self.addParameter(par) - for mode in self.scat.GetStretchModeBondAngleList(): name1 = mode.mpAtom0.GetName() name2 = mode.mpAtom1.GetName() @@ -350,21 +354,19 @@ def wrapStretchModeParameters(self): atom2 = getattr(self, name2) atom3 = getattr(self, name3) - par = ObjCrystBondAngleParameter(name, atom1, atom2, atom3, mode = - mode) + par = ObjCrystBondAngleParameter(name, atom1, atom2, atom3, mode=mode) atoms = [] for a in mode.GetAtoms(): name = a.GetName() - atoms.append( getattr(self, name) ) + atoms.append(getattr(self, name)) par.AddAtoms(atoms) self.addParameter(par) return - def restrainBondLength(self, atom1, atom2, length, sigma, delta, scaled = - False): + def restrainBondLength(self, atom1, atom2, length, sigma, delta, scaled=False): """Add a bond length restraint. This creates an instance of ObjCrystBondLengthRestraint and adds it to @@ -388,8 +390,7 @@ def restrainBondLength(self, atom1, atom2, length, sigma, delta, scaled = return res - def restrainBondLengthParameter(self, par, length, sigma, delta, scaled = - False): + def restrainBondLengthParameter(self, par, length, sigma, delta, scaled=False): """Add a bond length restraint. This creates an instance of ObjCrystBondLengthRestraint and adds it to @@ -407,11 +408,9 @@ def restrainBondLengthParameter(self, par, length, sigma, delta, scaled = 'unrestrain' method. """ - return self.restrainBondLength(par.atom1, par.atom2, length, sigma, - delta, scaled) + return self.restrainBondLength(par.atom1, par.atom2, length, sigma, delta, scaled) - def restrainBondAngle(self, atom1, atom2, atom3, angle, sigma, delta, - scaled = False): + def restrainBondAngle(self, atom1, atom2, atom3, angle, sigma, delta, scaled=False): """Add a bond angle restraint. This creates an instance of ObjCrystBondAngleRestraint and adds it to @@ -432,14 +431,12 @@ def restrainBondAngle(self, atom1, atom2, atom3, angle, sigma, delta, 'unrestrain' method. """ - res = ObjCrystBondAngleRestraint(atom1, atom2, atom3, angle, sigma, - delta, scaled) + res = ObjCrystBondAngleRestraint(atom1, atom2, atom3, angle, sigma, delta, scaled) self._restraints.add(res) return res - def restrainBondAngleParameter(self, par, angle, sigma, delta, - scaled = False): + def restrainBondAngleParameter(self, par, angle, sigma, delta, scaled=False): """Add a bond angle restraint. This creates an instance of ObjCrystBondAngleRestraint and adds it to @@ -457,11 +454,9 @@ def restrainBondAngleParameter(self, par, angle, sigma, delta, 'unrestrain' method. """ - return self.restrainBondAngle(par.atom1, par.atom2, par.atom3, angle, - sigma, delta, scaled) + return self.restrainBondAngle(par.atom1, par.atom2, par.atom3, angle, sigma, delta, scaled) - def restrainDihedralAngle(self, atom1, atom2, atom3, atom4, angle, sigma, - delta, scaled = False): + def restrainDihedralAngle(self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled=False): """Add a dihedral angle restraint. This creates an instance of ObjCrystDihedralAngleRestraint and adds it @@ -482,14 +477,12 @@ def restrainDihedralAngle(self, atom1, atom2, atom3, atom4, angle, sigma, 'unrestrain' method. """ - res = ObjCrystDihedralAngleRestraint(atom1, atom2, atom3, atom4, angle, - sigma, delta, scaled) + res = ObjCrystDihedralAngleRestraint(atom1, atom2, atom3, atom4, angle, sigma, delta, scaled) self._restraints.add(res) return res - def restrainDihedralAngleParameter(self, par, angle, sigma, delta, - scaled = False): + def restrainDihedralAngleParameter(self, par, angle, sigma, delta, scaled=False): """Add a dihedral angle restraint. This creates an instance of ObjCrystDihedralAngleRestraint and adds it @@ -508,11 +501,9 @@ def restrainDihedralAngleParameter(self, par, angle, sigma, delta, 'unrestrain' method. """ - return self.restrainDihedralAngle(par.atom1, par.atom2, par.atom3, - par.atom4, angle, sigma, delta, scaled) + return self.restrainDihedralAngle(par.atom1, par.atom2, par.atom3, par.atom4, angle, sigma, delta, scaled) - def addBondLengthParameter(self, name, atom1, atom2, value = None, const = - False): + def addBondLengthParameter(self, name, atom1, atom2, value=None, const=False): """Add a bond length to the Molecule. This creates a ObjCrystBondLengthParameter to the @@ -536,8 +527,7 @@ def addBondLengthParameter(self, name, atom1, atom2, value = None, const = return par - def addBondAngleParameter(self, name, atom1, atom2, atom3, value = None, - const = False): + def addBondAngleParameter(self, name, atom1, atom2, atom3, value=None, const=False): """Add a bond angle to the Molecule. This creates a ObjCrystBondAngleParameter to the ObjCrystMoleculeParSet @@ -558,14 +548,12 @@ def addBondAngleParameter(self, name, atom1, atom2, atom3, value = None, Returns the new ObjCrystBondAngleParameter. """ - par = ObjCrystBondAngleParameter(name, atom1, atom2, atom3, value, - const) + par = ObjCrystBondAngleParameter(name, atom1, atom2, atom3, value, const) self.addParameter(par) return par - def addDihedralAngleParameter(self, name, atom1, atom2, atom3, atom4, value - = None, const = False): + def addDihedralAngleParameter(self, name, atom1, atom2, atom3, atom4, value=None, const=False): """Add a dihedral angle to the Molecule. This creates a ObjCrystDihedralAngleParameter to the @@ -588,14 +576,15 @@ def addDihedralAngleParameter(self, name, atom1, atom2, atom3, atom4, value Returns the new ObjCrystDihedralAngleParameter. """ - par = ObjCrystDihedralAngleParameter(name, atom1, atom2, atom3, atom4, - value, const) + par = ObjCrystDihedralAngleParameter(name, atom1, atom2, atom3, atom4, value, const) self.addParameter(par) return par + # End class ObjCrystMoleculeParSet + class ObjCrystMolAtomParSet(ObjCrystScattererParSet): """A adaptor for an pyobjcryst.molecule.MolAtom. @@ -634,15 +623,15 @@ def __init__(self, name, scat, parent): # Only wrap this if there is a scattering power if sp is not None: - self.addParameter(ParameterAdapter("Biso", sp, attr = "Biso")) - self.addParameter(ParameterAdapter("B11", sp, attr = "B11")) - self.addParameter(ParameterAdapter("B22", sp, attr = "B22")) - self.addParameter(ParameterAdapter("B33", sp, attr = "B33")) - B12 = ParameterAdapter("B12", sp, attr = "B12") + self.addParameter(ParameterAdapter("Biso", sp, attr="Biso")) + self.addParameter(ParameterAdapter("B11", sp, attr="B11")) + self.addParameter(ParameterAdapter("B22", sp, attr="B22")) + self.addParameter(ParameterAdapter("B33", sp, attr="B33")) + B12 = ParameterAdapter("B12", sp, attr="B12") B21 = ParameterProxy("B21", B12) - B13 = ParameterAdapter("B13", sp, attr = "B13") + B13 = ParameterAdapter("B13", sp, attr="B13") B31 = ParameterProxy("B31", B13) - B23 = ParameterAdapter("B23", sp, attr = "B23") + B23 = ParameterAdapter("B23", sp, attr="B23") B32 = ParameterProxy("B32", B23) self.addParameter(B12) self.addParameter(B21) @@ -667,8 +656,10 @@ def isDummy(self): """Indicate whether this atom is a dummy atom.""" return self.scat.IsDummy() + # End class ObjCrystMolAtomParSet + class ObjCrystMoleculeRestraint(object): """Base class for adapting pyobjcryst Molecule restraints to srfit. @@ -685,7 +676,7 @@ class ObjCrystMoleculeRestraint(object): """ - def __init__(self, res, scaled = False): + def __init__(self, res, scaled=False): """Create a Restraint-like from a pyobjcryst Molecule restraint. res -- The pyobjcryst Molecule restraint. @@ -698,7 +689,7 @@ def __init__(self, res, scaled = False): self.scaled = scaled return - def penalty(self, w = 1.0): + def penalty(self, w=1.0): """Calculate the penalty of the restraint. w -- The point-average chi^2 which is optionally used to scale the @@ -710,8 +701,10 @@ def penalty(self, w = 1.0): penalty *= w return penalty + # End class ObjCrystMoleculeRestraint + class ObjCrystBondLengthRestraint(ObjCrystMoleculeRestraint): """Restrain the distance between two atoms. @@ -728,7 +721,7 @@ class ObjCrystBondLengthRestraint(ObjCrystMoleculeRestraint): """ - def __init__(self, atom1, atom2, length, sigma, delta, scaled = False): + def __init__(self, atom1, atom2, length, sigma, delta, scaled=False): """Create a bond length restraint. atom1 -- First atom (ObjCrystMolAtomParSet) in the bond @@ -751,15 +744,14 @@ def __init__(self, atom1, atom2, length, sigma, delta, scaled = False): return # Give access to the parameters of the restraint - length = property( lambda self: self.res.GetLength0(), - lambda self, val: self.res.SetLength0(val)) - sigma = property( lambda self: self.res.GetLengthSigma(), - lambda self, val: self.res.SetLengthSigma(val)) - delta = property( lambda self: self.res.GetLengthDelta(), - lambda self, val: self.res.SetLengthDelta(val)) + length = property(lambda self: self.res.GetLength0(), lambda self, val: self.res.SetLength0(val)) + sigma = property(lambda self: self.res.GetLengthSigma(), lambda self, val: self.res.SetLengthSigma(val)) + delta = property(lambda self: self.res.GetLengthDelta(), lambda self, val: self.res.SetLengthDelta(val)) + # End class ObjCrystBondLengthRestraint + class ObjCrystBondAngleRestraint(ObjCrystMoleculeRestraint): """Restrain the angle defined by three atoms. @@ -777,8 +769,7 @@ class ObjCrystBondAngleRestraint(ObjCrystMoleculeRestraint): """ - def __init__(self, atom1, atom2, atom3, angle, sigma, delta, scaled = - False): + def __init__(self, atom1, atom2, atom3, angle, sigma, delta, scaled=False): """Create a bond angle restraint. atom1 -- First atom (ObjCrystMolAtomParSet) in the bond angle @@ -798,22 +789,20 @@ def __init__(self, atom1, atom2, atom3, angle, sigma, delta, scaled = self.atom3 = atom3 m = self.atom1.scat.GetMolecule() - res = m.AddBondAngle(atom1.scat, atom2.scat, atom3.scat, angle, - sigma, delta) + res = m.AddBondAngle(atom1.scat, atom2.scat, atom3.scat, angle, sigma, delta) ObjCrystMoleculeRestraint.__init__(self, res, scaled) return # Give access to the parameters of the restraint - angle = property( lambda self: self.res.GetAngle0(), - lambda self, val: self.res.SetAngle0(val)) - sigma = property( lambda self: self.res.GetAngleSigma(), - lambda self, val: self.res.SetAngleSigma(val)) - delta = property( lambda self: self.res.GetAngleDelta(), - lambda self, val: self.res.SetAngleDelta(val)) + angle = property(lambda self: self.res.GetAngle0(), lambda self, val: self.res.SetAngle0(val)) + sigma = property(lambda self: self.res.GetAngleSigma(), lambda self, val: self.res.SetAngleSigma(val)) + delta = property(lambda self: self.res.GetAngleDelta(), lambda self, val: self.res.SetAngleDelta(val)) + # End class ObjCrystBondAngleRestraint + class ObjCrystDihedralAngleRestraint(ObjCrystMoleculeRestraint): """Restrain the dihedral (torsion) angle defined by four atoms. @@ -832,8 +821,7 @@ class ObjCrystDihedralAngleRestraint(ObjCrystMoleculeRestraint): """ - def __init__(self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled - = False): + def __init__(self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled=False): """Create a dihedral angle restraint. atom1 -- First atom (ObjCrystMolAtomParSet) in the angle @@ -854,22 +842,20 @@ def __init__(self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled self.atom4 = atom4 m = self.atom1.scat.GetMolecule() - res = m.AddDihedralAngle(atom1.scat, atom2.scat, atom3.scat, - atom4.scat, angle, sigma, delta) + res = m.AddDihedralAngle(atom1.scat, atom2.scat, atom3.scat, atom4.scat, angle, sigma, delta) ObjCrystMoleculeRestraint.__init__(self, res, scaled) return # Give access to the parameters of the restraint - angle = property( lambda self: self.res.GetAngle0(), - lambda self, val: self.res.SetAngle0(val)) - sigma = property( lambda self: self.res.GetAngleSigma(), - lambda self, val: self.res.SetAngleSigma(val)) - delta = property( lambda self: self.res.GetAngleDelta(), - lambda self, val: self.res.SetAngleDelta(val)) + angle = property(lambda self: self.res.GetAngle0(), lambda self, val: self.res.SetAngle0(val)) + sigma = property(lambda self: self.res.GetAngleSigma(), lambda self, val: self.res.SetAngleSigma(val)) + delta = property(lambda self: self.res.GetAngleDelta(), lambda self, val: self.res.SetAngleDelta(val)) + # End class ObjCrystDihedralAngleRestraint + class StretchModeParameter(Parameter): """Partial Parameter class encapsulating pyobjcryst stretch modes. @@ -887,7 +873,7 @@ class StretchModeParameter(Parameter): """ - def __init__(self, name, value = None, const = False): + def __init__(self, name, value=None, const=False): """Initialization. name -- The name of this Parameter (must be a valid attribute @@ -966,8 +952,10 @@ def notify(self, other=()): Parameter.notify(self, other) return + # End class StretchModeParameter + class ObjCrystBondLengthParameter(StretchModeParameter): """Class for abstracting a bond length in a Molecule to a Parameter. @@ -1016,8 +1004,7 @@ class ObjCrystBondLengthParameter(StretchModeParameter): """ - def __init__(self, name, atom1, atom2, value = None, const = False, mode = - None): + def __init__(self, name, atom1, atom2, value=None, const=False, mode=None): """Create a ObjCrystBondLengthParameter. name -- The name of the ObjCrystBondLengthParameter @@ -1061,7 +1048,7 @@ def __init__(self, name, atom1, atom2, value = None, const = False, mode = return - def setConst(self, const = True, value = None): + def setConst(self, const=True, value=None): """Toggle the Parameter as constant. This sets the underlying ObjCrystMolAtomParSet positions const as well. @@ -1098,6 +1085,7 @@ def getValue(self): # End class ObjCrystBondLengthParameter + class ObjCrystBondAngleParameter(StretchModeParameter): """Class for abstracting a bond angle in a Molecule to a Parameter. @@ -1132,8 +1120,7 @@ class ObjCrystBondAngleParameter(StretchModeParameter): """ - def __init__(self, name, atom1, atom2, atom3, value = None, const = False, - mode = None): + def __init__(self, name, atom1, atom2, atom3, value=None, const=False, mode=None): """Create a ObjCrystBondAngleParameter. name -- The name of the ObjCrystBondAngleParameter. @@ -1155,8 +1142,7 @@ def __init__(self, name, atom1, atom2, atom3, value = None, const = False, # Create the stretch mode self.mode = mode if mode is None: - self.mode = StretchModeBondAngle(atom1.scat, atom2.scat, - atom3.scat, None) + self.mode = StretchModeBondAngle(atom1.scat, atom2.scat, atom3.scat, None) # We only add the last atom. This is the one that will move self.mode.AddAtom(atom3.scat) self.matoms = set([atom3]) @@ -1180,7 +1166,7 @@ def __init__(self, name, atom1, atom2, atom3, value = None, const = False, return - def setConst(self, const = True, value = None): + def setConst(self, const=True, value=None): """Toggle the Parameter as constant. This sets the underlying ObjCrystMolAtomParSet positions const as well. @@ -1208,14 +1194,15 @@ def getValue(self): """ if self._value is None: - val = GetBondAngle(self.atom1.scat, self.atom2.scat, - self.atom3.scat) + val = GetBondAngle(self.atom1.scat, self.atom2.scat, self.atom3.scat) Parameter.setValue(self, val) return self._value + # End class ObjCrystBondAngleParameter + class ObjCrystDihedralAngleParameter(StretchModeParameter): """Class for abstracting a dihedral angle in a Molecule to a Parameter. @@ -1253,8 +1240,7 @@ class ObjCrystDihedralAngleParameter(StretchModeParameter): """ - def __init__(self, name, atom1, atom2, atom3, atom4, value = None, const = - False, mode = None): + def __init__(self, name, atom1, atom2, atom3, atom4, value=None, const=False, mode=None): """Create a ObjCrystDihedralAngleParameter. name -- The name of the ObjCrystDihedralAngleParameter @@ -1298,14 +1284,13 @@ def __init__(self, name, atom1, atom2, atom3, atom4, value = None, const = # We do this last so the atoms are defined before we set any values. if value is None: - value = GetDihedralAngle(atom1.scat, atom2.scat, atom3.scat, - atom4.scat) + value = GetDihedralAngle(atom1.scat, atom2.scat, atom3.scat, atom4.scat) StretchModeParameter.__init__(self, name, value, const) self.setConst(const) return - def setConst(self, const = True, value = None): + def setConst(self, const=True, value=None): """Toggle the Parameter as constant. This sets the underlying ObjCrystMolAtomParSet positions const as well. @@ -1333,14 +1318,15 @@ def getValue(self): """ if self._value is None: - val = GetDihedralAngle(self.atom1.scat, self.atom2.scat, - self.atom3.scat, self.atom4.scat) + val = GetDihedralAngle(self.atom1.scat, self.atom2.scat, self.atom3.scat, self.atom4.scat) Parameter.setValue(self, val) return self._value + # End class ObjCrystDihedralAngleParameter + class ObjCrystCrystalParSet(SrRealParSet): """A adaptor for pyobjcryst.crystal.Crystal instance. @@ -1381,14 +1367,12 @@ def __init__(self, name, cryst): self.stru = cryst self._sgpars = None - self.addParameter(ParameterAdapter("a", self.stru, attr = "a")) - self.addParameter(ParameterAdapter("b", self.stru, attr = "b")) - self.addParameter(ParameterAdapter("c", self.stru, attr = "c")) - self.addParameter(ParameterAdapter("alpha", self.stru, attr = - "alpha")) - self.addParameter(ParameterAdapter("beta", self.stru, attr = "beta")) - self.addParameter(ParameterAdapter("gamma", self.stru, attr = - "gamma")) + self.addParameter(ParameterAdapter("a", self.stru, attr="a")) + self.addParameter(ParameterAdapter("b", self.stru, attr="b")) + self.addParameter(ParameterAdapter("c", self.stru, attr="c")) + self.addParameter(ParameterAdapter("alpha", self.stru, attr="alpha")) + self.addParameter(ParameterAdapter("beta", self.stru, attr="beta")) + self.addParameter(ParameterAdapter("gamma", self.stru, attr="gamma")) # Now we must loop over the scatterers and create parameter sets from # them. @@ -1401,7 +1385,7 @@ def __init__(self, name, cryst): if not name: raise ValueError("Each Scatterer must have a name") if name in snames: - raise ValueError("Scatterer name '%s' is duplicated"%name) + raise ValueError("Scatterer name '%s' is duplicated" % name) # Now create the proper object cname = s.GetClassName() @@ -1410,7 +1394,7 @@ def __init__(self, name, cryst): elif cname == "Molecule": parset = ObjCrystMoleculeParSet(name, s, self) else: - raise TypeError("Unrecognized scatterer '%s'"%cname) + raise TypeError("Unrecognized scatterer '%s'" % cname) self.addParameterSet(parset) self.scatterers.append(parset) @@ -1424,11 +1408,13 @@ def _constrainSpaceGroup(self): return self._sgpars sg = self._createSpaceGroup(self.stru.GetSpaceGroup()) from diffpy.srfit.structure.sgconstraints import _constrainAsSpaceGroup + adpsymbols = ["B11", "B22", "B33", "B12", "B13", "B23"] isosymbol = "Biso" sgoffset = [0, 0, 0] - self._sgpars = _constrainAsSpaceGroup(self, sg, self.scatterers, - sgoffset, adpsymbols = adpsymbols, isosymbol = isosymbol) + self._sgpars = _constrainAsSpaceGroup( + self, sg, self.scatterers, sgoffset, adpsymbols=adpsymbols, isosymbol=isosymbol + ) return self._sgpars sgpars = property(_constrainSpaceGroup) @@ -1445,11 +1431,13 @@ def _createSpaceGroup(sgobjcryst): """ import copy + from diffpy.structure.spacegroups import GetSpaceGroup, SymOp + name = sgobjcryst.GetName() extnstr = ":%s" % sgobjcryst.GetExtension() if name.endswith(extnstr): - name = name[:-len(extnstr)] + name = name[: -len(extnstr)] # Get whatever spacegroup we can get by name. This will set the proper # crystal system. Creating a copy of the singleton from GetSpaceGroup, @@ -1465,7 +1453,7 @@ def _createSpaceGroup(sgobjcryst): for shift, rot in symops: tv = trans + shift tv -= numpy.floor(tv) - sg.symop_list.append( SymOp(rot, tv) ) + sg.symop_list.append(SymOp(rot, tv)) if sgobjcryst.IsCentrosymmetric(): center = sgobjcryst.GetInversionCenter() @@ -1473,7 +1461,7 @@ def _createSpaceGroup(sgobjcryst): for shift, rot in symops: tv = center - trans - shift tv -= numpy.floor(tv) - sg.symop_list.append( SymOp(-rot , tv) ) + sg.symop_list.append(SymOp(-rot, tv)) return sg @@ -1481,6 +1469,7 @@ def _createSpaceGroup(sgobjcryst): def canAdapt(self, stru): """Return whether the structure can be adapted by this class.""" from pyobjcryst.crystal import Crystal + return isinstance(stru, Crystal) def getLattice(self): @@ -1498,4 +1487,5 @@ def getScatterers(self): """ return self.scatterers + # End class ObjCrystCrystalParSet diff --git a/src/diffpy/srfit/structure/sgconstraints.py b/src/diffpy/srfit/structure/sgconstraints.py index 4f64eb3f..7e8b2011 100644 --- a/src/diffpy/srfit/structure/sgconstraints.py +++ b/src/diffpy/srfit/structure/sgconstraints.py @@ -17,17 +17,25 @@ import re + import numpy -from diffpy.srfit.fitbase.recipeorganizer import RecipeContainer from diffpy.srfit.fitbase.parameter import ParameterProxy +from diffpy.srfit.fitbase.recipeorganizer import RecipeContainer __all__ = ["constrainAsSpaceGroup"] -def constrainAsSpaceGroup(phase, spacegroup, scatterers = None, - sgoffset = [0, 0, 0], constrainlat = True, constrainadps = True, - adpsymbols = None, isosymbol = "Uiso"): +def constrainAsSpaceGroup( + phase, + spacegroup, + scatterers=None, + sgoffset=[0, 0, 0], + constrainlat=True, + constrainadps=True, + adpsymbols=None, + isosymbol="Uiso", +): """Constrain the structure to the space group. This applies space group constraints to a StructureParSet with P1 @@ -85,14 +93,23 @@ def constrainAsSpaceGroup(phase, spacegroup, scatterers = None, sg = spacegroup if not isinstance(spacegroup, SpaceGroup): sg = GetSpaceGroup(spacegroup) - sgp = _constrainAsSpaceGroup(phase, sg, scatterers, sgoffset, - constrainlat, constrainadps, adpsymbols, isosymbol) + sgp = _constrainAsSpaceGroup( + phase, sg, scatterers, sgoffset, constrainlat, constrainadps, adpsymbols, isosymbol + ) return sgp -def _constrainAsSpaceGroup(phase, sg, scatterers = None, - sgoffset = [0, 0, 0], constrainlat = True, constrainadps = True, - adpsymbols = None, isosymbol = "Uiso"): + +def _constrainAsSpaceGroup( + phase, + sg, + scatterers=None, + sgoffset=[0, 0, 0], + constrainlat=True, + constrainadps=True, + adpsymbols=None, + isosymbol="Uiso", +): """Restricted interface to constrainAsSpaceGroup. Arguments: As constrainAsSpaceGroup, except @@ -107,13 +124,14 @@ def _constrainAsSpaceGroup(phase, sg, scatterers = None, if adpsymbols is None: adpsymbols = stdUsymbols - sgp = SpaceGroupParameters(phase, sg, scatterers, sgoffset, - constrainlat, constrainadps, adpsymbols, isosymbol) + sgp = SpaceGroupParameters(phase, sg, scatterers, sgoffset, constrainlat, constrainadps, adpsymbols, isosymbol) return sgp + # End constrainAsSpaceGroup + class BaseSpaceGroupParameters(RecipeContainer): """Base class for holding space group Parameters. @@ -128,7 +146,7 @@ class is to make it easy to access the free variables of a structure for """ - def __init__(self, name = "sgpars"): + def __init__(self, name="sgpars"): """Create the BaseSpaceGroupParameters object. This initializes the attributes. @@ -137,7 +155,7 @@ def __init__(self, name = "sgpars"): RecipeContainer.__init__(self, name) return - def addParameter(self, par, check = True): + def addParameter(self, par, check=True): """Store a Parameter. par -- The Parameter to be stored. @@ -151,8 +169,10 @@ def addParameter(self, par, check = True): RecipeContainer._addObject(self, par, self._parameters, check) return + # End class BaseSpaceGroupParameters + class SpaceGroupParameters(BaseSpaceGroupParameters): """Class for holding and creating space group Parameters. @@ -183,8 +203,7 @@ class SpaceGroupParameters(BaseSpaceGroupParameters): """ - def __init__(self, phase, sg, scatterers, sgoffset, constrainlat, - constrainadps, adpsymbols, isosymbol): + def __init__(self, phase, sg, scatterers, sgoffset, constrainlat, constrainadps, adpsymbols, isosymbol): """Create the SpaceGroupParameters object. Arguments: @@ -226,13 +245,12 @@ def __init__(self, phase, sg, scatterers, sgoffset, constrainlat, def __iter__(self): """Iterate over top-level parameters.""" - if self._latpars is None or\ - self._xyzpars is None or\ - self._adppars is None: - self._makeConstraints() + if self._latpars is None or self._xyzpars is None or self._adppars is None: + self._makeConstraints() return RecipeContainer.__iter__(self) latpars = property(lambda self: self._getLatPars()) + def _getLatPars(self): """Accessor for _latpars.""" if self._latpars is None: @@ -240,9 +258,10 @@ def _getLatPars(self): return self._latpars xyzpars = property(lambda self: self._getXYZPars()) + def _getXYZPars(self): """Accessor for _xyzpars.""" - positions = [] + positions = [] for scatterer in self.scatterers: xyz = [scatterer.x, scatterer.y, scatterer.z] positions.append([p.value for p in xyz]) @@ -251,9 +270,10 @@ def _getXYZPars(self): return self._xyzpars adppars = property(lambda self: self._getADPPars()) + def _getADPPars(self): """Accessor for _adppars.""" - positions = [] + positions = [] for scatterer in self.scatterers: xyz = [scatterer.x, scatterer.y, scatterer.z] positions.append([p.value for p in xyz]) @@ -274,7 +294,7 @@ def _makeConstraints(self): scatterers = self.scatterers # Prepare positions - positions = [] + positions = [] for scatterer in scatterers: xyz = [scatterer.x, scatterer.y, scatterer.z] positions.append([p.value for p in xyz]) @@ -285,7 +305,6 @@ def _makeConstraints(self): return - def _clearConstraints(self): """Clear old constraints. @@ -299,7 +318,6 @@ def _clearConstraints(self): # Clear xyz for scatterer in scatterers: - for par in [scatterer.x, scatterer.y, scatterer.z]: if scatterer.isConstrained(par): scatterer.unconstrain(par) @@ -307,16 +325,14 @@ def _clearConstraints(self): # Clear the lattice if self.constrainlat: - lattice = phase.getLattice() - latpars = [lattice.a, lattice.b, lattice.c, lattice.alpha, - lattice.beta, lattice.gamma] + latpars = [lattice.a, lattice.b, lattice.c, lattice.alpha, lattice.beta, lattice.gamma] for par in latpars: if lattice.isConstrained(par): lattice.unconstrain(par) par.setConst(False) - ## Clear ADPs + # Clear ADPs if self.constrainadps: for scatterer in scatterers: if isosymbol: @@ -338,7 +354,8 @@ def _clearConstraints(self): def _constrainLattice(self): """Constrain the lattice parameters.""" - if not self.constrainlat: return + if not self.constrainlat: + return phase = self.phase sg = self.sg @@ -354,8 +371,7 @@ def _constrainLattice(self): # Now get the unconstrained, non-constant lattice pars and store them. self._latpars = BaseSpaceGroupParameters("latpars") - latpars = [lattice.a, lattice.b, lattice.c, lattice.alpha, - lattice.beta, lattice.gamma] + latpars = [lattice.a, lattice.b, lattice.c, lattice.alpha, lattice.beta, lattice.gamma] pars = [p for p in latpars if not p.const and not p.constrained] for par in pars: # FIXME - the original parameter will still appear as @@ -385,9 +401,9 @@ def _constrainXYZs(self, positions): self._xyzpars = BaseSpaceGroupParameters("xyzpars") # Make proxies to the free xyz parameters - xyznames = [name[:1]+"_"+name[1:] for name, val in g.pospars] + xyznames = [name[:1] + "_" + name[1:] for name, val in g.pospars] for pname in xyznames: - name, idx = pname.rsplit('_', 1) + name, idx = pname.rsplit("_", 1) idx = int(idx) par = scatterers[idx].get(name) newpar = self.__addPar(pname, par) @@ -400,8 +416,7 @@ def _constrainXYZs(self, positions): # Extract the constraint equation from the formula for parname, formula in fp.items(): - _makeconstraint(parname, formula, scatterer, idx, - self._parameters) + _makeconstraint(parname, formula, scatterer, idx, self._parameters) return @@ -412,10 +427,10 @@ def _constrainADPs(self, positions): """ - from diffpy.structure.symmetryutilities import stdUsymbols - from diffpy.structure.symmetryutilities import SymmetryConstraints + from diffpy.structure.symmetryutilities import SymmetryConstraints, stdUsymbols - if not self.constrainadps: return + if not self.constrainadps: + return sg = self.sg sgoffset = self.sgoffset @@ -431,17 +446,16 @@ def _constrainADPs(self, positions): nonadps = [] Uijs = [] for sidx, scatterer in enumerate(scatterers): - pars = [scatterer.get(symb) for symb in adpsymbols] if None in pars: nonadps.append(sidx) continue - Uij = numpy.zeros((3,3), dtype=float) + Uij = numpy.zeros((3, 3), dtype=float) for idx, par in enumerate(pars): i, j = _idxtoij[idx] - Uij[i,j] = Uij[j,i] = par.getValue() + Uij[i, j] = Uij[j, i] = par.getValue() Uijs.append(Uij) @@ -461,7 +475,7 @@ def _constrainADPs(self, positions): isoidx = [] isonames = [] for pname in adpnames: - name, idx = pname.rsplit('_', 1) + name, idx = pname.rsplit("_", 1) idx = int(idx) # Check for isotropic ADPs scatterer = scatterers[idx] @@ -482,24 +496,24 @@ def _constrainADPs(self, positions): # Constrain dependent isotropics for idx, isoname in zip(isoidx[:], isonames): for j in g.coremap[idx]: - if j == idx: continue + if j == idx: + continue isoidx.append(j) scatterer = scatterers[j] - scatterer.constrain(isosymbol, isoname, ns = self._parameters) + scatterer.constrain(isosymbol, isoname, ns=self._parameters) fadp = g.UFormulas(adpnames) # Constrain dependent anisotropics. We use the fact that an # anisotropic cannot be dependent on an isotropic. for idx, tmp in enumerate(zip(scatterers, fadp)): - if idx in isoidx: continue + if idx in isoidx: + continue scatterer, fa = tmp # Extract the constraint equation from the formula for stdparname, formula in fa.items(): pname = adpmap[stdparname] - _makeconstraint(pname, formula, scatterer, idx, - self._parameters) - + _makeconstraint(pname, formula, scatterer, idx, self._parameters) def __addPar(self, parname, par): """Constrain a parameter via proxy with a specified name @@ -512,18 +526,19 @@ def __addPar(self, parname, par): self.addParameter(newpar) return newpar + # End class SpaceGroupParameters # crystal system rules # ref: Benjamin, W. A., Introduction to crystallography, # New York (1969), p.60 -def _constrainTriclinic(lattice): - """Make constraints for Triclinic systems. - """ +def _constrainTriclinic(lattice): + """Make constraints for Triclinic systems.""" return + def _constrainMonoclinic(lattice): """Make constraints for Monoclinic systems. @@ -532,7 +547,8 @@ def _constrainMonoclinic(lattice): """ afactor = 1 - if lattice.angunits == "rad": afactor = deg2rad + if lattice.angunits == "rad": + afactor = deg2rad ang90 = 90.0 * afactor lattice.alpha.setConst(True, ang90) beta = lattice.beta.getValue() @@ -544,6 +560,7 @@ def _constrainMonoclinic(lattice): lattice.beta.setConst(True, ang90) return + def _constrainOrthorhombic(lattice): """Make constraints for Orthorhombic systems. @@ -551,13 +568,15 @@ def _constrainOrthorhombic(lattice): """ afactor = 1 - if lattice.angunits == "rad": afactor = deg2rad + if lattice.angunits == "rad": + afactor = deg2rad ang90 = 90.0 * afactor lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang90) return + def _constrainTetragonal(lattice): """Make constraints for Tetragonal systems. @@ -565,7 +584,8 @@ def _constrainTetragonal(lattice): """ afactor = 1 - if lattice.angunits == "rad": afactor = deg2rad + if lattice.angunits == "rad": + afactor = deg2rad ang90 = 90.0 * afactor lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) @@ -573,6 +593,7 @@ def _constrainTetragonal(lattice): lattice.constrain(lattice.b, lattice.a) return + def _constrainTrigonal(lattice): """Make constraints for Trigonal systems. @@ -582,7 +603,8 @@ def _constrainTrigonal(lattice): """ afactor = 1 - if lattice.angunits == "rad": afactor = deg2rad + if lattice.angunits == "rad": + afactor = deg2rad ang90 = 90.0 * afactor ang120 = 120.0 * afactor if lattice.gamma.getValue() == ang120: @@ -597,6 +619,7 @@ def _constrainTrigonal(lattice): lattice.constrain(lattice.gamma, lattice.alpha) return + def _constrainHexagonal(lattice): """Make constraints for Hexagonal systems. @@ -605,7 +628,8 @@ def _constrainHexagonal(lattice): """ afactor = 1 - if lattice.angunits == "rad": afactor = deg2rad + if lattice.angunits == "rad": + afactor = deg2rad ang90 = 90.0 * afactor ang120 = 120.0 * afactor lattice.constrain(lattice.b, lattice.a) @@ -614,6 +638,7 @@ def _constrainHexagonal(lattice): lattice.gamma.setConst(True, ang120) return + def _constrainCubic(lattice): """Make constraints for Cubic systems. @@ -621,7 +646,8 @@ def _constrainCubic(lattice): """ afactor = 1 - if lattice.angunits == "rad": afactor = deg2rad + if lattice.angunits == "rad": + afactor = deg2rad ang90 = 90.0 * afactor lattice.constrain(lattice.b, lattice.a) lattice.constrain(lattice.c, lattice.a) @@ -630,19 +656,21 @@ def _constrainCubic(lattice): lattice.gamma.setConst(True, ang90) return + # This is used to map the correct crystal system to the proper constraint # function. _constraintMap = { - "Triclinic" : _constrainTriclinic, - "Monoclinic" : _constrainMonoclinic, - "Orthorhombic" : _constrainOrthorhombic, - "Tetragonal" : _constrainTetragonal, - "Trigonal" : _constrainTrigonal, - "Hexagonal" : _constrainHexagonal, - "Cubic" : _constrainCubic + "Triclinic": _constrainTriclinic, + "Monoclinic": _constrainMonoclinic, + "Orthorhombic": _constrainOrthorhombic, + "Tetragonal": _constrainTetragonal, + "Trigonal": _constrainTrigonal, + "Hexagonal": _constrainHexagonal, + "Cubic": _constrainCubic, } -def _makeconstraint(parname, formula, scatterer, idx, ns = {}): + +def _makeconstraint(parname, formula, scatterer, idx, ns={}): """Constrain a parameter according to a formula. parname -- Name of parameter @@ -659,10 +687,10 @@ def _makeconstraint(parname, formula, scatterer, idx, ns = {}): if par is None: return - compname = "%s_%i"%(parname, idx) + compname = "%s_%i" % (parname, idx) # Check to see if this parameter is free - pat = r'%s *([+-] *\d+)?$' % compname + pat = r"%s *([+-] *\d+)?$" % compname if re.match(pat, formula): return par @@ -675,9 +703,10 @@ def _makeconstraint(parname, formula, scatterer, idx, ns = {}): # If we got here, then we have a constraint equation # Fix any division issues formula = formula.replace("/", "*1.0/") - scatterer.constrain(par, formula, ns = ns) + scatterer.constrain(par, formula, ns=ns) return + def _getFloat(formula): """Get a float from a formula string, or None if this is not possible.""" try: @@ -685,6 +714,7 @@ def _getFloat(formula): except NameError: return None + # Constants needed above _idxtoij = [(0, 0), (1, 1), (2, 2), (0, 1), (0, 2), (1, 2)] deg2rad = numpy.pi / 180 diff --git a/src/diffpy/srfit/structure/srrealparset.py b/src/diffpy/srfit/structure/srrealparset.py index e1376288..c3c505aa 100644 --- a/src/diffpy/srfit/structure/srrealparset.py +++ b/src/diffpy/srfit/structure/srrealparset.py @@ -42,7 +42,7 @@ def __init__(self, *args, **kw): self.stru = None return - def restrainBVS(self, sig = 1, scaled = False): + def restrainBVS(self, sig=1, scaled=False): """Restrain the bond-valence sum to zero. This adds a penalty to the cost function equal to @@ -70,7 +70,7 @@ def restrainBVS(self, sig = 1, scaled = False): # Return the Restraint object return res - def useSymmetry(self, use = True): + def useSymmetry(self, use=True): """Set this structure to use symmetry. This determines how the structure is treated by SrReal calculators. @@ -91,6 +91,7 @@ def _getSrRealStructure(self): """ from diffpy.srreal.structureadapter import nosymmetry + if self._usesymmetry: return self.stru return nosymmetry(self.stru) diff --git a/src/diffpy/srfit/tests/__init__.py b/src/diffpy/srfit/tests/__init__.py deleted file mode 100644 index 845b5c39..00000000 --- a/src/diffpy/srfit/tests/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srfit 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. -# -############################################################################## - -"""Unit tests for diffpy.srfit. -""" - -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.srfit 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.srfit package. - - Returns - ------- - result : `unittest.TestResult` - ''' - suite = testsuite() - runner = unittest.TextTestRunner() - result = runner.run(suite) - return result - - -# End of file diff --git a/src/diffpy/srfit/tests/debug.py b/src/diffpy/srfit/tests/debug.py deleted file mode 100644 index a71ff378..00000000 --- a/src/diffpy/srfit/tests/debug.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srfit 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.srfit.tests.debug - -Exceptions raised by failed tests or other errors are not caught. -""" - - -if __name__ == '__main__': - import sys - from diffpy.srfit.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/srfit/tests/run.py b/src/diffpy/srfit/tests/run.py deleted file mode 100644 index f1c83fa0..00000000 --- a/src/diffpy/srfit/tests/run.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srfit 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.srfit.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.srfit.tests import test - # produce zero exit code for a successful test - sys.exit(not test().wasSuccessful()) - -# End of file diff --git a/src/diffpy/srfit/tests/testparameter.py b/src/diffpy/srfit/tests/testparameter.py deleted file mode 100644 index 76eaee3b..00000000 --- a/src/diffpy/srfit/tests/testparameter.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.srfit 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. -# -############################################################################## - -"""Tests for refinableobj module.""" - -import unittest - -from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.fitbase.parameter import ParameterAdapter, ParameterProxy - - -class TestParameter(unittest.TestCase): - - def testSetValue(self): - """Test initialization.""" - l = Parameter("l") - - l.setValue(3.14) - self.assertAlmostEqual(3.14, l.getValue()) - - # Try array - import numpy - x = numpy.arange(0, 10, 0.1) - l.setValue(x) - self.assertTrue( l.getValue() is x ) - self.assertTrue( l.value is x ) - - # Change the array - y = numpy.arange(0, 10, 0.5) - l.value = y - self.assertTrue( l.getValue() is y ) - self.assertTrue( l.value is y ) - - # Back to scalar - l.setValue(1.01) - self.assertAlmostEqual(1.01, l.getValue()) - self.assertAlmostEqual(1.01, l.value) - return - -class TestParameterProxy(unittest.TestCase): - - def testProxy(self): - """Test the ParameterProxy class.""" - l = Parameter("l", 3.14) - - # Try Accessor adaptation - la = ParameterProxy("l2", l) - - self.assertEqual("l2", la.name) - self.assertEqual(l.getValue(), la.getValue()) - - # Change the parameter - l.value = 2.3 - self.assertEqual(l.getValue(), la.getValue()) - self.assertEqual(l.value, la.value) - - # Change the proxy - la.value = 3.2 - self.assertEqual(l.getValue(), la.getValue()) - self.assertEqual(l.value, la.value) - - return - -class TestParameterAdapter(unittest.TestCase): - - def testWrapper(self): - """Test the adapter. - - This adapts a Parameter to the Parameter interface. :) - """ - l = Parameter("l", 3.14) - - # Try Accessor adaptation - la = ParameterAdapter("l", l, getter = Parameter.getValue, setter = - Parameter.setValue) - - self.assertEqual(l.name, la.name) - self.assertEqual(l.getValue(), la.getValue()) - - # Change the parameter - l.setValue(2.3) - self.assertEqual(l.getValue(), la.getValue()) - - # Change the adapter - la.setValue(3.2) - self.assertEqual(l.getValue(), la.getValue()) - - # Try Attribute adaptation - la = ParameterAdapter("l", l, attr = "value") - - self.assertEqual(l.name, la.name) - self.assertEqual("value", la.attr) - self.assertEqual(l.getValue(), la.getValue()) - - # Change the parameter - l.setValue(2.3) - self.assertEqual(l.getValue(), la.getValue()) - - # Change the adapter - la.setValue(3.2) - self.assertEqual(l.getValue(), la.getValue()) - - return - - -if __name__ == "__main__": - unittest.main() diff --git a/src/diffpy/srfit/util/__init__.py b/src/diffpy/srfit/util/__init__.py index dd586190..c26dd72e 100644 --- a/src/diffpy/srfit/util/__init__.py +++ b/src/diffpy/srfit/util/__init__.py @@ -17,7 +17,7 @@ Utilities and constants used throughout SrFit. """ -_DASHEDLINE = 78 * '-' +_DASHEDLINE = 78 * "-" def sortKeyForNumericString(s): @@ -41,12 +41,13 @@ def sortKeyForNumericString(s): """ if sortKeyForNumericString._rx is None: import re - sortKeyForNumericString._rx = re.compile(r'(\d+)') + + sortKeyForNumericString._rx = re.compile(r"(\d+)") rx = sortKeyForNumericString._rx - rv = tuple((int(w) if i % 2 else w) - for i, w in enumerate(rx.split(s))) + rv = tuple((int(w) if i % 2 else w) for i, w in enumerate(rx.split(s))) return rv + sortKeyForNumericString._rx = None # End of file diff --git a/src/diffpy/srfit/util/argbinders.py b/src/diffpy/srfit/util/argbinders.py index a49aec02..c2934dde 100644 --- a/src/diffpy/srfit/util/argbinders.py +++ b/src/diffpy/srfit/util/argbinders.py @@ -19,17 +19,14 @@ class bind2nd(object): - - '''Freeze second argument of a callable object to a given constant. - ''' + """Freeze second argument of a callable object to a given constant.""" def __init__(self, func, arg1): - """Freeze the second argument of function func to arg1. - """ + """Freeze the second argument of function func to arg1.""" self.func = func self.arg1 = arg1 return def __call__(self, *args, **kwargs): - boundargs = ((args[0], self.arg1) + args[1:]) + boundargs = (args[0], self.arg1) + args[1:] return self.func(*boundargs, **kwargs) diff --git a/src/diffpy/srfit/util/inpututils.py b/src/diffpy/srfit/util/inpututils.py index e2b06988..aba08b1f 100644 --- a/src/diffpy/srfit/util/inpututils.py +++ b/src/diffpy/srfit/util/inpututils.py @@ -41,11 +41,12 @@ def inputToString(inpt): # TODO remove handling of string input accept only file or filename # FIXME check for typos in the file name elif os.path.exists(inpt) or (len(inpt) < 80 and inpt.count("\n") == 0): - with open(inpt, 'r') as infile: + with open(inpt, "r") as infile: inptstr = infile.read() else: inptstr = inpt return inptstr + # End of file diff --git a/src/diffpy/srfit/util/nameutils.py b/src/diffpy/srfit/util/nameutils.py index ddfe27a2..4f6cf326 100644 --- a/src/diffpy/srfit/util/nameutils.py +++ b/src/diffpy/srfit/util/nameutils.py @@ -19,7 +19,7 @@ import re -reident = re.compile(r'^[a-zA-Z_]\w*$') +reident = re.compile(r"^[a-zA-Z_]\w*$") def isIdentifier(s): @@ -27,7 +27,8 @@ def isIdentifier(s): From http://code.activestate.com/recipes/413487/ """ - if reident.match(s) is None: return False + if reident.match(s) is None: + return False return True @@ -38,7 +39,8 @@ def validateName(name): """ # Check that the name is valid if not isIdentifier(name): - raise ValueError("Name '%s' is not a valid identifier"%name) + raise ValueError("Name '%s' is not a valid identifier" % name) return + # End of file diff --git a/src/diffpy/srfit/util/observable.py b/src/diffpy/srfit/util/observable.py index 9bb88024..dc671409 100644 --- a/src/diffpy/srfit/util/observable.py +++ b/src/diffpy/srfit/util/observable.py @@ -39,7 +39,6 @@ class Observable(object): """ - def notify(self, other=()): """ Notify all observers @@ -51,7 +50,6 @@ def notify(self, other=()): callable(semaphors) return - # callback management def addObserver(self, callable): """ @@ -61,7 +59,6 @@ def addObserver(self, callable): self._observers.add(f) return - def removeObserver(self, callable): """ Remove callable from the set of observers @@ -70,7 +67,6 @@ def removeObserver(self, callable): self._observers.remove(f) return - def hasObserver(self, callable): """ True if `callable` is present in the set of observers. @@ -79,17 +75,18 @@ def hasObserver(self, callable): rv = f in self._observers return rv - # meta methods def __init__(self, **kwds): super(Observable, self).__init__(**kwds) self._observers = set() return + # end of class Observable # Local helpers -------------------------------------------------------------- + def _fbRemoveObserver(fobs, semaphors): # Remove WeakBoundMethod `fobs` from the observers of notifying object. # This is called from Observable.notify when the WeakBoundMethod @@ -98,4 +95,5 @@ def _fbRemoveObserver(fobs, semaphors): observable.removeObserver(fobs) return + # end of file diff --git a/src/diffpy/srfit/util/tagmanager.py b/src/diffpy/srfit/util/tagmanager.py index 585ca336..b702722d 100644 --- a/src/diffpy/srfit/util/tagmanager.py +++ b/src/diffpy/srfit/util/tagmanager.py @@ -42,12 +42,10 @@ def __init__(self): self.silent = True return - def alltags(self): """Get all tags managed by the TagManager.""" return self._tagdict.keys() - def tag(self, obj, *tags): """Tag an object. @@ -64,7 +62,6 @@ def tag(self, obj, *tags): oset.add(obj) return - def untag(self, obj, *tags): """Remove tags from an object. @@ -87,7 +84,6 @@ def untag(self, obj, *tags): return - def tags(self, obj): """Get all tags on an object. @@ -96,7 +92,6 @@ def tags(self, obj): tags = [k for (k, v) in self._tagdict.items() if obj in v] return tags - def hasTags(self, obj, *tags): """Determine if an object has all passed tags. @@ -106,7 +101,6 @@ def hasTags(self, obj, *tags): result = all(obj in s for s in setgen) return result - def union(self, *tags): """Get all objects that have any of the passed tags. @@ -119,7 +113,6 @@ def union(self, *tags): objs = functools.reduce(set.union, setgen) return objs - def intersection(self, *tags): """Get all objects that have all of the passed tags. @@ -131,7 +124,6 @@ def intersection(self, *tags): objs = functools.reduce(set.intersection, setgen) return objs - def verifyTags(self, *tags): """Check that tags are all extant. @@ -146,7 +138,6 @@ def verifyTags(self, *tags): return True - def __getObjectSet(self, tag): """Helper function for getting an object set with given tag. diff --git a/src/diffpy/srfit/util/weakrefcallable.py b/src/diffpy/srfit/util/weakrefcallable.py index 7afac822..92ead114 100644 --- a/src/diffpy/srfit/util/weakrefcallable.py +++ b/src/diffpy/srfit/util/weakrefcallable.py @@ -18,8 +18,8 @@ """ -import weakref import types +import weakref import six @@ -49,7 +49,7 @@ class WeakBoundMethod(object): This is only used for pickling. """ - __slots__ = ('function', 'fallback', '_wref', '_class') + __slots__ = ("function", "fallback", "_wref", "_class") def __init__(self, f, fallback=None): """Create a weak reference wrapper to bound method. @@ -72,7 +72,6 @@ def __init__(self, f, fallback=None): self._wref = weakref.ref(f.__self__) return - def __call__(self, *args, **kwargs): """Call the wrapped method if the weak-referenced object is alive. @@ -98,28 +97,24 @@ def __call__(self, *args, **kwargs): emsg = "Object bound to {} does not exist.".format(self.function) raise ReferenceError(emsg) - # support use of this class in hashed collections def __hash__(self): return hash((self.function, self._wref)) - def __eq__(self, other): - rv = (self.function == other.function and - (self._wref == other._wref or - None is self._wref() is other._wref())) + rv = self.function == other.function and ( + self._wref == other._wref or None is self._wref() is other._wref() + ) return rv - def __ne__(self, other): return not self.__eq__(other) # support pickling of this type def __getstate__(self): - """Return state with a resolved weak reference. - """ + """Return state with a resolved weak reference.""" mobj = self._wref() nm = self.function.__name__ amsg = "Unable to pickle this unbound function by name." @@ -130,10 +125,8 @@ def __getstate__(self): state = (self._class, nm, self.fallback, mobj) return state - def __setstate__(self, state): - """Restore the weak reference in this wrapper upon unpickling. - """ + """Restore the weak reference in this wrapper upon unpickling.""" (self._class, nm, self.fallback, mobj) = state self.function = getattr(self._class, nm) if mobj is None: @@ -144,15 +137,16 @@ def __setstate__(self, state): self._wref = weakref.ref(mobj) return - @staticmethod def __mimic_empty_ref(): return None + # end of class WeakBoundMethod # ---------------------------------------------------------------------------- + def weak_ref(f, fallback=None): """Create weak-reference wrapper to a bound method. diff --git a/src/diffpy/srfit/version.py b/src/diffpy/srfit/version.py index 98c20188..e8d23296 100644 --- a/src/diffpy/srfit/version.py +++ b/src/diffpy/srfit/version.py @@ -1,55 +1,26 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.srfit 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) 2024 The Trustees of Columbia University in the City of New York. +# All rights reserved. # -# File coded by: Chris Farrow +# 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.srfit/graphs/contributors +# +# See LICENSE.rst for license information. # ############################################################################## -""" -Definition of __version__, __date__, __timestamp__, __git_commit__. - -Notes ------ -Variable `__gitsha__` is deprecated as of version 3.0. -Use `__git_commit__` instead. -""" - -__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) +"""Definition of __version__.""" -__version__ = cp['version'] -__date__ = cp['date'] -__git_commit__ = cp['commit'] -__timestamp__ = int(cp['timestamp']) +# We do not use the other three variables, but can be added back if needed. +# __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"] -# TODO remove deprecated __gitsha__ in version 3.1. -__gitsha__ = __git_commit__ +# obtain version information +from importlib.metadata import version -del cp, fcfg, fp, kwords +__version__ = version("diffpy.srfit") # End of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..e3b63139 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,19 @@ +import json +from pathlib import Path + +import pytest + + +@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 diff --git a/src/diffpy/srfit/tests/testbuilder.py b/tests/test_builder.py similarity index 81% rename from src/diffpy/srfit/tests/testbuilder.py rename to tests/test_builder.py index 15a7ae84..60ac3d52 100644 --- a/src/diffpy/srfit/tests/testbuilder.py +++ b/tests/test_builder.py @@ -18,17 +18,14 @@ import unittest import numpy +from utils import _makeArgs, noObserversInGlobalBuilders import diffpy.srfit.equation.builder as builder import diffpy.srfit.equation.literals as literals -from diffpy.srfit.tests.utils import _makeArgs -from diffpy.srfit.tests.utils import noObserversInGlobalBuilders class TestBuilder(unittest.TestCase): - def testRegisterArg(self): - factory = builder.EquationFactory() v1 = _makeArgs(1)[0] @@ -52,7 +49,6 @@ def testRegisterArg(self): self.assertTrue(noObserversInGlobalBuilders()) return - def testRegisterOperator(self): """Try to use an operator without arguments in an equation.""" @@ -86,13 +82,12 @@ def testRegisterOperator(self): self.assertTrue(noObserversInGlobalBuilders()) return - def testSwapping(self): - def g1(v1, v2, v3, v4): return (v1 + v2) * (v3 + v4) + def g2(v1): - return 0.5*v1 + return 0.5 * v1 factory = builder.EquationFactory() v1, v2, v3, v4, v5 = _makeArgs(5) @@ -148,8 +143,7 @@ def g2(v1): return def testParseEquation(self): - - from numpy import sin, divide, sqrt, array_equal, e + from numpy import array_equal, divide, e, sin, sqrt factory = builder.EquationFactory() @@ -163,8 +157,8 @@ def testParseEquation(self): eq.x.setValue(x) eq.B.setValue(B) eq.C.setValue(C) - f = lambda A, x, B, C: A*sin(0.5*x)+divide(B,C) - self.assertTrue(array_equal(eq(), f(A,x,B,C))) + f = lambda A, x, B, C: A * sin(0.5 * x) + divide(B, C) # noqa: E731 + self.assertTrue(array_equal(eq(), f(A, x, B, C))) # Make sure that the arguments of eq are listed in the order in which # they appear in the equations. @@ -176,8 +170,8 @@ def testParseEquation(self): sigma = 0.1 eq.x.setValue(x) eq.sigma.setValue(sigma) - f = lambda x, sigma : sqrt(e**(-0.5*(x/sigma)**2)) - self.assertTrue(numpy.allclose(eq(), f(x,sigma))) + f = lambda x, sigma: sqrt(e ** (-0.5 * (x / sigma) ** 2)) # noqa: E731 + self.assertTrue(numpy.allclose(eq(), f(x, sigma))) self.assertEqual(eq.args, [eq.x, eq.sigma]) @@ -186,14 +180,14 @@ def testParseEquation(self): eq = factory.makeEquation("sqrt(e**(-0.5*(x/sigma)**2))") self.assertTrue("sigma" in eq.argdict) self.assertTrue("x" not in eq.argdict) - self.assertTrue(numpy.allclose(eq(sigma=sigma), f(x,sigma))) + self.assertTrue(numpy.allclose(eq(sigma=sigma), f(x, sigma))) self.assertEqual(eq.args, [eq.sigma]) # Equation with user-defined functions factory.registerFunction("myfunc", eq, ["sigma"]) eq2 = factory.makeEquation("c*myfunc(sigma)") - self.assertTrue(numpy.allclose(eq2(c=2, sigma=sigma), 2*f(x,sigma))) + self.assertTrue(numpy.allclose(eq2(c=2, sigma=sigma), 2 * f(x, sigma))) self.assertTrue("sigma" in eq2.argdict) self.assertTrue("c" in eq2.argdict) self.assertEqual(eq2.args, [eq2.c, eq2.sigma]) @@ -201,35 +195,31 @@ def testParseEquation(self): self.assertTrue(noObserversInGlobalBuilders()) return - def test_parse_constant(self): - """Verify parsing of constant numeric expressions. - """ + """Verify parsing of constant numeric expressions.""" factory = builder.EquationFactory() - eq = factory.makeEquation('3.12 + 2') + eq = factory.makeEquation("3.12 + 2") self.assertTrue(isinstance(eq, builder.Equation)) self.assertEqual(set(), factory.equations) self.assertEqual(5.12, eq()) self.assertRaises(ValueError, eq, 3) return - def testBuildEquation(self): - from numpy import array_equal # simple equation sin = builder.getBuilder("sin") - a = builder.ArgumentBuilder(name="a", value = 1) - A = builder.ArgumentBuilder(name="A", value = 2) + a = builder.ArgumentBuilder(name="a", value=1) + A = builder.ArgumentBuilder(name="A", value=2) x = numpy.arange(0, numpy.pi, 0.1) - beq = A*sin(a*x) + beq = A * sin(a * x) eq = beq.getEquation() self.assertTrue("a" in eq.argdict) self.assertTrue("A" in eq.argdict) - self.assertTrue(array_equal(eq(), 2*numpy.sin(x))) + self.assertTrue(array_equal(eq(), 2 * numpy.sin(x))) self.assertEqual(eq.args, [eq.A, eq.a]) @@ -238,13 +228,13 @@ def testBuildEquation(self): # custom function def _f(a, b): - return (a-b)*1.0/(a+b) + return (a - b) * 1.0 / (a + b) f = builder.wrapFunction("f", _f, 2, 1) - a = builder.ArgumentBuilder(name="a", value = 2) - b = builder.ArgumentBuilder(name="b", value = 1) + a = builder.ArgumentBuilder(name="a", value=2) + b = builder.ArgumentBuilder(name="b", value=1) - beq = sin(f(a,b)) + beq = sin(f(a, b)) eq = beq.getEquation() self.assertEqual(eq(), numpy.sin(_f(2, 1))) @@ -252,32 +242,32 @@ def _f(a, b): sqrt = builder.getBuilder("sqrt") e = numpy.e _x = numpy.arange(0, 1, 0.05) - x = builder.ArgumentBuilder(name="x", value = _x, const = True) - sigma = builder.ArgumentBuilder(name="sigma", value = 0.1) - beq = sqrt(e**(-0.5*(x/sigma)**2)) + x = builder.ArgumentBuilder(name="x", value=_x, const=True) + sigma = builder.ArgumentBuilder(name="sigma", value=0.1) + beq = sqrt(e ** (-0.5 * (x / sigma) ** 2)) eq = beq.getEquation() - f = lambda x, sigma : sqrt(e**(-0.5*(x/sigma)**2)) - self.assertTrue(numpy.allclose(eq(), numpy.sqrt(e**(-0.5*(_x/0.1)**2)))) + f = lambda x, sigma: sqrt(e ** (-0.5 * (x / sigma) ** 2)) # noqa: E731 + self.assertTrue(numpy.allclose(eq(), numpy.sqrt(e ** (-0.5 * (_x / 0.1) ** 2)))) # Equation with Equation - A = builder.ArgumentBuilder(name="A", value = 2) - B = builder.ArgumentBuilder(name="B", value = 4) + A = builder.ArgumentBuilder(name="A", value=2) + B = builder.ArgumentBuilder(name="B", value=4) beq = A + B eq = beq.getEquation() E = builder.wrapOperator("eq", eq) - eq2 = (2*E).getEquation() + eq2 = (2 * E).getEquation() # Make sure these evaulate to the same thing self.assertEqual(eq.args, [A.literal, B.literal]) - self.assertEqual(2*eq(), eq2()) + self.assertEqual(2 * eq(), eq2()) # Pass new arguments to the equation - C = builder.ArgumentBuilder(name="C", value = 5) - D = builder.ArgumentBuilder(name="D", value = 6) - eq3 = (E(C, D)+1).getEquation() + C = builder.ArgumentBuilder(name="C", value=5) + D = builder.ArgumentBuilder(name="D", value=6) + eq3 = (E(C, D) + 1).getEquation() self.assertEqual(12, eq3()) # Pass old and new arguments to the equation # If things work right, A has been given the value of C in the last # evaluation (5) - eq4 = (3*E(A, D)-1).getEquation() + eq4 = (3 * E(A, D) - 1).getEquation() self.assertEqual(32, eq4()) # Try to pass the wrong number of arguments self.assertRaises(ValueError, E, A) diff --git a/src/diffpy/srfit/tests/testcharacteristicfunctions.py b/tests/test_characteristicfunctions.py similarity index 80% rename from src/diffpy/srfit/tests/testcharacteristicfunctions.py rename to tests/test_characteristicfunctions.py index e619f4fc..ba9dd198 100644 --- a/src/diffpy/srfit/tests/testcharacteristicfunctions.py +++ b/tests/test_characteristicfunctions.py @@ -18,8 +18,8 @@ import unittest import numpy +from utils import _msg_nosas, has_sas -from diffpy.srfit.tests.utils import has_sas, _msg_nosas from diffpy.srfit.sas.sasimport import sasimport # Global variables to be assigned in setUp @@ -27,44 +27,43 @@ # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_sas, _msg_nosas) class TestSASCF(unittest.TestCase): - def setUp(self): global cf import diffpy.srfit.pdf.characteristicfunctions as cf - return + return def testSphere(self): radius = 25 # Calculate sphere cf from SphereModel - SphereModel = sasimport('sas.models.SphereModel').SphereModel + SphereModel = sasimport("sas.models.SphereModel").SphereModel model = SphereModel() model.setParam("radius", radius) ff = cf.SASCF("sphere", model) - r = numpy.arange(1, 60, 0.1, dtype = float) + r = numpy.arange(1, 60, 0.1, dtype=float) fr1 = ff(r) # Calculate sphere cf analytically - fr2 = cf.sphericalCF(r, 2*radius) + fr2 = cf.sphericalCF(r, 2 * radius) diff = fr1 - fr2 res = numpy.dot(diff, diff) res /= numpy.dot(fr2, fr2) self.assertAlmostEqual(0, res, 4) return - def testSpheroid(self): prad = 20.9 erad = 33.114 # Calculate cf from EllipsoidModel - EllipsoidModel = sasimport('sas.models.EllipsoidModel').EllipsoidModel + EllipsoidModel = sasimport("sas.models.EllipsoidModel").EllipsoidModel model = EllipsoidModel() model.setParam("radius_a", prad) model.setParam("radius_b", erad) ff = cf.SASCF("spheroid", model) - r = numpy.arange(0, 100, 1/numpy.pi, dtype = float) + r = numpy.arange(0, 100, 1 / numpy.pi, dtype=float) fr1 = ff(r) # Calculate cf analytically @@ -75,17 +74,16 @@ def testSpheroid(self): self.assertAlmostEqual(0, res, 4) return - def testShell(self): radius = 19.2 thickness = 7.8 # Calculate cf from VesicleModel - VesicleModel = sasimport('sas.models.VesicleModel').VesicleModel + VesicleModel = sasimport("sas.models.VesicleModel").VesicleModel model = VesicleModel() model.setParam("radius", radius) model.setParam("thickness", thickness) ff = cf.SASCF("vesicle", model) - r = numpy.arange(0, 99.45, 0.1, dtype = float) + r = numpy.arange(0, 99.45, 0.1, dtype=float) fr1 = ff(r) # Calculate sphere cf analytically @@ -96,23 +94,22 @@ def testShell(self): self.assertAlmostEqual(0, res, 4) return - def testCylinder(self): """Make sure cylinder works over different r-ranges""" radius = 100 length = 30 - CylinderModel = sasimport('sas.models.CylinderModel').CylinderModel + CylinderModel = sasimport("sas.models.CylinderModel").CylinderModel model = CylinderModel() model.setParam("radius", radius) model.setParam("length", length) ff = cf.SASCF("cylinder", model) - r1 = numpy.arange(0, 10, 0.1, dtype = float) - r2 = numpy.arange(0, 50, 0.1, dtype = float) - r3 = numpy.arange(0, 100, 0.1, dtype = float) - r4 = numpy.arange(0, 500, 0.1, dtype = float) + r1 = numpy.arange(0, 10, 0.1, dtype=float) + r2 = numpy.arange(0, 50, 0.1, dtype=float) + r3 = numpy.arange(0, 100, 0.1, dtype=float) + r4 = numpy.arange(0, 500, 0.1, dtype=float) fr1 = ff(r1) fr2 = ff(r2) @@ -120,36 +117,37 @@ def testCylinder(self): fr4 = ff(r4) d = fr1 - numpy.interp(r1, r2, fr2) - res12 = numpy.dot(d,d) + res12 = numpy.dot(d, d) res12 /= numpy.dot(fr1, fr1) self.assertAlmostEqual(0, res12, 4) d = fr1 - numpy.interp(r1, r3, fr3) - res13 = numpy.dot(d,d) + res13 = numpy.dot(d, d) res13 /= numpy.dot(fr1, fr1) self.assertAlmostEqual(0, res13, 4) d = fr1 - numpy.interp(r1, r4, fr4) - res14 = numpy.dot(d,d) + res14 = numpy.dot(d, d) res14 /= numpy.dot(fr1, fr1) self.assertAlmostEqual(0, res14, 4) d = fr2 - numpy.interp(r2, r3, fr3) - res23 = numpy.dot(d,d) + res23 = numpy.dot(d, d) res23 /= numpy.dot(fr2, fr2) self.assertAlmostEqual(0, res23, 4) d = fr2 - numpy.interp(r2, r4, fr4) - res24 = numpy.dot(d,d) + res24 = numpy.dot(d, d) res24 /= numpy.dot(fr2, fr2) self.assertAlmostEqual(0, res24, 4) d = fr3 - numpy.interp(r3, r4, fr4) - res34 = numpy.dot(d,d) + res34 = numpy.dot(d, d) res34 /= numpy.dot(fr3, fr3) self.assertAlmostEqual(0, res34, 4) return + # End of class TestSASCF if __name__ == "__main__": diff --git a/src/diffpy/srfit/tests/testconstraint.py b/tests/test_constraint.py similarity index 99% rename from src/diffpy/srfit/tests/testconstraint.py rename to tests/test_constraint.py index c2501f14..8c85d7ec 100644 --- a/src/diffpy/srfit/tests/testconstraint.py +++ b/tests/test_constraint.py @@ -17,14 +17,13 @@ import unittest +from diffpy.srfit.equation.builder import EquationFactory from diffpy.srfit.fitbase.constraint import Constraint -from diffpy.srfit.fitbase.recipeorganizer import equationFromString from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.equation.builder import EquationFactory +from diffpy.srfit.fitbase.recipeorganizer import equationFromString class TestConstraint(unittest.TestCase): - def testConstraint(self): """Test the Constraint class.""" diff --git a/src/diffpy/srfit/tests/testcontribution.py b/tests/test_contribution.py similarity index 85% rename from src/diffpy/srfit/tests/testcontribution.py rename to tests/test_contribution.py index c28e9958..9ca8f131 100644 --- a/src/diffpy/srfit/tests/testcontribution.py +++ b/tests/test_contribution.py @@ -17,18 +17,17 @@ import unittest -from numpy import arange, dot, array_equal, sin +from numpy import arange, array_equal, dot, sin +from utils import noObserversInGlobalBuilders +from diffpy.srfit.exceptions import SrFitError from diffpy.srfit.fitbase.fitcontribution import FitContribution -from diffpy.srfit.fitbase.profilegenerator import ProfileGenerator -from diffpy.srfit.fitbase.profile import Profile from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.exceptions import SrFitError -from diffpy.srfit.tests.utils import noObserversInGlobalBuilders +from diffpy.srfit.fitbase.profile import Profile +from diffpy.srfit.fitbase.profilegenerator import ProfileGenerator class TestContribution(unittest.TestCase): - def setUp(self): self.gen = ProfileGenerator("test") self.profile = Profile() @@ -47,16 +46,15 @@ def testSetProfile(self): self.assertTrue(fc._eq is None) self.assertTrue(fc._reseq is None) # check type checking - fc1 = FitContribution('test1') - self.assertRaises(TypeError, fc1.setProfile, 'invalid') + fc1 = FitContribution("test1") + self.assertRaises(TypeError, fc1.setProfile, "invalid") # check if residual equation is set up when possible - fc2 = FitContribution('test2') - fc2.setEquation('A * x') + fc2 = FitContribution("test2") + fc2.setEquation("A * x") fc2.setProfile(profile) self.assertFalse(fc2._reseq is None) return - def testAddProfileGenerator(self): fc = self.fitcontribution gen = self.gen @@ -115,7 +113,7 @@ def testReplacements(self): profile = self.profile profile.setObservedProfile(xobs, yobs) xobs2 = arange(0, 10, 0.8) - yobs2 = 0.5*xobs2 + yobs2 = 0.5 * xobs2 profile2 = Profile() profile2.setObservedProfile(xobs2, yobs2) gen = self.gen @@ -145,7 +143,6 @@ def testReplacements(self): self.assertEqual(len(xobs2), len(fc.residual())) return - def testResidual(self): """Test the residual, which requires all other methods.""" fc = self.fitcontribution @@ -195,7 +192,7 @@ def testResidual(self): self.assertTrue(fc._eq._value is None) self.assertTrue(fc._reseq._value is None) xobs = arange(0, 10, 0.5) - yobs = 9*sin(xobs) + yobs = 9 * sin(xobs) profile.setObservedProfile(xobs, yobs) self.assertTrue(fc._eq._value is None) self.assertTrue(fc._reseq._value is None) @@ -207,25 +204,23 @@ def testResidual(self): fc.setEquation("2*I") fc.setResidualEquation("resv") chiv = fc.residual() - self.assertAlmostEqual(sum((2*xobs-yobs)**2)/sum(yobs**2), - dot(chiv, chiv)) + self.assertAlmostEqual(sum((2 * xobs - yobs) ** 2) / sum(yobs**2), dot(chiv, chiv)) # Make a custom residual. fc.setResidualEquation("abs(eq-y)**0.5") chiv = fc.residual() - self.assertAlmostEqual(sum(abs(2*xobs-yobs)), dot(chiv, chiv)) + self.assertAlmostEqual(sum(abs(2 * xobs - yobs)), dot(chiv, chiv)) # Test configuration checks - fc1 = FitContribution('test1') - self.assertRaises(SrFitError, fc1.setResidualEquation, 'chiv') + fc1 = FitContribution("test1") + self.assertRaises(SrFitError, fc1.setResidualEquation, "chiv") fc1.setProfile(self.profile) - self.assertRaises(SrFitError, fc1.setResidualEquation, 'chiv') - fc1.setEquation('A * x') - fc1.setResidualEquation('chiv') + self.assertRaises(SrFitError, fc1.setResidualEquation, "chiv") + fc1.setEquation("A * x") + fc1.setResidualEquation("chiv") self.assertTrue(noObserversInGlobalBuilders()) return - def test_setEquation(self): """Check replacement of removed parameters.""" fc = self.fitcontribution @@ -234,61 +229,55 @@ def test_setEquation(self): self.assertEqual(7, fc.evaluate()) fc.removeParameter(fc.x) x = arange(0, 10, 0.5) - fc.newParameter('x', x) + fc.newParameter("x", x) self.assertTrue(array_equal(5 + x, fc.evaluate())) self.assertTrue(noObserversInGlobalBuilders()) return - def test_getEquation(self): """Check getting the current profile simulation formula.""" fc = self.fitcontribution - self.assertEqual('', fc.getEquation()) + self.assertEqual("", fc.getEquation()) fc.setEquation("A * sin(x + 5)") - self.assertEqual('(A * sin((x + 5)))', fc.getEquation()) + self.assertEqual("(A * sin((x + 5)))", fc.getEquation()) self.assertTrue(noObserversInGlobalBuilders()) return - def test_getResidualEquation(self): """Check getting the current formula for residual equation.""" fc = self.fitcontribution - self.assertEqual('', fc.getResidualEquation()) + self.assertEqual("", fc.getResidualEquation()) fc.setProfile(self.profile) - fc.setEquation('A * x + B') - self.assertEqual('((eq - y) / dy)', fc.getResidualEquation()) - fc.setResidualEquation('2 * (eq - y)') - self.assertEqual('(2 * (eq - y))', fc.getResidualEquation()) + fc.setEquation("A * x + B") + self.assertEqual("((eq - y) / dy)", fc.getResidualEquation()) + fc.setResidualEquation("2 * (eq - y)") + self.assertEqual("(2 * (eq - y))", fc.getResidualEquation()) return - def test_releaseOldEquations(self): - """Ensure EquationFactory does not hold to obsolete Equations. - """ + """Ensure EquationFactory does not hold to obsolete Equations.""" fc = self.fitcontribution self.assertEqual(0, len(fc._eqfactory.equations)) for i in range(5): - fc.setEquation('A * x + B') + fc.setEquation("A * x + B") self.assertEqual(1, len(fc._eqfactory.equations)) fc.setProfile(self.profile) for i in range(5): - fc.setResidualEquation('chiv') + fc.setResidualEquation("chiv") self.assertEqual(2, len(fc._eqfactory.equations)) return - def test_registerFunction(self): - """Ensure registered function works after second setEquation call. - """ + """Ensure registered function works after second setEquation call.""" fc = self.fitcontribution - fsquare = lambda x : x**2 - fc.registerFunction(fsquare, name='fsquare') - fc.setEquation('fsquare') + fsquare = lambda x: x**2 # noqa: E731 + fc.registerFunction(fsquare, name="fsquare") + fc.setEquation("fsquare") fc.x.setValue(5) self.assertEqual(25, fc.evaluate()) fc.x << 6 self.assertEqual(36, fc.evaluate()) - fc.setEquation('fsquare + 5') + fc.setEquation("fsquare + 5") self.assertEqual(41, fc.evaluate()) fc.x << -1 self.assertEqual(6, fc.evaluate()) diff --git a/src/diffpy/srfit/tests/testdiffpyparset.py b/tests/test_diffpyparset.py similarity index 85% rename from src/diffpy/srfit/tests/testdiffpyparset.py rename to tests/test_diffpyparset.py index fb9a2990..de449837 100644 --- a/src/diffpy/srfit/tests/testdiffpyparset.py +++ b/tests/test_diffpyparset.py @@ -15,36 +15,35 @@ """Tests for diffpy.srfit.structure package.""" -import unittest import pickle +import unittest import numpy - -from diffpy.srfit.tests.utils import has_structure, _msg_nostructure +from utils import _msg_nostructure, has_structure # Global variables to be assigned in setUp Atom = Lattice = Structure = DiffpyStructureParSet = None # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_structure, _msg_nostructure) class TestParameterAdapter(unittest.TestCase): - def setUp(self): global Atom, Lattice, Structure, DiffpyStructureParSet - from diffpy.structure import Atom, Lattice, Structure from diffpy.srfit.structure.diffpyparset import DiffpyStructureParSet - return + from diffpy.structure import Atom, Lattice, Structure + return def testDiffpyStructureParSet(self): """Test the structure conversion.""" - a1 = Atom("Cu", xyz = numpy.array([.0, .1, .2]), Uisoequiv = 0.003) - a2 = Atom("Ag", xyz = numpy.array([.3, .4, .5]), Uisoequiv = 0.002) - l = Lattice(2.5, 2.5, 2.5, 90, 90, 90) + a1 = Atom("Cu", xyz=numpy.array([0.0, 0.1, 0.2]), Uisoequiv=0.003) + a2 = Atom("Ag", xyz=numpy.array([0.3, 0.4, 0.5]), Uisoequiv=0.002) + lattice = Lattice(2.5, 2.5, 2.5, 90, 90, 90) - dsstru = Structure([a1,a2], l) + dsstru = Structure([a1, a2], lattice) # Structure makes copies a1 = dsstru[0] a2 = dsstru[1] @@ -63,14 +62,14 @@ def _testAtoms(): self.assertEqual(a2.Bisoequiv, s.Ag0.Biso.getValue()) for i in range(1, 4): for j in range(i, 4): - uijstru = getattr(a1, "U%i%i"%(i,j)) - uij = getattr(s.Cu0, "U%i%i"%(i,j)).getValue() - uji = getattr(s.Cu0, "U%i%i"%(j,i)).getValue() + uijstru = getattr(a1, "U%i%i" % (i, j)) + uij = getattr(s.Cu0, "U%i%i" % (i, j)).getValue() + uji = getattr(s.Cu0, "U%i%i" % (j, i)).getValue() self.assertEqual(uijstru, uij) self.assertEqual(uijstru, uji) - bijstru = getattr(a1, "B%i%i"%(i,j)) - bij = getattr(s.Cu0, "B%i%i"%(i,j)).getValue() - bji = getattr(s.Cu0, "B%i%i"%(j,i)).getValue() + bijstru = getattr(a1, "B%i%i" % (i, j)) + bij = getattr(s.Cu0, "B%i%i" % (i, j)).getValue() + bji = getattr(s.Cu0, "B%i%i" % (j, i)).getValue() self.assertEqual(bijstru, bij) self.assertEqual(bijstru, bji) @@ -79,9 +78,7 @@ def _testAtoms(): self.assertEqual(a1.xyz[2], s.Cu0.z.getValue()) return - def _testLattice(): - # Test the lattice self.assertEqual(dsstru.lattice.a, s.lattice.a.getValue()) self.assertEqual(dsstru.lattice.b, s.lattice.b.getValue()) @@ -114,10 +111,8 @@ def _testLattice(): self.assertNotEqual(d, dsstru.lattice.dist(a1.xyz, a2.xyz)) return - def test___repr__(self): - """Test representation of DiffpyStructureParSet objects. - """ + """Test representation of DiffpyStructureParSet objects.""" lat = Lattice(3, 3, 2, 90, 90, 90) atom = Atom("C", [0, 0.2, 0.5]) stru = Structure([atom], lattice=lat) @@ -127,10 +122,8 @@ def test___repr__(self): self.assertEqual(repr(atom), repr(dsps.atoms[0])) return - def test_pickling(self): - """Test pickling of DiffpyStructureParSet. - """ + """Test pickling of DiffpyStructureParSet.""" stru = Structure([Atom("C", [0, 0.2, 0.5])]) dsps = DiffpyStructureParSet("dsps", stru) data = pickle.dumps(dsps) @@ -139,6 +132,7 @@ def test_pickling(self): self.assertEqual(0.2, dsps2.atoms[0].y.value) return + # End of class TestParameterAdapter if __name__ == "__main__": diff --git a/src/diffpy/srfit/tests/testequation.py b/tests/test_equation.py similarity index 83% rename from src/diffpy/srfit/tests/testequation.py rename to tests/test_equation.py index 34b79361..8678fe5e 100644 --- a/src/diffpy/srfit/tests/testequation.py +++ b/tests/test_equation.py @@ -17,14 +17,13 @@ import unittest +from utils import _makeArgs, noObserversInGlobalBuilders + import diffpy.srfit.equation.literals as literals from diffpy.srfit.equation import Equation -from diffpy.srfit.tests.utils import _makeArgs -from diffpy.srfit.tests.utils import noObserversInGlobalBuilders class TestEquation(unittest.TestCase): - def testSimpleFunction(self): """Test a simple function.""" @@ -74,18 +73,18 @@ def testSimpleFunction(self): self.assertTrue(v3 is eq.v3) self.assertTrue(v4 is eq.v4) - self.assertEqual(20, eq()) # 20 = 2.5*(1+3)*(4-2) - self.assertEqual(20, eq.getValue()) # same as above - self.assertEqual(20, eq.value) # same as above - self.assertEqual(25, eq(v1=2)) # 25 = 2.5*(2+3)*(4-2) - self.assertEqual(50, eq(v2=0)) # 50 = 2.5*(2+3)*(4-0) - self.assertEqual(30, eq(v3=1)) # 30 = 2.5*(2+1)*(4-0) - self.assertEqual(0, eq(v4=0)) # 20 = 2.5*(2+1)*(0-0) + self.assertEqual(20, eq()) # 20 = 2.5*(1+3)*(4-2) + self.assertEqual(20, eq.getValue()) # same as above + self.assertEqual(20, eq.value) # same as above + self.assertEqual(25, eq(v1=2)) # 25 = 2.5*(2+3)*(4-2) + self.assertEqual(50, eq(v2=0)) # 50 = 2.5*(2+3)*(4-0) + self.assertEqual(30, eq(v3=1)) # 30 = 2.5*(2+1)*(4-0) + self.assertEqual(0, eq(v4=0)) # 20 = 2.5*(2+1)*(0-0) # Try some swapping eq.swap(v4, v1) self.assertTrue(eq._value is None) - self.assertEqual(15, eq()) # 15 = 2.5*(2+1)*(2-0) + self.assertEqual(15, eq()) # 15 = 2.5*(2+1)*(2-0) args = eq.args self.assertTrue(v4 not in args) @@ -160,18 +159,18 @@ def testEmbeddedEquation(self): self.assertTrue(eq._value is None) v1.value = 1 - self.assertEqual(20, eq()) # 20 = 2.5*(1+3)*(4-2) - self.assertEqual(20, eq.getValue()) # same as above - self.assertEqual(20, eq.value) # same as above - self.assertEqual(25, eq(v1=2)) # 25 = 2.5*(2+3)*(4-2) - self.assertEqual(50, eq(v2=0)) # 50 = 2.5*(2+3)*(4-0) - self.assertEqual(30, eq(v3=1)) # 30 = 2.5*(2+1)*(4-0) - self.assertEqual(0, eq(v4=0)) # 20 = 2.5*(2+1)*(0-0) + self.assertEqual(20, eq()) # 20 = 2.5*(1+3)*(4-2) + self.assertEqual(20, eq.getValue()) # same as above + self.assertEqual(20, eq.value) # same as above + self.assertEqual(25, eq(v1=2)) # 25 = 2.5*(2+3)*(4-2) + self.assertEqual(50, eq(v2=0)) # 50 = 2.5*(2+3)*(4-0) + self.assertEqual(30, eq(v3=1)) # 30 = 2.5*(2+1)*(4-0) + self.assertEqual(0, eq(v4=0)) # 20 = 2.5*(2+1)*(0-0) # Try some swapping. eq.swap(v4, v1) self.assertTrue(eq._value is None) - self.assertEqual(15, eq()) # 15 = 2.5*(2+1)*(2-0) + self.assertEqual(15, eq()) # 15 = 2.5*(2+1)*(2-0) args = eq.args self.assertTrue(v4 not in args) diff --git a/src/diffpy/srfit/tests/testfitrecipe.py b/tests/test_fitrecipe.py similarity index 90% rename from src/diffpy/srfit/tests/testfitrecipe.py rename to tests/test_fitrecipe.py index 75bf7c28..87951666 100644 --- a/src/diffpy/srfit/tests/testfitrecipe.py +++ b/tests/test_fitrecipe.py @@ -17,17 +17,16 @@ import unittest -from numpy import linspace, array_equal, pi, sin, dot +from numpy import array_equal, dot, linspace, pi, sin +from utils import capturestdout -from diffpy.srfit.fitbase.fitrecipe import FitRecipe from diffpy.srfit.fitbase.fitcontribution import FitContribution -from diffpy.srfit.fitbase.profile import Profile +from diffpy.srfit.fitbase.fitrecipe import FitRecipe from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.tests.utils import capturestdout +from diffpy.srfit.fitbase.profile import Profile class TestFitRecipe(unittest.TestCase): - def setUp(self): self.recipe = FitRecipe("recipe") self.recipe.fithooks[0].verbose = 0 @@ -152,16 +151,15 @@ def testResidual(self): # Change the c value to 1 so that the equation evaluates as sin(x+1) x = self.profile.x - y = sin(x+1) + y = sin(x + 1) self.recipe.cont.c.setValue(1) res = self.recipe.residual() - self.assertTrue(array_equal(y-self.profile.y, res)) + self.assertTrue(array_equal(y - self.profile.y, res)) # Try some constraints # Make c = 2*A, A = Avar var = self.recipe.newVar("Avar") - self.recipe.constrain(self.fitcontribution.c, - "2*A", {"A": self.fitcontribution.A}) + self.recipe.constrain(self.fitcontribution.c, "2*A", {"A": self.fitcontribution.A}) self.assertEqual(2, self.fitcontribution.c.value) self.recipe.constrain(self.fitcontribution.A, var) self.assertEqual(1, var.getValue()) @@ -170,9 +168,9 @@ def testResidual(self): self.assertEqual(2, self.fitcontribution.c.value) # The equation should evaluate to sin(x+2) x = self.profile.x - y = sin(x+2) + y = sin(x + 2) res = self.recipe.residual() - self.assertTrue(array_equal(y-self.profile.y, res)) + self.assertTrue(array_equal(y - self.profile.y, res)) # Now try some restraints. We want c to be exactly zero. It should give # a penalty of (c-0)**2, which is 4 in this case @@ -202,9 +200,9 @@ def testResidual(self): self.fitcontribution.constrain(self.fitcontribution.c, "2*A") # This should evaluate to sin(x+2) x = self.profile.x - y = sin(x+2) + y = sin(x + 2) res = self.recipe.residual() - self.assertTrue(array_equal(y-self.profile.y, res)) + self.assertTrue(array_equal(y - self.profile.y, res)) # Add a restraint at the fitcontribution level. r1 = self.fitcontribution.restrain(self.fitcontribution.c, 0, 0, 1) @@ -212,7 +210,7 @@ def testResidual(self): # The chi2 is the same as above, plus 4 res = self.recipe.residual() x = self.profile.x - y = sin(x+2) + y = sin(x + 2) chi2 = 4 + dot(y - self.profile.y, y - self.profile.y) self.assertAlmostEqual(chi2, dot(res, res)) @@ -241,25 +239,26 @@ def testResidual(self): def testPrintFitHook(self): "check output from default PrintFitHook." self.recipe.addVar(self.fitcontribution.c) - self.recipe.restrain('c', lb=5) - pfh, = self.recipe.getFitHooks() + self.recipe.restrain("c", lb=5) + (pfh,) = self.recipe.getFitHooks() out = capturestdout(self.recipe.scalarResidual) - self.assertEqual('', out) + self.assertEqual("", out) pfh.verbose = 1 out = capturestdout(self.recipe.scalarResidual) self.assertTrue(out.strip().isdigit()) - self.assertFalse('\nRestraints:' in out) + self.assertFalse("\nRestraints:" in out) pfh.verbose = 2 out = capturestdout(self.recipe.scalarResidual) - self.assertTrue('\nResidual:' in out) - self.assertTrue('\nRestraints:' in out) - self.assertFalse('\nVariables' in out) + self.assertTrue("\nResidual:" in out) + self.assertTrue("\nRestraints:" in out) + self.assertFalse("\nVariables" in out) pfh.verbose = 3 out = capturestdout(self.recipe.scalarResidual) - self.assertTrue('\nVariables' in out) - self.assertTrue('c = ' in out) + self.assertTrue("\nVariables" in out) + self.assertTrue("c = " in out) return + # End of class TestFitRecipe # ---------------------------------------------------------------------------- diff --git a/src/diffpy/srfit/tests/testfitresults.py b/tests/test_fitresults.py similarity index 93% rename from src/diffpy/srfit/tests/testfitresults.py rename to tests/test_fitresults.py index c5d6c9cc..928b72d1 100644 --- a/src/diffpy/srfit/tests/testfitresults.py +++ b/tests/test_fitresults.py @@ -17,13 +17,13 @@ import unittest +from utils import datafile + from diffpy.srfit.fitbase.fitrecipe import FitRecipe from diffpy.srfit.fitbase.fitresults import initializeRecipe -from diffpy.srfit.tests.utils import datafile class TestInitializeRecipe(unittest.TestCase): - def setUp(self): self.recipe = recipe = FitRecipe("recipe") recipe.newVar("A", 0) @@ -33,7 +33,7 @@ def setUp(self): self.Aval = 5.77619823e-01 self.sigval = -9.22758690e-01 - self.x0val = 6.12422115e+00 + self.x0val = 6.12422115e00 return def testInitializeFromFileName(self): @@ -52,7 +52,7 @@ def testInitializeFromFileObj(self): self.assertEqual(0, recipe.A.value) self.assertEqual(0, recipe.sig.value) self.assertEqual(0, recipe.x0.value) - infile = open(self.filename, 'r') + infile = open(self.filename, "r") initializeRecipe(recipe, infile) self.assertFalse(infile.closed) infile.close() @@ -61,13 +61,12 @@ def testInitializeFromFileObj(self): self.assertAlmostEqual(self.x0val, recipe.x0.value) return - def testInitializeFromString(self): recipe = self.recipe self.assertEqual(0, recipe.A.value) self.assertEqual(0, recipe.sig.value) self.assertEqual(0, recipe.x0.value) - infile = open(self.filename, 'r') + infile = open(self.filename, "r") resstr = infile.read() infile.close() initializeRecipe(recipe, resstr) @@ -76,6 +75,6 @@ def testInitializeFromString(self): self.assertAlmostEqual(self.x0val, recipe.x0.value) return -if __name__ == "__main__": +if __name__ == "__main__": unittest.main() diff --git a/src/diffpy/srfit/tests/testliterals.py b/tests/test_literals.py similarity index 81% rename from src/diffpy/srfit/tests/testliterals.py rename to tests/test_literals.py index 7667be3e..e7f24e2d 100644 --- a/src/diffpy/srfit/tests/testliterals.py +++ b/tests/test_literals.py @@ -24,8 +24,8 @@ # ---------------------------------------------------------------------------- -class TestArgument(unittest.TestCase): +class TestArgument(unittest.TestCase): def testInit(self): """Test that everthing initializes as expected.""" a = literals.Argument() @@ -34,7 +34,6 @@ def testInit(self): self.assertTrue(None is a.name) return - def testIdentity(self): """Make sure an Argument is an Argument.""" a = literals.Argument() @@ -42,7 +41,6 @@ def testIdentity(self): self.assertTrue(isinstance(a, abcs.ArgumentABC)) return - def testValue(self): """Test value setting.""" @@ -59,16 +57,15 @@ def testValue(self): self.assertAlmostEqual(3.14, a.getValue()) return + # ---------------------------------------------------------------------------- -class TestCustomOperator(unittest.TestCase): +class TestCustomOperator(unittest.TestCase): def setUp(self): - self.op = literals.makeOperator( - name="add", symbol="+", operation=numpy.add, nin=2, nout=1) + self.op = literals.makeOperator(name="add", symbol="+", operation=numpy.add, nin=2, nout=1) return - def testInit(self): """Test that everthing initializes as expected.""" op = self.op @@ -80,7 +77,6 @@ def testInit(self): self.assertEqual([], op.args) return - def testIdentity(self): """Make sure an Argument is an Argument.""" op = self.op @@ -88,13 +84,12 @@ def testIdentity(self): self.assertTrue(isinstance(op, abcs.OperatorABC)) return - def testValue(self): """Test value.""" # Test addition and operations op = self.op - a = literals.Argument(value = 0) - b = literals.Argument(value = 0) + a = literals.Argument(value=0) + b = literals.Argument(value=0) op.addLiteral(a) op.addLiteral(b) @@ -113,21 +108,20 @@ def testValue(self): return - def testAddLiteral(self): """Test adding a literal to an operator node.""" op = self.op - self.assertRaises(ValueError, op.getValue) + self.assertRaises(TypeError, op.getValue) op._value = 1 self.assertEqual(op.getValue(), 1) # Test addition and operations - a = literals.Argument(name = "a", value = 0) - b = literals.Argument(name = "b", value = 0) + a = literals.Argument(name="a", value=0) + b = literals.Argument(name="b", value=0) op.addLiteral(a) - self.assertRaises(ValueError, op.getValue) + self.assertRaises(TypeError, op.getValue) op.addLiteral(b) self.assertAlmostEqual(0, op.value) @@ -140,23 +134,22 @@ def testAddLiteral(self): # Test for self-references # Try to add self - op1 = literals.makeOperator(name="add", symbol="+", - operation=numpy.add, nin=2, nout=1) + op1 = literals.makeOperator(name="add", symbol="+", operation=numpy.add, nin=2, nout=1) op1.addLiteral(a) self.assertRaises(ValueError, op1.addLiteral, op1) # Try to add argument that contains self - op2 = literals.makeOperator( - name="sub", symbol="-", operation=numpy.subtract, nin=2, nout=1) + op2 = literals.makeOperator(name="sub", symbol="-", operation=numpy.subtract, nin=2, nout=1) op2.addLiteral(op1) self.assertRaises(ValueError, op1.addLiteral, op2) return + # ---------------------------------------------------------------------------- -class TestConvolutionOperator(unittest.TestCase): +class TestConvolutionOperator(unittest.TestCase): def testValue(self): """Make sure the convolution operator is working properly.""" @@ -169,10 +162,10 @@ def testValue(self): mu2 = 2.5 sig2 = 0.4 - g1 = exp(-0.5*((x-mu1)/sig1)**2) - a1 = literals.Argument(name = "g1", value = g1) - g2 = exp(-0.5*((x-mu2)/sig2)**2) - a2 = literals.Argument(name = "g2", value = g2) + g1 = exp(-0.5 * ((x - mu1) / sig1) ** 2) + a1 = literals.Argument(name="g1", value=g1) + g2 = exp(-0.5 * ((x - mu2) / sig2) ** 2) + a2 = literals.Argument(name="g2", value=g2) op = literals.ConvolutionOperator() op.addLiteral(a1) @@ -181,24 +174,24 @@ def testValue(self): g3c = op.value mu3 = mu1 - sig3 = (sig1**2 + sig2**2)**0.5 - g3 = exp(-0.5*((x-mu3)/sig3)**2) - g3 *= sum(g1)/sum(g3) + sig3 = (sig1**2 + sig2**2) ** 0.5 + g3 = exp(-0.5 * ((x - mu3) / sig3) ** 2) + g3 *= sum(g1) / sum(g3) self.assertAlmostEqual(sum(g3c), sum(g3)) - self.assertAlmostEqual(0, sum((g3-g3c)**2)) + self.assertAlmostEqual(0, sum((g3 - g3c) ** 2)) return + # ---------------------------------------------------------------------------- -class TestArrayOperator(unittest.TestCase): +class TestArrayOperator(unittest.TestCase): def test_value(self): - """Check ArrayOperator.value. - """ - x = literals.Argument('x', 1.0) - y = literals.Argument('y', 2.0) - z = literals.Argument('z', 3.0) + """Check ArrayOperator.value.""" + x = literals.Argument("x", 1.0) + y = literals.Argument("y", 2.0) + z = literals.Argument("z", 3.0) # check empty array op = literals.ArrayOperator() self.assertEqual(0, len(op.value)) @@ -213,6 +206,7 @@ def test_value(self): self.assertTrue(numpy.array_equal([1, 2, 7], op.value)) return + # ---------------------------------------------------------------------------- if __name__ == "__main__": diff --git a/src/diffpy/srfit/tests/testobjcrystparset.py b/tests/test_objcrystparset.py similarity index 89% rename from src/diffpy/srfit/tests/testobjcrystparset.py rename to tests/test_objcrystparset.py index 34cc2aa8..00e81700 100644 --- a/src/diffpy/srfit/tests/testobjcrystparset.py +++ b/tests/test_objcrystparset.py @@ -18,8 +18,7 @@ import unittest import numpy - -from diffpy.srfit.tests.utils import has_pyobjcryst, _msg_nopyobjcryst +from utils import _msg_nopyobjcryst, has_pyobjcryst # Global variables to be assigned in setUp ObjCrystCrystalParSet = spacegroups = None @@ -89,6 +88,7 @@ -2.279809890 -2.580456608 -0.724000000 """ + def makeC60(): """Make a crystal containing the C60 molecule using pyobjcryst.""" pi = numpy.pi @@ -99,28 +99,31 @@ def makeC60(): c.AddScatterer(m) sp = ScatteringPowerAtom("C", "C") - sp.SetBiso(8*pi*pi*0.003) - #c.AddScatteringPower(sp) + sp.SetBiso(8 * pi * pi * 0.003) + # c.AddScatteringPower(sp) for i, l in enumerate(c60xyz.strip().splitlines()): x, y, z = map(float, l.split()) - m.AddAtom(x, y, z, sp, "C%i"%i) + m.AddAtom(x, y, z, sp, "C%i" % i) return c + # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) class TestParameterAdapter(unittest.TestCase): - def setUp(self): global ObjCrystCrystalParSet, Crystal, Atom, Molecule global ScatteringPowerAtom - from diffpy.srfit.structure.objcrystparset import ObjCrystCrystalParSet - from pyobjcryst.crystal import Crystal from pyobjcryst.atom import Atom + from pyobjcryst.crystal import Crystal from pyobjcryst.molecule import Molecule from pyobjcryst.scatteringpower import ScatteringPowerAtom + + from diffpy.srfit.structure.objcrystparset import ObjCrystCrystalParSet + self.occryst = makeC60() self.ocmol = self.occryst.GetScatterer("c60") return @@ -130,7 +133,6 @@ def tearDown(self): del self.ocmol return - def testObjCrystParSet(self): """Test the structure conversion.""" @@ -143,7 +145,6 @@ def testObjCrystParSet(self): self.assertEqual(cryst.name, "bucky") def _testCrystal(): - # Test the lattice self.assertAlmostEqual(occryst.a, cryst.a.value) self.assertAlmostEqual(occryst.b, cryst.b.getValue()) @@ -155,7 +156,6 @@ def _testCrystal(): return def _testMolecule(): - # Test position / occupancy self.assertAlmostEqual(ocmol.X, m.x.getValue()) self.assertAlmostEqual(ocmol.Y, m.y.getValue()) @@ -181,11 +181,10 @@ def _testMolecule(): self.assertAlmostEqual(ocsp.Biso, a.Biso.getValue()) return - _testCrystal() _testMolecule() - ## Now change some values from ObjCryst + # Now change some values from ObjCryst ocmol[0].X *= 1.1 ocmol[0].Occupancy *= 1.1 ocmol[0].GetScatteringPower().Biso *= 1.1 @@ -195,18 +194,17 @@ def _testMolecule(): _testCrystal() _testMolecule() - ## Now change values from the srfit StructureParSet - cryst.c60.C44.x.setValue( 1.1 ) - cryst.c60.C44.occ.setValue( 1.1 ) - cryst.c60.C44.Biso.setValue( 1.1 ) - cryst.c60.q3.setValue( 1.1 ) + # Now change values from the srfit StructureParSet + cryst.c60.C44.x.setValue(1.1) + cryst.c60.C44.occ.setValue(1.1) + cryst.c60.C44.Biso.setValue(1.1) + cryst.c60.q3.setValue(1.1) cryst.a.setValue(1.1) _testCrystal() _testMolecule() return - def testImplicitBondLengthRestraints(self): """Test the structure with implicit bond lengths.""" occryst = self.occryst @@ -233,7 +231,6 @@ def testImplicitBondLengthRestraints(self): return - def testImplicitBondAngleRestraints(self): """Test the structure with implicit bond angles.""" occryst = self.occryst @@ -260,17 +257,14 @@ def testImplicitBondAngleRestraints(self): return - def testImplicitDihedralAngleRestraints(self): """Test the structure with implicit dihedral angles.""" occryst = self.occryst ocmol = self.ocmol # Add some bond angles to the molecule - ocmol.AddDihedralAngle(ocmol[0], ocmol[5], ocmol[8], ocmol[41], 1.1, - 0.1, 0.1) - ocmol.AddDihedralAngle(ocmol[0], ocmol[7], ocmol[44], ocmol[2], 1.3, - 0.1, 0.1) + ocmol.AddDihedralAngle(ocmol[0], ocmol[5], ocmol[8], ocmol[41], 1.1, 0.1, 0.1) + ocmol.AddDihedralAngle(ocmol[0], ocmol[7], ocmol[44], ocmol[2], 1.3, 0.1, 0.1) # make our crystal cryst = ObjCrystCrystalParSet("bucky", occryst) @@ -289,13 +283,11 @@ def testImplicitDihedralAngleRestraints(self): return - def testImplicitStretchModes(self): """Test the molecule with implicit stretch modes.""" # Not sure how to make this happen. pass - def testExplicitBondLengthRestraints(self): """Test the structure with explicit bond lengths.""" occryst = self.occryst @@ -321,7 +313,6 @@ def testExplicitBondLengthRestraints(self): return - def testExplicitBondAngleRestraints(self): """Test the structure with explicit bond angles. @@ -337,10 +328,8 @@ def testExplicitBondAngleRestraints(self): m = cryst.c60 # restrain some bond angles - res0 = m.restrainBondAngle(m.atoms[0], m.atoms[5], m.atoms[8], 3.3, - 0.1, 0.1) - res1 = m.restrainBondAngle(m.atoms[0], m.atoms[7], m.atoms[44], 3.3, - 0.1, 0.1) + res0 = m.restrainBondAngle(m.atoms[0], m.atoms[5], m.atoms[8], 3.3, 0.1, 0.1) + res1 = m.restrainBondAngle(m.atoms[0], m.atoms[7], m.atoms[44], 3.3, 0.1, 0.1) # make sure that we have some restraints in the molecule self.assertTrue(2, len(m._restraints)) @@ -353,7 +342,6 @@ def testExplicitBondAngleRestraints(self): return - def testExplicitDihedralAngleRestraints(self): """Test the structure with explicit dihedral angles.""" occryst = self.occryst @@ -364,11 +352,8 @@ def testExplicitDihedralAngleRestraints(self): m = cryst.c60 # Restrain some dihedral angles. - res0 = m.restrainDihedralAngle(m.atoms[0], m.atoms[5], m.atoms[8], - m.atoms[41], 1.1, 0.1, 0.1) - res1 = m.restrainDihedralAngle(m.atoms[0], m.atoms[7], m.atoms[44], - m.atoms[2], 1.1, 0.1, 0.1) - + res0 = m.restrainDihedralAngle(m.atoms[0], m.atoms[5], m.atoms[8], m.atoms[41], 1.1, 0.1, 0.1) + res1 = m.restrainDihedralAngle(m.atoms[0], m.atoms[7], m.atoms[44], m.atoms[2], 1.1, 0.1, 0.1) # make sure that we have some restraints in the molecule self.assertTrue(2, len(m._restraints)) @@ -381,7 +366,6 @@ def testExplicitDihedralAngleRestraints(self): return - def testExplicitBondLengthParameter(self): """Test adding bond length parameters to the molecule.""" occryst = self.occryst @@ -401,48 +385,45 @@ def testExplicitBondLengthParameter(self): xyz0 = numpy.array([a0.x.getValue(), a0.y.getValue(), a0.z.getValue()]) xyz7 = numpy.array([a7.x.getValue(), a7.y.getValue(), a7.z.getValue()]) - xyz20 = numpy.array([a20.x.getValue(), a20.y.getValue(), - a20.z.getValue()]) + xyz20 = numpy.array([a20.x.getValue(), a20.y.getValue(), a20.z.getValue()]) dd = xyz0 - xyz7 - d0 = numpy.dot(dd, dd)**0.5 + d0 = numpy.dot(dd, dd) ** 0.5 self.assertAlmostEqual(d0, p1.getValue(), 6) # Record the unit direction of change for later - u = dd/d0 + u = dd / d0 # Change the value scale = 1.05 - p1.setValue(scale*d0) + p1.setValue(scale * d0) # Verify that it has changed. - self.assertAlmostEqual(scale*d0, p1.getValue()) + self.assertAlmostEqual(scale * d0, p1.getValue()) xyz0a = numpy.array([a0.x.getValue(), a0.y.getValue(), a0.z.getValue()]) xyz7a = numpy.array([a7.x.getValue(), a7.y.getValue(), a7.z.getValue()]) - xyz20a = numpy.array([a20.x.getValue(), a20.y.getValue(), - a20.z.getValue()]) + xyz20a = numpy.array([a20.x.getValue(), a20.y.getValue(), a20.z.getValue()]) dda = xyz0a - xyz7a - d1 = numpy.dot(dda, dda)**0.5 + d1 = numpy.dot(dda, dda) ** 0.5 - self.assertAlmostEqual(scale*d0, d1) + self.assertAlmostEqual(scale * d0, d1) # Verify that only the second and third atoms have moved. self.assertTrue(numpy.array_equal(xyz0, xyz0a)) - xyz7calc = xyz7 + (1-scale)*d0*u + xyz7calc = xyz7 + (1 - scale) * d0 * u for i in range(3): self.assertAlmostEqual(xyz7a[i], xyz7calc[i], 6) - xyz20calc = xyz20 + (1-scale)*d0*u + xyz20calc = xyz20 + (1 - scale) * d0 * u for i in range(3): self.assertAlmostEqual(xyz20a[i], xyz20calc[i], 6) return - def testExplicitBondAngleParameter(self): """Test adding bond angle parameters to the molecule.""" occryst = self.occryst @@ -458,18 +439,15 @@ def testExplicitBondAngleParameter(self): xyz0 = numpy.array([a0.x.getValue(), a0.y.getValue(), a0.z.getValue()]) xyz7 = numpy.array([a7.x.getValue(), a7.y.getValue(), a7.z.getValue()]) - xyz20 = numpy.array([a20.x.getValue(), a20.y.getValue(), - a20.z.getValue()]) - xyz25 = numpy.array([a25.x.getValue(), a25.y.getValue(), - a25.z.getValue()]) - + xyz20 = numpy.array([a20.x.getValue(), a20.y.getValue(), a20.z.getValue()]) + xyz25 = numpy.array([a25.x.getValue(), a25.y.getValue(), a25.z.getValue()]) v1 = xyz7 - xyz0 - d1 = numpy.dot(v1, v1)**0.5 + d1 = numpy.dot(v1, v1) ** 0.5 v2 = xyz7 - xyz20 - d2 = numpy.dot(v2, v2)**0.5 + d2 = numpy.dot(v2, v2) ** 0.5 - angle0 = numpy.arccos(numpy.dot(v1, v2)/(d1*d2)) + angle0 = numpy.arccos(numpy.dot(v1, v2) / (d1 * d2)) # Add a parameter p1 = m.addBondAngleParameter("C0720", a0, a7, a20) @@ -480,26 +458,24 @@ def testExplicitBondAngleParameter(self): # Change the value scale = 1.05 - p1.setValue(scale*angle0) + p1.setValue(scale * angle0) # Verify that it has changed. - self.assertAlmostEqual(scale*angle0, p1.getValue(), 6) + self.assertAlmostEqual(scale * angle0, p1.getValue(), 6) xyz0a = numpy.array([a0.x.getValue(), a0.y.getValue(), a0.z.getValue()]) xyz7a = numpy.array([a7.x.getValue(), a7.y.getValue(), a7.z.getValue()]) - xyz20a = numpy.array([a20.x.getValue(), a20.y.getValue(), - a20.z.getValue()]) - xyz25a = numpy.array([a25.x.getValue(), a25.y.getValue(), - a25.z.getValue()]) + xyz20a = numpy.array([a20.x.getValue(), a20.y.getValue(), a20.z.getValue()]) + xyz25a = numpy.array([a25.x.getValue(), a25.y.getValue(), a25.z.getValue()]) v1a = xyz7a - xyz0a - d1a = numpy.dot(v1a, v1a)**0.5 + d1a = numpy.dot(v1a, v1a) ** 0.5 v2a = xyz7a - xyz20a - d2a = numpy.dot(v2a, v2a)**0.5 + d2a = numpy.dot(v2a, v2a) ** 0.5 - angle1 = numpy.arccos(numpy.dot(v1a, v2a)/(d1a*d2a)) + angle1 = numpy.arccos(numpy.dot(v1a, v2a) / (d1a * d2a)) - self.assertAlmostEqual(scale*angle0, angle1) + self.assertAlmostEqual(scale * angle0, angle1) # Verify that only the last two atoms have moved. @@ -510,7 +486,6 @@ def testExplicitBondAngleParameter(self): return - def testExplicitDihedralAngleParameter(self): """Test adding dihedral angle parameters to the molecule.""" occryst = self.occryst @@ -527,13 +502,9 @@ def testExplicitDihedralAngleParameter(self): xyz0 = numpy.array([a0.x.getValue(), a0.y.getValue(), a0.z.getValue()]) xyz7 = numpy.array([a7.x.getValue(), a7.y.getValue(), a7.z.getValue()]) - xyz20 = numpy.array([a20.x.getValue(), a20.y.getValue(), - a20.z.getValue()]) - xyz25 = numpy.array([a25.x.getValue(), a25.y.getValue(), - a25.z.getValue()]) - xyz33 = numpy.array([a33.x.getValue(), a33.y.getValue(), - a33.z.getValue()]) - + xyz20 = numpy.array([a20.x.getValue(), a20.y.getValue(), a20.z.getValue()]) + xyz25 = numpy.array([a25.x.getValue(), a25.y.getValue(), a25.z.getValue()]) + xyz33 = numpy.array([a33.x.getValue(), a33.y.getValue(), a33.z.getValue()]) v12 = xyz0 - xyz7 v23 = xyz7 - xyz20 @@ -541,9 +512,9 @@ def testExplicitDihedralAngleParameter(self): v123 = numpy.cross(v12, v23) v234 = numpy.cross(v23, v34) - d123 = numpy.dot(v123, v123)**0.5 - d234 = numpy.dot(v234, v234)**0.5 - angle0 = -numpy.arccos(numpy.dot(v123, v234)/(d123*d234)) + d123 = numpy.dot(v123, v123) ** 0.5 + d234 = numpy.dot(v234, v234) ** 0.5 + angle0 = -numpy.arccos(numpy.dot(v123, v234) / (d123 * d234)) # Add a parameter p1 = m.addDihedralAngleParameter("C072025", a0, a7, a20, a25) @@ -554,19 +525,16 @@ def testExplicitDihedralAngleParameter(self): # Change the value scale = 1.05 - p1.setValue(scale*angle0) + p1.setValue(scale * angle0) # Verify that it has changed. - self.assertAlmostEqual(scale*angle0, p1.getValue(), 6) + self.assertAlmostEqual(scale * angle0, p1.getValue(), 6) xyz0a = numpy.array([a0.x.getValue(), a0.y.getValue(), a0.z.getValue()]) xyz7a = numpy.array([a7.x.getValue(), a7.y.getValue(), a7.z.getValue()]) - xyz20a = numpy.array([a20.x.getValue(), a20.y.getValue(), - a20.z.getValue()]) - xyz25a = numpy.array([a25.x.getValue(), a25.y.getValue(), - a25.z.getValue()]) - xyz33a = numpy.array([a33.x.getValue(), a33.y.getValue(), - a33.z.getValue()]) + xyz20a = numpy.array([a20.x.getValue(), a20.y.getValue(), a20.z.getValue()]) + xyz25a = numpy.array([a25.x.getValue(), a25.y.getValue(), a25.z.getValue()]) + xyz33a = numpy.array([a33.x.getValue(), a33.y.getValue(), a33.z.getValue()]) v12a = xyz0a - xyz7a v23a = xyz7a - xyz20a @@ -574,11 +542,11 @@ def testExplicitDihedralAngleParameter(self): v123a = numpy.cross(v12a, v23a) v234a = numpy.cross(v23a, v34a) - d123a = numpy.dot(v123a, v123a)**0.5 - d234a = numpy.dot(v234a, v234a)**0.5 - angle1 = -numpy.arccos(numpy.dot(v123a, v234a)/(d123a*d234a)) + d123a = numpy.dot(v123a, v123a) ** 0.5 + d234a = numpy.dot(v234a, v234a) ** 0.5 + angle1 = -numpy.arccos(numpy.dot(v123a, v234a) / (d123a * d234a)) - self.assertAlmostEqual(scale*angle0, angle1) + self.assertAlmostEqual(scale * angle0, angle1) # Verify that only the last two atoms have moved. @@ -590,10 +558,12 @@ def testExplicitDihedralAngleParameter(self): return + # End of class TestParameterAdapter # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) class TestCreateSpaceGroup(unittest.TestCase): """Test space group creation from pyobjcryst structures. @@ -612,6 +582,7 @@ def setUp(self): def getObjCrystParSetSpaceGroup(sg): """Make an ObjCrystCrystalParSet with the proper space group.""" from pyobjcryst.spacegroup import SpaceGroup + sgobjcryst = SpaceGroup(sg.short_name) sgnew = ObjCrystCrystalParSet._createSpaceGroup(sgobjcryst) return sgnew @@ -619,7 +590,7 @@ def getObjCrystParSetSpaceGroup(sg): @staticmethod def hashDiffPySpaceGroup(sg): lines = [str(sg.number % 1000)] + sorted(map(str, sg.iter_symops())) - s = '\n'.join(lines) + s = "\n".join(lines) return s def sgsEquivalent(self, sg1, sg2): @@ -640,7 +611,7 @@ def xtestCreateSpaceGroup(self): for smbls in sgtbx.space_group_symbol_iterator(): shn = smbls.hermann_mauguin() - short_name = shn.replace(' ', '') + short_name = shn.replace(" ", "") if spacegroups.IsSpaceGroupIdentifier(short_name): sg = spacegroups.GetSpaceGroup(shn) sgnew = self.getObjCrystParSetSpaceGroup(sg) @@ -648,6 +619,7 @@ def xtestCreateSpaceGroup(self): self.assertTrue(self.sgsEquivalent(sg, sgnew)) return + # End of class TestCreateSpaceGroup if __name__ == "__main__": diff --git a/tests/test_parameter.py b/tests/test_parameter.py new file mode 100644 index 00000000..b3a089ec --- /dev/null +++ b/tests/test_parameter.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +############################################################################## +# +# diffpy.srfit 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. +# +############################################################################## + +"""Tests for refinableobj module.""" + +import unittest + +from diffpy.srfit.fitbase.parameter import Parameter, ParameterAdapter, ParameterProxy + + +class TestParameter(unittest.TestCase): + def testSetValue(self): + """Test initialization.""" + l_parameter = Parameter("l") + + l_parameter.setValue(3.14) + self.assertAlmostEqual(3.14, l_parameter.getValue()) + + # Try array + import numpy + + x = numpy.arange(0, 10, 0.1) + l_parameter.setValue(x) + self.assertTrue(l_parameter.getValue() is x) + self.assertTrue(l_parameter.value is x) + + # Change the array + y = numpy.arange(0, 10, 0.5) + l_parameter.value = y + self.assertTrue(l_parameter.getValue() is y) + self.assertTrue(l_parameter.value is y) + + # Back to scalar + l_parameter.setValue(1.01) + self.assertAlmostEqual(1.01, l_parameter.getValue()) + self.assertAlmostEqual(1.01, l_parameter.value) + return + + +class TestParameterProxy(unittest.TestCase): + def testProxy(self): + """Test the ParameterProxy class.""" + l_parameter = Parameter("l", 3.14) + + # Try Accessor adaptation + la = ParameterProxy("l2", l_parameter) + + self.assertEqual("l2", la.name) + self.assertEqual(l_parameter.getValue(), la.getValue()) + + # Change the parameter + l_parameter.value = 2.3 + self.assertEqual(l_parameter.getValue(), la.getValue()) + self.assertEqual(l_parameter.value, la.value) + + # Change the proxy + la.value = 3.2 + self.assertEqual(l_parameter.getValue(), la.getValue()) + self.assertEqual(l_parameter.value, la.value) + + return + + +class TestParameterAdapter(unittest.TestCase): + def testWrapper(self): + """Test the adapter. + + This adapts a Parameter to the Parameter interface. :) + """ + l_parameter = Parameter("l", 3.14) + + # Try Accessor adaptation + la = ParameterAdapter("l", l_parameter, getter=Parameter.getValue, setter=Parameter.setValue) + + self.assertEqual(l_parameter.name, la.name) + self.assertEqual(l_parameter.getValue(), la.getValue()) + + # Change the parameter + l_parameter.setValue(2.3) + self.assertEqual(l_parameter.getValue(), la.getValue()) + + # Change the adapter + la.setValue(3.2) + self.assertEqual(l_parameter.getValue(), la.getValue()) + + # Try Attribute adaptation + la = ParameterAdapter("l", l_parameter, attr="value") + + self.assertEqual(l_parameter.name, la.name) + self.assertEqual("value", la.attr) + self.assertEqual(l_parameter.getValue(), la.getValue()) + + # Change the parameter + l_parameter.setValue(2.3) + self.assertEqual(l_parameter.getValue(), la.getValue()) + + # Change the adapter + la.setValue(3.2) + self.assertEqual(l_parameter.getValue(), la.getValue()) + + return + + +if __name__ == "__main__": + unittest.main() diff --git a/src/diffpy/srfit/tests/testparameterset.py b/tests/test_parameterset.py similarity index 99% rename from src/diffpy/srfit/tests/testparameterset.py rename to tests/test_parameterset.py index abbad180..3e209e3d 100644 --- a/src/diffpy/srfit/tests/testparameterset.py +++ b/tests/test_parameterset.py @@ -22,7 +22,6 @@ class TestParameterSet(unittest.TestCase): - def setUp(self): self.parset = ParameterSet("test") return diff --git a/src/diffpy/srfit/tests/testpdf.py b/tests/test_pdf.py similarity index 71% rename from src/diffpy/srfit/tests/testpdf.py rename to tests/test_pdf.py index 426b7d81..9d43ee32 100644 --- a/src/diffpy/srfit/tests/testpdf.py +++ b/tests/test_pdf.py @@ -15,26 +15,23 @@ """Tests for pdf package.""" -import unittest -import pickle import io +import pickle +import unittest import numpy +from utils import _msg_nosrreal, _msg_nostructure, datafile, has_srreal, has_structure -from diffpy.srfit.tests.utils import datafile -from diffpy.srfit.tests.utils import has_srreal, _msg_nosrreal -from diffpy.srfit.tests.utils import has_structure, _msg_nostructure -from diffpy.srfit.pdf import PDFGenerator, PDFParser, PDFContribution from diffpy.srfit.exceptions import SrFitError +from diffpy.srfit.pdf import PDFContribution, PDFGenerator, PDFParser # ---------------------------------------------------------------------------- -class TestPDFParset(unittest.TestCase): +class TestPDFParset(unittest.TestCase): def setUp(self): return - def testParser1(self): data = datafile("ni-q27r100-neutron.gr") parser = PDFParser() @@ -42,16 +39,16 @@ def testParser1(self): meta = parser._meta - self.assertEqual(data, meta['filename']) - self.assertEqual(1, meta['nbanks']) - self.assertEqual('N', meta['stype']) - self.assertEqual(27, meta['qmax']) - self.assertEqual(300, meta.get('temperature')) - self.assertEqual(None, meta.get('qdamp')) - self.assertEqual(None, meta.get('qbroad')) - self.assertEqual(None, meta.get('spdiameter')) - self.assertEqual(None, meta.get('scale')) - self.assertEqual(None, meta.get('doping')) + self.assertEqual(data, meta["filename"]) + self.assertEqual(1, meta["nbanks"]) + self.assertEqual("N", meta["stype"]) + self.assertEqual(27, meta["qmax"]) + self.assertEqual(300, meta.get("temperature")) + self.assertEqual(None, meta.get("qdamp")) + self.assertEqual(None, meta.get("qbroad")) + self.assertEqual(None, meta.get("spdiameter")) + self.assertEqual(None, meta.get("scale")) + self.assertEqual(None, meta.get("doping")) x, y, dx, dy = parser.getData() self.assertTrue(dx is None) @@ -62,15 +59,13 @@ def testParser1(self): res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) - testy = numpy.array([1.144, 2.258, 3.312, 4.279, 5.135, 5.862, 6.445, - 6.875, 7.150, 7.272]) + testy = numpy.array([1.144, 2.258, 3.312, 4.279, 5.135, 5.862, 6.445, 6.875, 7.150, 7.272]) diff = testy - y[:10] res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) return - def testParser2(self): data = datafile("si-q27r60-xray.gr") parser = PDFParser() @@ -78,16 +73,16 @@ def testParser2(self): meta = parser._meta - self.assertEqual(data, meta['filename']) - self.assertEqual(1, meta['nbanks']) - self.assertEqual('X', meta['stype']) - self.assertEqual(27, meta['qmax']) - self.assertEqual(300, meta.get('temperature')) - self.assertEqual(None, meta.get('qdamp')) - self.assertEqual(None, meta.get('qbroad')) - self.assertEqual(None, meta.get('spdiameter')) - self.assertEqual(None, meta.get('scale')) - self.assertEqual(None, meta.get('doping')) + self.assertEqual(data, meta["filename"]) + self.assertEqual(1, meta["nbanks"]) + self.assertEqual("X", meta["stype"]) + self.assertEqual(27, meta["qmax"]) + self.assertEqual(300, meta.get("temperature")) + self.assertEqual(None, meta.get("qdamp")) + self.assertEqual(None, meta.get("qbroad")) + self.assertEqual(None, meta.get("spdiameter")) + self.assertEqual(None, meta.get("scale")) + self.assertEqual(None, meta.get("doping")) x, y, dx, dy = parser.getData() testx = numpy.linspace(0.01, 60, 5999, endpoint=False) @@ -95,15 +90,38 @@ def testParser2(self): res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) - testy = numpy.array([0.1105784, 0.2199684, 0.3270088, 0.4305913, - 0.5296853, 0.6233606, 0.7108060, 0.7913456, 0.8644501, 0.9297440]) + testy = numpy.array( + [ + 0.1105784, + 0.2199684, + 0.3270088, + 0.4305913, + 0.5296853, + 0.6233606, + 0.7108060, + 0.7913456, + 0.8644501, + 0.9297440, + ] + ) diff = testy - y[:10] res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) - testdy = numpy.array([0.001802192, 0.003521449, 0.005079115, - 0.006404892, 0.007440527, 0.008142955, 0.008486813, 0.008466340, - 0.008096858, 0.007416456]) + testdy = numpy.array( + [ + 0.001802192, + 0.003521449, + 0.005079115, + 0.006404892, + 0.007440527, + 0.008142955, + 0.008486813, + 0.008466340, + 0.008096858, + 0.007416456, + ] + ) diff = testdy - dy[:10] res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) @@ -111,27 +129,28 @@ def testParser2(self): self.assertTrue(dx is None) return + # End of class TestPDFParset # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_srreal, _msg_nosrreal) @unittest.skipUnless(has_structure, _msg_nostructure) class TestPDFGenerator(unittest.TestCase): - def setUp(self): self.gen = PDFGenerator() return - def testGenerator(self): qmax = 27.0 gen = self.gen - gen.setScatteringType('N') - self.assertEqual('N', gen.getScatteringType()) + gen.setScatteringType("N") + self.assertEqual("N", gen.getScatteringType()) gen.setQmax(qmax) self.assertAlmostEqual(qmax, gen.getQmax()) from diffpy.structure import PDFFitStructure + stru = PDFFitStructure() ciffile = datafile("ni.cif") stru.read(ciffile) @@ -158,12 +177,13 @@ def testGenerator(self): # output, we just have to make sure we can calculate from the # PDFGenerator interface. from diffpy.srreal.pdfcalculator import PDFCalculator + calc = PDFCalculator() calc.rstep = r[1] - r[0] calc.rmin = r[0] calc.rmax = r[-1] + 0.5 * calc.rstep calc.qmax = qmax - calc.setScatteringFactorTableByType('N') + calc.setScatteringFactorTableByType("N") calc.eval(stru) yref = calc.pdf @@ -172,10 +192,8 @@ def testGenerator(self): self.assertAlmostEqual(0, res) return - def test_setQmin(self): - """Verify qmin is propagated to the calculator object. - """ + """Verify qmin is propagated to the calculator object.""" gen = self.gen self.assertEqual(0, gen.getQmin()) self.assertEqual(0, gen._calc.qmin) @@ -184,37 +202,36 @@ def test_setQmin(self): self.assertEqual(0.93, gen._calc.qmin) return + # End of class TestPDFGenerator # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_srreal, _msg_nosrreal) @unittest.skipUnless(has_structure, _msg_nostructure) class TestPDFContribution(unittest.TestCase): - def setUp(self): - self.pc = PDFContribution('pdf') + self.pc = PDFContribution("pdf") return - def test_setQmax(self): - """check PDFContribution.setQmax() - """ + """check PDFContribution.setQmax()""" from diffpy.structure import Structure + pc = self.pc pc.setQmax(21) - pc.addStructure('empty', Structure()) + pc.addStructure("empty", Structure()) self.assertEqual(21, pc.empty.getQmax()) pc.setQmax(22) self.assertEqual(22, pc.getQmax()) self.assertEqual(22, pc.empty.getQmax()) return - def test_getQmax(self): - """check PDFContribution.getQmax() - """ + """check PDFContribution.getQmax()""" from diffpy.structure import Structure + # cover all code branches in PDFContribution._getMetaValue # (1) contribution metadata pc1 = self.pc @@ -222,54 +239,56 @@ def test_getQmax(self): pc1.setQmax(17) self.assertEqual(17, pc1.getQmax()) # (2) contribution metadata - pc2 = PDFContribution('pdf') - pc2.addStructure('empty', Structure()) + pc2 = PDFContribution("pdf") + pc2.addStructure("empty", Structure()) pc2.empty.setQmax(18) self.assertEqual(18, pc2.getQmax()) # (3) profile metadata - pc3 = PDFContribution('pdf') - pc3.profile.meta['qmax'] = 19 + pc3 = PDFContribution("pdf") + pc3.profile.meta["qmax"] = 19 self.assertEqual(19, pc3.getQmax()) return - def test_savetxt(self): "check PDFContribution.savetxt()" from diffpy.structure import Structure + pc = self.pc pc.loadData(datafile("si-q27r60-xray.gr")) pc.setCalculationRange(0, 10) - pc.addStructure('empty', Structure()) + pc.addStructure("empty", Structure()) fp = io.BytesIO() self.assertRaises(SrFitError, pc.savetxt, fp) pc.evaluate() pc.savetxt(fp) txt = fp.getvalue().decode() - nlines = len(txt.strip().split('\n')) + nlines = len(txt.strip().split("\n")) self.assertEqual(1001, nlines) return - def test_pickling(self): "validate PDFContribution.residual() after pickling." from itertools import chain + from diffpy.structure import loadStructure + pc = self.pc pc.loadData(datafile("ni-q27r100-neutron.gr")) ni = loadStructure(datafile("ni.cif")) ni.Uisoequiv = 0.003 - pc.addStructure('ni', ni) + pc.addStructure("ni", ni) pc.setCalculationRange(0, 10) pc2 = pickle.loads(pickle.dumps(pc)) res0 = pc.residual() self.assertTrue(numpy.array_equal(res0, pc2.residual())) - for p in chain(pc.iterPars('Uiso'), pc2.iterPars('Uiso')): + for p in chain(pc.iterPars("Uiso"), pc2.iterPars("Uiso")): p.value = 0.004 res1 = pc.residual() self.assertFalse(numpy.allclose(res0, res1)) self.assertTrue(numpy.array_equal(res1, pc2.residual())) return + # End of class TestPDFContribution # ---------------------------------------------------------------------------- diff --git a/src/diffpy/srfit/tests/testprofile.py b/tests/test_profile.py similarity index 82% rename from src/diffpy/srfit/tests/testprofile.py rename to tests/test_profile.py index 78f221d9..74b8382c 100644 --- a/src/diffpy/srfit/tests/testprofile.py +++ b/tests/test_profile.py @@ -15,19 +15,18 @@ """Tests for refinableobj module.""" -import unittest -import re import io +import re +import unittest -from numpy import array, arange, array_equal, ones_like, allclose +from numpy import allclose, arange, array, array_equal, ones_like +from utils import datafile -from diffpy.srfit.fitbase.profile import Profile from diffpy.srfit.exceptions import SrFitError -from diffpy.srfit.tests.utils import datafile +from diffpy.srfit.fitbase.profile import Profile class TestProfile(unittest.TestCase): - def setUp(self): self.profile = Profile() return @@ -55,9 +54,9 @@ def testSetObservedProfile(self): prof = self.profile prof.setObservedProfile(x, y, dy) - self.assertTrue( array_equal(x, prof.xobs) ) - self.assertTrue( array_equal(y, prof.yobs) ) - self.assertTrue( array_equal(dy, prof.dyobs) ) + self.assertTrue(array_equal(x, prof.xobs)) + self.assertTrue(array_equal(y, prof.yobs)) + self.assertTrue(array_equal(dy, prof.dyobs)) # Make a profile with undefined dy x = arange(0, 10, 0.1) @@ -66,18 +65,17 @@ def testSetObservedProfile(self): self.profile.setObservedProfile(x, y, dy) - self.assertTrue( array_equal(x, prof.xobs) ) - self.assertTrue( array_equal(y, prof.yobs) ) - self.assertTrue( array_equal(ones_like(prof.xobs), prof.dyobs)) + self.assertTrue(array_equal(x, prof.xobs)) + self.assertTrue(array_equal(y, prof.yobs)) + self.assertTrue(array_equal(ones_like(prof.xobs), prof.dyobs)) # Get the ranged profile to make sure its the same - self.assertTrue( array_equal(x, prof.x) ) - self.assertTrue( array_equal(y, prof.y) ) - self.assertTrue( array_equal(ones_like(prof.xobs), prof.dy)) + self.assertTrue(array_equal(x, prof.x)) + self.assertTrue(array_equal(y, prof.y)) + self.assertTrue(array_equal(ones_like(prof.xobs), prof.dy)) return - def testSetCalculationRange(self): """Test the setCalculationRange method.""" x = arange(2, 9.6, 0.5) @@ -110,26 +108,24 @@ def testSetCalculationRange(self): self.assertTrue(array_equal(y, prof.y)) self.assertTrue(array_equal(dy, prof.dy)) # Test xmin > xmax - self.assertRaises(ValueError, prof.setCalculationRange, - xmin=10, xmax=3) + self.assertRaises(ValueError, prof.setCalculationRange, xmin=10, xmax=3) # Test xmax - xmin < dx - self.assertRaises(ValueError, prof.setCalculationRange, - xmin=3, xmax=3.9, dx=1.0) + self.assertRaises(ValueError, prof.setCalculationRange, xmin=3, xmax=3.9, dx=1.0) # Test dx <= 0 self.assertRaises(ValueError, prof.setCalculationRange, dx=0) self.assertRaises(ValueError, prof.setCalculationRange, dx=-0.000001) # using string other than 'obs' - self.assertRaises(ValueError, prof.setCalculationRange, xmin='oobs') - self.assertRaises(ValueError, prof.setCalculationRange, xmax='oobs') - self.assertRaises(ValueError, prof.setCalculationRange, dx='oobs') + self.assertRaises(ValueError, prof.setCalculationRange, xmin="oobs") + self.assertRaises(ValueError, prof.setCalculationRange, xmax="oobs") + self.assertRaises(ValueError, prof.setCalculationRange, dx="oobs") # This should be alright prof.setCalculationRange(3, 5) - prof.setCalculationRange(xmin='obs', xmax=7, dx=0.001) + prof.setCalculationRange(xmin="obs", xmax=7, dx=0.001) self.assertEqual(5001, len(prof.x)) self.assertEqual(len(prof.x), len(prof.y)) self.assertEqual(len(prof.x), len(prof.dy)) # Test an internal bound - prof.setCalculationRange(4, 7, dx='obs') + prof.setCalculationRange(4, 7, dx="obs") self.assertTrue(array_equal(prof.x, arange(4, 7.1, 0.5))) self.assertTrue(array_equal(prof.y, arange(4, 7.1, 0.5))) self.assertTrue(array_equal(prof.y, arange(4, 7.1, 0.5))) @@ -158,7 +154,6 @@ def testSetCalculationRange(self): self.assertTrue(array_equal(prof.x, arange(4.5, 6.1, 0.5))) return - def testSetCalculationPoints(self): """Test the setCalculationPoints method.""" prof = self.profile @@ -170,11 +165,11 @@ def testSetCalculationPoints(self): # Test without data xcalc = arange(3, 12.2, 0.2) prof.setCalculationPoints(xcalc) - self.assertTrue( array_equal(xcalc, prof.x) ) + self.assertTrue(array_equal(xcalc, prof.x)) # Add the data. This should change the bounds of the calculation array. prof.setObservedProfile(x, y, dy) - self.assertTrue( array_equal(arange(3, 10.1, 0.2), prof.x ) ) + self.assertTrue(array_equal(arange(3, 10.1, 0.2), prof.x)) return @@ -190,17 +185,17 @@ def _test(p): self.assertAlmostEqual(1.802192e-3, p.dy[0]) # Test normal load - prof.loadtxt(data, usecols=(0,1,3)) + prof.loadtxt(data, usecols=(0, 1, 3)) _test(prof) # Test trying to not set unpack - prof.loadtxt(data, usecols=(0,1,3), unpack = False) + prof.loadtxt(data, usecols=(0, 1, 3), unpack=False) _test(prof) - prof.loadtxt(data, float, '#', None, None, 0, (0,1,3), False) + prof.loadtxt(data, float, "#", None, None, 0, (0, 1, 3), False) _test(prof) # Try not including dy - prof.loadtxt(data, usecols=(0,1)) + prof.loadtxt(data, usecols=(0, 1)) self.assertAlmostEqual(1e-2, prof.x[0]) self.assertAlmostEqual(1.105784e-1, prof.y[0]) self.assertAlmostEqual(1, prof.dy[0]) @@ -209,23 +204,23 @@ def _test(p): self.assertRaises(ValueError, prof.loadtxt, data, usecols=(0,)) return - def test_savetxt(self): "Check the savetxt method." prof = self.profile - self.assertRaises(SrFitError, prof.savetxt, 'foo') + self.assertRaises(SrFitError, prof.savetxt, "foo") xobs = arange(-2, 3.01, 0.25) - yobs = xobs ** 2 + yobs = xobs**2 prof.setObservedProfile(xobs, yobs) prof.ycalc = yobs.copy() fp = io.BytesIO() prof.savetxt(fp) txt = fp.getvalue().decode() - self.assertTrue(re.match(r'^# x +ycalc +y +dy\b', txt)) - nlines = len(txt.strip().split('\n')) + self.assertTrue(re.match(r"^# x +ycalc +y +dy\b", txt)) + nlines = len(txt.strip().split("\n")) self.assertEqual(22, nlines) return + # End of class TestProfile # ---------------------------------------------------------------------------- diff --git a/src/diffpy/srfit/tests/testprofilegenerator.py b/tests/test_profilegenerator.py similarity index 96% rename from src/diffpy/srfit/tests/testprofilegenerator.py rename to tests/test_profilegenerator.py index 4fb43002..4af71d84 100644 --- a/src/diffpy/srfit/tests/testprofilegenerator.py +++ b/tests/test_profilegenerator.py @@ -20,12 +20,11 @@ from numpy import arange, array_equal -from diffpy.srfit.fitbase.profilegenerator import ProfileGenerator from diffpy.srfit.fitbase.profile import Profile +from diffpy.srfit.fitbase.profilegenerator import ProfileGenerator class TestProfileGenerator(unittest.TestCase): - def setUp(self): self.gen = ProfileGenerator("test") self.profile = Profile() @@ -34,7 +33,6 @@ def setUp(self): self.gen.setProfile(self.profile) return - def testOperation(self): """Test the operation method.""" gen = self.gen @@ -49,7 +47,6 @@ def testOperation(self): self.assertTrue(array_equal(2 * prof.x, val)) return - def testUpdate(self): """Update and change the profile to make sure generator is flushed.""" gen = self.gen @@ -77,18 +74,17 @@ def testUpdate(self): self.assertTrue(array_equal(x, gen.value)) return - def test_pickling(self): - """Test pickling of ProfileGenerator. - """ + """Test pickling of ProfileGenerator.""" data = pickle.dumps(self.gen) gen2 = pickle.loads(data) - self.assertEqual('test', gen2.name) + self.assertEqual("test", gen2.name) x = self.profile.x self.assertTrue(array_equal(x, gen2.operation())) self.assertTrue(array_equal(3 * x, gen2(3 * x))) return + # End of class TestProfileGenerator if __name__ == "__main__": diff --git a/src/diffpy/srfit/tests/testrecipeorganizer.py b/tests/test_recipeorganizer.py similarity index 86% rename from src/diffpy/srfit/tests/testrecipeorganizer.py rename to tests/test_recipeorganizer.py index 08efde92..70537c47 100644 --- a/src/diffpy/srfit/tests/testrecipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -17,20 +17,18 @@ import unittest +import numpy +from utils import capturestdout + from diffpy.srfit.equation.builder import EquationFactory from diffpy.srfit.fitbase.calculator import Calculator from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.fitbase.recipeorganizer import equationFromString -from diffpy.srfit.fitbase.recipeorganizer import RecipeContainer -from diffpy.srfit.fitbase.recipeorganizer import RecipeOrganizer -from diffpy.srfit.tests.utils import capturestdout - -import numpy +from diffpy.srfit.fitbase.recipeorganizer import RecipeContainer, RecipeOrganizer, equationFromString # ---------------------------------------------------------------------------- -class TestEquationFromString(unittest.TestCase): +class TestEquationFromString(unittest.TestCase): def testEquationFromString(self): """Test the equationFromString method.""" @@ -56,7 +54,7 @@ def testEquationFromString(self): self.assertRaises(ValueError, equationFromString, "p1+p2+p3", factory) # Pass that argument in the ns dictionary - eq = equationFromString("p1+p2+p3", factory, {"p3":p3}) + eq = equationFromString("p1+p2+p3", factory, {"p3": p3}) self.assertEqual(3, len(eq.args)) self.assertTrue(p1 in eq.args) self.assertTrue(p2 in eq.args) @@ -67,19 +65,18 @@ def testEquationFromString(self): self.assertTrue("p3" not in factory.builders) # Pass and use an unregistered parameter - self.assertRaises(ValueError, equationFromString, "p1+p2+p3+p4", - factory, {"p3":p3}) + self.assertRaises(ValueError, equationFromString, "p1+p2+p3+p4", factory, {"p3": p3}) # Try to overload a registered parameter - self.assertRaises(ValueError, equationFromString, "p1+p2", - factory, {"p2":p4}) + self.assertRaises(ValueError, equationFromString, "p1+p2", factory, {"p2": p4}) return + # ---------------------------------------------------------------------------- -class TestRecipeContainer(unittest.TestCase): +class TestRecipeContainer(unittest.TestCase): def setUp(self): self.m = RecipeContainer("test") @@ -107,14 +104,18 @@ def testAccessors(self): self.assertTrue(m1.m2.p2 is p2) self.assertTrue(m1[0] is p1) - self.assertTrue(m1[0:] == [p1,]) + self.assertTrue( + m1[0:] + == [ + p1, + ] + ) self.assertTrue(m2[0] is p2) self.assertEqual(1, len(m1)) self.assertEqual(1, len(m2)) return - def testLocateManagedObject(self): """Test the locateManagedObject method.""" m1 = self.m @@ -155,10 +156,11 @@ def testLocateManagedObject(self): return + # ---------------------------------------------------------------------------- -class TestRecipeOrganizer(unittest.TestCase): +class TestRecipeOrganizer(unittest.TestCase): def setUp(self): self.m = RecipeOrganizer("test") # Add a managed container so we can do more in-depth tests. @@ -169,7 +171,6 @@ def setUp(self): def tearDown(self): return - def testNewParameter(self): """Test the addParameter method.""" @@ -186,7 +187,6 @@ def testNewParameter(self): self.assertTrue(p2 is m.p2) return - def testAddParameter(self): """Test the addParameter method.""" @@ -261,7 +261,6 @@ def testConstrain(self): self.assertEqual(0, len(self.m._constraints)) self.m.constrain(p1, "2*p2") - self.assertTrue(p1.constrained) self.assertTrue(p1 in self.m._constraints) self.assertEqual(1, len(self.m._constraints)) @@ -273,7 +272,7 @@ def testConstrain(self): # Check errors on unregistered parameters self.assertRaises(ValueError, self.m.constrain, p1, "2*p3") - self.assertRaises(ValueError, self.m.constrain, p1, "2*p2", {"p2":p3}) + self.assertRaises(ValueError, self.m.constrain, p1, "2*p2", {"p2": p3}) # Remove the constraint self.m.unconstrain(p1) @@ -298,21 +297,21 @@ def testRestrain(self): self.m._eqfactory.registerArgument("p2", p2) self.assertEqual(0, len(self.m._restraints)) - r = self.m.restrain("p1+p2", ub = 10) + r = self.m.restrain("p1+p2", ub=10) self.assertEqual(1, len(self.m._restraints)) p2.setValue(10) self.assertEqual(1, r.penalty()) self.m.unrestrain(r) self.assertEqual(0, len(self.m._restraints)) - r = self.m.restrain(p1, ub = 10) + r = self.m.restrain(p1, ub=10) self.assertEqual(1, len(self.m._restraints)) p1.setValue(11) self.assertEqual(1, r.penalty()) # Check errors on unregistered parameters self.assertRaises(ValueError, self.m.restrain, "2*p3") - self.assertRaises(ValueError, self.m.restrain, "2*p2", ns = {"p2":p3}) + self.assertRaises(ValueError, self.m.restrain, "2*p2", ns={"p2": p3}) return def testGetConstraints(self): @@ -370,9 +369,7 @@ def testGetRestraints(self): return def testRegisterCalculator(self): - class GCalc(Calculator): - def __init__(self, name): Calculator.__init__(self, name) self.newParameter("A", 1.0) @@ -384,7 +381,7 @@ def __call__(self, x): A = self.A.getValue() c = self.center.getValue() w = self.width.getValue() - return A * numpy.exp(-0.5*((x-c)/w)**2) + return A * numpy.exp(-0.5 * ((x - c) / w) ** 2) # End class GCalc @@ -397,27 +394,27 @@ def __call__(self, x): self.m.g.center.setValue(3.0) - self.assertTrue(numpy.array_equal(numpy.exp(-0.5*((x-3.0)/0.1)**2), - g(x))) + self.assertTrue(numpy.array_equal(numpy.exp(-0.5 * ((x - 3.0) / 0.1) ** 2), g(x))) self.m.g.center.setValue(5.0) - self.assertTrue(numpy.array_equal(numpy.exp(-0.5*((x-5.0)/0.1)**2), - g(x))) + self.assertTrue(numpy.array_equal(numpy.exp(-0.5 * ((x - 5.0) / 0.1) ** 2), g(x))) # Use this in another equation eq = self.m.registerStringFunction("g/x - 1", "pdf") - self.assertTrue(numpy.array_equal(g(x)/x - 1, eq())) + self.assertTrue(numpy.array_equal(g(x) / x - 1, eq())) return def testRegisterFunction(self): """Test registering various functions.""" + def g1(A, c, w, x): - return A * numpy.exp(-0.5*((x-c)/w)**2) + return A * numpy.exp(-0.5 * ((x - c) / w) ** 2) + def g2(A): - return A+1 + return A + 1 eq = self.m.registerFunction(g1, "g") @@ -430,12 +427,11 @@ def g2(A): self.m.c.setValue(3.0) self.m.w.setValue(0.1) - self.assertTrue(numpy.array_equal(numpy.exp(-0.5*((x-3.0)/0.1)**2), - eq())) + self.assertTrue(numpy.array_equal(numpy.exp(-0.5 * ((x - 3.0) / 0.1) ** 2), eq())) # Use this in another equation eq2 = self.m.registerStringFunction("g/x - 1", "pdf") - self.assertTrue(numpy.array_equal(eq()/x - 1, eq2())) + self.assertTrue(numpy.array_equal(eq() / x - 1, eq2())) # Make sure we can swap out "g". self.m.registerFunction(g2, "g") @@ -443,8 +439,11 @@ def g2(A): # Try a bound method class temp(object): - def eval(self): return 1.23 - def __call__(self): return 4.56 + def eval(self): + return 1.23 + + def __call__(self): + return 4.56 t = temp() eq = self.m.registerFunction(t.eval, "eval") @@ -494,50 +493,48 @@ def testRegisterStringFunction(self): return - def test_releaseOldEquations(self): - """Verify EquationFactory does not hold temporary equations. - """ - self.m._newParameter('x', 12) - self.assertEqual(36, self.m.evaluateEquation('3 * x')) + """Verify EquationFactory does not hold temporary equations.""" + self.m._newParameter("x", 12) + self.assertEqual(36, self.m.evaluateEquation("3 * x")) self.assertEqual(0, len(self.m._eqfactory.equations)) return - def test_show(self): - """Verify output from the show function. - """ + """Verify output from the show function.""" + def capture_show(*args, **kwargs): rv = capturestdout(self.m.show, *args, **kwargs) return rv - self.assertEqual('', capture_show()) - self.m._newParameter('x', 1) - self.m._newParameter('y', 2) + + self.assertEqual("", capture_show()) + self.m._newParameter("x", 1) + self.m._newParameter("y", 2) out1 = capture_show() - lines1 = out1.strip().split('\n') + lines1 = out1.strip().split("\n") self.assertEqual(4, len(lines1)) - self.assertTrue('Parameters' in lines1) - self.assertFalse('Constraints' in lines1) - self.assertFalse('Restraints' in lines1) - self.m._newParameter('z', 7) - self.m.constrain('y', '3 * z') + self.assertTrue("Parameters" in lines1) + self.assertFalse("Constraints" in lines1) + self.assertFalse("Restraints" in lines1) + self.m._newParameter("z", 7) + self.m.constrain("y", "3 * z") out2 = capture_show() - lines2 = out2.strip().split('\n') + lines2 = out2.strip().split("\n") self.assertEqual(9, len(lines2)) - self.assertTrue('Parameters' in lines2) - self.assertTrue('Constraints' in lines2) - self.assertFalse('Restraints' in lines2) - self.m.restrain('z', lb=2, ub=3, sig=0.001) + self.assertTrue("Parameters" in lines2) + self.assertTrue("Constraints" in lines2) + self.assertFalse("Restraints" in lines2) + self.m.restrain("z", lb=2, ub=3, sig=0.001) out3 = capture_show() - lines3 = out3.strip().split('\n') + lines3 = out3.strip().split("\n") self.assertEqual(13, len(lines3)) - self.assertTrue('Parameters' in lines3) - self.assertTrue('Constraints' in lines3) - self.assertTrue('Restraints' in lines3) - out4 = capture_show(pattern='x') - lines4 = out4.strip().split('\n') + self.assertTrue("Parameters" in lines3) + self.assertTrue("Constraints" in lines3) + self.assertTrue("Restraints" in lines3) + out4 = capture_show(pattern="x") + lines4 = out4.strip().split("\n") self.assertEqual(9, len(lines4)) - out5 = capture_show(pattern='^') + out5 = capture_show(pattern="^") self.assertEqual(out3, out5) # check output with another level of hierarchy self.m._addObject(RecipeOrganizer("foo"), self.m._containers) @@ -545,11 +542,12 @@ def capture_show(*args, **kwargs): out6 = capture_show() self.assertTrue("foo.bar" in out6) # filter out foo.bar - out7 = capture_show('^(?!foo).') + out7 = capture_show("^(?!foo).") self.assertFalse("foo.bar" in out7) self.assertEqual(out3, out7) return + # ---------------------------------------------------------------------------- if __name__ == "__main__": diff --git a/src/diffpy/srfit/tests/testrestraint.py b/tests/test_restraint.py similarity index 100% rename from src/diffpy/srfit/tests/testrestraint.py rename to tests/test_restraint.py index 2968dd32..8cdae607 100644 --- a/src/diffpy/srfit/tests/testrestraint.py +++ b/tests/test_restraint.py @@ -17,14 +17,13 @@ import unittest -from diffpy.srfit.fitbase.restraint import Restraint -from diffpy.srfit.fitbase.recipeorganizer import equationFromString -from diffpy.srfit.fitbase.parameter import Parameter from diffpy.srfit.equation.builder import EquationFactory +from diffpy.srfit.fitbase.parameter import Parameter +from diffpy.srfit.fitbase.recipeorganizer import equationFromString +from diffpy.srfit.fitbase.restraint import Restraint class TestRestraint(unittest.TestCase): - def testRestraint(self): """Test the Restraint class.""" @@ -63,6 +62,7 @@ def testRestraint(self): # Make a really large number to check the upper bound import numpy + r.ub = numpy.inf p1.setValue(1e100) self.assertEqual(0, r.penalty()) diff --git a/src/diffpy/srfit/tests/testsas.py b/tests/test_sas.py similarity index 78% rename from src/diffpy/srfit/tests/testsas.py rename to tests/test_sas.py index 9122cf97..fe17ca13 100644 --- a/src/diffpy/srfit/tests/testsas.py +++ b/tests/test_sas.py @@ -18,17 +18,16 @@ import unittest import numpy +from utils import _msg_nosas, datafile, has_sas from diffpy.srfit.sas import SASGenerator, SASParser, SASProfile -from diffpy.srfit.tests.utils import datafile -from diffpy.srfit.tests.utils import has_sas, _msg_nosas from diffpy.srfit.sas.sasimport import sasimport # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_sas, _msg_nosas) class TestSASParser(unittest.TestCase): - def testParser(self): data = datafile("sas_ascii_test_1.txt") parser = SASParser() @@ -36,43 +35,45 @@ def testParser(self): x, y, dx, dy = parser.getData() - testx = numpy.array([0.002618, 0.007854, 0.01309, 0.01832, 0.02356, - 0.02879, 0.03402, 0.03925, 0.04448, 0.0497]) + testx = numpy.array( + [0.002618, 0.007854, 0.01309, 0.01832, 0.02356, 0.02879, 0.03402, 0.03925, 0.04448, 0.0497] + ) diff = testx - x res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) - testy = numpy.array([ 0.02198, 0.02201, 0.02695, 0.02645, 0.03024, - 0.3927, 7.305, 17.43, 13.43, 8.346]) + testy = numpy.array([0.02198, 0.02201, 0.02695, 0.02645, 0.03024, 0.3927, 7.305, 17.43, 13.43, 8.346]) diff = testy - y res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) - testdy = numpy.array([ 0.002704, 0.001643, 0.002452, 0.001769, - 0.001531, 0.1697, 1.006, 0.5351, 0.3677, 0.191]) + testdy = numpy.array( + [0.002704, 0.001643, 0.002452, 0.001769, 0.001531, 0.1697, 1.006, 0.5351, 0.3677, 0.191] + ) diff = testdy - dy res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) - testdx = numpy.array([0.0004091, 0.005587, 0.005598, 0.005624, - 0.005707, 0.005975, 0.006264, 0.006344, 0.006424, 0.006516]) + testdx = numpy.array( + [0.0004091, 0.005587, 0.005598, 0.005624, 0.005707, 0.005975, 0.006264, 0.006344, 0.006424, 0.006516] + ) diff = testdx - dx res = numpy.dot(diff, diff) self.assertAlmostEqual(0, res) return + # End of class TestSASParser # ---------------------------------------------------------------------------- + @unittest.skipUnless(has_sas, _msg_nosas) class TestSASGenerator(unittest.TestCase): - def testGenerator(self): - # Test generator output - SphereModel = sasimport('sas.models.SphereModel').SphereModel + SphereModel = sasimport("sas.models.SphereModel").SphereModel model = SphereModel() gen = SASGenerator("sphere", model) @@ -88,8 +89,7 @@ def testGenerator(self): self.assertEqual(defval, par.getValue()) self.assertEqual(defval, model.getParam(pname)) - - r = numpy.arange(1, 10, 0.1, dtype = float) + r = numpy.arange(1, 10, 0.1, dtype=float) y = gen(r) refy = model.evalDistribution(r) diff = y - refy @@ -98,16 +98,14 @@ def testGenerator(self): return - def testGenerator2(self): - # Test generator with a profile - EllipsoidModel = sasimport('sas.models.EllipsoidModel').EllipsoidModel + EllipsoidModel = sasimport("sas.models.EllipsoidModel").EllipsoidModel model = EllipsoidModel() gen = SASGenerator("ellipsoid", model) # Load the data using SAS tools - Loader = sasimport('sas.dataloader.loader').Loader + Loader = sasimport("sas.dataloader.loader").Loader loader = Loader() data = datafile("sas_ellipsoid_testdata.txt") datainfo = loader.load(data) @@ -125,6 +123,7 @@ def testGenerator2(self): self.assertAlmostEqual(0, res) return + # End of class TestSASGenerator if __name__ == "__main__": diff --git a/src/diffpy/srfit/tests/testsgconstraints.py b/tests/test_sgconstraints.py similarity index 85% rename from src/diffpy/srfit/tests/testsgconstraints.py rename to tests/test_sgconstraints.py index 01c4d49b..4b985a21 100644 --- a/src/diffpy/srfit/tests/testsgconstraints.py +++ b/tests/test_sgconstraints.py @@ -18,15 +18,12 @@ import unittest import numpy - -from diffpy.srfit.tests.utils import datafile -from diffpy.srfit.tests.utils import has_pyobjcryst, _msg_nopyobjcryst -from diffpy.srfit.tests.utils import has_structure, _msg_nostructure +from utils import _msg_nopyobjcryst, _msg_nostructure, datafile, has_pyobjcryst, has_structure # ---------------------------------------------------------------------------- -class TestSGConstraints(unittest.TestCase): +class TestSGConstraints(unittest.TestCase): @unittest.skipUnless(has_pyobjcryst, _msg_nopyobjcryst) def test_ObjCryst_constrainSpaceGroup(self): """Make sure that all Parameters are constrained properly. @@ -50,18 +47,18 @@ def test_ObjCryst_constrainSpaceGroup(self): stru.sgpars.adppars # Check the orthorhombic lattice - l = stru.getLattice() - self.assertTrue( l.alpha.const ) - self.assertTrue( l.beta.const ) - self.assertTrue( l.gamma.const ) - self.assertEqual(pi/2, l.alpha.getValue()) - self.assertEqual(pi/2, l.beta.getValue()) - self.assertEqual(pi/2, l.gamma.getValue()) - - self.assertFalse( l.a.const ) - self.assertFalse( l.b.const ) - self.assertFalse( l.c.const ) - self.assertEqual(0, len(l._constraints)) + lattice = stru.getLattice() + self.assertTrue(lattice.alpha.const) + self.assertTrue(lattice.beta.const) + self.assertTrue(lattice.gamma.const) + self.assertEqual(pi / 2, lattice.alpha.getValue()) + self.assertEqual(pi / 2, lattice.beta.getValue()) + self.assertEqual(pi / 2, lattice.gamma.getValue()) + + self.assertFalse(lattice.a.const) + self.assertFalse(lattice.b.const) + self.assertFalse(lattice.c.const) + self.assertEqual(0, len(lattice._constraints)) # Now make sure the scatterers are constrained properly scatterers = stru.getScatterers() @@ -96,12 +93,12 @@ def test_ObjCryst_constrainSpaceGroup(self): # Nor can we make them into variables from diffpy.srfit.fitbase.fitrecipe import FitRecipe + f = FitRecipe() self.assertRaises(ValueError, f.addVar, mn.x) return - @unittest.skipUnless(has_structure, _msg_nostructure) def test_DiffPy_constrainAsSpaceGroup(self): """Test the constrainAsSpaceGroup function.""" @@ -111,14 +108,14 @@ def test_DiffPy_constrainAsSpaceGroup(self): stru = makeLaMnO3_P1() parset = DiffpyStructureParSet("LaMnO3", stru) - sgpars = constrainAsSpaceGroup(parset, "P b n m", - scatterers = parset.getScatterers()[::2], - constrainadps = True) + sgpars = constrainAsSpaceGroup( + parset, "P b n m", scatterers=parset.getScatterers()[::2], constrainadps=True + ) # Make sure that the new parameters were created for par in sgpars: self.assertNotEqual(None, par) - self.assertNotEqual(None, par.getValue() ) + self.assertNotEqual(None, par.getValue()) # Test the unconstrained atoms for scatterer in parset.getScatterers()[1::2]: @@ -137,10 +134,13 @@ def test_DiffPy_constrainAsSpaceGroup(self): def _consttest(par): return par.const + def _constrainedtest(par): return par.constrained + def _proxytest(par): return par in proxied + def _alltests(par): return _consttest(par) or _constrainedtest(par) or _proxytest(par) @@ -152,26 +152,24 @@ def _alltests(par): self.assertTrue(test) test = False - for par in [scatterer.U11, scatterer.U22, scatterer.U33, - scatterer.U12, scatterer.U13, scatterer.U23]: + for par in [scatterer.U11, scatterer.U22, scatterer.U33, scatterer.U12, scatterer.U13, scatterer.U23]: test |= _alltests(par) self.assertTrue(test) return - @unittest.skipUnless(has_structure, _msg_nostructure) def test_ConstrainAsSpaceGroup_args(self): - """Test the arguments processing of constrainAsSpaceGroup function. - """ + """Test the arguments processing of constrainAsSpaceGroup function.""" from diffpy.srfit.structure.diffpyparset import DiffpyStructureParSet from diffpy.srfit.structure.sgconstraints import constrainAsSpaceGroup from diffpy.structure.spacegroups import GetSpaceGroup + stru = makeLaMnO3_P1() parset = DiffpyStructureParSet("LaMnO3", stru) sgpars = constrainAsSpaceGroup(parset, "P b n m") - sg = GetSpaceGroup('P b n m') + sg = GetSpaceGroup("P b n m") parset2 = DiffpyStructureParSet("LMO", makeLaMnO3_P1()) sgpars2 = constrainAsSpaceGroup(parset2, sg) list(sgpars) @@ -179,20 +177,23 @@ def test_ConstrainAsSpaceGroup_args(self): self.assertEqual(sgpars.names, sgpars2.names) return + # End of class TestSGConstraints # Local helper functions ----------------------------------------------------- + def makeLaMnO3_P1(): from diffpy.structure import Structure + stru = Structure() - stru.read(datafile('LaMnO3.stru')) + stru.read(datafile("LaMnO3.stru")) return stru def makeLaMnO3(): - from pyobjcryst.crystal import Crystal from pyobjcryst.atom import Atom + from pyobjcryst.crystal import Crystal from pyobjcryst.scatteringpower import ScatteringPowerAtom pi = numpy.pi @@ -201,31 +202,32 @@ def makeLaMnO3(): crystal.SetName("LaMnO3") # La1 sp = ScatteringPowerAtom("La1", "La") - sp.SetBiso(8*pi*pi*0.003) + sp.SetBiso(8 * pi * pi * 0.003) atom = Atom(0.996096, 0.0321494, 0.25, "La1", sp) crystal.AddScatteringPower(sp) crystal.AddScatterer(atom) # Mn1 sp = ScatteringPowerAtom("Mn1", "Mn") - sp.SetBiso(8*pi*pi*0.003) + sp.SetBiso(8 * pi * pi * 0.003) atom = Atom(0, 0.5, 0, "Mn1", sp) crystal.AddScatteringPower(sp) crystal.AddScatterer(atom) # O1 sp = ScatteringPowerAtom("O1", "O") - sp.SetBiso(8*pi*pi*0.003) + sp.SetBiso(8 * pi * pi * 0.003) atom = Atom(0.0595746, 0.496164, 0.25, "O1", sp) crystal.AddScatteringPower(sp) crystal.AddScatterer(atom) # O2 sp = ScatteringPowerAtom("O2", "O") - sp.SetBiso(8*pi*pi*0.003) + sp.SetBiso(8 * pi * pi * 0.003) atom = Atom(0.720052, 0.289387, 0.0311126, "O2", sp) crystal.AddScatteringPower(sp) crystal.AddScatterer(atom) return crystal + # ---------------------------------------------------------------------------- if __name__ == "__main__": diff --git a/src/diffpy/srfit/tests/speedtest.py b/tests/test_speed.py similarity index 82% rename from src/diffpy/srfit/tests/speedtest.py rename to tests/test_speed.py index 373d5843..93ef32f9 100644 --- a/src/diffpy/srfit/tests/speedtest.py +++ b/tests/test_speed.py @@ -18,12 +18,12 @@ from __future__ import print_function import random + import numpy +from utils import _makeArgs -import diffpy.srfit.equation.visitors as visitors import diffpy.srfit.equation.literals as literals - -from diffpy.srfit.tests.utils import _makeArgs +import diffpy.srfit.equation.visitors as visitors x = numpy.arange(0, 20, 0.05) @@ -57,7 +57,7 @@ def makeLazyEquation(): mult2.addLiteral(exp) v2.setValue(x) - v3.setValue(50*x) + v3.setValue(50 * x) v5.setValue(2.11) v6.setValue(numpy.e) @@ -75,23 +75,27 @@ def _f(a, b, c, d, e): return _f + def makeEquation1(): """Make the same equation as the lazy one.""" - y = 50*x + y = 50 * x def _f(a, b, c, d, e): - return ((a+x)*(y-b))**c * d**e + return ((a + x) * (y - b)) ** c * d**e return _f + def timeFunction(f, *args, **kw): """Time a function in ms.""" import time + t1 = time.time() f(*args, **kw) t2 = time.time() - return (t2-t1)*1000 + return (t2 - t1) * 1000 + def speedTest1(): f1 = makeLazyEquation() @@ -102,7 +106,7 @@ def speedTest1(): total1 = 0 total2 = 0 for i in range(len(args)): - args[i] = 10*random.random() + args[i] = 10 * random.random() print("Changing argument %i" % (i + 1)) t1 = timeFunction(f1, *args) t2 = timeFunction(f2, *args) @@ -114,11 +118,12 @@ def speedTest1(): print("Totals:") print("lazy", total1) print("regular", total2) - print("Ratio (lazy/regular)", total1/total2) + print("Ratio (lazy/regular)", total1 / total2) -def speedTest2(mutate = 2): +def speedTest2(mutate=2): from diffpy.srfit.equation.builder import EquationFactory + factory = EquationFactory() x = numpy.arange(0, 20, 0.05) @@ -144,17 +149,19 @@ def speedTest2(mutate = 2): eq.b7.setValue(2.0) eq.b8.setValue(2.0) - from numpy import exp - from numpy import polyval + from numpy import exp, polyval + def f(A0, qsig, sigma1, sigma2, b1, b2, b3, b4, b5, b6, b7, b8): - return A0*exp(-(x*qsig)**2)*(exp(-((x-1.0)/sigma1)**2)+exp(-((x-2.0)/sigma2)**2)) + polyval([b8, b7, b6, b5,b4,b3,b2,b1],x) + return A0 * exp(-((x * qsig) ** 2)) * ( + exp(-(((x - 1.0) / sigma1) ** 2)) + exp(-(((x - 2.0) / sigma2) ** 2)) + ) + polyval([b8, b7, b6, b5, b4, b3, b2, b1], x) tnpy = 0 teq = 0 # Randomly change variables numargs = len(eq.args) choices = range(numargs) - args = [0.0]*(len(eq.args)) + args = [0.0] * (len(eq.args)) # The call-loop random.seed() @@ -174,15 +181,15 @@ def f(A0, qsig, sigma1, sigma2, b1, b2, b3, b4, b5, b6, b7, b8): tnpy += timeFunction(f, *args) teq += timeFunction(eq, *args) - print("Average call time (%i calls, %i mutations/call):" % - (numcalls, mutate)) - print("numpy: ", tnpy/numcalls) - print("equation: ", teq/numcalls) - print("ratio: ", teq/tnpy) + print("Average call time (%i calls, %i mutations/call):" % (numcalls, mutate)) + print("numpy: ", tnpy / numcalls) + print("equation: ", teq / numcalls) + print("ratio: ", teq / tnpy) return -def speedTest3(mutate = 2): + +def speedTest3(mutate=2): """Test wrt sympy. Results - sympy is 10 to 24 times faster without using arrays (ouch!). @@ -192,6 +199,7 @@ def speedTest3(mutate = 2): """ from diffpy.srfit.equation.builder import EquationFactory + factory = EquationFactory() x = numpy.arange(0, 20, 0.05) @@ -217,17 +225,25 @@ def speedTest3(mutate = 2): eq.b7.setValue(2.0) eq.b8.setValue(2.0) - from sympy import var, exp, lambdify from numpy import polyval - A0, qsig, sigma1, sigma2, b1, b2, b3, b4, b5, b6, b7, b8, xx = vars = var("A0 qsig sigma1 sigma2 b1 b2 b3 b4 b5 b6 b7 b8 xx") - f = lambdify(vars, A0*exp(-(xx*qsig)**2)*(exp(-((xx-1.0)/sigma1)**2)+exp(-((xx-2.0)/sigma2)**2)) + polyval([b1, b2, b3, b4, b5, b6, b7, b8], xx), "numpy") + from sympy import exp, lambdify, var + + A0, qsig, sigma1, sigma2, b1, b2, b3, b4, b5, b6, b7, b8, xx = vars = var( + "A0 qsig sigma1 sigma2 b1 b2 b3 b4 b5 b6 b7 b8 xx" + ) + f = lambdify( + vars, + A0 * exp(-((xx * qsig) ** 2)) * (exp(-(((xx - 1.0) / sigma1) ** 2)) + exp(-(((xx - 2.0) / sigma2) ** 2))) + + polyval([b1, b2, b3, b4, b5, b6, b7, b8], xx), + "numpy", + ) tnpy = 0 teq = 0 # Randomly change variables numargs = len(eq.args) choices = range(numargs) - args = [1.0]*(len(eq.args)) + args = [1.0] * (len(eq.args)) args.append(x) # The call-loop @@ -248,15 +264,15 @@ def speedTest3(mutate = 2): teq += timeFunction(eq, *(args[:-1])) tnpy += timeFunction(f, *args) - print("Average call time (%i calls, %i mutations/call):" % - (numcalls, mutate)) - print("sympy: ", tnpy/numcalls) - print("equation: ", teq/numcalls) - print("ratio: ", teq/tnpy) + print("Average call time (%i calls, %i mutations/call):" % (numcalls, mutate)) + print("sympy: ", tnpy / numcalls) + print("equation: ", teq / numcalls) + print("ratio: ", teq / tnpy) return -def speedTest4(mutate = 2): + +def speedTest4(mutate=2): """Test wrt sympy. Results - sympy is 10 to 24 times faster without using arrays (ouch!). @@ -266,6 +282,7 @@ def speedTest4(mutate = 2): """ from diffpy.srfit.equation.builder import EquationFactory + factory = EquationFactory() x = numpy.arange(0, 20, 0.05) @@ -276,8 +293,9 @@ def speedTest4(mutate = 2): factory.registerConstant("x", x) eq = factory.makeEquation(eqstr) - from sympy import var, lambdify from numpy import polyval + from sympy import lambdify, var + b1, b2, b3, b4, b5, b6, b7, b8, xx = vars = var("b1 b2 b3 b4 b5 b6 b7 b8 xx") f = lambdify(vars, polyval([b1, b2, b3, b4, b5, b6, b7, b8], xx), "numpy") @@ -286,7 +304,7 @@ def speedTest4(mutate = 2): # Randomly change variables numargs = len(eq.args) choices = range(numargs) - args = [1.0]*(len(eq.args)) + args = [1.0] * (len(eq.args)) args.append(x) # The call-loop @@ -307,18 +325,19 @@ def speedTest4(mutate = 2): teq += timeFunction(eq, *(args[:-1])) tnpy += timeFunction(f, *args) - print("Average call time (%i calls, %i mutations/call):" % - (numcalls, mutate)) - print("sympy: ", tnpy/numcalls) - print("equation: ", teq/numcalls) - print("ratio: ", teq/tnpy) + print("Average call time (%i calls, %i mutations/call):" % (numcalls, mutate)) + print("sympy: ", tnpy / numcalls) + print("equation: ", teq / numcalls) + print("ratio: ", teq / tnpy) return -def weightedTest(mutate = 2): + +def weightedTest(mutate=2): """Show the benefits of a properly balanced equation tree.""" from diffpy.srfit.equation.builder import EquationFactory + factory = EquationFactory() x = numpy.arange(0, 10, 0.01) @@ -338,20 +357,21 @@ def weightedTest(mutate = 2): eq.b7.setValue(2.0) eq.b8.setValue(2.0) - #scale = visitors.NodeWeigher() - #eq.root.identify(scale) - #print(scale.output) + # scale = visitors.NodeWeigher() + # eq.root.identify(scale) + # print(scale.output) from numpy import polyval + def f(b1, b2, b3, b4, b5, b6, b7, b8): - return polyval([b8, b7, b6, b5,b4,b3,b2,b1],x) + return polyval([b8, b7, b6, b5, b4, b3, b2, b1], x) tnpy = 0 teq = 0 # Randomly change variables numargs = len(eq.args) choices = range(numargs) - args = [0.1]*numargs + args = [0.1] * numargs # The call-loop random.seed() @@ -367,23 +387,23 @@ def f(b1, b2, b3, b4, b5, b6, b7, b8): c.remove(idx) args[idx] = random.random() - #print(args) + # print(args) # Time the different functions with these arguments teq += timeFunction(eq, *args) tnpy += timeFunction(f, *args) - print("Average call time (%i calls, %i mutations/call):" % - (numcalls, mutate)) - print("numpy: ", tnpy/numcalls) - print("equation: ", teq/numcalls) - print("ratio: ", teq/tnpy) + print("Average call time (%i calls, %i mutations/call):" % (numcalls, mutate)) + print("numpy: ", tnpy / numcalls) + print("equation: ", teq / numcalls) + print("ratio: ", teq / tnpy) return -def profileTest(): +def profileTest(): from diffpy.srfit.builder import EquationFactory + factory = EquationFactory() x = numpy.arange(0, 10, 0.001) @@ -406,7 +426,7 @@ def profileTest(): mutate = 8 numargs = len(eq.args) choices = range(numargs) - args = [0.1]*numargs + args = [0.1] * numargs # The call-loop random.seed() diff --git a/src/diffpy/srfit/tests/testtagmanager.py b/tests/test_tagmanager.py similarity index 90% rename from src/diffpy/srfit/tests/testtagmanager.py rename to tests/test_tagmanager.py index 5518f3aa..ea24cbc6 100644 --- a/src/diffpy/srfit/tests/testtagmanager.py +++ b/tests/test_tagmanager.py @@ -20,9 +20,9 @@ from diffpy.srfit.util.tagmanager import TagManager + ############################################################################## class TestTagManager(unittest.TestCase): - def setUp(self): self.m = TagManager() self.m.silent = False @@ -32,8 +32,7 @@ def tearDown(self): return def test_tag(self): - """check TagManager.tag() - """ + """check TagManager.tag()""" m = self.m obj = 3 m.tag(obj, "3", "three") @@ -48,8 +47,7 @@ def test_tag(self): return def test_untag(self): - """check TagManager.untag() - """ + """check TagManager.untag()""" m = self.m obj = 3 m.tag(obj, "3", "three", "tri", "tres", "trois") @@ -70,12 +68,11 @@ def test_untag(self): return def test_union_and_intersection(self): - """check TagManager.union() and TagManager.intersection() - """ + """check TagManager.union() and TagManager.intersection()""" m = self.m m.tag(3, "3", "number") m.tag(4, "4", "number") - objs = set([3,4]) + objs = set([3, 4]) self.assertEqual(m.union(), set()) self.assertEqual(m.union("number"), objs) self.assertEqual(m.union("3"), set([3])) @@ -95,22 +92,22 @@ def test_union_and_intersection(self): return def test_hasTags(self): - """check TagManager.hasTags() - """ + """check TagManager.hasTags()""" m = self.m m.tag(3, "3", "number") m.tag(4, "4", "number") - self.assertTrue( m.hasTags(3, "3") ) - self.assertTrue( m.hasTags(3, "3", "number") ) - self.assertFalse( m.hasTags(3, "3", "4") ) + self.assertTrue(m.hasTags(3, "3")) + self.assertTrue(m.hasTags(3, "3", "number")) + self.assertFalse(m.hasTags(3, "3", "4")) self.assertRaises(KeyError, m.hasTags, 3, "fail") m.silent = True self.assertFalse(m.hasTags(3, "fail")) return + # End of class TestTagManager -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..23983a0a --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,10 @@ +"""Unit tests for __version__.py +""" + +import diffpy.srfit + + +def test_package_version(): + """Ensure the package version is defined and not set to the initial placeholder.""" + assert hasattr(diffpy.srfit, "__version__") + assert diffpy.srfit.__version__ != "0.0.0" diff --git a/src/diffpy/srfit/tests/testvisitors.py b/tests/test_visitors.py similarity index 98% rename from src/diffpy/srfit/tests/testvisitors.py rename to tests/test_visitors.py index 0250f064..2a32d5e3 100644 --- a/src/diffpy/srfit/tests/testvisitors.py +++ b/tests/test_visitors.py @@ -17,12 +17,13 @@ import unittest -import diffpy.srfit.equation.visitors as visitors +from utils import _makeArgs + import diffpy.srfit.equation.literals as literals -from diffpy.srfit.tests.utils import _makeArgs +import diffpy.srfit.equation.visitors as visitors -class TestValidator(unittest.TestCase): +class TestValidator(unittest.TestCase): def testSimpleFunction(self): """Test a simple function.""" @@ -72,6 +73,7 @@ def testSimpleFunction(self): # Fix the operation of plus import numpy + plus.operation = numpy.add validator.reset() mult.identify(validator) @@ -85,8 +87,8 @@ def testSimpleFunction(self): return -class TestArgFinder(unittest.TestCase): +class TestArgFinder(unittest.TestCase): def testSimpleFunction(self): """Test a simple function.""" @@ -134,8 +136,8 @@ def testArg(self): self.assertTrue(args[0] is v1) return -class TestSwapper(unittest.TestCase): +class TestSwapper(unittest.TestCase): def testSimpleFunction(self): """Test a simple function.""" @@ -194,7 +196,7 @@ def testSimpleFunction(self): self.assertTrue(plus2.hasObserver(mult._flush)) # plus2 has no arguments yet. Verify this. - self.assertRaises(ValueError, mult.getValue) + self.assertRaises(TypeError, mult.getValue) # Add the arguments to plus2. plus2.addLiteral(v4) plus2.addLiteral(v5) diff --git a/src/diffpy/srfit/tests/testweakrefcallable.py b/tests/test_weakrefcallable.py similarity index 83% rename from src/diffpy/srfit/tests/testweakrefcallable.py rename to tests/test_weakrefcallable.py index a3d589ca..1178f661 100644 --- a/src/diffpy/srfit/tests/testweakrefcallable.py +++ b/tests/test_weakrefcallable.py @@ -18,46 +18,41 @@ """ -import unittest import pickle +import unittest from diffpy.srfit.fitbase import FitContribution from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.util.weakrefcallable import weak_ref, WeakBoundMethod +from diffpy.srfit.util.weakrefcallable import WeakBoundMethod, weak_ref # ---------------------------------------------------------------------------- -class TestWeakBoundMethod(unittest.TestCase): +class TestWeakBoundMethod(unittest.TestCase): def setUp(self): - self.f = FitContribution('f') - self.f.setEquation('7') + self.f = FitContribution("f") + self.f.setEquation("7") self.w = weak_ref(self.f._eq._flush, fallback=_fallback_example) return - def tearDown(self): self.f = None self.assertTrue(None is self.w._wref()) - obj, args, kw = self.w('any', 'argument', foo=37) + obj, args, kw = self.w("any", "argument", foo=37) self.assertTrue(obj is self.w) - self.assertEqual(('any', 'argument'), args) - self.assertEqual({'foo' : 37}, kw) + self.assertEqual(("any", "argument"), args) + self.assertEqual({"foo": 37}, kw) return - def test___init__(self): - """check WeakBoundMethod.__init__() - """ + """check WeakBoundMethod.__init__()""" self.assertTrue(self.w.fallback is _fallback_example) wf = weak_ref(self.f._flush) self.assertTrue(None is wf.fallback) return - def test___call__(self): - """check WeakBoundMethod.__call__() - """ + """check WeakBoundMethod.__call__()""" f = self.f self.assertEqual(7, f.evaluate()) self.assertEqual(7, f._eq._value) @@ -65,18 +60,16 @@ def test___call__(self): self.w(()) self.assertTrue(None is f._eq._value) # check WeakBoundMethod behavior with no fallback - x = Parameter('x', value=3) + x = Parameter("x", value=3) wgetx = weak_ref(x.getValue) self.assertEqual(3, wgetx()) del x self.assertRaises(ReferenceError, wgetx) return - def test___hash__(self): - """check WeakBoundMethod.__hash__() - """ - f1 = FitContribution('f1') + """check WeakBoundMethod.__hash__()""" + f1 = FitContribution("f1") w1 = weak_ref(f1._flush) h0 = hash(w1) del f1 @@ -87,11 +80,9 @@ def test___hash__(self): self.assertEqual(hash(w1c1), hash(w1c2)) return - def test___eq__(self): - """check WeakBoundMethod.__eq__() - """ - f1 = FitContribution('f1') + """check WeakBoundMethod.__eq__()""" + f1 = FitContribution("f1") w1 = weak_ref(f1._flush) w2 = weak_ref(f1._flush) self.assertEqual(w1, w2) @@ -108,10 +99,8 @@ def test___eq__(self): self.assertEqual(w1, w1cc) return - def test_pickling(self): - """Verify unpickling works when it involves __hash__ call. - """ + """Verify unpickling works when it involves __hash__ call.""" holder = set([self.w]) objs = [holder, self.f._eq, self.w] data = pickle.dumps(objs) @@ -121,13 +110,11 @@ def test_pickling(self): self.assertTrue(feq2 is w2._wref()) return - def test_observable_deregistration(self): - """check if Observable drops dead Observer. - """ + """check if Observable drops dead Observer.""" f = self.f - x = f.newParameter('x', 5) - f.setEquation('3 * x') + x = f.newParameter("x", 5) + f.setEquation("3 * x") self.assertEqual(15, f.evaluate()) self.assertEqual(15, f._eq._value) # get one of the observer callables that are associated with f @@ -146,15 +133,17 @@ def test_observable_deregistration(self): self.assertEqual(0, len(x._observers)) return + # End of class TestWeakBoundMethod # Local Routines ------------------------------------------------------------- + def _fallback_example(wbm, *args, **kwargs): return (wbm, args, kwargs) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # End of file diff --git a/src/diffpy/srfit/tests/testdata/LaMnO3.stru b/tests/testdata/LaMnO3.stru similarity index 100% rename from src/diffpy/srfit/tests/testdata/LaMnO3.stru rename to tests/testdata/LaMnO3.stru diff --git a/src/diffpy/srfit/tests/testdata/ni-q27r100-neutron.gr b/tests/testdata/ni-q27r100-neutron.gr similarity index 100% rename from src/diffpy/srfit/tests/testdata/ni-q27r100-neutron.gr rename to tests/testdata/ni-q27r100-neutron.gr diff --git a/src/diffpy/srfit/tests/testdata/ni.cif b/tests/testdata/ni.cif similarity index 100% rename from src/diffpy/srfit/tests/testdata/ni.cif rename to tests/testdata/ni.cif diff --git a/src/diffpy/srfit/tests/testdata/results.res b/tests/testdata/results.res similarity index 100% rename from src/diffpy/srfit/tests/testdata/results.res rename to tests/testdata/results.res diff --git a/src/diffpy/srfit/tests/testdata/sas_ascii_test_1.txt b/tests/testdata/sas_ascii_test_1.txt similarity index 100% rename from src/diffpy/srfit/tests/testdata/sas_ascii_test_1.txt rename to tests/testdata/sas_ascii_test_1.txt diff --git a/src/diffpy/srfit/tests/testdata/sas_ellipsoid_testdata.txt b/tests/testdata/sas_ellipsoid_testdata.txt similarity index 100% rename from src/diffpy/srfit/tests/testdata/sas_ellipsoid_testdata.txt rename to tests/testdata/sas_ellipsoid_testdata.txt diff --git a/src/diffpy/srfit/tests/testdata/si-q27r60-xray.gr b/tests/testdata/si-q27r60-xray.gr similarity index 100% rename from src/diffpy/srfit/tests/testdata/si-q27r60-xray.gr rename to tests/testdata/si-q27r60-xray.gr diff --git a/src/diffpy/srfit/tests/testdata/testdata.txt b/tests/testdata/testdata.txt similarity index 100% rename from src/diffpy/srfit/tests/testdata/testdata.txt rename to tests/testdata/testdata.txt diff --git a/src/diffpy/srfit/tests/utils.py b/tests/utils.py similarity index 78% rename from src/diffpy/srfit/tests/utils.py rename to tests/utils.py index 963bbd64..e2afb4be 100644 --- a/src/diffpy/srfit/tests/utils.py +++ b/tests/utils.py @@ -15,13 +15,13 @@ """Helper routines for testing.""" +import logging as logger import sys + import six import diffpy.srfit.equation.literals as literals from diffpy.srfit.sas.sasimport import sasimport -from diffpy.srfit.tests import logger - # Resolve availability of optional third-party packages. @@ -29,51 +29,58 @@ try: _msg_nosas = "No module named 'sas.pr.invertor'" - sasimport('sas.pr.invertor') + sasimport("sas.pr.invertor") _msg_nosas = "No module named 'sas.models'" - sasimport('sas.models') + sasimport("sas.models") has_sas = True except ImportError as e: has_sas = False - logger.warning('%s, SaS tests skipped.', e) + logger.warning("%s, SaS tests skipped.", e) # diffpy.structure _msg_nostructure = "No module named 'diffpy.structure'" try: - import diffpy.structure as m; del m + import diffpy.structure as m + + del m has_structure = True except ImportError: has_structure = False - logger.warning('Cannot import diffpy.structure, Structure tests skipped.') + logger.warning("Cannot import diffpy.structure, Structure tests skipped.") # pyobjcryst _msg_nopyobjcryst = "No module named 'pyobjcryst'" try: - import pyobjcryst as m; del m + import pyobjcryst as m + + del m has_pyobjcryst = True except ImportError: has_pyobjcryst = False - logger.warning('Cannot import pyobjcryst, pyobjcryst tests skipped.') + logger.warning("Cannot import pyobjcryst, pyobjcryst tests skipped.") # diffpy.srreal _msg_nosrreal = "No module named 'diffpy.srreal'" try: - import diffpy.srreal.pdfcalculator as m; del m + import diffpy.srreal.pdfcalculator as m + + del m has_srreal = True except ImportError: has_srreal = False - logger.warning('Cannot import diffpy.srreal, PDF tests skipped.') + logger.warning("Cannot import diffpy.srreal, PDF tests skipped.") # Helper functions for testing ----------------------------------------------- + def _makeArgs(num): args = [] for i in range(num): - j=i+1 - args.append(literals.Argument(name="v%i"%j, value=j)) + j = i + 1 + args.append(literals.Argument(name="v%i" % j, value=j)) return args @@ -83,6 +90,7 @@ def noObserversInGlobalBuilders(): Ensure objects are not immortal due to a reference from static value. """ from diffpy.srfit.equation.builder import _builders + rv = True for n, b in _builders.items(): if b.literal and b.literal._observers: @@ -93,13 +101,13 @@ def noObserversInGlobalBuilders(): def datafile(filename): from pkg_resources import resource_filename + rv = resource_filename(__name__, "testdata/" + filename) return rv def capturestdout(f, *args, **kwargs): - """Capture the standard output from a call of function f. - """ + """Capture the standard output from a call of function f.""" savestdout = sys.stdout fp = six.StringIO() try: @@ -109,4 +117,5 @@ def capturestdout(f, *args, **kwargs): sys.stdout = savestdout return fp.getvalue() + # End of file