diff --git a/.codecov.yml b/.codecov.yml index 04dd6510..5a94096e 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,34 +1,14 @@ -# codecov can find this file anywhere in the repo, so we don't need to clutter -# the root folder. -#comment: false - -codecov: - notify: - require_ci_to_pass: no - coverage: status: - patch: + project: # more options at https://docs.codecov.com/docs/commit-status default: - target: '70' - if_no_uploads: error - if_not_found: success - if_ci_failed: failure - project: - default: false - library: - target: auto - if_no_uploads: error - if_not_found: success - if_ci_failed: error - paths: '!*/tests/.*' - - tests: - target: 97.9% - paths: '*/tests/.*' - if_not_found: success - -flags: - tests: - paths: - - tests/ + target: auto # use the coverage from the base commit, fail if coverage is lower + threshold: 0% # allow the coverage to drop by + +comment: + layout: " diff, flags, files" + behavior: default + require_changes: false + require_base: false # [true :: must have a base report to post] + require_head: false # [true :: must have a head report to post] + hide_project_coverage: false # [true :: only show coverage on the git diff aka patch coverage] diff --git a/.codespell/ignore_lines.txt b/.codespell/ignore_lines.txt new file mode 100644 index 00000000..07fa7c8c --- /dev/null +++ b/.codespell/ignore_lines.txt @@ -0,0 +1,2 @@ +;; Please include filenames and explanations for each ignored line. +;; See https://docs.openverse.org/meta/codespell.html for docs. diff --git a/.codespell/ignore_words.txt b/.codespell/ignore_words.txt new file mode 100644 index 00000000..749b99e2 --- /dev/null +++ b/.codespell/ignore_words.txt @@ -0,0 +1,14 @@ +;; Please include explanations for each ignored word (lowercase). +;; See https://docs.openverse.org/meta/codespell.html for docs. + +;; abbreviation for "materials" often used in a journal title +mater + +;; alternative use of socioeconomic +socio-economic + +;; Frobenius norm used in np.linalg.norm +fro + +;; library used for Python package release, no longer used +rever diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index c02bc861..00000000 --- a/.coveragerc +++ /dev/null @@ -1,13 +0,0 @@ -[run] -source = - diffpy.pdfmorph -[report] -omit = - */python?.?/* - */site-packages/nose/* - # ignore _version.py and versioneer.py - .*version.* - *_version.py - -exclude_lines = - if __name__ == '__main__': diff --git a/.flake8 b/.flake8 index 2d2cb168..5a56eddd 100644 --- a/.flake8 +++ b/.flake8 @@ -5,7 +5,7 @@ exclude = build, dist, doc/source/conf.py -max-line-length = 115 +max-line-length = 79 # Ignore some style 'errors' produced while formatting by 'black' # https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#labels-why-pycodestyle-warnings extend-ignore = E203 diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index cb43882a..00000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -diffpy.pdfmorph/_version.py export-subst diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md index a87a44a8..0f560278 100644 --- a/.github/ISSUE_TEMPLATE/release_checklist.md +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -6,17 +6,30 @@ labels: "release" assignees: "" --- -### Release checklist for GitHub contributors +### 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 and - tested -- [ ] Successfully run any tutorial examples or do functional testing in some other way. -- [ ] Grammar and writing quality have been checked (no typos). +- [ ] 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 when you are ready for release. Include any additional comments necessary, such as -version information and details about the pre-release. +Please mention @sbillinge here when you are ready for PyPI/GitHub release. Include any additional comments necessary, such as +version information and details about the pre-release here: + +### 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/build-wheel-release-upload.yml b/.github/workflows/build-wheel-release-upload.yml index 2d790422..cd49342e 100644 --- a/.github/workflows/build-wheel-release-upload.yml +++ b/.github/workflows/build-wheel-release-upload.yml @@ -1,4 +1,4 @@ -name: Release (GitHub/PyPI) +name: Release (GitHub/PyPI) and Deploy Docs on: workflow_dispatch: @@ -10,7 +10,9 @@ jobs: release: uses: Billingegroup/release-scripts/.github/workflows/_build-wheel-release-upload.yml@v0 with: - project: diffpy.pdfmorph + project: diffpy.morph + github_admin_username: sbillinge + secrets: PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} PAT_TOKEN: ${{ secrets.PAT_TOKEN }} diff --git a/.github/workflows/check-news-item.yml b/.github/workflows/check-news-item.yml index 84d37cff..d672d34d 100644 --- a/.github/workflows/check-news-item.yml +++ b/.github/workflows/check-news-item.yml @@ -6,7 +6,7 @@ on: - main jobs: - build: + check-news-item: uses: Billingegroup/release-scripts/.github/workflows/_check-news-item.yml@v0 with: - project: diffpy.pdfmorph + project: diffpy.morph diff --git a/.github/workflows/matrix-and-codecov-on-merge-to-main.yml b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml index a865a270..4a5af435 100644 --- a/.github/workflows/matrix-and-codecov-on-merge-to-main.yml +++ b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml @@ -11,10 +11,10 @@ on: workflow_dispatch: jobs: - coverage: + matrix-coverage: uses: Billingegroup/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 with: - project: diffpy.pdfmorph + project: diffpy.morph c_extension: false headless: false secrets: diff --git a/.github/workflows/publish-docs-on-release.yml b/.github/workflows/publish-docs-on-release.yml deleted file mode 100644 index d358d16c..00000000 --- a/.github/workflows/publish-docs-on-release.yml +++ /dev/null @@ -1,14 +0,0 @@ -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.pdfmorph - c_extension: false diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index b33f974c..8d0a909f 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -8,9 +8,11 @@ on: workflow_dispatch: jobs: - validate: + tests-on-pr: uses: Billingegroup/release-scripts/.github/workflows/_tests-on-pr.yml@v0 with: - project: diffpy.pdfmorph + project: diffpy.morph c_extension: false headless: false + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.isort.cfg b/.isort.cfg index e0926f42..6d831957 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,4 +1,4 @@ [settings] -line_length = 115 +line_length = 79 multi_line_output = 3 include_trailing_comma = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3070e199..9cf0556f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,3 +44,9 @@ repos: name: Prevent Commit to Main Branch args: ["--branch", "main"] stages: [pre-commit] + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + additional_dependencies: + - tomli diff --git a/AUTHORS.rst b/AUTHORS.rst index dfff1afe..d9f94cb0 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -1,5 +1,5 @@ Authors -======= +======== Billinge Group and community contributors. @@ -7,4 +7,4 @@ Contributors ------------ For a list of contributors, visit -https://github.com/diffpy/diffpy.pdfmorph/graphs/contributors +https://github.com/diffpy/diffpy.morph/graphs/contributors diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7bf10279..ab9d9273 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,23 @@ Release Notes .. current developments +0.1.3 +===== + +**Added:** + +* Add GitHub action to build wheel, release, upload. +* Add issue and bug report templates. + +**Changed:** + +* README file installation instructions updated. + +**Fixed:** + +* Mathematical error in manual + + 0.1.2 ===== diff --git a/README.rst b/README.rst index 7abe1958..4c54d9b6 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,8 @@ -|Icon| `diffpy.pdfmorph `_ -==================================================================== +|Icon| `diffpy.morph `_ +============================================================== .. |Icon| image:: https://avatars.githubusercontent.com/diffpy - :target: https://diffpy.github.io/diffpy.pdfmorph + :target: https://diffpy.github.io/diffpy.morph :height: 100px |PyPi| |Forge| |PythonVersion| |PR| @@ -12,29 +12,29 @@ .. |Black| image:: https://img.shields.io/badge/code_style-black-black :target: https://github.com/psf/black -.. |CI| image:: https://github.com/diffpy/diffpy.pdfmorph/actions/workflows/matrix-and-codecov-on-merge-to-main.yml/badge.svg - :target: https://github.com/diffpy/diffpy.pdfmorph/actions/workflows/matrix-and-codecov-on-merge-to-main.yml +.. |CI| image:: https://github.com/diffpy/diffpy.morph/actions/workflows/matrix-and-codecov-on-merge-to-main.yml/badge.svg + :target: https://github.com/diffpy/diffpy.morph/actions/workflows/matrix-and-codecov-on-merge-to-main.yml -.. |Codecov| image:: https://codecov.io/gh/diffpy/diffpy.pdfmorph/branch/main/graph/badge.svg - :target: https://codecov.io/gh/diffpy/diffpy.pdfmorph +.. |Codecov| image:: https://codecov.io/gh/diffpy/diffpy.morph/branch/main/graph/badge.svg + :target: https://codecov.io/gh/diffpy/diffpy.morph -.. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/diffpy.pdfmorph - :target: https://anaconda.org/conda-forge/diffpy.pdfmorph +.. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/diffpy.morph + :target: https://anaconda.org/conda-forge/diffpy.morph .. |PR| image:: https://img.shields.io/badge/PR-Welcome-29ab47ff -.. |PyPi| image:: https://img.shields.io/pypi/v/diffpy.pdfmorph - :target: https://pypi.org/project/diffpy.pdfmorph/ +.. |PyPi| image:: https://img.shields.io/pypi/v/diffpy.morph + :target: https://pypi.org/project/diffpy.morph/ -.. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/diffpy.pdfmorph - :target: https://pypi.org/project/diffpy.pdfmorph/ +.. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/diffpy.morph + :target: https://pypi.org/project/diffpy.morph/ .. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue - :target: https://github.com/diffpy/diffpy.pdfmorph/issues + :target: https://github.com/diffpy/diffpy.morph/issues Tools for manipulating and comparing PDF profiles -PDFmorph is a Python software package designed to increase the insight +``diffpy.morph`` is a Python software package designed to increase the insight researchers can obtain from measured atomic pair distribution functions (PDFs) in a model-independent way. The program was designed to help a researcher answer the question: "Has my material undergone a phase @@ -44,42 +44,42 @@ One approach is to compare the two PDFs in a plot and view the difference curve underneath. However, significant signal can be seen in the difference curve from benign effects such as thermal expansion (peak shifts) and increased thermal motion (peak broadening) or a change in -scale due to differences in incident flux, for example. PDFmorph will +scale due to differences in incident flux, for example. ``diffpy.morph`` will do its best to correct for these benign effects before computing and plotting the difference curve. One measured PDF (typically that collected at higher temperature) is identified as the target PDF and the second PDF is then morphed by "stretching" (changing the r-axis to simulate a uniform lattice expansion), "smearing" (broadening peaks through a uniform convolution to simulate increased thermal motion), and "scaling" -(self-explanatory). PDFmorph will vary the amplitude of the morphing +(self-explanatory). ``diffpy.morph`` will vary the amplitude of the morphing transformations to obtain the best fit between the morphed and the target PDFs, then plot them on top of each other with the difference plotted below. There are also a few other morphing transformations in the program. -Finally, we note that PDFmorph should work on other spectra that are not +Finally, we note that ``diffpy.morph`` should work on other spectra that are not PDFs, though it has not been extensively tested beyond the PDF. -For more information about the diffpy.pdfmorph library, please consult our `online documentation `_. +For more information about the diffpy.morph library, please consult our `online documentation `_. Citation -------- -If you use diffpy.pdfmorph in a scientific publication, we would like you to cite this package as +If you use diffpy.morph in a scientific publication, we would like you to cite this package as - diffpy.pdfmorph Package, https://github.com/diffpy/diffpy.pdfmorph + diffpy.morph Package, https://github.com/diffpy/diffpy.morph REQUIREMENTS ------------------------------------------------------------------------ -PDFmorph is currently run from the command line, which requires opening +``diffpy.morph`` is currently run from the command line, which requires opening and typing into a terminal window or Windows command prompt. It is recommended that you consult online resources and become somewhat -familiar before using PDFmorph. +familiar before using ``diffpy.morph``. -PDFmorph can be run with Python 3.10 or higher. It makes use of several third party +``diffpy.morph`` can be run with Python 3.10 or higher. It makes use of several third party libraries that you'll need to run the app and its components. * `NumPy` - library for scientific computing with Python @@ -102,29 +102,29 @@ To add "conda-forge" to the conda channels, run the following in a terminal. :: conda config --add channels conda-forge We want to install our packages in a suitable conda environment. -The following creates and activates a new environment named ``diffpy.pdfmorph_env`` :: +The following creates and activates a new environment named ``diffpy.morph_env`` :: - conda create -n diffpy.pdfmorph_env diffpy.pdfmorph - conda activate diffpy.pdfmorph_env + conda create -n diffpy.morph_env diffpy.morph + conda activate diffpy.morph_env To confirm that the installation was successful, type:: - python -c "import diffpy.pdfmorph; print(diffpy.pdfmorph.__version__)" + python -c "import diffpy.morph; print(diffpy.morph.__version__)" The output should print the latest version displayed on the badges above. If the above does not work, you can use ``pip`` to download and install the latest release from `Python Package Index `_. -To install using ``pip`` into your ``diffpy.pdfmorph_env`` environment, we will also have to install dependencies :: +To install using ``pip`` into your ``diffpy.morph_env`` environment, we will also have to install dependencies :: - pip install -r https://raw.githubusercontent.com/diffpy/diffpy.pdfmorph/main/requirements/pip.txt + pip install -r https://raw.githubusercontent.com/diffpy/diffpy.morph/main/requirements/pip.txt and then install the package :: - pip install diffpy.pdfmorph + pip install diffpy.morph If you prefer to install from sources, after installing the dependencies, obtain the source archive from -`GitHub `_. Once installed, ``cd`` into your ``diffpy.pdfmorph`` directory +`GitHub `_. Once installed, ``cd`` into your ``diffpy.morph`` directory and run the following :: pip install . @@ -132,32 +132,32 @@ and run the following :: Getting Started --------------- -You may consult our `online documentation `_ for tutorials and API references. +You may consult our `online documentation `_ for tutorials and API references. -USING PDFmorph ------------------------------------------------------------------------- +USING diffpy.morph +------------------ For detailed instructions and full tutorial, consult the user manual -on our `website `. +on our `website `. -Once the required software, including PDFmorph is all installed, open +Once the required software, including ``diffpy.morph`` is all installed, open up a terminal and check installation has worked properly by running :: - source activate pdfmorph_env #if the environment isn't already active - pdfmorph -h #get some helpful information - pdfmorph --version + source activate diffpy.morph_env #if the environment isn't already active + diffpy.morph -h #get some helpful information + diffpy.morph --version If installed correctly, this last command should return the version -of PDFmorph that you have installed on your system. To begin using -PDFmorph, run a command like :: +of ``diffpy.morph`` that you have installed on your system. To begin using +``diffpy.morph``, run a command like :: - pdfmorph + diffpy.morph where both PDFs file are text files which contain PDF data, such as ``.gr`` or ``.cgr`` files that are produced by ``PDFgetX2``, ``PDFgetX3``, or ``PDFgui``. Though some file extensions other than ``.gr`` or ``.cgr``, but with the same content structure, have been shown to work with -PDFmorph, it is recommended to stick with ``.gr`` files. +``diffpy.morph``, it is recommended to stick with ``.gr`` files. Enjoy! @@ -165,16 +165,16 @@ Enjoy! Support and Contribute ---------------------- -`Diffpy user group `_ is the discussion forum for general questions and discussions about the use of diffpy.pdfmorph. Please join the diffpy.pdfmorph users community by joining the Google group. The diffpy.pdfmorph project welcomes your expertise and enthusiasm! +`Diffpy user group `_ is the discussion forum for general questions and discussions about the use of diffpy.morph. Please join the diffpy.morph users community by joining the Google group. The diffpy.morph 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 `_. +If you see a bug or want to request a feature, please `report it as an issue `_ and/or `submit a fix as a PR `_. You can also post it to the `Diffpy user group `_. -Feel free to fork the project and contribute. To install diffpy.pdfmorph +Feel free to fork the project and contribute. To install diffpy.morph in a development mode, with its sources being directly used by Python rather than copied to a package directory, use the following in the root directory :: - pip install -e . + 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. @@ -190,9 +190,9 @@ trying to commit again. Improvements and fixes are always appreciated. -Before contribuing, please read our `Code of Conduct `_. +Before contributing, please read our `Code of Conduct `_. Contact ------- -For more information on diffpy.pdfmorph please visit the project `web-page `_ or email Prof. Simon Billinge at sb2896@columbia.edu. +For more information on diffpy.morph please visit the project `web-page `_ or email Prof. Simon Billinge at sb2896@columbia.edu. diff --git a/TUTORIAL.rst b/TUTORIAL.rst index 114ebd2f..433c301a 100644 --- a/TUTORIAL.rst +++ b/TUTORIAL.rst @@ -1,7 +1,7 @@ -PDFmorph Tutorial -################# +diffpy.morph Tutorial +##################### -Welcome! This will be a quick tutorial to accquaint users with PDFmorph +Welcome! This will be a quick tutorial to accquaint users with ``diffpy.morph`` and some of what it can do. As we described in the README and installation instructions, please make @@ -11,23 +11,23 @@ before using this application. Before you've started this tutorial, please ensure that you've installed all necessary software and dependencies. -Basic PDFmorph Workflow ------------------------ +Basic diffpy.morph Workflow +--------------------------- 1. Open your Terminal or Command Prompt. - 2. It it's not active already, activate your PDFmorph-equipped + 2. It it's not active already, activate your ``diffpy.morph``-equipped conda environment by typing in :: - source activate + source activate - on Linux or ``activate `` on Windows. + on Linux or ``activate `` on Windows. * If you need to list your available conda environments, run the command ``conda info --envs`` or ``conda env list`` - * Run the ``pdfmorph --help`` command and read over the + * Run the ``diffpy.morph --help`` command and read over the info on that page for a brief overview of some of what we will explore in this tutorial. @@ -52,31 +52,31 @@ Basic PDFmorph Workflow those generated by the `PDFgui `_ program. - 4. First, we will run the PDFmorph application without any morphing + 4. First, we will run the ``diffpy.morph`` application without any morphing and only using one PDF. Type the following command into your Command Line :: - pdfmorph darkSub_rh20_C_01.gr darkSub_rh20_C_01.gr + diffpy.morph darkSub_rh20_C_01.gr darkSub_rh20_C_01.gr This should produce two PDF curves which are congruent, resulting in a flat green line underneath them. - 5. Now, we will see PDFmorph run with two different PDFs and no + 5. Now, we will see ``diffpy.morph`` run with two different PDFs and no morphing. Type the following command into your command line :: - pdfmorph darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr Without morphing, the difference Rw = 0.407. This indicates that the two PDFs vary drastically. - * While running the ``pdfmorph`` command, it is important + * While running the ``diffpy.morph`` command, it is important to remember that the first PDF file argument you provide (in this case, ``darkSub_rh20_C_01.gr``) is the PDF which will get morphed, while the second PDF file argument you provide (here, ``darkSub_rh20_C_44.gr``) is the PDF which acts as the model and does not get morphed. Hereinafter, we will refer to the first PDF argument as the "morph" - and the second as the "target", as the PDFmorph display + and the second as the "target", as the ``diffpy.morph`` display does. 6. Now, we will start the morphing process, which requires us to @@ -84,30 +84,30 @@ Basic PDFmorph Workflow and stretch, separately. We will start with the scaling factor. Begin by typing the command :: - pdfmorph --scale=2 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=2 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr Now, the difference Rw = 1.457, a significant increase from our value previously. We must modify our initial value for the scaling factor and do so until we see a reduction in the difference Rw from the unmorphed value. Type :: - pdfmorph --scale=0.9 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.9 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr The difference Rw is now 0.351, lower than our unmorphed - example's value. To see PDFmorph optimize the scale factor, + example's value. To see ``diffpy.morph`` optimize the scale factor, simply drop ``-a`` from the command and type :: - pdfmorph --scale=0.9 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.9 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr - PDFmorph, given a reasonable initial guess, will use find the + ``diffpy.morph``, given a reasonable initial guess, will use find the optimal value for each morphing feature. Here, we see that - PDFmorph displays ``scale = 0.799025`` in the command prompt, + ``diffpy.morph`` displays ``scale = 0.799025`` in the command prompt, meaning that it has found this to be the most optimal value for the scale factor. The difference Rw = 0.330, indicating a better fit than our reasonable initial guess. * It is the choice of the user whether or not to run values - before removing ``-a`` when analyzing data with PDFmorph. + before removing ``-a`` when analyzing data with ``diffpy.morph``. By including it, you allow the possibility to move towards convergence before allowing the program to optimize by removing it; when including it, you may reach a highly @@ -118,10 +118,10 @@ Basic PDFmorph Workflow 7. Now, we will examine the Gaussian smearing factor. We provide an initial guess by typing :: - pdfmorph --scale=0.8 --smear=0.5 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=0.5 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr And viewing the results. We've tailored our scale factor to be - close to the value given by PDFmorph, but see that the difference + close to the value given by ``diffpy.morph``, but see that the difference Rw has increased substantially due to our smear value. One approach, as described above, is to remove the ``-a`` from the above command and run it again. @@ -135,7 +135,7 @@ Basic PDFmorph Workflow an effect, we restrict the ``rmin`` and ``rmax`` values to reflect relevant data range by typing :: - pdfmorph --scale=0.8 --smear=0.5 --rmin=1.5 --rmax=30 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=0.5 --rmin=1.5 --rmax=30 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr Now, we see that the difference Rw = 0.204 and that the optimized ``smear=-0.084138``. @@ -148,10 +148,10 @@ Basic PDFmorph Workflow We are getting closer to an acceptably close fit to our data! - 8. Finally, we will examine the stretch factor. Provide an intial + 8. Finally, we will examine the stretch factor. Provide an initial guess by typing :: - pdfmorph --scale=0.8 --smear=-0.08 --stretch=0.5 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.5 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr And noting that the difference has increased. Before continuing, see if you can see which direction (higher or lower) our initial @@ -160,7 +160,7 @@ Basic PDFmorph Workflow If you cannot, type :: - pdfmorph --scale=0.8 --smear=-0.08 --stretch=0.005 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.005 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr to observe decreased difference and then remove ``-a`` to see the optimized ``--stretch=0.001762``. We have now reached @@ -174,8 +174,8 @@ Basic PDFmorph Workflow Enjoy the software! -.. Additional PDFmorph Functionality/Exploration -.. --------------------------------------------- +.. Additional diffpy.morph Functionality/Exploration +.. ------------------------------------------------- .. TODO include undoped PDF example, phase changed PDFs, and nano/non-nano PDFs @@ -183,5 +183,5 @@ Enjoy the software! Bug Reports ----------- -Please enjoy using our software! If you come accross any bugs in the +Please enjoy using our software! If you come across any bugs in the application, please report them to diffpy-dev@googlegroups.com diff --git a/doc/Makefile b/doc/Makefile index cb041d35..4dac79b8 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -6,7 +6,7 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build -BASENAME = $(subst .,,$(subst $() $(),,diffpy.pdfmorph)) +BASENAME = $(subst .,,$(subst $() $(),,diffpy.morph)) # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) diff --git a/doc/manual/acknowledgements.texinfo b/doc/manual/acknowledgements.texinfo index 3b5533c8..e45bf5f9 100644 --- a/doc/manual/acknowledgements.texinfo +++ b/doc/manual/acknowledgements.texinfo @@ -1,7 +1,7 @@ The PDFmorph app was developed by the Billinge group at Columbia University. The examples in the tutorial part of the manual were made possible by data collected by Soham Banerjee at Brookhaven National Laboratory, -Benjamin Frandsen during his PhD at Columbia Unversity, +Benjamin Frandsen during his PhD at Columbia University, and Long Yang from Tonji University. The DiffPy development team is the set of all contributors to DiffPy projects. diff --git a/doc/manual/pdfmorph.pdf b/doc/manual/pdfmorph.pdf index 06e470ca..779e49f4 100644 Binary files a/doc/manual/pdfmorph.pdf and b/doc/manual/pdfmorph.pdf differ diff --git a/doc/manual/pdfmorph.texinfo b/doc/manual/pdfmorph.texinfo index 7239202c..e8d7b3a5 100644 --- a/doc/manual/pdfmorph.texinfo +++ b/doc/manual/pdfmorph.texinfo @@ -156,7 +156,7 @@ it has not been extensively tested beyond the PDF. It runs on Windows, Mac OS, Linux, and all major Unix systems. The source code is freely available at @url{https://github.com/diffpy/diffpy.pdfmorph}. -If you come accross any bugs in the application, please open an +If you come across any bugs in the application, please open an @url{https://github.com/diffpy/diffpy.pdfmorph/issues, issue} or email diffpy-dev@@googlegroups.com. @@ -440,8 +440,8 @@ extra tutorial data @code{additionalData.zip}} before proceeding. @cindex performing multiple morphs It may be useful to morph a PDF against multiple targets: for example, you may want -to morph a PDF against a sequence of PDFs measured at various temepratures to determine -whether a phase change has occured. PDFmorph currently allows users to morph a PDF +to morph a PDF against a sequence of PDFs measured at various temperatures to determine +whether a phase change has occurred. PDFmorph currently allows users to morph a PDF against all files in a selected directory and plot resulting @math{R_W} values from each morph. It is advised that the lowest temperature PDF be that morphed and the higher temperature PDFs act as targets as the smear morph is only able to account for increases in thermal motion. @@ -462,10 +462,10 @@ Long Yang. @item Let us start by getting the @math{R_W} of @code{SrFe2As2_150K.gr} compared to the other, higher-temperature PDFs in the directory. Run @example -pdfmorph SrFe2As2_150K.gr . --multiple +pdfmorph SrFe2As2_150K.gr . --multiple-targets @end example @itemize -@item The @code{--multiple} tag tells @code{PDFmorph} to compare the morphed file +@item The @code{--multiple-targets} tag tells @code{PDFmorph} to compare the morphed file @code{SrFe2As2_150K.gr} against all PDFs in a directory. The directory we have supplied is @code{.}, which is shorthand for the current working directory. In our case, this is the @code{morphMultiple} directory. @@ -489,7 +489,7 @@ included are temperature, wavelength, and composition.} @end float @item By running @example -pdfmorph SrFe2As2_150K.gr . --multiple --sort-by=temperature +pdfmorph SrFe2As2_150K.gr . --multiple-targets --sort-by=temperature @end example we can sort the plotted @math{R_W} values by the temperature parameter included within each file. @itemize @@ -506,7 +506,7 @@ instead of @code{temperature}. a phase change. To be more certain, let us apply morphs to take into account isotropic expansion and differences in incident flux (stretching and scaling). @example -pdfmorph SrFe2As2_150K.gr . --scale=1 --stretch=0 --multiple \ +pdfmorph SrFe2As2_150K.gr . --scale=1 --stretch=0 --multiple-targets \ --sort-by=temperature @end example The change in @math{R_W} has become more pronounced. @@ -517,7 +517,7 @@ not significantly change the @math{R_W} values in this example. @item We can also change what is being plotted in the ordinate using @code{--plot-parameter}. In our case, it is useful to look at the @code{stretch} factor @example -pdfmorph SrFe2As2_150K.gr . --scale=1 --stretch=0 --multiple \ +pdfmorph SrFe2As2_150K.gr . --scale=1 --stretch=0 --multiple-targets \ --sort-by=temperature --plot-parameter=stretch @end example We can see that the stretch factor generally increases, but from @math{192K} to @math{198K}, there is no increase. @@ -533,7 +533,7 @@ can be done with @url{https://www.diffpy.org/products/pdfgui, PDFgui}. @end float @item Finally, let us save all the morphed PDFs into a directory named @code{savedMorphs}. @example -pdfmorph SrFe2As2_150K.gr . --scale=1 --stretch=0 --multiple \ +pdfmorph SrFe2As2_150K.gr . --scale=1 --stretch=0 --multiple-targets \ --sort-by=temperature --plot-parameter=stretch \ --save=savedMorphs @end example @@ -619,7 +619,7 @@ Again, we can begin by plotting the bulk material against our nanoparticle. @example pdfmorph Ni_bulk.gr Ni_nano_spheroid.cgr @end example -@item The nanoparticle shape of the calculated PDF is an oblate spheroid with equitorial +@item The nanoparticle shape of the calculated PDF is an oblate spheroid with equatorial radius of about @math{12} and polar radius of about @math{6} (this information is contained within the @code{Ni_nano_spheroid.cgr} file). To apply the spheroidal shape effects onto the bulk, run @@ -627,7 +627,7 @@ bulk, run pdfmorph Ni_bulk.gr Ni_nano_spheroid.cgr --radius=12 --pradius=6 -a @end example @itemize -@item The @code{--radius} option corresponds to the equitorial radius. +@item The @code{--radius} option corresponds to the equatorial radius. @item The @code{--pradius} option corresponds to the polar radius. @end itemize @item Run the same command without @code{-a} to refine. Refining should give @@ -792,14 +792,14 @@ using atomic pair distribution function analysis. Phys. Rev. B, 76(11), 115413. The available shape morphs are listed below: @itemize @item @code{--radius=RADIUS} - Multiply the PDF by the nanoparticle form factor for a sphere of radius @code{RADIUS}. -If used with @code{--pradius}, multiply the PDF by the nanoparticle form factor for a spheroid of equitorial radius +If used with @code{--pradius}, multiply the PDF by the nanoparticle form factor for a spheroid of equatorial radius @code{RADIUS} and polar radius @code{PRADIUS}. @itemize @item The sphere form factor was computed by Kodama et al. @footnote{Kodama, K., Iikubo, S., Taguchi, T., & Shamoto, S. (2006). Finite size effects of nanoparticles on the atomic pair distribution functions. Acta Crystallographica Section A, 62(6), 444–453. @url{https://doi.org/10.1107/S0108767306034635}}. @end itemize -@item @code{--pradius=PRADIUS} - Multiply the PDF by the nanoparticle form factor for a spheroid of equitorial radius +@item @code{--pradius=PRADIUS} - Multiply the PDF by the nanoparticle form factor for a spheroid of equatorial radius @code{RADIUS} and polar radius @code{PRADIUS}. @itemize @item The spheroid form factor was computed by Lei et al. @footnote{Lei, M., de Graff, A. M. R., Thorpe, M. F., Wells, @@ -807,9 +807,9 @@ S. A., & Sartbaeva, A. (2009). Uncovering the intrinsic geometry from the atomic nanomaterials. Phys. Rev. B, 80(2), 024118. @url{https://doi.org/10.1103/PhysRevB.80.024118}}. @end itemize @item @code{--iradius=IRADIUS} - Divide the PDF by the nanoparticle form factor for a sphere of radius @code{IRADIUS}. -If used with @code{--ipradius}, divide the PDF by the nanoparticle form factor for a spheroid of equitorial radius +If used with @code{--ipradius}, divide the PDF by the nanoparticle form factor for a spheroid of equatorial radius @code{IRADIUS} and polar radius @code{IPRADIUS}. -@item @code{--ipradius=IPRADIUS} - Divide the PDF by the nanoparticle form factor for a spheroid of equitorial radius +@item @code{--ipradius=IPRADIUS} - Divide the PDF by the nanoparticle form factor for a spheroid of equatorial radius @code{IRADIUS} and polar radius @code{IPRADIUS}. @end itemize @@ -842,7 +842,7 @@ post-expansion. The volume of the nanoparticle also increases by a factor @math{ @displaymath \gamma'_0(\vec{r}) = {1 \over {\alpha^3 V}} \int \int \int s'(\vec{r}')s'(\vec{r}'+\vec{r})d\vec{r}'. @end displaymath -Aplying a change of variables @math{\vec{r}' \rightarrow \vec{r}' / \alpha} gives +Applying a change of variables @math{\vec{r}' \rightarrow \vec{r}' / \alpha} gives @displaymath \gamma'_0(\vec{r}) = {1 \over {\alpha^3 V}} \int \int \int s'(\alpha \vec{r}')s'(\alpha \vec{r}'+\vec{r})\alpha^3d\vec{r}', @end displaymath @@ -877,8 +877,8 @@ the residual with the target PDF. @noindent @code{-s NAME, --save=NAME} @* @indent Save the morphed PDF into a file named @code{NAME}. You can use @code{-} to save to @code{stdout} instead. -When you have the @code{--multiple} tag enabled, multiple morphed PDFs are -generated. Using this command with the @code{--multiple} tag will save all +When you have the @code{--multiple-targets} tag enabled, multiple morphed PDFs are +generated. Using this command with the @code{--multiple-targets} tag will save all these morphs into a directory named @code{NAME} as well as a @code{.txt} file summary of refined morph parameters (if applicable) and @math{R_W} for each morph done. To specify names for each saved PDF file, use the @@ -963,7 +963,7 @@ requires us to apply a Gaussian dampening envelope centered at @math{r=0} with w @noindent @code{--radius=RADIUS} @* @indent Apply the nanoparticle form factor @math{\gamma_0} for a sphere of radius @code{RADIUS}. If @code{PRADIUS} is also specified, instead apply the characteristic function of a spheroid with -equitorial radius @code{RADIUS} and polar radius @code{PRADIUS}. +equatorial radius @code{RADIUS} and polar radius @code{PRADIUS}. @noindent @code{--pradius=PRADIUS} @* @indent If @code{RADIUS} is also specified, see @code{--radius}. Otherwise, apply the characteristic @@ -972,7 +972,7 @@ function of a sphere with radius @code{PRADIUS}. @noindent @code{--iradius=IRADIUS} @* @indent Apply the inverse characteristic function @math{1/\gamma_0} of a sphere of radius @code{IRADIUS}/ If @code{IPRADIUS} is also specified, instead apply the characteristic function of a spheroid with -equitorial radius @code{IRADIUS} and polar radius @code{PRADIUS}. +equatorial radius @code{IRADIUS} and polar radius @code{PRADIUS}. @noindent @code{--ipradius=IPRADIUS} @* @indent If @code{IRADIUS} is also specified, see @code{--iradius}. Otherwise, apply the characteristic @@ -981,7 +981,7 @@ function of a sphere with radius @code{IPRADIUS}. @noindent @titlefont{@b{Plot options}} These options control plotting. The morphed and target PDFs will be plotted against each other with a -difference curve shown below. The following changes occur when @code{--multiple} is enabled. +difference curve shown below. The following changes occur when @code{--multiple-targets} is enabled. (1) The @math{R_W} for each morphed PDF compared to the target will be plotted unless another parameter is specified by @code{--plot-parameter}. (2) The plot will be a bar chart where the abscissa names are the file names of the target PDFs unless otherwise specified by @@ -1020,11 +1020,11 @@ be useful as PDF amplitude can get very small for large @math{r}. No magnificati @noindent @titlefont{@b{Multiple morphs}} -@code{PDFmorph} allows one to morph one PDF against multiple different targets when @code{--multiple} +@code{PDFmorph} allows one to morph one PDF against multiple different targets when @code{--multiple-targets} is enabled. See @code{-s} and the description under "Plot options" for how saving and plotting -change when @code{--multiple} is enabled. +change when @code{--multiple-targets} is enabled. -@noindent @code{--multiple} +@noindent @code{--multiple-targets} @* @indent Changes usage of @code{PDFmorph} to @example pdfmorph @@ -1034,7 +1034,7 @@ in the directory @code{TARGET_DIRECTORY} as the target. Files in @code{TARGET_DI be sorted in ASCII sort order order unless a sorting parameter is specified by @code{sort-by}. @noindent @code{--sort-by=FIELD} -@* @indent Used with @code{--multiple}. Sort the files in @code{TARGET_DIRECTORY} by some parameter +@* @indent Used with @code{--multiple-targets}. Sort the files in @code{TARGET_DIRECTORY} by some parameter named @code{FIELD}. Parameters can be specified within each target PDF file by lines of the form @code{ = } in the header (anywhere above the @math{r} versus @math{G(r)} data table). @code{PDFmorph} will attempt to find a parameter named @code{FIELD} using a @@ -1042,17 +1042,17 @@ case-insensitive search. Numerical @code{PARAM_VALUE} will be sorted in ascendin non-numerical ones will be sorted in ASCII sort order. @noindent @code{--reverse} -@* @indent Used with @code{--multiple}. Sort the files in @code{TARGET_DIRECTORY} in reverse +@* @indent Used with @code{--multiple-targets}. Sort the files in @code{TARGET_DIRECTORY} in reverse ASCII sort order. If a parameter is given by @code{--sort-by}, reverse the order given by @code{--sort-by}. @noindent @code{--serial-file=SERIALFILE} -@* @indent Used with @code{--multiple} and @code{--sort-by}. Look for @code{FIELD} in a serial +@* @indent Used with @code{--multiple-targets} and @code{--sort-by}. Look for @code{FIELD} in a serial file named @code{SERIALFILE} instead. Only serial file types supported by @url{https://www.diffpy.org/diffpy.utils, diffpy.utils} such as @code{.json} are allowed. @noindent @code{--save-names-file=NAMESFILE} -@* @indent Used with @code{--multiple} and @code{-s}. Specify names for each manipulated PDF when +@* @indent Used with @code{--multiple-targets} and @code{-s}. Specify names for each manipulated PDF when saving using a serial file named @code{NAMESFILE}. The format of @code{NAMESFILE} should be as follows: (1) Each target PDF file name is an entry in @code{NAMESFILE}. (2) For each entry, there should be a key @code{save_morph_as} whose value specified the name to save the manipulated @@ -1076,7 +1076,7 @@ PDF as. An example @code{.json} @code{NAMESFILE} is below. such as @code{.json} are allowed. @noindent @code{--plot-parameter=PLOTPARAM} -@* @indent Used with @code{--multiple} and when plotting is enabled. Choose a parameter @code{PLOTPARAM} +@* @indent Used with @code{--multiple-targets} and when plotting is enabled. Choose a parameter @code{PLOTPARAM} to plot for each morph. When not specified, the @math{R_W} values for each morphed PDF compared to the target PDFs will be plotted. This option is not case sensitive meaning @code{--plot-parameter=Scale} and @code{--plot-parameter=scale} will both cause the parameter @code{scale} to be plotted. diff --git a/doc/source/api/diffpy.morph.morph_helpers.rst b/doc/source/api/diffpy.morph.morph_helpers.rst new file mode 100644 index 00000000..e8889166 --- /dev/null +++ b/doc/source/api/diffpy.morph.morph_helpers.rst @@ -0,0 +1,28 @@ +:tocdepth: -1 + +diffpy.morph.morph_helpers package +================================== + +.. automodule:: diffpy.morph.morph_helpers + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.morph.morph_helpers.transformpdftordf module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morph_helpers.transformpdftordf + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morph_helpers.transformrdftopdf module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morph_helpers.transformrdftopdf + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.morph.morphs.rst b/doc/source/api/diffpy.morph.morphs.rst new file mode 100644 index 00000000..c4b55150 --- /dev/null +++ b/doc/source/api/diffpy.morph.morphs.rst @@ -0,0 +1,92 @@ +:tocdepth: -1 + +diffpy.morph.morphs package +=========================== + +.. automodule:: diffpy.morph.morphs + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +diffpy.morph.morphs.morphishape module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphishape + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphchain module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphchain + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphresolution module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphresolution + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphshape module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphshape + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphshift module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphshift + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morph module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morph + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphrgrid module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphrgrid + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphsmear module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphsmear + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphstretch module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphstretch + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphs.morphscale module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphs.morphscale + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.morph.rst b/doc/source/api/diffpy.morph.rst new file mode 100644 index 00000000..f29ebe1a --- /dev/null +++ b/doc/source/api/diffpy.morph.rst @@ -0,0 +1,77 @@ +:tocdepth: -1 + +diffpy.morph package +==================== + +.. automodule:: diffpy.morph + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :titlesonly: + + diffpy.morph.morphs + diffpy.morph.morph_helpers + +Submodules +---------- + +diffpy.morph.pdfplot module +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.pdfplot + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morph_api module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morph_api + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.refine module +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.refine + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.tools module +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.tools + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morph_io module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morph_io + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.log module +^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.log + :members: + :undoc-members: + :show-inheritance: + +diffpy.morph.morphapp module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: diffpy.morph.morphapp + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/diffpy.pdfmorph.morph_helpers.rst b/doc/source/api/diffpy.pdfmorph.morph_helpers.rst deleted file mode 100644 index ccc7f6b2..00000000 --- a/doc/source/api/diffpy.pdfmorph.morph_helpers.rst +++ /dev/null @@ -1,28 +0,0 @@ -:tocdepth: -1 - -diffpy.pdfmorph.morph_helpers package -===================================== - -.. automodule:: diffpy.pdfmorph.morph_helpers - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -diffpy.pdfmorph.morph_helpers.transformpdftordf module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morph_helpers.transformpdftordf - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morph_helpers.transformrdftopdf module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morph_helpers.transformrdftopdf - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/source/api/diffpy.pdfmorph.morphs.rst b/doc/source/api/diffpy.pdfmorph.morphs.rst deleted file mode 100644 index a69f5eb7..00000000 --- a/doc/source/api/diffpy.pdfmorph.morphs.rst +++ /dev/null @@ -1,92 +0,0 @@ -:tocdepth: -1 - -diffpy.pdfmorph.morphs package -============================== - -.. automodule:: diffpy.pdfmorph.morphs - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -diffpy.pdfmorph.morphs.morphishape module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphishape - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphchain module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphchain - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphresolution module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphresolution - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphshape module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphshape - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphshift module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphshift - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morph module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morph - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphrgrid module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphrgrid - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphsmear module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphsmear - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphstretch module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphstretch - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.morphs.morphscale module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.morphs.morphscale - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/source/api/diffpy.pdfmorph.rst b/doc/source/api/diffpy.pdfmorph.rst deleted file mode 100644 index 57178852..00000000 --- a/doc/source/api/diffpy.pdfmorph.rst +++ /dev/null @@ -1,77 +0,0 @@ -:tocdepth: -1 - -diffpy.pdfmorph package -======================= - -.. automodule:: diffpy.pdfmorph - :members: - :undoc-members: - :show-inheritance: - -Subpackages ------------ - -.. toctree:: - :titlesonly: - - diffpy.pdfmorph.morphs - diffpy.pdfmorph.morph_helpers - -Submodules ----------- - -diffpy.pdfmorph.pdfplot module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.pdfplot - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.pdfmorph_api module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.pdfmorph_api - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.refine module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.refine - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.tools module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.tools - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.pdfmorph_io module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.pdfmorph_io - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.log module -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.log - :members: - :undoc-members: - :show-inheritance: - -diffpy.pdfmorph.pdfmorphapp module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automodule:: diffpy.pdfmorph.pdfmorphapp - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/source/conf.py b/doc/source/conf.py index 92959232..aad111d5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# diffpy.pdfmorph documentation build configuration file, created by +# diffpy.morph 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 @@ -63,7 +63,7 @@ master_doc = "index" # General information about the project. -project = "diffpy.pdfmorph" +project = "diffpy.morph" 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 @@ -113,7 +113,7 @@ pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -modindex_common_prefix = ["diffpy.pdfmorph"] +modindex_common_prefix = ["diffpy.morph"] # Display all warnings for missing links. nitpicky = True @@ -204,7 +204,7 @@ # html_file_suffix = None # Output file base name for HTML help builder. -basename = "diffpy.pdfmorph".replace(" ", "").replace(".", "") +basename = "diffpy.morph".replace(" ", "").replace(".", "") htmlhelp_basename = basename + "doc" @@ -223,7 +223,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ("index", "diffpy.pdfmorph.tex", "diffpy.pdfmorph Documentation", ab_authors, "manual"), + ( + "index", + "diffpy.morph.tex", + "diffpy.morph Documentation", + ab_authors, + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -251,7 +257,15 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [("index", "diffpy.pdfmorph", "diffpy.pdfmorph Documentation", ab_authors, 1)] +man_pages = [ + ( + "index", + "diffpy.morph", + "diffpy.morph Documentation", + ab_authors, + 1, + ) +] # If true, show URL addresses after external links. # man_show_urls = False @@ -265,10 +279,10 @@ texinfo_documents = [ ( "index", - "diffpy.pdfmorph", - "diffpy.pdfmorph Documentation", + "diffpy.morph", + "diffpy.morph Documentation", ab_authors, - "diffpy.pdfmorph", + "diffpy.morph", "One line description of project.", "Miscellaneous", ), diff --git a/doc/source/images/ex_tutorial_bar.png b/doc/source/images/ex_tutorial_bar.png new file mode 100644 index 00000000..260b0a7e Binary files /dev/null and b/doc/source/images/ex_tutorial_bar.png differ diff --git a/doc/source/images/ex_tutorial_temp.png b/doc/source/images/ex_tutorial_temp.png new file mode 100644 index 00000000..c9bdedc3 Binary files /dev/null and b/doc/source/images/ex_tutorial_temp.png differ diff --git a/doc/source/images/qs_tutorial_morphed.png b/doc/source/images/qs_tutorial_morphed.png new file mode 100644 index 00000000..96458a59 Binary files /dev/null and b/doc/source/images/qs_tutorial_morphed.png differ diff --git a/doc/source/images/qs_tutorial_scaled.png b/doc/source/images/qs_tutorial_scaled.png new file mode 100644 index 00000000..ff50a254 Binary files /dev/null and b/doc/source/images/qs_tutorial_scaled.png differ diff --git a/doc/source/images/qs_tutorial_unmorphed.png b/doc/source/images/qs_tutorial_unmorphed.png new file mode 100644 index 00000000..1747d318 Binary files /dev/null and b/doc/source/images/qs_tutorial_unmorphed.png differ diff --git a/doc/source/index.rst b/doc/source/index.rst index eebd5462..9044436d 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,8 +1,8 @@ ############################################# -diffpy.pdfmorph documentation +diffpy.morph documentation ############################################# -diffpy.pdfmorph - Tools for manipulating and comparing PDF profiles. +diffpy.morph - Tools for manipulating and comparing diffraction data. | Software version |release|. | Last updated |today|. @@ -10,7 +10,7 @@ diffpy.pdfmorph - Tools for manipulating and comparing PDF profiles. Introduction ++++++++++++ -``PDFmorph`` is a Python package that increases the insight +``diffpy.morph`` is a Python package that increases the insight researchers can obtain from measured atomic pair distribution functions (PDFs) in a model-independent way. It was designed to help a researcher answer the question: "Has my material undergone a phase @@ -20,45 +20,44 @@ One approach is to compare the two PDFs in a plot and view the difference curve underneath. However, significant signal can be seen in the difference curve from benign effects such as thermal expansion (peak shifts) and increased thermal motion (peak broadening) or a change in -scale due to differences in incident flux, for example. ``PDFmorph`` will +scale due to differences in incident flux, for example. ``diffpy.morph`` will do its best to correct for these benign effects before computing and plotting the difference curve. One measured PDF (typically that collected under higher temperature) is identified as the target PDF and the second PDF is then morphed by "stretching" (changing the r-axis to simulate a uniform lattice expansion), "smearing" (broadening peaks through a uniform convolution to simulate increased thermal motion), and -"scaling" (self-explanatory). PDFmorph will vary the amplitude of the +"scaling" (self-explanatory). ``diffpy.morph`` will vary the amplitude of the morphing transformations to obtain the best fit between the morphed and the target PDFs, then plot them on top of each other with the difference plotted below. There are also a few other morphing transformations in the program. -If no morphing transformation is specified, ``PDFmorph`` will return just +If no morphing transformation is specified, ``diffpy.morph`` will return just the plotted PDFs. -Finally, we note that though ``PDFmorph`` should work on other spectra +Finally, we note that though ``diffpy.morph`` should work on other spectra that are not PDFs, it has not been extensively tested beyond the PDF. -To get started, please download our :download:`user manual <../manual/pdfmorph.pdf>` -or visit the :ref:`quick_start`. +To get started, please visit the :ref:`quick_start`. -======= +======== Authors -======= +======== -``PDFmorph`` is developed by members of the Billinge Group at +``diffpy.morph`` is developed by members of the Billinge Group at Columbia University and Brookhaven National Laboratory including Christopher L. Farrow, Christopher J. Wright, Pavol Juhás, Chia-Hao (Timothy) Liu, Andrew Yang, and Simon J. L. Billinge. For a detailed list of contributors see -https://github.com/diffpy/diffpy.pdfmorph/graphs/contributors. +https://github.com/diffpy/diffpy.morph/graphs/contributors. ============ Installation ============ -See the `README `_ +See the `README `_ file included with the distribution. ================= @@ -70,11 +69,11 @@ Table of contents quickstart license release - Package API + Package API -======= +======== Indices -======= +======== * :ref:`genindex` * :ref:`search` diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst index d8935198..0164ce20 100644 --- a/doc/source/quickstart.rst +++ b/doc/source/quickstart.rst @@ -1,11 +1,16 @@ .. _quick_start: -PDFmorph Tutorial -################# +diffpy.morph Tutorial +##################### + +Welcome! This will be a quick tutorial to accquaint users with ``diffpy.morph`` +and some of what it can do. To see more details and definitions about +the morphs please see the publication describing ``diffpy.morph``. + +To be published: + +* -Welcome! This will be a quick tutorial to accquaint users with PDFmorph -and some of what it can do. For a more detailed tutorial, check out -our :download:`user manual <../manual/pdfmorph.pdf>`. As we described in the README and installation instructions, please make sure that you are familiar with working with your command line terminal @@ -14,22 +19,27 @@ before using this application. Before you've started this tutorial, please ensure that you've installed all necessary software and dependencies. -Basic PDFmorph Workflow -======================= +In this tutorial, we will demonstrate how to use ``diffpy.morph`` to compare +two +PDFs measured from the same material at different temperatures. +The morphs showcased include "stretch", "scale", and "smear". + +Basic diffpy.morph Workflow +=========================== 1. Open your Terminal or Command Prompt. - 2. If it's not active already, activate your PDFmorph-equipped + 2. If it's not active already, activate your diffpy.morph-equipped conda environment by typing in :: - conda activate + conda activate * If you need to list your available conda environments, run the command ``conda info --envs`` or ``conda env list`` - * Run the ``pdfmorph --help`` command and read over the + * Run the ``diffpy.morph --help`` command and read over the info on that page for a brief overview of some of what we will explore in this tutorial. @@ -51,65 +61,72 @@ Basic PDFmorph Workflow * Note that these files have the ``.gr`` extension, which indicates that they are measured PDFs. The ``.cgr`` file extension indicates that a file is a calculated PDF, such as - those generated by the `PDFgui `_ + those generated by the + `PDFgui `_ program. - 4. First, we will run the PDFmorph application without any morphing + 4. First, we will run the ``diffpy.morph`` application without any morphing and only using one PDF. Type the following command into your command line :: - pdfmorph darkSub_rh20_C_01.gr darkSub_rh20_C_01.gr + diffpy.morph darkSub_rh20_C_01.gr darkSub_rh20_C_01.gr This should produce two PDF curves which are congruent, resulting in a flat green line underneath them. - 5. Now, we will see PDFmorph run with two different PDFs and no + 5. Now, we will see ``diffpy.morph`` run with two different PDFs and no morphing. Type the following command into your command line :: - pdfmorph darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr Without morphing, the difference Rw = 0.407. This indicates that the two PDFs vary drastically. - * While running the ``pdfmorph`` command, it is important + * While running the ``diffpy.morph`` command, it is important to remember that the first PDF file argument you provide (in this case, ``darkSub_rh20_C_01.gr``) is the PDF which will get morphed, while the second PDF file argument you provide (here, ``darkSub_rh20_C_44.gr``) is the PDF which acts as the model and does not get morphed. Hereinafter, we will refer to the first PDF argument as the "morph" - and the second as the "target", as the PDFmorph display + and the second as the "target", as the ``diffpy.morph`` display does. + .. figure:: images/qs_tutorial_unmorphed.png + :align: center + :figwidth: 100% + + Using ``diffpy.morph`` to compare two different PDFs without morphing. + 6. Now, we will start the morphing process, which requires us to provide initial guesses for our scaling factor, Gaussian smear, and stretch, separately. We will start with the scaling factor. Begin by typing the command :: - pdfmorph --scale=2 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=2 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr Now, the difference Rw = 1.457, a significant increase from our value previously. We must modify our initial value for the scaling factor and do so until we see a reduction in the difference Rw from the unmorphed value. Type :: - pdfmorph --scale=0.9 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.9 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr The difference Rw is now 0.351, lower than our unmorphed - example's value. To see PDFmorph optimize the scale factor, + example's value. To see ``diffpy.morph`` optimize the scale factor, simply drop ``-a`` from the command and type :: - pdfmorph --scale=0.9 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.9 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr - PDFmorph, given a reasonable initial guess, will use find the + ``diffpy.morph``, given a reasonable initial guess, will use find the optimal value for each morphing feature. Here, we see that - PDFmorph displays ``scale = 0.799025`` in the command prompt, + ``diffpy.morph`` displays ``scale = 0.799025`` in the command prompt, meaning that it has found this to be the most optimal value for the scale factor. The difference Rw = 0.330, indicating a better fit than our reasonable initial guess. * It is the choice of the user whether or not to run values - before removing ``-a`` when analyzing data with PDFmorph. + before removing ``-a`` when analyzing data with ``diffpy.morph``. By including it, you allow the possibility to move towards convergence before allowing the program to optimize by removing it; when including it, you may reach a highly @@ -117,13 +134,19 @@ Basic PDFmorph Workflow In this tutorial, we will use it every time to check for convergence. + .. figure:: images/qs_tutorial_scaled.png + :align: center + :figwidth: 100% + + ``diffpy.morph`` found an optimal value for the scale factor. + 7. Now, we will examine the Gaussian smearing factor. We provide an initial guess by typing :: - pdfmorph --scale=0.8 --smear=0.5 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=0.5 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr And viewing the results. We've tailored our scale factor to be - close to the value given by PDFmorph, but see that the difference + close to the value given by ``diffpy.morph``, but see that the difference Rw has increased substantially due to our smear value. One approach, as described above, is to remove the ``-a`` from the above command and run it again. @@ -137,7 +160,7 @@ Basic PDFmorph Workflow an effect, we restrict the ``rmin`` and ``rmax`` values to reflect relevant data range by typing :: - pdfmorph --scale=0.8 --smear=0.5 --rmin=1.5 --rmax=30 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=0.5 --rmin=1.5 --rmax=30 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr Now, we see that the difference Rw = 0.204 and that the optimized ``smear=-0.084138``. @@ -150,10 +173,10 @@ Basic PDFmorph Workflow We are getting closer to an acceptably close fit to our data! - 8. Finally, we will examine the stretch factor. Provide an intial + 8. Finally, we will examine the stretch factor. Provide an initial guess by typing :: - pdfmorph --scale=0.8 --smear=-0.08 --stretch=0.5 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.5 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr And noting that the difference has increased. Before continuing, see if you can see which direction (higher or lower) our initial @@ -162,12 +185,18 @@ Basic PDFmorph Workflow If you cannot, type :: - pdfmorph --scale=0.8 --smear=-0.08 --stretch=0.005 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr + diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.005 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr to observe decreased difference and then remove ``-a`` to see the optimized ``--stretch=0.001762``. We have now reached the optimal fit for our PDF! + .. figure:: images/qs_tutorial_morphed.png + :align: center + :figwidth: 100% + + The optimal fit after applying the scale, smear, and stretch morphs. + 9. Now, try it on your own! If you have personally collected or otherwise readily available PDF data, try this process to see if you can morph your PDFs to one another. Many of the parameters @@ -176,14 +205,14 @@ Basic PDFmorph Workflow Enjoy the software! -.. Additional PDFmorph Functionality/Exploration -.. --------------------------------------------- +.. Additional diffpy.morph Functionality/Exploration +.. ------------------------------------------------- .. TODO include undoped PDF example Extra Tutorials =============== -PDFmorph has some more functionalities not showcased in the basic workflow above -(see `pdfmorph --help` for an overview of these functionalities). +``diffpy.morph`` has some more functionalities not showcased in the basic workflow above +(see `diffpy.morph --help` for an overview of these functionalities). Tutorials for these additional functionalities are included below. Additional files for these tutorials can be downloaded :download:`here <../../tutorial/additionalData.zip>`. @@ -193,106 +222,183 @@ Performing Multiple Morphs It may be useful to morph a PDF against multiple targets: for example, you may want to morph a PDF against multiple PDFs measured -at various temepratures to determine whether a phase change has occured. -PDFmorph currently allows users to morph a PDF against all files in a +at various temperatures to determine whether a phase change has occurred. +``diffpy.morph`` currently allows users to morph a PDF against all files in a selected directory and plot resulting :math:`R_w` values from each morph. -1. Within the ``additionalData`` directory, ``cd`` into the ``morphMultiple`` directory. - Inside, you will find multiple PDFs of :math:`SrFe_2As_2` measured at various temperatures. - These PDFs are from `"Atomic Pair Distribution Function Analysis: A primer" `_. -2. Let us start by getting the Rw of ``SrFe2As2_150K.gr`` compared to all other files in the - directory. Run :: +1. Within the ``additionalData`` directory, ``cd`` into the + ``morphsequence`` directory. Inside, you will find multiple PDFs of + :math:`SrFe_2As_2` measured at various temperatures. These PDFs are + from `"Atomic Pair Distribution Function Analysis: A primer" + `_. + +2. Let us start by getting the Rw of ``SrFe2As2_150K.gr`` compared to + all other files in the directory. Run :: + + diffpy.morph SrFe2As2_150K.gr . --multiple-targets + + The multiple tag indicates we are comparing PDF file (first input) + against all PDFs in a directory (second input). Our choice of file + was ``SeFe2As2_150K.gr`` and directory was the cwd, which should be + ``morphsequence``.:: + + diffpy.morph SrFe2As2_150K.gr . --multiple-targets --sort-by=temperature + +.. figure:: images/ex_tutorial_bar.png + :align: center + :figwidth: 100% - pdfmorph SrFe2As2_150K.gr . --multiple + Bar chart of :math:`R_W` values for each target file. Target files are + listed in ASCII sort order. - The multiple tag indicates we are comparing PDF file (first input) against all PDFs in - a directory (second input). Our choice of file was ``SeFe2As2_150K.gr`` - and directory was the cwd, which should be ``morphMultiple``. -3. After running this, we get chart of Rw values for each target file. However, this chart can - be a bit confusing to interpret. To get a more understandable plot, run :: +3. After running this, we get chart of Rw values for each target file. + However, this chart can be a bit confusing to interpret. To get a + more understandable plot, run :: - pdfmorph SrFe2As2_150K.gr . --multiple --sort-by=temperature + diffpy.morph SrFe2As2_150K.gr . --multiple-targets --sort-by=temperature - This plots the Rw against the temperature parameter value provided at the top of each file. - Parameters are entries of the form `` = `` and are located - above the ``r`` versus ``gr`` table in each PDF file. -4. Between 192K and 198K, the Rw has a sharp increase, indicating that we may have a phase change. - To confirm, let us now apply morphs onto ``SrFe2As2_150K.gr`` with all other files in ``morphMultiple`` - as targets :: + This plots the Rw against the temperature parameter value provided + at the top of each file. Parameters are entries of the form + `` = `` and are located above + the ``r`` versus ``gr`` table in each PDF file.:: - pdfmorph --scale=1 --stretch=0 SrFe2As2_150K.gr . --multiple --sort-by=temperature + # SrFe2As2_150K.gr + [PDF Parameters] + temperature = 150 + wavelength = 0.1 + ... + +.. figure:: images/ex_tutorial_temp.png + :align: center + :figwidth: 100% + + The :math:`R_W` plotted against the temperature the target PDF was + measured at. + +4. Between 192K and 198K, the Rw has a sharp increase, indicating that + we may have a phase change. To confirm, let us now apply morphs + onto `` SrFe2As2_150K.gr`` with all other files in + ``morphsequence`` as targets :: + + diffpy.morph --scale=1 --stretch=0 SrFe2As2_150K.gr . --multiple-targets --sort-by=temperature + + Note that we are not applying a smear since it takes a long time to + apply and does not significantly change the Rw values in this example. - Note that we are not applying a smear since it takes a long time to apply and does not significantly - change the Rw values in this example. 5. We should now see a sharper increase in Rw between 192K and 198K. + 6. Go back to the terminal to see optimized morphing parameters from each morph. -7. On the morph with ``SrFe2As2_192K.gr`` as target, ``scale = 0.972085`` and ``stretch = 0.000508`` - and with ``SrFe2As2_198K.gr`` as target, ``scale = 0.970276`` and ``stretch = 0.000510``. - These are very similar, meaning that thermal lattice expansion (accounted for by ``stretch``) - is not occurring. This, coupled with the fact that the Rw significantly increases suggests - a phase change in this temperature regime. (In fact, :math:`SrFe_2As_2` does transition from - orthorhombic at lower temperature to tetragonal at higher temperature!) + +7. On the morph with ``SrFe2As2_192K.gr`` as target, ``scale = + 0.972085`` and ``stretch = 0.000508`` and with ``SrFe2As2_198K.gr`` + as target, ``scale = 0.970276`` and ``stretch = 0.000510``. These + are very similar, meaning that thermal lattice expansion (accounted + for by ``stretch``) is not occurring. This, coupled with the fact + that the Rw significantly increases suggests a phase change in this + temperature regime. (In fact, :math:`SrFe_2As_2` does transition + from orthorhombic at lower temperature to tetragonal at higher + temperature!). More sophisticated analysis can be done with + `PDFgui `_. + +8. Finally, let us save all the morphed PDFs into a directory + named ``saved-morphs``. :: + + diffpy.morph SrFe2As2_150K.gr . --scale=1 --stretch=0 --multiple-targets \ + --sort-by=temperature --plot-parameter=stretch \ + --save=saved-morphs + + Entering the directory with ``cd`` and viewing its contents with + ``ls``, we see a file named ``morph-reference-table.txt`` with data + about the input morph parameters and re- fined output parameters + and a directory named ``morphs`` containing all the morphed + PDFs. See the ``--save-names-file`` option to see how you can set + the names for these saved morphs! Nanoparticle Shape Effects -------------------------- A nanoparticle's finite size and shape can affect the shape of its PDF. -We can use PDFmorph to morph a bulk material PDF to simulate these shape effects. +We can use ``diffpy.morph`` to morph a bulk material PDF to simulate these shape effects. Currently, the supported nanoparticle shapes include: spheres and spheroids. -* Within the ``additionalData`` directory, ``cd`` into the ``morphShape`` subdirectory. - Inside, you will find a sample Ni bulk material PDF ``Ni_bulk.gr``. - This PDF is from `"Atomic Pair Distribution Function Analysis: A primer" `_. +* Within the ``additionalData`` directory, ``cd`` into the + ``morphShape`` subdirectory. Inside, you will find a sample Ni bulk + material PDF ``Ni_bulk.gr``. This PDF is from `"Atomic Pair + Distribution Function Analysis: + A primer" `_. There are also multiple ``.cgr`` files with calculated Ni nanoparticle PDFs. -* Let us apply various shape effect morphs on the bulk material to reproduce these calculated PDFs. +* Let us apply various shape effect morphs on the bulk material to + reproduce these calculated PDFs. * Spherical Shape - 1. The ``Ni_nano_sphere.cgr`` file contains a generated spherical nanoparticle with unknown radius. - First, let us plot ``Ni_blk.gr`` against ``Ni_nano_sphere.cgr`` :: + 1. The ``Ni_nano_sphere.cgr`` file contains a generated + spherical nanoparticle with unknown radius. First, let us + plot ``Ni_blk.gr`` against ``Ni_nano_sphere.cgr`` :: - pdfmorph Ni_bulk.gr Ni_nano_sphere.cgr + diffpy.morph Ni_bulk.gr Ni_nano_sphere.cgr Despite the two being the same material, the Rw is quite large. To reduce the Rw, we will apply spherical shape effects onto the PDF. - However, in order to do so, we first need the radius of the spherical nanoparticle. - 2. To get the radius, we can first observe a plot of ``Ni_nano_sphere.cgr`` :: + However, in order to do so, we first need the radius of the + spherical nanoparticle. - pdfmorph Ni_nano_sphere.cgr Ni_nano_sphere.cgr + 2. To get the radius, we can first observe a plot of + ``Ni_nano_sphere.cgr`` :: - 3. Nanoparticles tend to have broader peaks at r-values larger than the particle size, - corresponding to the much weaker correlations between molecules. - On our plot, beyond r=22.5, peaks are too broad to be visible, - indicating our particle size to be about 22.4. - The approximate radius of a sphere would be half of that, or 11.2. - 4. Now, we are ready to perform a morph applying spherical effects. To do so, we use the ``--radius`` parameter :: + diffpy.morph Ni_nano_sphere.cgr Ni_nano_sphere.cgr - pdfmorph Ni_bulk.gr Ni_nano_sphere.cgr --radius=11.2 -a + 3. Nanoparticles tend to have broader peaks at r-values larger + than the particle size, corresponding to the much weaker + correlations between molecules. On our plot, beyond r=22.5, + peaks are too broad to be visible, indicating our particle + size to be about 22.4. The approximate radius of a sphere + would be half of that, or 11.2.:: - 5. We can see that the Rw value has significantly decreased from before. Run without the ``-a`` tag to refine :: + diffpy.morph Ni_bulk.gr Ni_nano_sphere.cgr --radius=11.2 -a - pdfmorph Ni_bulk.gr Ni_nano_sphere.cgr --radius=11.2 - 6. After refining, we see the actual radius of the nanoparticle was closer to 12. + 4. Now, we are ready to perform a morph applying spherical + effects. To do so, we use the ``--radius`` parameter :: + + diffpy.morph Ni_bulk.gr Ni_nano_sphere.cgr --radius=11.2 -a + + 5. We can see that the Rw value has significantly decreased + from before. Run without the ``-a`` tag to refine :: + + diffpy.morph Ni_bulk.gr Ni_nano_sphere.cgr --radius=11.2 + + 6. After refining, we see the actual radius of the + nanoparticle was closer to 12. + * Spheroidal Shape - 1. The ``Ni_nano_spheroid.cgr`` file contains a calculated spheroidal Ni nanoparticle. - Again, we can begin by plotting the bulk material against our nanoparticle :: - pdfmorph Ni_bulk.gr Ni_nano_spheroid.cgr + 1. The ``Ni_nano_spheroid.cgr`` file contains a calculated + spheroidal Ni nanoparticle. Again, we can begin by plotting + the bulk material against our nanoparticle :: + + diffpy.morph Ni_bulk.gr Ni_nano_spheroid.cgr + + 2. Inside the ``Ni_nano_spheroid.cgr`` file, we are given that + the equatorial radius is 12 and polar radius is 6. This is + enough information to define our spheroid. To apply + spheroid shape effects onto our bulk, run :: - 2. Inside the ``Ni_nano_spheroid.cgr`` file, we are given that the equatorial radius is 12 and polar radius is 6. - This is enough information to define our spheroid. To apply spheroid shape effects onto our bulk, run :: + diffpy.morph Ni_bulk.gr Ni_nano_spheroid.cgr --radius=12 --pradius=6 -a - pdfmorph Ni_bulk.gr Ni_nano_spheroid.cgr --radius=12 --pradius=6 -a + Note that the equatorial radius corresponds to the + ``--radius`` parameter and polar radius to ``--pradius``. - Note that the equitorial radius corresponds to the ``--radius`` parameter and polar radius to ``--pradius``. 3. Remove the ``-a`` tag to refine. -There is also support for morphing from a nanoparticle to a bulk. When applying the inverse morphs, -it is recommended to set ``--rmax=psize`` where ``psize`` is the longest diameter of the nanoparticle. +There is also support for morphing from a nanoparticle to a bulk. When +applying the inverse morphs, it is recommended to set ``--rmax=psize`` +where ``psize`` is the longest diameter of the nanoparticle. Bug Reports =========== -Please enjoy using our software! If you come accross any bugs in the +Please enjoy using our software! If you come across any bugs in the application, please report them to diffpy-users@googlegroups.com. diff --git a/environment.yml b/environment.yml index a46b257d..582b2ccc 100644 --- a/environment.yml +++ b/environment.yml @@ -1,4 +1,4 @@ -name: diffpy.pdfmorph +name: diffpy.morph channels: - conda-forge dependencies: diff --git a/news/re-cut.rst b/news/codecov.rst similarity index 50% rename from news/re-cut.rst rename to news/codecov.rst index 8d0d08f8..1c91077e 100644 --- a/news/re-cut.rst +++ b/news/codecov.rst @@ -1,11 +1,11 @@ **Added:** -* Add GitHub action to build wheel, release, upload. -* Add issue and bug report templates. +* Spelling check via Codespell in pre-commit +* Coverage report in each PR **Changed:** -* README file installation instructions updated. +* **Deprecated:** diff --git a/news/data_temperature.rst b/news/data_temperature.rst new file mode 100644 index 00000000..e251bcfe --- /dev/null +++ b/news/data_temperature.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* add temperature field to tutorial/additionalData. + +**Security:** + +* diff --git a/news/diffpy_morph.rst b/news/diffpy_morph.rst new file mode 100644 index 00000000..52beb663 --- /dev/null +++ b/news/diffpy_morph.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* Renamed PDFmorph to diffpy.morph + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/news/explicit_paths.rst b/news/explicit_paths.rst new file mode 100644 index 00000000..2b418ce4 --- /dev/null +++ b/news/explicit_paths.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* Paths to diffpy.utils.parsers functions made explicitly to the file level. + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/news/error_fixes.rst b/news/line79.rst similarity index 81% rename from news/error_fixes.rst rename to news/line79.rst index e47f6677..932cde58 100644 --- a/news/error_fixes.rst +++ b/news/line79.rst @@ -16,7 +16,7 @@ **Fixed:** -* Mathematical error in manual +* reduce the line width limit to 79 **Security:** diff --git a/news/morphshift.rst b/news/morphshift.rst new file mode 100644 index 00000000..71a2714a --- /dev/null +++ b/news/morphshift.rst @@ -0,0 +1,23 @@ +**Added:** + +* Shifting morph for vertical and horizontal shifts. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/news/multiple_morphs.rst b/news/multiple_morphs.rst new file mode 100644 index 00000000..d05da828 --- /dev/null +++ b/news/multiple_morphs.rst @@ -0,0 +1,23 @@ +**Added:** + +* --multiple-morphs: morph multiple files against a single target + +**Changed:** + +* --multiple changed to --multiple-targets for clarity + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/news/new_parser_api.rst b/news/new_parser_api.rst new file mode 100644 index 00000000..cd9139b3 --- /dev/null +++ b/news/new_parser_api.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* import `loadData` and `deserialize_data` directly to integrate with `diffpy.utils(3.6.0)` + +**Security:** + +* diff --git a/news/py313.rst b/news/py313.rst new file mode 100644 index 00000000..34bb75e8 --- /dev/null +++ b/news/py313.rst @@ -0,0 +1,23 @@ +**Added:** + +* Support for python 3.13 + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* Support for python 3.10 + +**Fixed:** + +* + +**Security:** + +* diff --git a/news/rm_texinfo.rst b/news/rm_texinfo.rst new file mode 100644 index 00000000..8e6242f0 --- /dev/null +++ b/news/rm_texinfo.rst @@ -0,0 +1,23 @@ +**Added:** + +* manual information is added into online docs. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* manual. + +**Fixed:** + +* + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml index 5924635e..f007f315 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=62.0", "setuptools-git-versioning>=2.0"] build-backend = "setuptools.build_meta" [project] -name = "diffpy.pdfmorph" +name = "diffpy.morph" dynamic=['version', 'dependencies'] authors = [ { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, @@ -11,10 +11,10 @@ authors = [ maintainers = [ { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, ] -description = "Tools for manipulating and comparing PDF profiles" +description = "Python package for manipulating and comparing PDF profiles" keywords = ['diffpy', 'pdf', 'data interpretation'] readme = "README.rst" -requires-python = ">=3.10" +requires-python = ">=3.11, <3.14" classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -25,19 +25,19 @@ classifiers = [ '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', + 'Programming Language :: Python :: 3.13', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Scientific/Engineering :: Chemistry', ] [project.scripts] -pdfmorph = "diffpy.pdfmorph.pdfmorphapp:main" +"diffpy.morph" = "diffpy.morph.morphapp:main" [project.urls] -Homepage = "https://github.com/diffpy/diffpy.pdfmorph/" -Issues = "https://github.com/diffpy/diffpy.pdfmorph/issues/" +Homepage = "https://github.com/diffpy/diffpy.morph/" +Issues = "https://github.com/diffpy/diffpy.morph/issues/" [tool.setuptools-git-versioning] enabled = true @@ -48,14 +48,19 @@ 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 = ["tests"] # exclude packages matching these glob patterns (empty by default) +exclude = [] # exclude packages matching these glob patterns (empty by default) namespaces = false # to disable scanning PEP 420 namespaces (true by default) [tool.setuptools.dynamic] dependencies = {file = ["requirements/pip.txt"]} +[tool.codespell] +exclude-file = ".codespell/ignore_lines.txt" +ignore-words = ".codespell/ignore_words.txt" +skip = "*.cif,*.dat,*agr" + [tool.black] -line-length = 115 +line-length = 79 include = '\.pyi?$' exclude = ''' /( diff --git a/requirements/README.txt b/requirements/README.txt deleted file mode 100644 index 3de372a7..00000000 --- a/requirements/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -# YOU MAY DELETE THIS FILE AFTER SETTING UP DEPENDENCIES! -# -# This directory is where you should place your project dependencies. -# "pip.txt" should contain all required packages not available on conda. -# All other files should contain only packages available to download from conda. -# build.txt should contain all packages required to build (not run) the project. -# run.txt should contain all packages (including optional packages) required for a user to run the program. -# test.txt should contain all packages required for the testing suite and to ensure all tests pass. -# docs.txt should contain all packages required for building the package documentation page. -# -# YOU MAY DELETE THIS FILE AFTER SETTING UP DEPENDENCIES! diff --git a/src/diffpy/__init__.py b/src/diffpy/__init__.py index 5a43b52f..38fef756 100644 --- a/src/diffpy/__init__.py +++ b/src/diffpy/__init__.py @@ -13,7 +13,7 @@ # ############################################################################## -"""pdfmorph - tools for manipulating and comparing PDF data. +"""morph - tools for manipulating and comparing PDF data. """ diff --git a/src/diffpy/pdfmorph/__init__.py b/src/diffpy/morph/__init__.py similarity index 86% rename from src/diffpy/pdfmorph/__init__.py rename to src/diffpy/morph/__init__.py index db1b45ee..b1006e3c 100644 --- a/src/diffpy/pdfmorph/__init__.py +++ b/src/diffpy/morph/__init__.py @@ -7,7 +7,7 @@ # File coded by: Billinge Group members and community contributors. # # See GitHub contributions for a more detailed list of contributors. -# https://github.com/diffpy/diffpy.pdfmorph/graphs/contributors +# https://github.com/diffpy/diffpy.morph/graphs/contributors # # See LICENSE.rst for license information. # @@ -19,7 +19,7 @@ __save_morph_as__ = "save_morph_as" # package version -from diffpy.pdfmorph.version import __version__ +from diffpy.morph.version import __version__ # silence the pyflakes syntax checker assert __version__ or True diff --git a/src/diffpy/pdfmorph/log.py b/src/diffpy/morph/log.py similarity index 89% rename from src/diffpy/pdfmorph/log.py rename to src/diffpy/morph/log.py index 945f1a73..7e49af6b 100644 --- a/src/diffpy/pdfmorph/log.py +++ b/src/diffpy/morph/log.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -23,11 +23,11 @@ import logging # logging configuration -plog = logging.getLogger("diffpy.pdfmorph") +plog = logging.getLogger("diffpy.morph") def set_verbosity(vb): - """Set verbosity of the pdfmorph logger. + """Set verbosity of the diffpy.morph logger. Parameters ---------- diff --git a/src/diffpy/pdfmorph/pdfmorph_api.py b/src/diffpy/morph/morph_api.py similarity index 86% rename from src/diffpy/pdfmorph/pdfmorph_api.py rename to src/diffpy/morph/morph_api.py index cc9407a0..65b0fa66 100644 --- a/src/diffpy/pdfmorph/pdfmorph_api.py +++ b/src/diffpy/morph/morph_api.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2018 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -24,9 +24,9 @@ import matplotlib.pyplot as plt -from diffpy.pdfmorph import morph_helpers, morphs -from diffpy.pdfmorph import refine as ref -from diffpy.pdfmorph import tools +from diffpy.morph import morph_helpers, morphs +from diffpy.morph import refine as ref +from diffpy.morph import tools # map of operation dict # TODO: include morphing on psize @@ -40,7 +40,9 @@ ], qdamp=morphs.MorphResolutionDamping, ) -_default_config = dict(scale=None, stretch=None, smear=None, baselineslope=None, qdamp=None) +_default_config = dict( + scale=None, stretch=None, smear=None, baselineslope=None, qdamp=None +) def morph_default_config(**kwargs): @@ -71,7 +73,7 @@ def morph_default_config(**kwargs): return rv -def pdfmorph( +def morph( x_morph, y_morph, x_target, @@ -86,7 +88,7 @@ def pdfmorph( verbose=False, **kwargs, ): - """function to perfom PDF morphing. + """function to perform PDF morphing. Parameters ---------- @@ -139,10 +141,11 @@ def pdfmorph( morph_rv_dict: dict A dictionary contains following key-value pairs: - - morph_chain: diffpy.pdfmorph.morphs.morphchain.MorphChain + - morph_chain: diffpy.morph.morphs.morphchain.MorphChain The instance of processed morph chain. - Calling ``x_morph, y_morph, x_target, y_target = morph_chain.xyallout`` - will conviniently retrun morphed data and reference data + Calling + ``x_morph, y_morph, x_target, y_target = morph_chain.xyallout`` + will conveniently return morphed data and reference data - morphed_cfg: dict A dictionary of refined morphing parameters - rw: float @@ -154,11 +157,12 @@ def pdfmorph( Examples -------- - # morphing (x_morph, y_morph) pair to (x_target, y_target) pair with scaling - from diffpy.pdfmorph.pdfmorph_api import pdfmorph, morph_default_config, plot_morph + # morphing (x_morph, y_morph) pair to (x_target, y_target) pair with + scaling from diffpy.morph.morph_api import morph, + morph_default_config, plot_morph morph_cfg = morph_default_config(scale=1.01) - morph_rv_dict = pdfmorph(x_morph, y_morph, x_target, y_target, **morph_cfg) + morph_rv_dict = morph(x_morph, y_morph, x_target, y_target, **morph_cfg) # plot morhing result plot_morph(morph_rv_dict['morph_chain']) @@ -172,7 +176,11 @@ def pdfmorph( # input config rv_cfg = dict(kwargs) # configure morph operations - active_morphs = [k for k, v in rv_cfg.items() if (v is not None) and k in _morph_step_dict] + active_morphs = [ + k + for k, v in rv_cfg.items() + if (v is not None) and k in _morph_step_dict + ] rv_cfg["rmin"] = rmin rv_cfg["rmax"] = rmax rv_cfg["rstep"] = rstep @@ -230,7 +238,9 @@ def pdfmorph( print("== INFO: Following steps are fixed during morphing ==:\n") print("\n".join(fixed_operations)) print("== INFO: Refined morph parameters ==:\n") - output = "\n".join(["# %s = %f" % (k, v) for k, v in rv_cfg.items() if v is not None]) + output = "\n".join( + ["# %s = %f" % (k, v) for k, v in rv_cfg.items() if v is not None] + ) output += "\n# Rw = %f" % rw output += "\n# Pearson = %f" % pcc print(output) @@ -246,18 +256,20 @@ def plot_morph(chain, ax=None, **kwargs): Parameters ---------- - chain: diffpy.pdfmorph.morphs.morphchain.MorphChain + chain: diffpy.morph.morphs.morphchain.MorphChain An instance of processed morph chain. - ax: matplotlib.axes.Axes, optinal + ax: matplotlib.axes.Axes, optional An instance of Axes class to plot the morphing result. - If ax is None, instances of new Figure and Axes will be created. Default to None. + If ax is None, instances of new Figure and Axes will be created. + Default to None. kwargs: - Additional keyword arguements will be passed to ``ax.plot(...**kwargs)`` + Additional keyword arguments will be passed to ``ax.plot(...**kwargs)`` Returns ------- l_list: list - A list of ``matplotlib.lines.Line2D`` objects representing the plotted data. + A list of ``matplotlib.lines.Line2D`` objects representing the + plotted data. """ if ax is None: fig, ax = plt.subplots() diff --git a/src/diffpy/pdfmorph/morph_helpers/__init__.py b/src/diffpy/morph/morph_helpers/__init__.py similarity index 64% rename from src/diffpy/pdfmorph/morph_helpers/__init__.py rename to src/diffpy/morph/morph_helpers/__init__.py index b3a5c483..46b8fe5b 100644 --- a/src/diffpy/pdfmorph/morph_helpers/__init__.py +++ b/src/diffpy/morph/morph_helpers/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -11,10 +11,11 @@ # ############################################################################## -"""List of helpers for certain morphing operations (currently only used for smear).""" +"""List of helpers for certain morphing operations +(currently only used for smear).""" -from diffpy.pdfmorph.morph_helpers.transformpdftordf import TransformXtalPDFtoRDF -from diffpy.pdfmorph.morph_helpers.transformrdftopdf import TransformXtalRDFtoPDF +from diffpy.morph.morph_helpers.transformpdftordf import TransformXtalPDFtoRDF +from diffpy.morph.morph_helpers.transformrdftopdf import TransformXtalRDFtoPDF # List of helpers morph_helpers = [ diff --git a/src/diffpy/pdfmorph/morph_helpers/transformpdftordf.py b/src/diffpy/morph/morph_helpers/transformpdftordf.py similarity index 88% rename from src/diffpy/pdfmorph/morph_helpers/transformpdftordf.py rename to src/diffpy/morph/morph_helpers/transformpdftordf.py index 99e39061..556edcbf 100644 --- a/src/diffpy/pdfmorph/morph_helpers/transformpdftordf.py +++ b/src/diffpy/morph/morph_helpers/transformpdftordf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -18,7 +18,7 @@ """ -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, LABEL_RR, Morph +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, LABEL_RR, Morph class TransformXtalPDFtoRDF(Morph): @@ -51,7 +51,9 @@ def morph(self, x_morph, y_morph, x_target, y_target): morph_baseline = self.baselineslope * self.x_morph_in self.y_morph_out = self.x_morph_in * (self.y_morph_in - morph_baseline) target_baseline = self.baselineslope * self.x_target_in - self.y_target_out = self.x_target_in * (self.y_target_in - target_baseline) + self.y_target_out = self.x_target_in * ( + self.y_target_in - target_baseline + ) return self.xyallout diff --git a/src/diffpy/pdfmorph/morph_helpers/transformrdftopdf.py b/src/diffpy/morph/morph_helpers/transformrdftopdf.py similarity index 84% rename from src/diffpy/pdfmorph/morph_helpers/transformrdftopdf.py rename to src/diffpy/morph/morph_helpers/transformrdftopdf.py index 97a0c33e..4dce9731 100644 --- a/src/diffpy/pdfmorph/morph_helpers/transformrdftopdf.py +++ b/src/diffpy/morph/morph_helpers/transformrdftopdf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,7 +20,7 @@ import numpy -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, LABEL_RR, Morph +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, LABEL_RR, Morph class TransformXtalRDFtoPDF(Morph): @@ -53,10 +53,14 @@ def morph(self, x_morph, y_morph, x_target, y_target): morph_baseline = self.baselineslope * self.x_morph_in target_baseline = self.baselineslope * self.x_target_in with numpy.errstate(divide="ignore", invalid="ignore"): - self.y_target_out = self.y_target_in / self.x_target_in + target_baseline + self.y_target_out = ( + self.y_target_in / self.x_target_in + target_baseline + ) self.y_target_out[self.x_target_in == 0] = 0 with numpy.errstate(divide="ignore", invalid="ignore"): - self.y_morph_out = self.y_morph_in / self.x_morph_in + morph_baseline + self.y_morph_out = ( + self.y_morph_in / self.x_morph_in + morph_baseline + ) self.y_morph_out[self.x_target_in == 0] = 0 return self.xyallout diff --git a/src/diffpy/pdfmorph/pdfmorph_io.py b/src/diffpy/morph/morph_io.py similarity index 64% rename from src/diffpy/pdfmorph/pdfmorph_io.py rename to src/diffpy/morph/morph_io.py index 81a96ece..722771c9 100644 --- a/src/diffpy/pdfmorph/pdfmorph_io.py +++ b/src/diffpy/morph/morph_io.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -21,8 +21,8 @@ import numpy -import diffpy.pdfmorph.tools as tools -from diffpy.pdfmorph import __save_morph_as__ +import diffpy.morph.tools as tools +from diffpy.morph import __save_morph_as__ def single_morph_output( @@ -44,12 +44,13 @@ def single_morph_output( morph_results: dict Resulting data after morphing. save_file - Name of file to print to. If None (defualt) print to terminal. + Name of file to print to. If None (default) print to terminal. morph_file - Name of the morphed PDF file. Required when printing to a non-terminal file. + Name of the morphed PDF file. Required when printing to a + non-terminal file. param xy_out: list - List of the form [x_morph_out, y_morph_out]. x_morph_out is a List of r values and - y_morph_out is a List of gr values. + List of the form [x_morph_out, y_morph_out]. x_morph_out is a List of + r values and y_morph_out is a List of gr values. verbose: bool Print additional details about the morph when True (default False). stdout_flag: bool @@ -58,10 +59,17 @@ def single_morph_output( # Input and output parameters morphs_in = "\n# Input morphing parameters:\n" - morphs_in += "\n".join(f"# {key} = {morph_inputs[key]}" for key in morph_inputs.keys()) + "\n" + morphs_in += ( + "\n".join( + f"# {key} = {morph_inputs[key]}" for key in morph_inputs.keys() + ) + + "\n" + ) morphs_out = "# Optimized morphing parameters:\n" - morphs_out += "\n".join(f"# {key} = {morph_results[key]:.6f}" for key in morph_results.keys()) + morphs_out += "\n".join( + f"# {key} = {morph_results[key]:.6f}" for key in morph_results.keys() + ) # Printing to terminal if stdout_flag: @@ -70,7 +78,7 @@ def single_morph_output( # Saving to file if save_file is not None: path_name = str(Path(morph_file).resolve()) - header = "# PDF created by pdfmorph\n" + header = "# PDF created by diffpy.morph\n" header += f"# from {path_name}" header_verbose = f"{morphs_in}\n{morphs_out}" @@ -100,14 +108,16 @@ def single_morph_output( def create_morphs_directory(save_directory): """Create a directory for saving multiple morphed PDFs. - Takes in a user-given path to a directory save_directory and create a subdirectory named Morphs. - PDFmorph will save all morphs into the Morphs subdirectory while metadata about the morphs will - be stored in save_directory outside Morphs. + Takes in a user-given path to a directory save_directory and create a + subdirectory named Morphs. diffpy.morph will save all morphs into the + Morphs subdirectory while metadata about the morphs will be stored in + save_directory outside Morphs. Parameters ---------- save_directory - Path to a directory. PDFmorph will save all generated files within this directory. + Path to a directory. diffpy.morph will save all generated files within + this directory. Returns ------- @@ -124,24 +134,28 @@ def create_morphs_directory(save_directory): return str(morphs_subdirectory.resolve()) -def get_multisave_names(target_list: list, save_names_file=None): +def get_multisave_names(target_list: list, save_names_file=None, mm=False): """Create or import a dictionary that specifies names to save morphs as. - First attempt to import names from a specified file. If names for certain morphs not found, - use default naming scheme: 'Morph_with_Target_.cgr'. + First attempt to import names from a specified file. + If names for certain morphs not found, use default naming scheme: + 'Morph_with_Target_.cgr'. Used when saving multiple morphs. Parameters ---------- target_list: list - Target PDFs used for each morph. + Target (or Morph if mm enabled) PDFs used for each morph. save_names_file Name of file to import save names dictionary from (default None). + mm: bool + Rather than multiple targets, multiple morphs are being done. Returns ------- dict - The names to save each morph as. Keys are the target PDF file names used to produce that morph. + The names to save each morph as. Keys are the target PDF file names + used to produce that morph. """ # Dictionary storing save file names @@ -154,7 +168,26 @@ def get_multisave_names(target_list: list, save_names_file=None): # Apply default naming scheme to missing targets for target_file in target_list: if target_file.name not in save_names.keys(): - save_names.update({target_file.name: {__save_morph_as__: f"Morph_with_Target_{target_file.stem}.cgr"}}) + if not mm: + save_names.update( + { + target_file.name: { + __save_morph_as__: ( + f"Morph_with_Target_{target_file.stem}.cgr" + ) + } + } + ) + else: + save_names.update( + { + target_file.name: { + __save_morph_as__: ( + f"Morph_of_{target_file.stem}.cgr" + ) + } + } + ) return save_names @@ -169,6 +202,7 @@ def multiple_morph_output( target_directory=None, verbose=False, stdout_flag=False, + mm=False, ): """Helper function for printing details about a series of multiple morphs. Handles both printing to terminal and printing to a file. @@ -184,11 +218,14 @@ def multiple_morph_output( save_directory Name of directory to save morphs in. field - Name of field if data was sorted by a particular field. Otherwise, leave blank. + Name of field if data was sorted by a particular field. + Otherwise, leave blank. field_list: list - List of field values for each target PDF. Generated by diffpy.pdfmorph.tools.field_sort(). + List of field values for each target PDF. + Generated by diffpy.morph.tools.field_sort(). morph_file - Name of the morphed PDF file. Required to give summary data after saving to a directory. + Name of the morphed PDF file. + Required to give summary data after saving to a directory. target_directory Name of the directory containing the target PDF files. Required to give summary data after saving to a directory. @@ -196,21 +233,31 @@ def multiple_morph_output( Print additional summary details when True (default False). stdout_flag: bool Print to terminal when True (default False). + mm: bool + Multiple morphs done with a single target rather than multiple + targets for a single morphed file. Swaps morph and target in the code. """ # Input parameters used for every morph inputs = "\n# Input morphing parameters:\n" - inputs += "\n".join(f"# {key} = {morph_inputs[key]}" for key in morph_inputs.keys()) + inputs += "\n".join( + f"# {key} = {morph_inputs[key]}" for key in morph_inputs.keys() + ) # Verbose to get output for every morph verbose_outputs = "" if verbose: - # Output for every morph (information repeated in a succinct table below) + # Output for every morph + # (information repeated in a succinct table below) for target in morph_results.keys(): - output = f"\n# Target: {target}\n" + if not mm: + output = f"\n# Target: {target}\n" + else: + output = f"\n# Morph: {target}\n" output += "# Optimized morphing parameters:\n" output += "\n".join( - f"# {param} = {morph_results[target][param]:.6f}" for param in morph_results[target] + f"# {param} = {morph_results[target][param]:.6f}" + for param in morph_results[target] ) verbose_outputs += f"{output}\n" @@ -218,7 +265,10 @@ def multiple_morph_output( tabulated_results = tabulate_results(morph_results) # Table labels - labels = "\n# Labels: [Target]" + if not mm: + labels = "\n# Labels: [Target]" + else: + labels = "\n# Labels: [Morph]" if field is not None: labels += f" [{field}]" for param in tabulated_results.keys(): @@ -246,41 +296,58 @@ def multiple_morph_output( morph_path_name = str(Path(morph_file).resolve()) target_path_name = str(Path(target_directory).resolve()) - header = "# Data generated by pdfmorph\n" - header += f"# from morphing {morph_path_name}\n" - header += f"# with target directory {target_path_name}" - reference_table = Path(save_directory).joinpath("Morph_Reference_Table.txt") + header = "# Data generated by diffpy.morph\n" + if not mm: + header += f"# from morphing {morph_path_name}\n" + header += f"# with target directory {target_path_name}" + else: + header += f"# from morphing directory {target_path_name}\n" + header += f"# with target {morph_path_name}" + reference_table = Path(save_directory).joinpath( + "Morph_Reference_Table.txt" + ) with open(reference_table, "w") as reference: - print(f"{header}\n{inputs}\n{verbose_outputs}{table}", file=reference) + print( + f"{header}\n{inputs}\n{verbose_outputs}{table}", file=reference + ) if stdout_flag: # Indicate successful save - save_message = f"# Morphs saved in the directory {save_directory}\n" + save_message = ( + f"# Morphs saved in the directory {save_directory}\n" + ) print(save_message) def tabulate_results(multiple_morph_results): - """Helper function to make a data table summarizing details about the results of multiple morphs. + """Helper function to make a data table summarizing details about the + results of multiple morphs. Parameters ---------- multiple_morph_results - A collection of Dictionaries. Each Dictionary summarizes the resultsof a single morph. + A collection of Dictionaries. Each Dictionary summarizes the + resultsof a single morph. Returns ------- tabulated_results: dict - Keys in tabulated_results are the table's column names and each corresponding value is a list - of data for that column. + Keys in tabulated_results are the table's column names and each + corresponding value is a list of data for that column. """ # We only care about the following parameters in our data tables relevant_parameters = ["Scale", "Smear", "Stretch", "Pearson", "Rw"] - # Keys in this table represent column names and the value will be a list of column data + # Keys in this table represent column names and the value will be a list + # of column data tabulated_results = {} for param in relevant_parameters: tabulated_results.update( - {param: tools.get_values_from_dictionary_collection(multiple_morph_results, param)} + { + param: tools.get_values_from_dictionary_collection( + multiple_morph_results, param + ) + } ) return tabulated_results diff --git a/src/diffpy/morph/morphapp.py b/src/diffpy/morph/morphapp.py new file mode 100755 index 00000000..b8418c70 --- /dev/null +++ b/src/diffpy/morph/morphapp.py @@ -0,0 +1,1016 @@ +#!/usr/bin/env python +############################################################################## +# +# diffpy.morph by DANSE Diffraction group +# Simon J. L. Billinge +# (c) 2010 Trustees of the Columbia University +# in the City of New York. All rights reserved. +# +# File coded by: Chris Farrow +# +# See AUTHORS.txt for a list of people who contributed. +# See LICENSE.txt for license information. +# +############################################################################## + +from __future__ import print_function + +import sys +from pathlib import Path + +import diffpy.morph.morph_helpers as helpers +import diffpy.morph.morph_io as io +import diffpy.morph.morphs as morphs +import diffpy.morph.pdfplot as pdfplot +import diffpy.morph.refine as refine +import diffpy.morph.tools as tools +from diffpy.morph import __save_morph_as__ +from diffpy.morph.version import __version__ + + +def create_option_parser(): + import optparse + + prog_short = Path( + sys.argv[0] + ).name # Program name, compatible w/ all OS paths + + class CustomParser(optparse.OptionParser): + def __init__(self, *args, **kwargs): + super(CustomParser, self).__init__(*args, **kwargs) + + def custom_error(self, msg): + """custom_error(msg : string) + + Print a message incorporating 'msg' to stderr and exit. + Does not print usage. + """ + self.exit(2, "%s: error: %s\n" % (self.get_prog_name(), msg)) + + parser = CustomParser( + usage="\n".join( + [ + "%prog [options] FILE1 FILE2", + "Manipulate and compare PDFs.", + "Use --help for help.", + ] + ), + epilog="\n".join( + [ + "Please report bugs to diffpy-users@googlegroups.com.", + ( + "For more information, see the diffpy.morph website at " + "https://www.diffpy.org/diffpy.morph." + ), + ] + ), + ) + + parser.add_option( + "-V", + "--version", + action="version", + help="Show program version and exit.", + ) + parser.version = __version__ + parser.add_option( + "-s", + "--save", + metavar="NAME", + dest="slocation", + help=( + "Save the manipulated PDF to a file named NAME. " + "Use '-' for stdout.\n" + "When --multiple- is enabled, " + "save each manipulated PDF as a file in a directory named NAME;\n" + "you can specify names for each saved PDF file using " + "--save-names-file." + ), + ) + parser.add_option( + "-v", + "--verbose", + dest="verbose", + action="store_true", + help="Print additional header details to saved files.", + ) + parser.add_option( + "--rmin", + type="float", + help="Minimum r-value to use for PDF comparisons.", + ) + parser.add_option( + "--rmax", + type="float", + help="Maximum r-value to use for PDF comparisons.", + ) + parser.add_option( + "--pearson", + action="store_true", + dest="pearson", + help=( + "Maximize agreement in the Pearson function. " + "Note that this is insensitive to scale." + ), + ) + parser.add_option( + "--addpearson", + action="store_true", + dest="addpearson", + help="""Maximize agreement in the Pearson function as well as + minimizing the residual.""", + ) + + # Manipulations + group = optparse.OptionGroup( + parser, + "Manipulations", + ( + "These options select the manipulations that are to be applied to " + "the PDF from FILE1. " + "The passed values will be refined unless specifically " + "excluded with the -a or -x options. " + "If no option is specified, the PDFs from FILE1 and FILE2 will " + "be plotted without any manipulations." + ), + ) + parser.add_option_group(group) + group.add_option( + "-a", + "--apply", + action="store_false", + dest="refine", + help="Apply manipulations but do not refine.", + ) + group.add_option( + "-x", + "--exclude", + action="append", + dest="exclude", + metavar="MANIP", + help="""Exclude a manipulation from refinement by name. This can + appear multiple times.""", + ) + group.add_option( + "--scale", + type="float", + metavar="SCALE", + help="Apply scale factor SCALE.", + ) + group.add_option( + "--stretch", + type="float", + metavar="STRETCH", + help="Stretch PDF by a fraction STRETCH.", + ) + group.add_option( + "--smear", + type="float", + metavar="SMEAR", + help="Smear peaks with a Gaussian of width SMEAR.", + ) + group.add_option( + "--slope", + type="float", + dest="baselineslope", + help="""Slope of the baseline. This is used when applying the smear + factor. It will be estimated if not provided.""", + ) + group.add_option( + "--hshift", + type="float", + metavar="HSHIFT", + help="Shift the PDF horizontally by HSHIFT to the right.", + ) + group.add_option( + "--vshift", + type="float", + metavar="VSHIFT", + help="Shift the PDF vertically by VSHIFT upward.", + ) + group.add_option( + "--qdamp", + type="float", + metavar="QDAMP", + help="Dampen PDF by a factor QDAMP.", + ) + group.add_option( + "--radius", + type="float", + metavar="RADIUS", + help=( + "Apply characteristic function of sphere with radius " + "RADIUS. If PRADIUS is also specified, instead apply " + "characteristic function of spheroid with equatorial " + "radius RADIUS and polar radius PRADIUS." + ), + ) + group.add_option( + "--pradius", + type="float", + metavar="PRADIUS", + help=( + "Apply characteristic function of spheroid with " + "equatorial radius RADIUS and polar radius PRADIUS. If only " + "PRADIUS is specified, instead apply characteristic function of " + "sphere with radius PRADIUS." + ), + ) + group.add_option( + "--iradius", + type="float", + metavar="IRADIUS", + help=( + "Apply inverse characteristic function of sphere with radius " + "IRADIUS. If IPRADIUS is also specified, instead apply inverse " + "characteristic function of spheroid with equatorial radius " + "IRADIUS and polar radius IPRADIUS." + ), + ) + group.add_option( + "--ipradius", + type="float", + metavar="IPRADIUS", + help=( + "Apply inverse characteristic function of spheroid with " + "equatorial radius IRADIUS and polar radius IPRADIUS. If only " + "IPRADIUS is specified, instead apply inverse characteristic " + "function of sphere with radius IPRADIUS." + ), + ) + + # Plot Options + group = optparse.OptionGroup( + parser, + "Plot Options", + ( + "These options control plotting. The manipulated and target PDFs " + "will be plotted against each other with a difference curve " + "below. " + "When --multiple- is enabled, the value of a " + "parameter (specified by --plot-parameter) will be plotted " + "instead." + ), + ) + parser.add_option_group(group) + group.add_option( + "-n", + "--noplot", + action="store_false", + dest="plot", + help="""Do not show a plot.""", + ) + group.add_option( + "--mlabel", + metavar="MLABEL", + dest="mlabel", + help=( + "Set label for morphed data to MLABEL on plot. " + "Default label is FILE1." + ), + ) + group.add_option( + "--tlabel", + metavar="TLABEL", + dest="tlabel", + help=( + "Set label for target data to TLABEL on plot. " + "Default label is FILE2." + ), + ) + group.add_option( + "--pmin", + type="float", + help="Minimum r-value to plot. Defaults to RMIN.", + ) + group.add_option( + "--pmax", + type="float", + help="Maximum r-value to plot. Defaults to RMAX.", + ) + group.add_option( + "--maglim", + type="float", + help="Magnify plot curves beyond r=MAGLIM by MAG.", + ) + group.add_option( + "--mag", + type="float", + help="Magnify plot curves beyond r=MAGLIM by MAG.", + ) + group.add_option( + "--lwidth", type="float", help="Line thickness of plotted curves." + ) + + # Multiple morph options + group = optparse.OptionGroup( + parser, + "Multiple Morphs", + ( + "This program can morph a PDF against multiple targets in one " + "command. See -s and Plot Options for how saving and plotting " + "functionality changes when performing multiple morphs." + ), + ) + parser.add_option_group(group) + group.add_option( + "--multiple-morphs", + dest="multiple_morphs", + action="store_true", + help=( + f"Changes usage to '{prog_short} [options] FILE DIRECTORY'. " + f"FILE will be morphed with each file in DIRECTORY as target. " + f"Files in DIRECTORY are sorted by alphabetical order unless a " + f"field is specified by --sort-by." + ), + ) + group.add_option( + "--multiple-targets", + dest="multiple_targets", + action="store_true", + help=( + f"Changes usage to '{prog_short} [options] DIRECTORY FILE'. " + f"Each file in DIRECTORY will be morphed with FILE as target. " + f"Files in DIRECTORY are sorted by alphabetical order unless a " + f"field is specified by --sort-by." + ), + ) + group.add_option( + "--sort-by", + metavar="FIELD", + dest="field", + help=( + "Used with --multiple- to sort files in DIRECTORY " + "by FIELD. " + "If the FIELD being used has a numerical value, sort from lowest " + "to highest. Otherwise, sort in ASCII sort order. FIELD must be " + "included in the header of all the PDF files." + ), + ) + group.add_option( + "--reverse", + dest="reverse", + action="store_true", + help="""Sort from highest to lowest instead.""", + ) + group.add_option( + "--serial-file", + metavar="SERIALFILE", + dest="serfile", + help="""Look for FIELD in a serial file instead. + Must specify name of serial file SERIALFILE.""", + ) + group.add_option( + "--save-names-file", + metavar="NAMESFILE", + dest="snamesfile", + help=( + "Used when both -s and --multiple- are enabled. " + "Specify names for each manipulated PDF when saving (see -s) " + "using a serial file NAMESFILE. The format of NAMESFILE should be " + "as follows: each target PDF is an entry in NAMESFILE. For each " + "entry, there should be a key {__save_morph_as__} whose value " + "specifies the name to save the manipulated PDF as. An example " + ".json serial file is shown in the diffpy.morph manual." + ), + ) + group.add_option( + "--plot-parameter", + metavar="PLOTPARAM", + dest="plotparam", + help=( + "Used when both plotting and --multiple- are " + "enabled. Choose a PLOTPARAM to plot for each morph (i.e. adding " + "--plot-parameter=Pearson means the program will display a plot " + "of the Pearson correlation coefficient for each morph-target pair" + "). PLOTPARAM is not case sensitive, so both Pearson and pearson " + "indicate the same parameter. When PLOTPARAM is not specified, Rw " + "values for each morph-target pair will be plotted. PLOTPARAM " + "will be displayed as the vertical axis label for the plot." + ), + ) + + # Defaults + parser.set_defaults(multiple=False) + parser.set_defaults(reverse=False) + parser.set_defaults(plot=True) + parser.set_defaults(refine=True) + parser.set_defaults(pearson=False) + parser.set_defaults(addpearson=False) + parser.set_defaults(mag=5) + parser.set_defaults(lwidth=1.5) + + return parser + + +def single_morph(parser, opts, pargs, stdout_flag=True): + if len(pargs) < 2: + parser.error("You must supply FILE1 and FILE2.") + elif len(pargs) > 2: + parser.error( + "Too many arguments. Make sure you only supply FILE1 and FILE2." + ) + + # Get the PDFs + x_morph, y_morph = getPDFFromFile(pargs[0]) + x_target, y_target = getPDFFromFile(pargs[1]) + + if y_morph is None: + parser.error(f"No data table found in file: {pargs[0]}.") + if y_target is None: + parser.error(f"No data table found in file: {pargs[1]}.") + + # Get configuration values + scale_in = "None" + stretch_in = "None" + smear_in = "None" + hshift_in = "None" + vshift_in = "None" + config = {} + config["rmin"] = opts.rmin + config["rmax"] = opts.rmax + config["rstep"] = None + if ( + opts.rmin is not None + and opts.rmax is not None + and opts.rmax <= opts.rmin + ): + e = "rmin must be less than rmax" + parser.custom_error(e) + + # Set up the morphs + chain = morphs.MorphChain(config) + # Add the r-range morph, we will remove it when saving and plotting + chain.append(morphs.MorphRGrid()) + refpars = [] + + # Scale + if opts.scale is not None: + scale_in = opts.scale + chain.append(morphs.MorphScale()) + config["scale"] = scale_in + refpars.append("scale") + # Stretch + if opts.stretch is not None: + stretch_in = opts.stretch + chain.append(morphs.MorphStretch()) + config["stretch"] = stretch_in + refpars.append("stretch") + # Shift + if opts.hshift is not None or opts.vshift is not None: + chain.append(morphs.MorphShift()) + if opts.hshift is not None: + hshift_in = opts.hshift + config["hshift"] = hshift_in + refpars.append("hshift") + if opts.vshift is not None: + vshift_in = opts.vshift + config["vshift"] = vshift_in + refpars.append("vshift") + # Smear + if opts.smear is not None: + smear_in = opts.smear + chain.append(helpers.TransformXtalPDFtoRDF()) + chain.append(morphs.MorphSmear()) + chain.append(helpers.TransformXtalRDFtoPDF()) + refpars.append("smear") + config["smear"] = smear_in + # Set baselineslope if not given + config["baselineslope"] = opts.baselineslope + if opts.baselineslope is None: + config["baselineslope"] = -0.5 + refpars.append("baselineslope") + # Size + radii = [opts.radius, opts.pradius] + nrad = 2 - radii.count(None) + if nrad == 1: + radii.remove(None) + config["radius"] = tools.nn_value(radii[0], "radius or pradius") + chain.append(morphs.MorphSphere()) + refpars.append("radius") + elif nrad == 2: + config["radius"] = tools.nn_value(radii[0], "radius") + refpars.append("radius") + config["pradius"] = tools.nn_value(radii[1], "pradius") + refpars.append("pradius") + chain.append(morphs.MorphSpheroid()) + iradii = [opts.iradius, opts.ipradius] + inrad = 2 - iradii.count(None) + if inrad == 1: + iradii.remove(None) + config["iradius"] = tools.nn_value(iradii[0], "iradius or ipradius") + chain.append(morphs.MorphISphere()) + refpars.append("iradius") + elif inrad == 2: + config["iradius"] = tools.nn_value(iradii[0], "iradius") + refpars.append("iradius") + config["ipradius"] = tools.nn_value(iradii[1], "ipradius") + refpars.append("ipradius") + chain.append(morphs.MorphISpheroid()) + + # Resolution + if opts.qdamp is not None: + chain.append(morphs.MorphResolutionDamping()) + refpars.append("qdamp") + config["qdamp"] = opts.qdamp + + # Now remove non-refinable parameters + if opts.exclude is not None: + refpars = list(set(refpars) - set(opts.exclude)) + + # Refine or execute the morph + refiner = refine.Refiner(chain, x_morph, y_morph, x_target, y_target) + if opts.pearson: + refiner.residual = refiner._pearson + if opts.addpearson: + refiner.residual = refiner._add_pearson + if opts.refine and refpars: + try: + # This works better when we adjust scale and smear first. + if "smear" in refpars: + rptemp = ["smear"] + if "scale" in refpars: + rptemp.append("scale") + refiner.refine(*rptemp) + # Adjust all parameters + refiner.refine(*refpars) + except ValueError as e: + parser.custom_error(str(e)) + # Smear is not being refined, but baselineslope needs to refined to apply + # smear + # Note that baselineslope is only added to the refine list if smear is + # applied + elif "baselineslope" in refpars: + try: + refiner.refine( + "baselineslope", baselineslope=config["baselineslope"] + ) + except ValueError as e: + parser.custom_error(str(e)) + else: + chain(x_morph, y_morph, x_target, y_target) + + # Get Rw for the morph range + rw = tools.getRw(chain) + pcc = tools.get_pearson(chain) + # Replace the MorphRGrid with Morph identity + chain[0] = morphs.Morph() + chain(x_morph, y_morph, x_target, y_target) + + # Input morph parameters + morph_inputs = { + "scale": scale_in, + "stretch": stretch_in, + "smear": smear_in, + } + morph_inputs.update({"hshift": hshift_in, "vshift": vshift_in}) + + # Output morph parameters + morph_results = dict(config.items()) + # Ensure Rw, Pearson last two outputs + morph_results.update({"Rw": rw}) + morph_results.update({"Pearson": pcc}) + + # Print summary to terminal and save morph to file if requested + try: + io.single_morph_output( + morph_inputs, + morph_results, + save_file=opts.slocation, + morph_file=pargs[0], + xy_out=[chain.x_morph_out, chain.y_morph_out], + verbose=opts.verbose, + stdout_flag=stdout_flag, + ) + + except (FileNotFoundError, RuntimeError): + save_fail_message = "Unable to save to designated location." + parser.custom_error(save_fail_message) + + if opts.plot: + pairlist = [chain.xy_morph_out, chain.xy_target_out] + labels = [pargs[0], pargs[1]] # Default is to use file names + + # If user chooses labels + if opts.mlabel is not None: + labels[0] = opts.mlabel + if opts.tlabel is not None: + labels[1] = opts.tlabel + + # Plot extent defaults to calculation extent + pmin = opts.pmin if opts.pmin is not None else opts.rmin + pmax = opts.pmax if opts.pmax is not None else opts.rmax + maglim = opts.maglim + mag = opts.mag + l_width = opts.lwidth + pdfplot.comparePDFs( + pairlist, + labels, + rmin=pmin, + rmax=pmax, + maglim=maglim, + mag=mag, + rw=rw, + l_width=l_width, + ) + + return morph_results + + +def multiple_targets(parser, opts, pargs, stdout_flag=True): + # Custom error messages since usage is distinct when --multiple tag is + # applied + if len(pargs) < 2: + parser.custom_error( + "You must supply FILE and DIRECTORY. " + "See --multiple-targets under --help for usage." + ) + elif len(pargs) > 2: + parser.custom_error( + "Too many arguments. You must only supply a FILE and a DIRECTORY." + ) + + # Parse paths + morph_file = Path(pargs[0]) + if not morph_file.is_file(): + parser.custom_error( + f"{morph_file} is not a file. Go to --help for usage." + ) + target_directory = Path(pargs[1]) + if not target_directory.is_dir(): + parser.custom_error( + f"{target_directory} is not a directory. Go to --help for usage." + ) + + # Get list of files from target directory + target_list = list(target_directory.iterdir()) + for target in target_list: + if target.is_dir(): + target_list.remove(target) + + # Do not morph morph_file against itself if it is in the same directory + if morph_file in target_list: + target_list.remove(morph_file) + + # Format field name for printing and plotting + field = None + if opts.field is not None: + field_words = opts.field.split() + field = "" + for word in field_words: + field += f"{word[0].upper()}{word[1:].lower()}" + field_list = None + + # Sort files in directory by some field + if field is not None: + try: + target_list, field_list = tools.field_sort( + target_list, + field, + opts.reverse, + opts.serfile, + get_field_values=True, + ) + except KeyError: + if opts.serfile is not None: + parser.custom_error( + "The requested field was not found in the metadata file." + ) + else: + parser.custom_error( + "The requested field is missing from a PDF file header." + ) + else: + # Default is alphabetical sort + target_list.sort(reverse=opts.reverse) + + # Disable single morph plotting + plot_opt = opts.plot + opts.plot = False + + # Set up saving + save_directory = opts.slocation # User-given directory for saves + save_names_file = ( + opts.snamesfile + ) # User-given serialfile with names for each morph + save_morphs_here = None # Subdirectory for saving morphed PDFs + save_names = {} # Dictionary of names to save each morph as + if save_directory is not None: + try: + save_morphs_here = io.create_morphs_directory(save_directory) + + # Could not create directory or find names to save morphs as + except (FileNotFoundError, RuntimeError): + save_fail_message = "\nUnable to create directory" + parser.custom_error(save_fail_message) + + try: + save_names = io.get_multisave_names( + target_list, save_names_file=save_names_file + ) + # Could not create directory or find names to save morphs as + except FileNotFoundError: + save_fail_message = "\nUnable to read from save names file" + parser.custom_error(save_fail_message) + + # Morph morph_file against all other files in target_directory + morph_results = {} + for target_file in target_list: + if target_file.is_file: + # Set the save file destination to be a file within the SLOC + # directory + if save_directory is not None: + save_as = save_names[target_file.name][__save_morph_as__] + opts.slocation = Path(save_morphs_here).joinpath(save_as) + # Perform a morph of morph_file against target_file + pargs = [morph_file, target_file] + morph_results.update( + { + target_file.name: single_morph( + parser, opts, pargs, stdout_flag=False + ), + } + ) + + target_file_names = [] + for key in morph_results.keys(): + target_file_names.append(key) + + morph_inputs = { + "scale": opts.scale, + "stretch": opts.stretch, + "smear": opts.smear, + } + morph_inputs.update({"hshift": opts.hshift, "vshift": opts.vshift}) + + try: + # Print summary of morphs to terminal and to file (if requested) + io.multiple_morph_output( + morph_inputs, + morph_results, + target_file_names, + save_directory=save_directory, + morph_file=morph_file, + target_directory=target_directory, + field=field, + field_list=field_list, + verbose=opts.verbose, + stdout_flag=stdout_flag, + ) + except (FileNotFoundError, RuntimeError): + save_fail_message = "Unable to save summary to directory." + parser.custom_error(save_fail_message) + + # Plot the values of some parameter for each target if requested + if plot_opt: + plot_results = io.tabulate_results(morph_results) + # Default parameter is Rw + param_name = r"$R_w$" + param_list = plot_results["Rw"] + # Find parameter if specified + if opts.plotparam is not None: + param_name = opts.plotparam + param_list = tools.case_insensitive_dictionary_search( + opts.plotparam, plot_results + ) + # Not an available parameter to plot or no values found for the + # parameter + if param_list is None: + parser.custom_error( + "Cannot find specified plot parameter. No plot shown." + ) + else: + try: + if field_list is not None: + pdfplot.plot_param( + field_list, param_list, param_name, field + ) + else: + pdfplot.plot_param( + target_file_names, param_list, param_name + ) + # Can occur for non-refined plotting parameters + # i.e. --smear is not selected as an option, but smear is the + # plotting parameter + except ValueError: + parser.custom_error( + "The plot parameter is missing values for at least one " + "morph and target pair. No plot shown." + ) + + return morph_results + + +def multiple_morphs(parser, opts, pargs, stdout_flag=True): + # Custom error messages since usage is distinct when --multiple tag is + # applied + if len(pargs) < 2: + parser.custom_error( + "You must supply DIRECTORY and FILE. " + "See --multiple-morphs under --help for usage." + ) + elif len(pargs) > 2: + parser.custom_error( + "Too many arguments. You must only supply a DIRECTORY and FILE." + ) + + # Parse paths + target_file = Path(pargs[1]) + if not target_file.is_file(): + parser.custom_error( + f"{target_file} is not a file. Go to --help for usage." + ) + morph_directory = Path(pargs[0]) + if not morph_directory.is_dir(): + parser.custom_error( + f"{morph_directory} is not a directory. Go to --help for usage." + ) + + # Get list of files from morph directory + morph_list = list(morph_directory.iterdir()) + for morph in morph_list: + if morph.is_dir(): + morph_list.remove(morph) + + # Do not morph target_file against itself if it is in the same directory + if target_file in morph_list: + morph_list.remove(target_file) + + # Format field name for printing and plotting + field = None + if opts.field is not None: + field_words = opts.field.split() + field = "" + for word in field_words: + field += f"{word[0].upper()}{word[1:].lower()}" + field_list = None + + # Sort files in directory by some field + if field is not None: + try: + morph_list, field_list = tools.field_sort( + morph_list, + field, + opts.reverse, + opts.serfile, + get_field_values=True, + ) + except KeyError: + if opts.serfile is not None: + parser.custom_error( + "The requested field was not found in the metadata file." + ) + else: + parser.custom_error( + "The requested field is missing from a PDF file header." + ) + else: + # Default is alphabetical sort + morph_list.sort(reverse=opts.reverse) + + # Disable single morph plotting + plot_opt = opts.plot + opts.plot = False + + # Set up saving + save_directory = opts.slocation # User-given directory for saves + save_names_file = ( + opts.snamesfile + ) # User-given serialfile with names for each morph + save_morphs_here = None # Subdirectory for saving morphed PDFs + save_names = {} # Dictionary of names to save each morph as + if save_directory is not None: + try: + save_morphs_here = io.create_morphs_directory(save_directory) + + # Could not create directory or find names to save morphs as + except (FileNotFoundError, RuntimeError): + save_fail_message = "\nUnable to create directory" + parser.custom_error(save_fail_message) + + try: + save_names = io.get_multisave_names( + morph_list, save_names_file=save_names_file + ) + # Could not create directory or find names to save morphs as + except FileNotFoundError: + save_fail_message = "\nUnable to read from save names file" + parser.custom_error(save_fail_message) + + # Morph morph_file against all other files in target_directory + morph_results = {} + for morph_file in morph_list: + if morph_file.is_file: + # Set the save file destination to be a file within the SLOC + # directory + if save_directory is not None: + save_as = save_names[morph_file.name][__save_morph_as__] + opts.slocation = Path(save_morphs_here).joinpath(save_as) + # Perform a morph of morph_file against target_file + pargs = [morph_file, target_file] + morph_results.update( + { + morph_file.name: single_morph( + parser, opts, pargs, stdout_flag=False + ), + } + ) + + morph_file_names = [] + for key in morph_results.keys(): + morph_file_names.append(key) + + morph_inputs = { + "scale": opts.scale, + "stretch": opts.stretch, + "smear": opts.smear, + } + morph_inputs.update({"hshift": opts.hshift, "vshift": opts.vshift}) + + try: + # Print summary of morphs to terminal and to file (if requested) + io.multiple_morph_output( + morph_inputs, + morph_results, + morph_file_names, + save_directory=save_directory, + morph_file=target_file, + target_directory=morph_directory, + field=field, + field_list=field_list, + verbose=opts.verbose, + stdout_flag=stdout_flag, + mm=True, + ) + except (FileNotFoundError, RuntimeError): + save_fail_message = "Unable to save summary to directory." + parser.custom_error(save_fail_message) + + # Plot the values of some parameter for each target if requested + if plot_opt: + plot_results = io.tabulate_results(morph_results) + # Default parameter is Rw + param_name = r"$R_w$" + param_list = plot_results["Rw"] + # Find parameter if specified + if opts.plotparam is not None: + param_name = opts.plotparam + param_list = tools.case_insensitive_dictionary_search( + opts.plotparam, plot_results + ) + # Not an available parameter to plot or no values found for the + # parameter + if param_list is None: + parser.custom_error( + "Cannot find specified plot parameter. No plot shown." + ) + else: + try: + if field_list is not None: + pdfplot.plot_param( + field_list, param_list, param_name, field + ) + else: + pdfplot.plot_param( + morph_file_names, param_list, param_name + ) + # Can occur for non-refined plotting parameters + # i.e. --smear is not selected as an option, but smear is the + # plotting parameter + except ValueError: + parser.custom_error( + "The plot parameter is missing values for at least one " + "morph and target pair. No plot shown." + ) + + return morph_results + + +def getPDFFromFile(fn): + from diffpy.morph.tools import readPDF + + try: + r, gr = readPDF(fn) + except IOError as errmsg: + print("%s: %s" % (fn, errmsg), file=sys.stderr) + sys.exit(1) + except ValueError: + print("Cannot read %s" % fn, file=sys.stderr) + sys.exit(1) + + return r, gr + + +def main(): + parser = create_option_parser() + (opts, pargs) = parser.parse_args() + if opts.multiple_targets: + multiple_targets(parser, opts, pargs, stdout_flag=True) + elif opts.multiple_morphs: + multiple_morphs(parser, opts, pargs, stdout_flag=True) + else: + single_morph(parser, opts, pargs, stdout_flag=True) + + +if __name__ == "__main__": + main() diff --git a/src/diffpy/pdfmorph/morphs/__init__.py b/src/diffpy/morph/morphs/__init__.py similarity index 52% rename from src/diffpy/pdfmorph/morphs/__init__.py rename to src/diffpy/morph/morphs/__init__.py index 37c5d4ef..3cc0fdc9 100644 --- a/src/diffpy/pdfmorph/morphs/__init__.py +++ b/src/diffpy/morph/morphs/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -17,16 +17,16 @@ """ -from diffpy.pdfmorph.morphs.morph import Morph # noqa: F401 -from diffpy.pdfmorph.morphs.morphchain import MorphChain # noqa: F401 -from diffpy.pdfmorph.morphs.morphishape import MorphISphere, MorphISpheroid -from diffpy.pdfmorph.morphs.morphresolution import MorphResolutionDamping -from diffpy.pdfmorph.morphs.morphrgrid import MorphRGrid -from diffpy.pdfmorph.morphs.morphscale import MorphScale -from diffpy.pdfmorph.morphs.morphshape import MorphSphere, MorphSpheroid -from diffpy.pdfmorph.morphs.morphshift import MorphShift -from diffpy.pdfmorph.morphs.morphsmear import MorphSmear -from diffpy.pdfmorph.morphs.morphstretch import MorphStretch +from diffpy.morph.morphs.morph import Morph # noqa: F401 +from diffpy.morph.morphs.morphchain import MorphChain # noqa: F401 +from diffpy.morph.morphs.morphishape import MorphISphere, MorphISpheroid +from diffpy.morph.morphs.morphresolution import MorphResolutionDamping +from diffpy.morph.morphs.morphrgrid import MorphRGrid +from diffpy.morph.morphs.morphscale import MorphScale +from diffpy.morph.morphs.morphshape import MorphSphere, MorphSpheroid +from diffpy.morph.morphs.morphshift import MorphShift +from diffpy.morph.morphs.morphsmear import MorphSmear +from diffpy.morph.morphs.morphstretch import MorphStretch # List of morphs morphs = [ diff --git a/src/diffpy/pdfmorph/morphs/morph.py b/src/diffpy/morph/morphs/morph.py similarity index 92% rename from src/diffpy/pdfmorph/morphs/morph.py rename to src/diffpy/morph/morphs/morph.py index e55ec92a..0b7cb543 100644 --- a/src/diffpy/pdfmorph/morphs/morph.py +++ b/src/diffpy/morph/morphs/morph.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -26,10 +26,12 @@ class Morph(object): """Base class for implementing a morph given a target. - Adapted from diffpy.pdfgetx to include two sets of arrays that get passed through. + Adapted from diffpy.pdfgetx to include two sets of arrays that get passed + through. - Attributes are taken from config when not found locally. The morph may modify the config dictionary. - This is the means by which to communicate automatically modified attributes. + Attributes are taken from config when not found locally. The morph may + modify the config dictionary. This is the means by which to communicate + automatically modified attributes. Class Attributes ---------------- @@ -142,7 +144,8 @@ def __init__(self, config=None): def morph(self, x_morph, y_morph, x_target, y_target): """Morph arrays morphed or target. - Identity operation. This method should be overloaded in a derived class. + Identity operation. + This method should be overloaded in a derived class. Parameters ---------- @@ -154,7 +157,8 @@ def morph(self, x_morph, y_morph, x_target, y_target): Returns ------- tuple - A tuple of numpy arrays (x_morph_out, y_morph_out, x_target_out, y_target_out) + A tuple of numpy arrays + (x_morph_out, y_morph_out, x_target_out, y_target_out) """ self.x_morph_in = x_morph self.y_morph_in = y_morph @@ -223,7 +227,8 @@ def plotOutputs(self, xylabels=True, **plotargs): xylabels: bool Flag for updating x and y axes labels. plotargs - Arguments passed to the pylab plot function. Note that "label" will be ignored. + Arguments passed to the pylab plot function. + Note that "label" will be ignored. Returns ------- @@ -234,7 +239,9 @@ def plotOutputs(self, xylabels=True, **plotargs): pargs = dict(plotargs) pargs.pop("label", None) - rv = plot(self.x_target_out, self.y_target_out, label="target", **pargs) + rv = plot( + self.x_target_out, self.y_target_out, label="target", **pargs + ) rv = plot(self.x_morph_out, self.y_morph_out, label="morph", **pargs) if xylabels: xlabel(self.xoutlabel) diff --git a/src/diffpy/pdfmorph/morphs/morphchain.py b/src/diffpy/morph/morphs/morphchain.py similarity index 69% rename from src/diffpy/pdfmorph/morphs/morphchain.py rename to src/diffpy/morph/morphs/morphchain.py index ca310879..1dc05b41 100644 --- a/src/diffpy/pdfmorph/morphs/morphchain.py +++ b/src/diffpy/morph/morphs/morphchain.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,8 +20,9 @@ class MorphChain(list): """Class for chaining morphs together. - This class is a queue of morphs that get executed in order via the 'morph' method. - This class derives from the built-in list, and list methods are used to modify the queue. + This class is a queue of morphs that get executed in order via the 'morph' + method. This class derives from the built-in list, and list methods are + used to modify the queue. This derives from list and relies on its methods where possible. @@ -57,7 +58,8 @@ class MorphChain(list): xy_target_out Tuple of (x_target_out, y_target_out) from last morph. xyallout - Tuple of (x_morph_out, y_morph_out, x_target_out, y_target_out) from last morph. + Tuple of (x_morph_out, y_morph_out, x_target_out, y_target_out) + from last morph. parnames Names of parameters collected from morphs (Read only). @@ -66,19 +68,47 @@ class MorphChain(list): The properties return tuples of None if there are no morphs. """ - x_morph_in = property(lambda self: None if len(self) == 0 else self[0].x_morph_in) - y_morph_in = property(lambda self: None if len(self) == 0 else self[0].y_morph_in) - x_target_in = property(lambda self: None if len(self) == 0 else self[0].x_target_in) - y_target_in = property(lambda self: None if len(self) == 0 else self[0].y_target_in) - x_morph_out = property(lambda self: None if len(self) == 0 else self[-1].x_morph_out) - y_morph_out = property(lambda self: None if len(self) == 0 else self[-1].y_morph_out) - x_target_out = property(lambda self: None if len(self) == 0 else self[-1].x_target_out) - y_target_out = property(lambda self: None if len(self) == 0 else self[-1].y_target_out) - xy_morph_in = property(lambda self: (None, None) if len(self) == 0 else self[0].xy_morph_in) - xy_morph_out = property(lambda self: (None, None) if len(self) == 0 else self[-1].xy_morph_out) - xy_target_in = property(lambda self: (None, None) if len(self) == 0 else self[0].xy_target_in) - xy_target_out = property(lambda self: (None, None) if len(self) == 0 else self[-1].xy_target_out) - xyallout = property(lambda self: (None, None, None, None) if len(self) == 0 else self[-1].xyallout) + x_morph_in = property( + lambda self: None if len(self) == 0 else self[0].x_morph_in + ) + y_morph_in = property( + lambda self: None if len(self) == 0 else self[0].y_morph_in + ) + x_target_in = property( + lambda self: None if len(self) == 0 else self[0].x_target_in + ) + y_target_in = property( + lambda self: None if len(self) == 0 else self[0].y_target_in + ) + x_morph_out = property( + lambda self: None if len(self) == 0 else self[-1].x_morph_out + ) + y_morph_out = property( + lambda self: None if len(self) == 0 else self[-1].y_morph_out + ) + x_target_out = property( + lambda self: None if len(self) == 0 else self[-1].x_target_out + ) + y_target_out = property( + lambda self: None if len(self) == 0 else self[-1].y_target_out + ) + xy_morph_in = property( + lambda self: (None, None) if len(self) == 0 else self[0].xy_morph_in + ) + xy_morph_out = property( + lambda self: (None, None) if len(self) == 0 else self[-1].xy_morph_out + ) + xy_target_in = property( + lambda self: (None, None) if len(self) == 0 else self[0].xy_target_in + ) + xy_target_out = property( + lambda self: (None, None) if len(self) == 0 else self[-1].xy_target_out + ) + xyallout = property( + lambda self: ( + (None, None, None, None) if len(self) == 0 else self[-1].xyallout + ) + ) parnames = property(lambda self: set(p for m in self for p in m.parnames)) def __init__(self, config, *args): @@ -89,7 +119,8 @@ def __init__(self, config, *args): Notes ----- - Additional arguments are morphs that will extend the queue of morphs. + Additional arguments are morphs that will extend the queue of + morphs. """ self.config = config self.extend(args) @@ -108,7 +139,8 @@ def morph(self, x_morph, y_morph, x_target, y_target): Returns ------- tuple - A tuple of numpy arrays (x_morph_out, y_morph_out, x_target_out, y_target_out). + A tuple of numpy arrays + (x_morph_out, y_morph_out, x_target_out, y_target_out). Notes ----- diff --git a/src/diffpy/pdfmorph/morphs/morphishape.py b/src/diffpy/morph/morphs/morphishape.py similarity index 92% rename from src/diffpy/pdfmorph/morphs/morphishape.py rename to src/diffpy/morph/morphs/morphishape.py index 91e24a2a..5cf3da91 100644 --- a/src/diffpy/pdfmorph/morphs/morphishape.py +++ b/src/diffpy/morph/morphs/morphishape.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -21,8 +21,8 @@ class MorphISpheroid -- apply inverse spheroidal shape function import numpy -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, Morph -from diffpy.pdfmorph.morphs.morphshape import _sphericalCF, _spheroidalCF +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph +from diffpy.morph.morphs.morphshape import _sphericalCF, _spheroidalCF class MorphISphere(Morph): diff --git a/src/diffpy/pdfmorph/morphs/morphresolution.py b/src/diffpy/morph/morphs/morphresolution.py similarity index 92% rename from src/diffpy/pdfmorph/morphs/morphresolution.py rename to src/diffpy/morph/morphs/morphresolution.py index e0011590..a7246ad3 100644 --- a/src/diffpy/pdfmorph/morphs/morphresolution.py +++ b/src/diffpy/morph/morphs/morphresolution.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,7 +20,7 @@ import numpy -from diffpy.pdfmorph.morphs.morph import LABEL_RA, LABEL_RR, Morph +from diffpy.morph.morphs.morph import LABEL_RA, LABEL_RR, Morph class MorphResolutionDamping(Morph): diff --git a/src/diffpy/pdfmorph/morphs/morphrgrid.py b/src/diffpy/morph/morphs/morphrgrid.py similarity index 74% rename from src/diffpy/pdfmorph/morphs/morphrgrid.py rename to src/diffpy/morph/morphs/morphrgrid.py index 7e5a7bfe..dc9b68d3 100644 --- a/src/diffpy/pdfmorph/morphs/morphrgrid.py +++ b/src/diffpy/morph/morphs/morphrgrid.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,7 +20,7 @@ import numpy -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, Morph +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph # roundoff tolerance for selecting bounds on arrays. epsilon = 1e-8 @@ -43,9 +43,10 @@ class MorphRGrid(Morph): Notes ----- - If any of these is not defined or outside the bounds of the input arrays, - then it will be taken to be the most inclusive value from the input arrays. - These modified values will be stored as the above attributes. + If any of these is not defined or outside the bounds of the input + arrays, then it will be taken to be the most inclusive value from the + input arrays. These modified values will be stored as the above + attributes. """ # Define input output types @@ -63,7 +64,10 @@ def morph(self, x_morph, y_morph, x_target, y_target): r_step_target = self.x_target_in[1] - self.x_target_in[0] r_step_morph = self.x_morph_in[1] - self.x_morph_in[0] rstepinc = max(r_step_target, r_step_morph) - rmaxinc = min(self.x_target_in[-1] + r_step_target, self.x_morph_in[-1] + r_step_morph) + rmaxinc = min( + self.x_target_in[-1] + r_step_target, + self.x_morph_in[-1] + r_step_morph, + ) if self.rmin is None or self.rmin < rmininc: self.rmin = rmininc if self.rmax is None or self.rmax > rmaxinc: @@ -71,10 +75,16 @@ def morph(self, x_morph, y_morph, x_target, y_target): if self.rstep is None or self.rstep < rstepinc: self.rstep = rstepinc # Make sure that rmax is exclusive - self.x_morph_out = numpy.arange(self.rmin, self.rmax - epsilon, self.rstep) - self.y_morph_out = numpy.interp(self.x_morph_out, self.x_morph_in, self.y_morph_in) + self.x_morph_out = numpy.arange( + self.rmin, self.rmax - epsilon, self.rstep + ) + self.y_morph_out = numpy.interp( + self.x_morph_out, self.x_morph_in, self.y_morph_in + ) self.x_target_out = self.x_morph_out.copy() - self.y_target_out = numpy.interp(self.x_target_out, self.x_target_in, self.y_target_in) + self.y_target_out = numpy.interp( + self.x_target_out, self.x_target_in, self.y_target_in + ) return self.xyallout diff --git a/src/diffpy/pdfmorph/morphs/morphscale.py b/src/diffpy/morph/morphs/morphscale.py similarity index 91% rename from src/diffpy/pdfmorph/morphs/morphscale.py rename to src/diffpy/morph/morphs/morphscale.py index 0251b776..2f5c6b4d 100644 --- a/src/diffpy/pdfmorph/morphs/morphscale.py +++ b/src/diffpy/morph/morphs/morphscale.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -18,7 +18,7 @@ """ -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, Morph +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph class MorphScale(Morph): diff --git a/src/diffpy/pdfmorph/morphs/morphshape.py b/src/diffpy/morph/morphs/morphshape.py similarity index 79% rename from src/diffpy/pdfmorph/morphs/morphshape.py rename to src/diffpy/morph/morphs/morphshape.py index 755469e3..9aae3d78 100644 --- a/src/diffpy/pdfmorph/morphs/morphshape.py +++ b/src/diffpy/morph/morphs/morphshape.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -24,7 +24,7 @@ class MorphSpheroid -- apply a spheroidal shape function to the morph from numpy import arctanh as atanh from numpy import sqrt -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, Morph +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph class MorphSphere(Morph): @@ -89,7 +89,8 @@ def morph(self, x_morph, y_morph, x_target, y_target): def _sphericalCF(r, psize): """Spherical nanoparticle characteristic function. - From Kodama et al., Acta Cryst. A, 62, 444-453 (converted from radius to diameter). + From Kodama et al., Acta Cryst. A, 62, 444-453 + (converted from radius to diameter). Parameters ---------- @@ -163,21 +164,19 @@ def _spheroidalCF2(r, psize, axrat): 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)) + 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)) # fmt: skip # noqa: E501 ) r = rx[numpy.logical_and(rx > v * psize, rx <= psize)] r2 = r * r + # fmt: off 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)) + 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)) # noqa: E501 ) - * v - / sqrt(1 - v2) + * v / sqrt(1-v2) ) + # fmt: on r = rx[rx > psize] f3 = numpy.zeros_like(r) @@ -188,24 +187,13 @@ def _spheroidalCF2(r, psize, axrat): 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)) + 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)) # fmt: skip # noqa: E501 ) 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))) + 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))) # fmt: skip # noqa:E501 ) r = rx[rx > v * psize] diff --git a/src/diffpy/pdfmorph/morphs/morphshift.py b/src/diffpy/morph/morphs/morphshift.py similarity index 78% rename from src/diffpy/pdfmorph/morphs/morphshift.py rename to src/diffpy/morph/morphs/morphshift.py index f10a8325..b3ac8818 100644 --- a/src/diffpy/pdfmorph/morphs/morphshift.py +++ b/src/diffpy/morph/morphs/morphshift.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,7 +20,7 @@ import numpy -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, Morph +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph class MorphShift(Morph): @@ -47,10 +47,19 @@ class MorphShift(Morph): def morph(self, x_morph, y_morph, x_target, y_target): """Apply the shifts.""" + try: + hshift = self.hshift + except AttributeError: + hshift = 0 + try: + vshift = self.vshift + except AttributeError: + vshift = 0 + Morph.morph(self, x_morph, y_morph, x_target, y_target) - r = self.x_morph_in - self.hshift + r = self.x_morph_in - hshift self.y_morph_out = numpy.interp(r, self.x_morph_in, self.y_morph_in) - self.y_morph_out += self.vshift + self.y_morph_out += vshift return self.xyallout diff --git a/src/diffpy/pdfmorph/morphs/morphsmear.py b/src/diffpy/morph/morphs/morphsmear.py similarity index 95% rename from src/diffpy/pdfmorph/morphs/morphsmear.py rename to src/diffpy/morph/morphs/morphsmear.py index 8161e50d..2495ee38 100644 --- a/src/diffpy/pdfmorph/morphs/morphsmear.py +++ b/src/diffpy/morph/morphs/morphsmear.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,7 +20,7 @@ import numpy -from diffpy.pdfmorph.morphs.morph import LABEL_RA, LABEL_RR, Morph +from diffpy.morph.morphs.morph import LABEL_RA, LABEL_RR, Morph class MorphSmear(Morph): diff --git a/src/diffpy/pdfmorph/morphs/morphstretch.py b/src/diffpy/morph/morphs/morphstretch.py similarity index 92% rename from src/diffpy/pdfmorph/morphs/morphstretch.py rename to src/diffpy/morph/morphs/morphstretch.py index 88c494e1..6b99caea 100644 --- a/src/diffpy/pdfmorph/morphs/morphstretch.py +++ b/src/diffpy/morph/morphs/morphstretch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,7 +20,7 @@ import numpy -from diffpy.pdfmorph.morphs.morph import LABEL_GR, LABEL_RA, Morph +from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph class MorphStretch(Morph): diff --git a/src/diffpy/pdfmorph/pdfplot.py b/src/diffpy/morph/pdfplot.py similarity index 89% rename from src/diffpy/pdfmorph/pdfplot.py rename to src/diffpy/morph/pdfplot.py index 16bd078f..9d4dd06a 100644 --- a/src/diffpy/pdfmorph/pdfplot.py +++ b/src/diffpy/morph/pdfplot.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2008 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -31,15 +31,18 @@ def plotPDFs(pairlist, labels=None, offset="auto", rmin=None, rmax=None): pairlist Iterable of (r, gr) pairs to plot. labels - Iterable of names for the pairs. If this is not the same length as the pairlist, a legend will not - be shown (default []). + Iterable of names for the pairs. If this is not the same length as + the pairlist, a legend will not be shown (default []). offset - Offset to place between plots. PDFs will be sequentially shifted in the y-direction by the offset. - If offset is 'auto' (default), the optimal offset will be determined automatically. + Offset to place between plots. PDFs will be sequentially shifted in + the y-direction by the offset. If offset is 'auto' (default), the + optimal offset will be determined automatically. rmin - The minimum r-value to plot. If this is None (default), the lower bound of the PDF is not altered. + The minimum r-value to plot. If this is None (default), the lower + bound of the PDF is not altered. rmax - The maximum r-value to plot. If this is None (default), the upper bound of the PDF is not altered. + The maximum r-value to plot. If this is None (default), the upper + bound of the PDF is not altered. """ if labels is None: labels = [] @@ -79,24 +82,27 @@ def comparePDFs( ): """Plot two PDFs on top of each other and difference curve. - The second PDF will be shown as blue circles below and the first as a red line. - The difference curve will be in green and offset for clarity. + The second PDF will be shown as blue circles below and the first as a red + line. The difference curve will be in green and offset for clarity. Parameters ---------- pairlist Iterable of (r, gr) pairs to plot labels - Iterable of names for the pairs. If this is not the same length as the pairlist, a legend will not - be shown (default []). + Iterable of names for the pairs. If this is not the same length as + the pairlist, a legend will not be shown (default []). rmin - The minimum r-value to plot. If this is None (default), the lower bound of the PDF is not altered. + The minimum r-value to plot. If this is None (default), the lower + bound of the PDF is not altered. rmax - The maximum r-value to plot. If this is None (default), the upper bound of the PDF is not altered. + The maximum r-value to plot. If this is None (default), the upper + bound of the PDF is not altered. show Show the plot (default True) maglim - Point after which to magnify the signal by mag. If None (default), no magnification will take place. + Point after which to magnify the signal by mag. If None (default), no + magnification will take place. mag Magnification factor (default 5) rw @@ -178,7 +184,15 @@ def comparePDFs( if maglim is not None: # Add a line for the magnification cutoff - plt.axvline(maglim, 0, 1, linestyle="--", color="black", linewidth=1.5, dashes=(14, 7)) + plt.axvline( + maglim, + 0, + 1, + linestyle="--", + color="black", + linewidth=1.5, + dashes=(14, 7), + ) # FIXME - look for a place to put the maglim xpos = (rvmax * 0.85 + maglim) / 2 / (rvmax - rvmin) if xpos <= 0.9: @@ -219,14 +233,17 @@ def plot_param(target_labels, param_list, param_name=None, field=None): Parameters ---------- target_labels: list - Names (or field if --sort-by given) of each file acting as target for the morph. + Names (or field if --sort-by given) of each file acting as target for + the morph. param_list: list Contains the values of some parameter corresponding to each file. param_name: str Name of the parameter. field: list or None - When not None and entries in field are numerical, a line chart of Rw versus field is made. - When None (default) or values are non-numerical, it plots a bar chart of Rw values per file. + When not None and entries in field are numerical, a line chart of Rw + versus field is made. + When None (default) or values are non-numerical, it plots a bar chart + of Rw values per file. """ # ensure all entries in target_labels are distinct for plotting @@ -298,9 +315,11 @@ def truncatePDFs(r, gr, rmin=None, rmax=None): gr PDF g(r) values. rmin - The minimum r-value. If this is None (default), the lower bound of the PDF is not altered. + The minimum r-value. If this is None (default), the lower bound of + the PDF is not altered. rmax - The maximum r-value. If this is None (default), the upper bound of the PDF is not altered. + The maximum r-value. If this is None (default), the upper bound of + the PDF is not altered. Returns ------- diff --git a/src/diffpy/pdfmorph/refine.py b/src/diffpy/morph/refine.py similarity index 90% rename from src/diffpy/pdfmorph/refine.py rename to src/diffpy/morph/refine.py index e505d491..e1f55ac3 100644 --- a/src/diffpy/pdfmorph/refine.py +++ b/src/diffpy/morph/refine.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -39,7 +39,8 @@ class Refiner(object): pars List of names of parameters to be refined. residual - The residual function to optimize. Default _residual. Can be assigned to other functions. + The residual function to optimize. Default _residual. Can be assigned + to other functions. """ def __init__(self, chain, x_morph, y_morph, x_target, y_target): @@ -71,7 +72,8 @@ def _pearson(self, pvals): """Pearson correlation function. This gives e**-p (vector), where p is the pearson correlation function. - We seek to minimize this, which occurrs when the correlation is the largest. + We seek to minimize this, which occurs when the correlation is the + largest. """ self._update_chain(pvals) _x_morph, _y_morph, _x_target, _y_target = self.chain( @@ -90,14 +92,17 @@ def _add_pearson(self, pvals): def refine(self, *args, **kw): """Refine the chain. - Additional arguments are used to specify which parameters are to be refined. + Additional arguments are used to specify which parameters are to be + refined. If no arguments are passed, then all parameters will be refined. - Keywords pass initial values to the parameters, whether or not they are refined. + Keywords pass initial values to the parameters, whether or not they + are refined. This uses the leastsq algorithm from scipy.optimize. This returns the final scalar residual value. - The parameters from the fit can be retrieved from the config dictionary of the morph or morph chain. + The parameters from the fit can be retrieved from the config + dictionary of the morph or morph chain. Raises ------ @@ -114,7 +119,9 @@ def refine(self, *args, **kw): return 0.0 initial = [config[p] for p in self.pars] - sol, cov_sol, infodict, emesg, ier = leastsq(self.residual, initial, full_output=1) + sol, cov_sol, infodict, emesg, ier = leastsq( + self.residual, initial, full_output=1 + ) fvec = infodict["fvec"] if ier not in (1, 2, 3, 4): emesg diff --git a/src/diffpy/pdfmorph/tools.py b/src/diffpy/morph/tools.py similarity index 80% rename from src/diffpy/pdfmorph/tools.py rename to src/diffpy/morph/tools.py index f8f98a37..f9b024dd 100644 --- a/src/diffpy/pdfmorph/tools.py +++ b/src/diffpy/morph/tools.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# diffpy.pdfmorph by DANSE Diffraction group +# diffpy.morph by DANSE Diffraction group # Simon J. L. Billinge # (c) 2010 Trustees of the Columbia University # in the City of New York. All rights reserved. @@ -20,7 +20,8 @@ import numpy -import diffpy.utils.parsers as parsers +from diffpy.utils.parsers.loaddata import loadData +from diffpy.utils.parsers.serialization import deserialize_data def estimateScale(y_morph_in, y_target_in): @@ -42,14 +43,17 @@ def estimateBaselineSlope(r, gr, rmin=None, rmax=None): gr The PDF over the r-grid. rmin - The minimum r-value to consider. If this is None (default) is None, then the minimum of r is used. + The minimum r-value to consider. If this is None (default) is None, + then the minimum of r is used. rmax - The maximum r-value to consider. If this is None (default) is None, then the maximum of r is used. + The maximum r-value to consider. If this is None (default) is None, + then the maximum of r is used. Returns ------- slope: float - The slope of baseline. If the PDF is scaled properly, this is equal to -4*pi*rho0. + The slope of baseline. If the PDF is scaled properly, this is equal + to -4*pi*rho0. """ from numpy import dot from scipy.optimize import leastsq @@ -116,7 +120,6 @@ def readPDF(fname): r,gr Arrays read from data. """ - from diffpy.utils.parsers import loadData rv = loadData(fname, unpack=True) if len(rv) >= 2: @@ -127,7 +130,10 @@ def readPDF(fname): def nn_value(val, name): """Convenience function for ensuring certain non-negative inputs.""" if val < 0: - negative_value_warning = f"\n# Negative value for {name} given. Using absolute value instead." + negative_value_warning = ( + f"\n# Negative value for {name} given. " + "Using absolute value instead." + ) print(negative_value_warning) return -val return val @@ -146,7 +152,7 @@ def deserialize(serial_file): dict Data read from serial file. """ - return parsers.deserialize_data(serial_file) + return deserialize_data(serial_file) def case_insensitive_dictionary_search(key: str, dictionary: dict): @@ -171,7 +177,9 @@ def case_insensitive_dictionary_search(key: str, dictionary: dict): return dictionary.get(key) -def field_sort(filepaths: list, field, reverse=False, serfile=None, get_field_values=False): +def field_sort( + filepaths: list, field, reverse=False, serfile=None, get_field_values=False +): """Sort a list of files by a field stored in header information. All files must contain this header information. @@ -186,25 +194,29 @@ def field_sort(filepaths: list, field, reverse=False, serfile=None, get_field_va serfile Path to a serial file with field information for each file. get_field_values: bool - Boolean indicating whether to also return a List of field values (default False). - This List of field values is parallel to the sorted list of filepaths with items - in the same position corresponding to each other. + Boolean indicating whether to also return a List of field values + (default False). This List of field values is parallel to the sorted + list of filepaths with items in the same position corresponding to + each other. Returns ------- list - Sorted list of paths. When get_fv is true, also return an associated field list. + Sorted list of paths. When get_fv is true, also return an associated + field list. """ # Get the field from each file files_field_values = [] if serfile is None: for path in filepaths: - fhd = parsers.loadData(path, headers=True) - files_field_values.append([path, case_insensitive_dictionary_search(field, fhd)]) + fhd = loadData(path, headers=True) + files_field_values.append( + [path, case_insensitive_dictionary_search(field, fhd)] + ) else: # deserialize the serial file - des_dict = parsers.deserialize_data(serfile) + des_dict = deserialize_data(serfile) # get names of each file to search the serial file import pathlib @@ -221,22 +233,28 @@ def field_sort(filepaths: list, field, reverse=False, serfile=None, get_field_va except (ValueError, TypeError): raise KeyError("Field missing.") if get_field_values: - return [pair[0] for pair in files_field_values], [pair[1] for pair in files_field_values] + return [pair[0] for pair in files_field_values], [ + pair[1] for pair in files_field_values + ] else: return [pair[0] for pair in files_field_values] -def get_values_from_dictionary_collection(dictionary_collection: iter, target_key): - """In an (iterable) collection of dictionaries, search for a target key in each dictionary. - Return a list of all found values corresponding to that key. +def get_values_from_dictionary_collection( + dictionary_collection: iter, target_key +): + """In an (iterable) collection of dictionaries, search for a target key + in each dictionary. Return a list of all found values corresponding + to that key. Parameters ---------- dictionary_collection: iter The collection of dictionaries to search through. target_key - The key to search for in each dictionary. For each dictionary in dictionary_collection that has that key, - the corresponding value is appended to a List called values. + The key to search for in each dictionary. For each dictionary in + dictionary_collection that has that key, the corresponding value is + appended to a List called values. Returns ------- @@ -249,7 +267,8 @@ def get_values_from_dictionary_collection(dictionary_collection: iter, target_ke # Handle dictionary-type iterable if type(dictionary_collection) is dict: - # Assume the dictionaries are stored in the values and keys indicate names of the dictionaries + # Assume the dictionaries are stored in the values and keys indicate + # names of the dictionaries dictionary_collection = dictionary_collection.values() # All other type iterables are handled the same way as a list diff --git a/src/diffpy/pdfmorph/version.py b/src/diffpy/morph/version.py similarity index 95% rename from src/diffpy/pdfmorph/version.py rename to src/diffpy/morph/version.py index cf614327..1077d2ca 100644 --- a/src/diffpy/pdfmorph/version.py +++ b/src/diffpy/morph/version.py @@ -21,6 +21,6 @@ # obtain version information from importlib.metadata import version -__version__ = version("diffpy.pdfmorph") +__version__ = version("diffpy.morph") # End of file diff --git a/src/diffpy/pdfmorph/pdfmorphapp.py b/src/diffpy/pdfmorph/pdfmorphapp.py deleted file mode 100755 index 7f26ac00..00000000 --- a/src/diffpy/pdfmorph/pdfmorphapp.py +++ /dev/null @@ -1,643 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# diffpy.pdfmorph by DANSE Diffraction group -# Simon J. L. Billinge -# (c) 2010 Trustees of the Columbia University -# in the City of New York. All rights reserved. -# -# File coded by: Chris Farrow -# -# See AUTHORS.txt for a list of people who contributed. -# See LICENSE.txt for license information. -# -############################################################################## - -from __future__ import print_function - -import sys -from pathlib import Path - -import diffpy.pdfmorph.morph_helpers as helpers -import diffpy.pdfmorph.morphs as morphs -import diffpy.pdfmorph.pdfmorph_io as io -import diffpy.pdfmorph.pdfplot as pdfplot -import diffpy.pdfmorph.refine as refine -import diffpy.pdfmorph.tools as tools -from diffpy.pdfmorph import __save_morph_as__ -from diffpy.pdfmorph.version import __version__ - - -def create_option_parser(): - import optparse - - prog_short = Path(sys.argv[0]).name # Program name, compatible w/ all OS paths - - class CustomParser(optparse.OptionParser): - def __init__(self, *args, **kwargs): - super(CustomParser, self).__init__(*args, **kwargs) - - def custom_error(self, msg): - """custom_error(msg : string) - - Print a message incorporating 'msg' to stderr and exit. - Does not print usage. - """ - self.exit(2, "%s: error: %s\n" % (self.get_prog_name(), msg)) - - parser = CustomParser( - usage="\n".join( - [ - "%prog [options] FILE1 FILE2", - "Manipulate and compare PDFs.", - "Use --help for help.", - ] - ), - epilog="\n".join( - [ - "Please report bugs to diffpy-users@googlegroups.com.", - "For more information, see the PDFmorph website at https://www.diffpy.org/diffpy.pdfmorph.", - ] - ), - ) - - parser.add_option("-V", "--version", action="version", help="Show program version and exit.") - parser.version = __version__ - parser.add_option( - "-s", - "--save", - metavar="NAME", - dest="slocation", - help="""Save the manipulated PDF to a file named NAME. Use \'-\' for stdout. - When --multiple is enabled, save each manipulated PDF as a file in a directory named NAME; - you can specify names for each saved PDF file using --save-names-file.""", - ) - parser.add_option( - "-v", - "--verbose", - dest="verbose", - action="store_true", - help="Print additional header details to saved files.", - ) - parser.add_option("--rmin", type="float", help="Minimum r-value to use for PDF comparisons.") - parser.add_option("--rmax", type="float", help="Maximum r-value to use for PDF comparisons.") - parser.add_option( - "--pearson", - action="store_true", - dest="pearson", - help="Maximize agreement in the Pearson function. Note that this is insensitive to scale.", - ) - parser.add_option( - "--addpearson", - action="store_true", - dest="addpearson", - help="""Maximize agreement in the Pearson function as well as - minimizing the residual.""", - ) - - # Manipulations - group = optparse.OptionGroup( - parser, - "Manipulations", - """These options select the manipulations that are to be applied to - the PDF from FILE1. The passed values will be refined unless specifically - excluded with the -a or -x options. If no option is specified, the PDFs from FILE1 and FILE2 will - be plotted without any manipulations.""", - ) - parser.add_option_group(group) - group.add_option( - "-a", - "--apply", - action="store_false", - dest="refine", - help="Apply manipulations but do not refine.", - ) - group.add_option( - "-x", - "--exclude", - action="append", - dest="exclude", - metavar="MANIP", - help="""Exclude a manipulation from refinement by name. This can - appear multiple times.""", - ) - group.add_option("--scale", type="float", metavar="SCALE", help="Apply scale factor SCALE.") - group.add_option( - "--smear", - type="float", - metavar="SMEAR", - help="Smear peaks with a Gaussian of width SMEAR.", - ) - group.add_option( - "--stretch", - type="float", - metavar="STRETCH", - help="Stretch PDF by a fraction STRETCH.", - ) - group.add_option( - "--slope", - type="float", - dest="baselineslope", - help="""Slope of the baseline. This is used when applying the smear - factor. It will be estimated if not provided.""", - ) - group.add_option( - "--qdamp", - type="float", - metavar="QDAMP", - help="Dampen PDF by a factor QDAMP.", - ) - group.add_option( - "--radius", - type="float", - metavar="RADIUS", - help="""Apply characteristic function of sphere with radius RADIUS. - If PRADIUS is also specified, instead apply characteristic function of spheroid with equatorial - radius RADIUS and polar radius PRADIUS.""", - ) - group.add_option( - "--pradius", - type="float", - metavar="PRADIUS", - help="""Apply characteristic function of spheroid with equatorial - radius RADIUS and polar radius PRADIUS. If only PRADIUS is specified, instead apply - characteristic function of sphere with radius PRADIUS.""", - ) - group.add_option( - "--iradius", - type="float", - metavar="IRADIUS", - help="""Apply inverse characteristic function of sphere with radius IRADIUS. - If IPRADIUS is also specified, instead apply inverse characteristic function of spheroid - with equatorial radius IRADIUS and polar radius IPRADIUS.""", - ) - group.add_option( - "--ipradius", - type="float", - metavar="IPRADIUS", - help="""Apply inverse characteristic function of spheroid with equatorial radius IRADIUS - and polar radius IPRADIUS. If only IPRADIUS is specified, instead apply inverse characteristic - function of sphere with radius IPRADIUS.""", - ) - - # Plot Options - group = optparse.OptionGroup( - parser, - "Plot Options", - """These options control plotting. - The manipulated and target PDFs will be plotted against each other with a - difference curve below. When --multiple is enabled, the value of a parameter (specified by - --plot-parameter) will be plotted instead.""", - ) - parser.add_option_group(group) - group.add_option( - "-n", - "--noplot", - action="store_false", - dest="plot", - help="""Do not show a plot.""", - ) - group.add_option( - "--mlabel", - metavar="MLABEL", - dest="mlabel", - help="Set label for morphed data to MLABEL on plot. Default label is FILE1.", - ) - group.add_option( - "--tlabel", - metavar="TLABEL", - dest="tlabel", - help="Set label for target data to TLABEL on plot. Default label is FILE2.", - ) - group.add_option("--pmin", type="float", help="Minimum r-value to plot. Defaults to RMIN.") - group.add_option("--pmax", type="float", help="Maximum r-value to plot. Defaults to RMAX.") - group.add_option("--maglim", type="float", help="Magnify plot curves beyond r=MAGLIM by MAG.") - group.add_option("--mag", type="float", help="Magnify plot curves beyond r=MAGLIM by MAG.") - group.add_option("--lwidth", type="float", help="Line thickness of plotted curves.") - - # Multiple morph options - group = optparse.OptionGroup( - parser, - "Multiple Morphs", - """This program can morph a PDF against multiple targets in one command. - See -s and Plot Options for how saving and plotting functionality changes when performing multiple morphs.""", - ) - parser.add_option_group(group) - group.add_option( - "--multiple", - dest="multiple", - action="store_true", - help=f"""Changes usage to \'{prog_short} [options] FILE DIRECTORY\'. FILE - will be morphed with each file in DIRECTORY as target. - Files in DIRECTORY are sorted by alphabetical order unless a field is - specified by --sort-by.""", - ) - group.add_option( - "--sort-by", - metavar="FIELD", - dest="field", - help="""Used with --multiple to sort files in DIRECTORY by FIELD. - If the FIELD being used has a numerical value, sort from lowest to highest. - Otherwise, sort in ASCII sort order. - FIELD must be included in the header of all the PDF files.""", - ) - group.add_option( - "--reverse", - dest="reverse", - action="store_true", - help="""Sort from highest to lowest instead.""", - ) - group.add_option( - "--serial-file", - metavar="SERIALFILE", - dest="serfile", - help="""Look for FIELD in a serial file instead. - Must specify name of serial file SERIALFILE.""", - ) - group.add_option( - "--save-names-file", - metavar="NAMESFILE", - dest="snamesfile", - help=f"""Used when both -s and --multiple are enabled. - Specify names for each manipulated PDF when saving (see -s) using a serial file - NAMESFILE. The format of NAMESFILE should be as follows: each target PDF - is an entry in NAMESFILE. For each entry, there should be a key {__save_morph_as__} - whose value specifies the name to save the manipulated PDF as. An example .json - serial file is shown in the PDFmorph manual.""", - ) - group.add_option( - "--plot-parameter", - metavar="PLOTPARAM", - dest="plotparam", - help="""Used when both plotting and --multiple are enabled. - Choose a PLOTPARAM to plot for each morph (i.e. adding --plot-parameter=Pearson means the - program will display a plot of the Pearson correlation coefficient for each morph-target - pair). PLOTPARAM is not case sensitive, so both Pearson and pearson indicate the same - parameter. When PLOTPARAM is not specified, Rw values for each morph-target pair will be - plotted. PLOTPARAM will be displayed as the vertical axis label for the plot.""", - ) - - # Defaults - parser.set_defaults(multiple=False) - parser.set_defaults(reverse=False) - parser.set_defaults(plot=True) - parser.set_defaults(refine=True) - parser.set_defaults(pearson=False) - parser.set_defaults(addpearson=False) - parser.set_defaults(mag=5) - parser.set_defaults(lwidth=1.5) - - return parser - - -def single_morph(parser, opts, pargs, stdout_flag=True): - if len(pargs) < 2: - parser.error("You must supply FILE1 and FILE2.") - elif len(pargs) > 2: - parser.error("Too many arguments. Make sure you only supply FILE1 and FILE2.") - - # Get the PDFs - x_morph, y_morph = getPDFFromFile(pargs[0]) - x_target, y_target = getPDFFromFile(pargs[1]) - - # Get configuration values - scale_in = "None" - stretch_in = "None" - smear_in = "None" - config = {} - config["rmin"] = opts.rmin - config["rmax"] = opts.rmax - config["rstep"] = None - if opts.rmin is not None and opts.rmax is not None and opts.rmax <= opts.rmin: - e = "rmin must be less than rmax" - parser.custom_error(e) - - # Set up the morphs - chain = morphs.MorphChain(config) - # Add the r-range morph, we will remove it when saving and plotting - chain.append(morphs.MorphRGrid()) - refpars = [] - - # Scale - if opts.scale is not None: - scale_in = opts.scale - chain.append(morphs.MorphScale()) - config["scale"] = opts.scale - refpars.append("scale") - # Stretch - if opts.stretch is not None: - stretch_in = opts.stretch - chain.append(morphs.MorphStretch()) - config["stretch"] = opts.stretch - refpars.append("stretch") - # Smear - if opts.smear is not None: - smear_in = opts.smear - chain.append(helpers.TransformXtalPDFtoRDF()) - chain.append(morphs.MorphSmear()) - chain.append(helpers.TransformXtalRDFtoPDF()) - refpars.append("smear") - config["smear"] = opts.smear - # Set baselineslope if not given - config["baselineslope"] = opts.baselineslope - if opts.baselineslope is None: - config["baselineslope"] = -0.5 - refpars.append("baselineslope") - # Size - radii = [opts.radius, opts.pradius] - nrad = 2 - radii.count(None) - if nrad == 1: - radii.remove(None) - config["radius"] = tools.nn_value(radii[0], "radius or pradius") - chain.append(morphs.MorphSphere()) - refpars.append("radius") - elif nrad == 2: - config["radius"] = tools.nn_value(radii[0], "radius") - refpars.append("radius") - config["pradius"] = tools.nn_value(radii[1], "pradius") - refpars.append("pradius") - chain.append(morphs.MorphSpheroid()) - iradii = [opts.iradius, opts.ipradius] - inrad = 2 - iradii.count(None) - if inrad == 1: - iradii.remove(None) - config["iradius"] = tools.nn_value(iradii[0], "iradius or ipradius") - chain.append(morphs.MorphISphere()) - refpars.append("iradius") - elif inrad == 2: - config["iradius"] = tools.nn_value(iradii[0], "iradius") - refpars.append("iradius") - config["ipradius"] = tools.nn_value(iradii[1], "ipradius") - refpars.append("ipradius") - chain.append(morphs.MorphISpheroid()) - - # Resolution - if opts.qdamp is not None: - chain.append(morphs.MorphResolutionDamping()) - refpars.append("qdamp") - config["qdamp"] = opts.qdamp - - # Now remove non-refinable parameters - if opts.exclude is not None: - refpars = list(set(refpars) - set(opts.exclude)) - - # Refine or execute the morph - refiner = refine.Refiner(chain, x_morph, y_morph, x_target, y_target) - if opts.pearson: - refiner.residual = refiner._pearson - if opts.addpearson: - refiner.residual = refiner._add_pearson - if opts.refine and refpars: - try: - # This works better when we adjust scale and smear first. - if "smear" in refpars: - rptemp = ["smear"] - if "scale" in refpars: - rptemp.append("scale") - refiner.refine(*rptemp) - # Adjust all parameters - refiner.refine(*refpars) - except ValueError as e: - parser.custom_error(str(e)) - # Smear is not being refined, but baselineslope needs to refined to apply smear - # Note that baselineslope is only added to the refine list if smear is applied - elif "baselineslope" in refpars: - try: - refiner.refine("baselineslope", baselineslope=config["baselineslope"]) - except ValueError as e: - parser.custom_error(str(e)) - else: - chain(x_morph, y_morph, x_target, y_target) - - # Get Rw for the morph range - rw = tools.getRw(chain) - pcc = tools.get_pearson(chain) - # Replace the MorphRGrid with Morph identity - chain[0] = morphs.Morph() - chain(x_morph, y_morph, x_target, y_target) - - # Input morph parameters - morph_inputs = {"scale": scale_in, "stretch": stretch_in, "smear": smear_in} - - # Output morph parameters - morph_results = dict(config.items()) - # Ensure Rw, Pearson last two outputs - morph_results.update({"Rw": rw}) - morph_results.update({"Pearson": pcc}) - - # Print summary to terminal and save morph to file if requested - try: - io.single_morph_output( - morph_inputs, - morph_results, - save_file=opts.slocation, - morph_file=pargs[0], - xy_out=[chain.x_morph_out, chain.y_morph_out], - verbose=opts.verbose, - stdout_flag=stdout_flag, - ) - - except (FileNotFoundError, RuntimeError): - save_fail_message = "Unable to save to designated location." - parser.custom_error(save_fail_message) - - if opts.plot: - pairlist = [chain.xy_morph_out, chain.xy_target_out] - labels = [pargs[0], pargs[1]] # Default is to use file names - - # If user chooses labels - if opts.mlabel is not None: - labels[0] = opts.mlabel - if opts.tlabel is not None: - labels[1] = opts.tlabel - - # Plot extent defaults to calculation extent - pmin = opts.pmin if opts.pmin is not None else opts.rmin - pmax = opts.pmax if opts.pmax is not None else opts.rmax - maglim = opts.maglim - mag = opts.mag - l_width = opts.lwidth - pdfplot.comparePDFs( - pairlist, - labels, - rmin=pmin, - rmax=pmax, - maglim=maglim, - mag=mag, - rw=rw, - l_width=l_width, - ) - - return morph_results - - -def multiple_morphs(parser, opts, pargs, stdout_flag=True): - # Custom error messages since usage is distinct when --multiple tag is applied - if len(pargs) < 2: - parser.custom_error("You must supply FILE and DIRECTORY. See --multiple under --help for usage.") - elif len(pargs) > 2: - parser.custom_error("Too many arguments. You must only supply a FILE and a DIRECTORY.") - - # Parse paths - morph_file = Path(pargs[0]) - if not morph_file.is_file(): - parser.custom_error(f"{morph_file} is not a file. Go to --help for usage.") - target_directory = Path(pargs[1]) - if not target_directory.is_dir(): - parser.custom_error(f"{target_directory} is not a directory. Go to --help for usage.") - - # Get list of files from target directory - target_list = list(target_directory.iterdir()) - for target in target_list: - if target.is_dir(): - target_list.remove(target) - - # Do not morph morph_file against itself if it is in the same directory - if morph_file in target_list: - target_list.remove(morph_file) - - # Format field name for printing and plotting - field = None - if opts.field is not None: - field_words = opts.field.split() - field = "" - for word in field_words: - field += f"{word[0].upper()}{word[1:].lower()}" - field_list = None - - # Sort files in directory by some field - if field is not None: - try: - target_list, field_list = tools.field_sort( - target_list, field, opts.reverse, opts.serfile, get_field_values=True - ) - except KeyError: - if opts.serfile is not None: - parser.custom_error("The requested field was not found in the metadata file.") - else: - parser.custom_error("The requested field is missing from a PDF file header.") - else: - # Default is alphabetical sort - target_list.sort(reverse=opts.reverse) - - # Disable single morph plotting - plot_opt = opts.plot - opts.plot = False - - # Set up saving - save_directory = opts.slocation # User-given directory for saves - save_names_file = opts.snamesfile # User-given serialfile with names for each morph - save_morphs_here = None # Subdirectory for saving morphed PDFs - save_names = {} # Dictionary of names to save each morph as - if save_directory is not None: - try: - save_morphs_here = io.create_morphs_directory(save_directory) - - # Could not create directory or find names to save morphs as - except (FileNotFoundError, RuntimeError): - save_fail_message = "\nUnable to create directory" - parser.custom_error(save_fail_message) - - try: - save_names = io.get_multisave_names(target_list, save_names_file=save_names_file) - # Could not create directory or find names to save morphs as - except FileNotFoundError: - save_fail_message = "\nUnable to read from save names file" - parser.custom_error(save_fail_message) - - # Morph morph_file against all other files in target_directory - morph_results = {} - for target_file in target_list: - if target_file.is_file: - # Set the save file destination to be a file within the SLOC directory - if save_directory is not None: - save_as = save_names[target_file.name][__save_morph_as__] - opts.slocation = Path(save_morphs_here).joinpath(save_as) - # Perform a morph of morph_file against target_file - pargs = [morph_file, target_file] - morph_results.update( - { - target_file.name: single_morph(parser, opts, pargs, stdout_flag=False), - } - ) - - target_file_names = [] - for key in morph_results.keys(): - target_file_names.append(key) - - morph_inputs = {"scale": opts.scale, "stretch": opts.stretch, "smear": opts.smear} - - try: - # Print summary of morphs to terminal and to file (if requested) - io.multiple_morph_output( - morph_inputs, - morph_results, - target_file_names, - save_directory=save_directory, - morph_file=morph_file, - target_directory=target_directory, - field=field, - field_list=field_list, - verbose=opts.verbose, - stdout_flag=stdout_flag, - ) - except (FileNotFoundError, RuntimeError): - save_fail_message = "Unable to save summary to directory." - parser.custom_error(save_fail_message) - - # Plot the values of some parameter for each target if requested - if plot_opt: - plot_results = io.tabulate_results(morph_results) - # Default parameter is Rw - param_name = r"$R_w$" - param_list = plot_results["Rw"] - # Find parameter if specified - if opts.plotparam is not None: - param_name = opts.plotparam - param_list = tools.case_insensitive_dictionary_search(opts.plotparam, plot_results) - # Not an available parameter to plot or no values found for the parameter - if param_list is None: - parser.custom_error("Cannot find specified plot parameter. No plot shown.") - else: - try: - if field_list is not None: - pdfplot.plot_param(field_list, param_list, param_name, field) - else: - pdfplot.plot_param(target_file_names, param_list, param_name) - # Can occur for non-refined plotting parameters - # i.e. --smear is not selected as an option, but smear is the plotting parameter - except ValueError: - parser.custom_error( - "The plot parameter is missing values for at least one morph and target pair. " - "No plot shown." - ) - - return morph_results - - -def getPDFFromFile(fn): - from diffpy.pdfmorph.tools import readPDF - - try: - r, gr = readPDF(fn) - except IOError as errmsg: - print("%s: %s" % (fn, errmsg), file=sys.stderr) - sys.exit(1) - except ValueError: - print("Cannot read %s" % fn, file=sys.stderr) - sys.exit(1) - - return r, gr - - -def main(): - parser = create_option_parser() - (opts, pargs) = parser.parse_args() - if opts.multiple: - multiple_morphs(parser, opts, pargs, stdout_flag=True) - else: - single_morph(parser, opts, pargs, stdout_flag=True) - - -if __name__ == "__main__": - main() diff --git a/tests/debug.py b/tests/debug.py index 908a3940..0d3926c0 100644 --- a/tests/debug.py +++ b/tests/debug.py @@ -16,7 +16,7 @@ """ Convenience module for debugging the unit tests using -python -m diffpy.pdfmorph.tests.debug +python -m diffpy.morph.tests.debug Exceptions raised by failed tests or other errors are not caught. """ diff --git a/tests/run.py b/tests/run.py index 499f63bf..dc27a05b 100644 --- a/tests/run.py +++ b/tests/run.py @@ -7,13 +7,13 @@ # File coded by: Billinge Group members and community contributors. # # See GitHub contributions for a more detailed list of contributors. -# https://github.com/diffpy/diffpy.pdfmorph/graphs/contributors +# https://github.com/diffpy/diffpy.morph/graphs/contributors # # See LICENSE.rst for license information. # ############################################################################## """Convenience module for executing all unit tests with -python -m diffpy.pdfmorph.tests.run +python -m diffpy.morph.tests.run """ import sys diff --git a/tests/test_morph_func.py b/tests/test_morph_func.py index 30b1c54a..3f62f872 100644 --- a/tests/test_morph_func.py +++ b/tests/test_morph_func.py @@ -3,7 +3,7 @@ import numpy as np -from diffpy.pdfmorph.pdfmorph_api import morph_default_config, pdfmorph +from diffpy.morph.morph_api import morph, morph_default_config from tests.test_morphstretch import heaviside @@ -16,7 +16,7 @@ def test_morphfunc_verbose(): x_morph = x_target.copy() y_morph = heaviside(x_target, lb * (1 + stretch), ub * (1 + stretch)) cfg = morph_default_config(stretch=0.1) # off init - pdfmorph(x_morph, y_morph, x_target, y_target, verbose=True, **cfg) + morph(x_morph, y_morph, x_target, y_target, verbose=True, **cfg) def test_fixed_morph_with_morphfunc(): @@ -29,7 +29,7 @@ def test_fixed_morph_with_morphfunc(): y_morph = heaviside(x_target, lb * (1 + stretch), ub * (1 + stretch)) cfg = morph_default_config(stretch=0.1) # off init cfg["scale"] = 30 - pdfmorph( + morph( x_morph, y_morph, x_target, @@ -50,7 +50,7 @@ def test_stretch_with_morphfunc(): x_morph = x_target.copy() y_morph = heaviside(x_target, lb * (1 + stretch), ub * (1 + stretch)) cfg = morph_default_config(stretch=0.1) # off init - morph_rv = pdfmorph(x_morph, y_morph, x_target, y_target, **cfg) + morph_rv = morph(x_morph, y_morph, x_target, y_target, **cfg) morphed_cfg = morph_rv["morphed_config"] # verified they are morphable x1, y1, x0, y0 = morph_rv["morph_chain"].xyallout @@ -72,7 +72,7 @@ def test_scale_with_morphfunc(): y_morph = y_target.copy() y_morph *= scale cfg = morph_default_config(scale=1.5) # off init - morph_rv = pdfmorph(x_morph, y_morph, x_target, y_target, **cfg) + morph_rv = morph(x_morph, y_morph, x_target, y_target, **cfg) morphed_cfg = morph_rv["morphed_config"] # verified they are morphable x1, y1, x0, y0 = morph_rv["morph_chain"].xyallout @@ -93,7 +93,7 @@ def test_smear_with_morph_func(): x_morph = x_target.copy() y_morph = np.exp(-0.5 * ((x_morph - r0) / sigma0) ** 2) cfg = morph_default_config(smear=0.1, scale=1.1, stretch=0.1) # off init - morph_rv = pdfmorph(x_morph, y_morph, x_target, y_target, **cfg) + morph_rv = morph(x_morph, y_morph, x_target, y_target, **cfg) morphed_cfg = morph_rv["morphed_config"] # verified they are morphable x1, y1, x0, y0 = morph_rv["morph_chain"].xyallout diff --git a/tests/test_pdfmorphapp.py b/tests/test_morphapp.py similarity index 64% rename from tests/test_pdfmorphapp.py rename to tests/test_morphapp.py index f035d064..fa39a2e5 100644 --- a/tests/test_pdfmorphapp.py +++ b/tests/test_morphapp.py @@ -4,7 +4,11 @@ import pytest -from diffpy.pdfmorph.pdfmorphapp import create_option_parser, multiple_morphs, single_morph +from diffpy.morph.morphapp import ( + create_option_parser, + multiple_targets, + single_morph, +) thisfile = locals().get("__file__", "file.py") tests_dir = Path(thisfile).parent.resolve() @@ -55,8 +59,25 @@ def test_parser_numerical(self, setup_parser): "--slope", "--qdamp", ] - n_values = ["2.5", "40", "2.1", "-0.8", "0.0000005", "-0.0000005", ".00000003"] - n_names.extend(["--radius", "--pradius", "--iradius", "--ipradius", "--pmin", "--pmax"]) + n_values = [ + "2.5", + "40", + "2.1", + "-0.8", + "0.0000005", + "-0.0000005", + ".00000003", + ] + n_names.extend( + [ + "--radius", + "--pradius", + "--iradius", + "--ipradius", + "--pmin", + "--pmax", + ] + ) n_values.extend(["+0.5", "-0.2", "+.3", "-.1", "2.5", "40"]) n_names.extend(["--lwidth", "--maglim", "--mag"]) n_values.extend(["1.6", "50", "5"]) @@ -72,11 +93,15 @@ def test_parser_numerical(self, setup_parser): n_parsed_name = n_names[idx][2:] n_parsed_val = n_opts_dict.get(n_parsed_name) if n_parsed_val is None: - assert n_parsed_name in renamed_dests # Ensure .get() failed due to destination renaming + assert ( + n_parsed_name in renamed_dests + ) # Ensure .get() failed due to destination renaming n_parsed_name = renamed_dests.get(n_parsed_name) n_parsed_val = n_opts_dict.get(n_parsed_name) assert isinstance(n_parsed_val, float) # Check if value is a float - assert n_parsed_val == float(n_values[idx]) # Check correct value parsed + assert n_parsed_val == float( + n_values[idx] + ) # Check correct value parsed assert len(n_args) == 1 # Check one leftover def test_parser_systemexits(self, setup_parser): @@ -87,20 +112,23 @@ def test_parser_systemexits(self, setup_parser): with pytest.raises(SystemExit): single_morph(self.parser, opts, pargs, stdout_flag=False) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) (opts, pargs) = self.parser.parse_args(["too", "many", "files"]) with pytest.raises(SystemExit): single_morph(self.parser, opts, pargs, stdout_flag=False) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) # Make sure rmax greater than rmin - (opts, pargs) = self.parser.parse_args([f"{nickel_PDF}", f"{nickel_PDF}", "--rmin", "10", "--rmax", "1"]) + (opts, pargs) = self.parser.parse_args( + [f"{nickel_PDF}", f"{nickel_PDF}", "--rmin", "10", "--rmax", "1"] + ) with pytest.raises(SystemExit): single_morph(self.parser, opts, pargs, stdout_flag=False) # ###Tests exclusive to multiple morphs### - # Make sure we save to a directory that exists (user must create the directory if non-existing) + # Make sure we save to a directory that exists + # (user must create the directory if non-existing) (opts, pargs) = self.parser.parse_args( [ f"{nickel_PDF}", @@ -112,20 +140,26 @@ def test_parser_systemexits(self, setup_parser): with pytest.raises(SystemExit): single_morph(self.parser, opts, pargs, stdout_flag=False) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) # Ensure first parg is a FILE and second parg is a DIRECTORY - (opts, pargs) = self.parser.parse_args([f"{nickel_PDF}", f"{nickel_PDF}"]) + (opts, pargs) = self.parser.parse_args( + [f"{nickel_PDF}", f"{nickel_PDF}"] + ) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) - (opts, pargs) = self.parser.parse_args([f"{testsequence_dir}", f"{testsequence_dir}"]) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) + (opts, pargs) = self.parser.parse_args( + [f"{testsequence_dir}", f"{testsequence_dir}"] + ) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) # Try sorting by non-existing field - (opts, pargs) = self.parser.parse_args([f"{nickel_PDF}", f"{testsequence_dir}", "--sort-by", "fake_field"]) + (opts, pargs) = self.parser.parse_args( + [f"{nickel_PDF}", f"{testsequence_dir}", "--sort-by", "fake_field"] + ) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) (opts, pargs) = self.parser.parse_args( [ f"{nickel_PDF}", @@ -137,26 +171,44 @@ def test_parser_systemexits(self, setup_parser): ] ) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) # Try plotting an unknown parameter (opts, pargs) = self.parser.parse_args( - [f"{nickel_PDF}", f"{testsequence_dir}", "--plot-parameter", "unknown"] + [ + f"{nickel_PDF}", + f"{testsequence_dir}", + "--plot-parameter", + "unknown", + ] ) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) # Try plotting an unrefined parameter (opts, pargs) = self.parser.parse_args( - [f"{nickel_PDF}", f"{testsequence_dir}", "--plot-parameter", "stretch"] + [ + f"{nickel_PDF}", + f"{testsequence_dir}", + "--plot-parameter", + "stretch", + ] ) with pytest.raises(SystemExit): - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) def test_morphsequence(self, setup_morphsequence): # Parse arguments sorting by field (opts, pargs) = self.parser.parse_args( - ["--scale", "1", "--stretch", "0", "-n", "--sort-by", "temperature"] + [ + "--scale", + "1", + "--stretch", + "0", + "-n", + "--sort-by", + "temperature", + ] ) # Run multiple single morphs @@ -164,12 +216,20 @@ def test_morphsequence(self, setup_morphsequence): morph_file = self.testfiles[0] for target_file in self.testfiles[1:]: pargs = [morph_file, target_file] - # store in same format of dictionary as multiple_morphs - single_results.update({target_file.name: single_morph(self.parser, opts, pargs, stdout_flag=False)}) + # store in same format of dictionary as multiple_targets + single_results.update( + { + target_file.name: single_morph( + self.parser, opts, pargs, stdout_flag=False + ) + } + ) pargs = [morph_file, testsequence_dir] # Run a morph sequence - sequence_results = multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + sequence_results = multiple_targets( + self.parser, opts, pargs, stdout_flag=False + ) # Compare results assert sequence_results == single_results @@ -190,7 +250,9 @@ def test_morphsequence(self, setup_morphsequence): ] ) pargs = [morph_file, testsequence_dir] - s_sequence_results = multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + s_sequence_results = multiple_targets( + self.parser, opts, pargs, stdout_flag=False + ) assert s_sequence_results == sequence_results diff --git a/tests/test_morphchain.py b/tests/test_morphchain.py index 2ad0b184..92ed0189 100644 --- a/tests/test_morphchain.py +++ b/tests/test_morphchain.py @@ -6,9 +6,9 @@ import numpy import pytest -from diffpy.pdfmorph.morphs.morphchain import MorphChain -from diffpy.pdfmorph.morphs.morphrgrid import MorphRGrid -from diffpy.pdfmorph.morphs.morphscale import MorphScale +from diffpy.morph.morphs.morphchain import MorphChain +from diffpy.morph.morphs.morphrgrid import MorphRGrid +from diffpy.morph.morphs.morphscale import MorphScale # useful variables thisfile = locals().get("__file__", "file.py") @@ -40,7 +40,9 @@ def test_morph(self, setup): mscale = MorphScale() chain = MorphChain(config, mgrid, mscale) - x_morph, y_morph, x_target, y_target = chain(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = chain( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) assert (x_morph == x_target).all() pytest.approx(x_morph[0], 1.0) diff --git a/tests/test_pdfmorphio.py b/tests/test_morphio.py similarity index 90% rename from tests/test_pdfmorphio.py rename to tests/test_morphio.py index 012cca06..ed4059ff 100644 --- a/tests/test_pdfmorphio.py +++ b/tests/test_morphio.py @@ -4,7 +4,11 @@ import pytest -from diffpy.pdfmorph.pdfmorphapp import create_option_parser, multiple_morphs, single_morph +from diffpy.morph.morphapp import ( + create_option_parser, + multiple_targets, + single_morph, +) # Support Python 2 try: @@ -61,7 +65,7 @@ def ignore_path(line): (opts, pargs) = self.parser.parse_args( [ - "--multiple", + "--multiple-targets", "--sort-by", "temperature", "-s", @@ -72,7 +76,7 @@ def ignore_path(line): ] ) pargs = [morph_file, testsequence_dir] - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) # Save a single succinct morph ssm = tmp_succinct.joinpath("single_succinct_morph.cgr") @@ -99,7 +103,7 @@ def ignore_path(line): (opts, pargs) = self.parser.parse_args( [ - "--multiple", + "--multiple-targets", "--sort-by", "temperature", "-s", @@ -111,12 +115,14 @@ def ignore_path(line): ] ) pargs = [morph_file, testsequence_dir] - multiple_morphs(self.parser, opts, pargs, stdout_flag=False) + multiple_targets(self.parser, opts, pargs, stdout_flag=False) # Save a single verbose morph svm = tmp_verbose.joinpath("single_verbose_morph.cgr") svm_name = svm.resolve().as_posix() - (opts, pargs) = self.parser.parse_args(["-s", svm_name, "-n", "--verbose"]) + (opts, pargs) = self.parser.parse_args( + ["-s", svm_name, "-n", "--verbose"] + ) pargs = [morph_file, target_file] single_morph(self.parser, opts, pargs, stdout_flag=False) diff --git a/tests/test_morphpdftordf.py b/tests/test_morphpdftordf.py index d425cc1b..1c39b534 100644 --- a/tests/test_morphpdftordf.py +++ b/tests/test_morphpdftordf.py @@ -6,7 +6,7 @@ import numpy import pytest -from diffpy.pdfmorph.morph_helpers.transformpdftordf import TransformXtalPDFtoRDF +from diffpy.morph.morph_helpers.transformpdftordf import TransformXtalPDFtoRDF # useful variables thisfile = locals().get("__file__", "file.py") @@ -18,9 +18,15 @@ class TestTransformXtalPDFtoRDF: @pytest.fixture def setup(self): self.x_morph = numpy.arange(0.01, 5, 0.01) - self.y_morph = numpy.exp(-0.5 * (self.x_morph - 1.0) ** 2) / self.x_morph - self.x_morph + self.y_morph = ( + numpy.exp(-0.5 * (self.x_morph - 1.0) ** 2) / self.x_morph + - self.x_morph + ) self.x_target = numpy.arange(0.01, 5, 0.01) - self.y_target = numpy.exp(-0.5 * (self.x_morph - 2.0) ** 2) / self.x_morph - self.x_morph + self.y_target = ( + numpy.exp(-0.5 * (self.x_morph - 2.0) ** 2) / self.x_morph + - self.x_morph + ) return def test_transform(self, setup): @@ -28,7 +34,9 @@ def test_transform(self, setup): config = {"baselineslope": -1.0} transform = TransformXtalPDFtoRDF(config) - x_morph, y_morph, x_target, y_target = transform(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = transform( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) rdf1 = numpy.exp(-0.5 * (x_morph - 1.0) ** 2) rdf2 = numpy.exp(-0.5 * (x_target - 2.0) ** 2) diff --git a/tests/test_morphrdftopdf.py b/tests/test_morphrdftopdf.py index f48e0e68..ee139cc1 100644 --- a/tests/test_morphrdftopdf.py +++ b/tests/test_morphrdftopdf.py @@ -6,7 +6,7 @@ import numpy import pytest -from diffpy.pdfmorph.morph_helpers.transformrdftopdf import TransformXtalRDFtoPDF +from diffpy.morph.morph_helpers.transformrdftopdf import TransformXtalRDFtoPDF # useful variables thisfile = locals().get("__file__", "file.py") @@ -28,7 +28,9 @@ def test_transform(self, setup): config = {"baselineslope": -1.0} transform = TransformXtalRDFtoPDF(config) - x_morph, y_morph, x_target, y_target = transform(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = transform( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) rdf1 = numpy.exp(-0.5 * (x_morph - 1.0) ** 2) / x_morph - x_morph rdf2 = numpy.exp(-0.5 * (x_target - 2.0) ** 2) / x_target - x_target diff --git a/tests/test_morphresolution.py b/tests/test_morphresolution.py index 53d3cefa..13a56d8d 100644 --- a/tests/test_morphresolution.py +++ b/tests/test_morphresolution.py @@ -6,7 +6,7 @@ import numpy import pytest -from diffpy.pdfmorph.morphs.morphresolution import MorphResolutionDamping +from diffpy.morph.morphs.morphresolution import MorphResolutionDamping # useful variables thisfile = locals().get("__file__", "file.py") @@ -28,7 +28,9 @@ def test_morph(self, setup): config = {"qdamp": 0.01} morph = MorphResolutionDamping(config) - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) assert numpy.allclose(self.y_target, y_target) assert numpy.allclose(y_morph, y_target) diff --git a/tests/test_morphrgrid.py b/tests/test_morphrgrid.py index 2439936c..d823dbad 100644 --- a/tests/test_morphrgrid.py +++ b/tests/test_morphrgrid.py @@ -6,7 +6,7 @@ import numpy import pytest -from diffpy.pdfmorph.morphs.morphrgrid import MorphRGrid +from diffpy.morph.morphs.morphrgrid import MorphRGrid # useful variables thisfile = locals().get("__file__", "file.py") @@ -42,7 +42,9 @@ def testRangeInBounds(self, setup): "rstep": 0.1, } morph = MorphRGrid(config) - xyallout = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + xyallout = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) pytest.approx(config["rmin"], morph.rmin) pytest.approx(config["rmax"], morph.rmax) pytest.approx(config["rstep"], morph.rstep) @@ -58,7 +60,9 @@ def testRmaxOut(self, setup): "rstep": 0.1, } morph = MorphRGrid(config) - xyallout = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + xyallout = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) pytest.approx(config["rmin"], morph.rmin) pytest.approx(5, morph.rmax) pytest.approx(config["rstep"], morph.rstep) @@ -74,7 +78,9 @@ def testRminOut(self, setup): "rstep": 0.01, } morph = MorphRGrid(config) - xyallout = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + xyallout = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) pytest.approx(1.0, morph.rmin) pytest.approx(config["rmax"], morph.rmax) pytest.approx(config["rstep"], morph.rstep) @@ -90,7 +96,9 @@ def testRstepOut(self, setup): "rstep": 0.001, } morph = MorphRGrid(config) - xyallout = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + xyallout = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) pytest.approx(config["rmin"], morph.rmin) pytest.approx(config["rmax"], morph.rmax) pytest.approx(0.01, morph.rstep) diff --git a/tests/test_morphscale.py b/tests/test_morphscale.py index c6ebd9fb..ccabb483 100644 --- a/tests/test_morphscale.py +++ b/tests/test_morphscale.py @@ -6,7 +6,7 @@ import numpy import pytest -from diffpy.pdfmorph.morphs.morphscale import MorphScale +from diffpy.morph.morphs.morphscale import MorphScale # useful variables thisfile = locals().get("__file__", "file.py") @@ -28,7 +28,9 @@ def test_morph(self, setup): config = {"scale": 2.0} morph = MorphScale(config) - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) assert numpy.allclose(2 * self.y_morph, y_morph) assert numpy.allclose(self.y_target, y_target) diff --git a/tests/test_morphshape.py b/tests/test_morphshape.py index 990b0ca5..1905ebe8 100644 --- a/tests/test_morphshape.py +++ b/tests/test_morphshape.py @@ -6,8 +6,8 @@ import numpy import pytest -from diffpy.pdfmorph.morphs.morphishape import MorphISpheroid -from diffpy.pdfmorph.morphs.morphshape import MorphSphere, MorphSpheroid +from diffpy.morph.morphs.morphishape import MorphISpheroid +from diffpy.morph.morphs.morphshape import MorphSphere, MorphSpheroid # FIXME: add MorphISphere test @@ -31,7 +31,9 @@ def test_morph(self, setup): config = {"radius": 17.5} morph = MorphSphere(config) - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) assert numpy.allclose(self.y_target, y_target) assert numpy.allclose(y_morph, y_target) @@ -52,7 +54,9 @@ class TestMorphSpheroid: ispheroid_configs = [iconfig_sphere, iconfig_oblate] # Files used for testing - flag_inverse = 0 # Indicates whether we are testing MorphSpheroid or MorphISpheroid + flag_inverse = ( + 0 # Indicates whether we are testing MorphSpheroid or MorphISpheroid + ) testfiles = [ ["ni_qmax25.cgr", "ni_qmax25_psize35.cgr"], # Sphere ["ni_qmax25.cgr", "ni_qmax25_e17.5_p5.0.cgr"], # Oblate spheroid @@ -63,9 +67,13 @@ def reset(self): if len(self.testfile) == 0: # Ignore first init return - morph_file = os.path.join(testdata_dir, self.testfile[0 - self.flag_inverse]) + morph_file = os.path.join( + testdata_dir, self.testfile[0 - self.flag_inverse] + ) self.x_morph, self.y_morph = numpy.loadtxt(morph_file, unpack=True) - target_file = os.path.join(testdata_dir, self.testfile[1 - self.flag_inverse]) + target_file = os.path.join( + testdata_dir, self.testfile[1 - self.flag_inverse] + ) self.x_target, self.y_target = numpy.loadtxt(target_file, unpack=True) return @@ -89,7 +97,9 @@ def test_morph(self): def shape_test_helper(self, config): morph = MorphSpheroid(config) - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) assert numpy.allclose(self.y_target, y_target) assert numpy.allclose(y_morph, y_target) @@ -98,7 +108,9 @@ def shape_test_helper(self, config): def ishape_test_helper(self, config): morph = MorphISpheroid(config) - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) assert numpy.allclose(self.y_target, y_target) diff --git a/tests/test_morphshift.py b/tests/test_morphshift.py new file mode 100644 index 00000000..4f2e552b --- /dev/null +++ b/tests/test_morphshift.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + + +import os + +import numpy +import pytest + +from diffpy.morph.morphs.morphshift import MorphShift + +# useful variables +thisfile = locals().get("__file__", "file.py") +tests_dir = os.path.dirname(os.path.abspath(thisfile)) +# testdata_dir = os.path.join(tests_dir, 'testdata') + + +class TestMorphShift: + @pytest.fixture + def setup(self): + self.hshift = 2.0 + self.vshift = 3.0 + + # Original dataset goes from 0.1 to 5.0 + self.x_morph = numpy.arange(0.01, 5 + self.hshift, 0.01) + self.y_morph = numpy.arange(0.01, 5 + self.hshift, 0.01) + + # New dataset is moved to the right by 2.0 and upward by 3.0 + self.x_target = numpy.arange(0.01 + self.hshift, 5 + self.hshift, 0.01) + self.y_target = numpy.arange(0.01 + self.vshift, 5 + self.vshift, 0.01) + return + + def test_morph(self, setup): + """check MorphScale.morph()""" + config = {"hshift": self.hshift, "vshift": self.vshift} + morph = MorphShift(config) + + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) + + # Only care about the shifted data past the shift + # Everything to left of shift is outside our input data domain + assert numpy.allclose(y_morph[x_morph > self.hshift], y_target) + assert numpy.allclose(self.x_target, x_target) + assert numpy.allclose(self.y_target, y_target) + return + + +# End of class TestMorphScale + +if __name__ == "__main__": + TestMorphShift() + +# End of file diff --git a/tests/test_morphsmear.py b/tests/test_morphsmear.py index b947d9da..aa431c0e 100644 --- a/tests/test_morphsmear.py +++ b/tests/test_morphsmear.py @@ -6,7 +6,7 @@ import numpy import pytest -from diffpy.pdfmorph.morphs.morphsmear import MorphSmear +from diffpy.morph.morphs.morphsmear import MorphSmear # useful variables thisfile = locals().get("__file__", "file.py") @@ -20,7 +20,9 @@ def setup(self): self.smear = 0.1 self.r0 = 7 * numpy.pi / 22.0 * 2 self.x_morph = numpy.arange(0.01, 5, 0.01) - self.y_morph = numpy.exp(-0.5 * ((self.x_morph - self.r0) / self.smear) ** 2) + self.y_morph = numpy.exp( + -0.5 * ((self.x_morph - self.r0) / self.smear) ** 2 + ) self.x_target = self.x_morph.copy() self.y_target = self.x_target.copy() return @@ -30,7 +32,9 @@ def test_morph(self, setup): morph = MorphSmear() morph.smear = 0.15 - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) # Target should be unchanged assert numpy.allclose(self.y_target, y_target) diff --git a/tests/test_morphstretch.py b/tests/test_morphstretch.py index 8f753351..37b23a0c 100644 --- a/tests/test_morphstretch.py +++ b/tests/test_morphstretch.py @@ -6,7 +6,7 @@ import numpy import pytest -from diffpy.pdfmorph.morphs.morphstretch import MorphStretch +from diffpy.morph.morphs.morphstretch import MorphStretch # useful variables thisfile = locals().get("__file__", "file.py") @@ -30,7 +30,9 @@ def test_morph(self, setup): # Stretch by 50% morph.stretch = 0.5 - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) # Target should be unchanged assert numpy.allclose(self.y_target, y_target) @@ -45,7 +47,9 @@ def test_morph(self, setup): # Stretch by -10% morph.stretch = -0.1 - x_morph, y_morph, x_target, y_target = morph(self.x_morph, self.y_morph, self.x_target, self.y_target) + x_morph, y_morph, x_target, y_target = morph( + self.x_morph, self.y_morph, self.x_target, self.y_target + ) # Target should be unchanged assert numpy.allclose(self.y_target, y_target) diff --git a/tests/test_pdfplot.py b/tests/test_pdfplot.py index 012efb1b..2b6d06af 100644 --- a/tests/test_pdfplot.py +++ b/tests/test_pdfplot.py @@ -5,7 +5,7 @@ import matplotlib.pyplot as plt import numpy as np -from diffpy.pdfmorph.pdfmorph_api import morph_default_config, pdfmorph, plot_morph +from diffpy.morph.morph_api import morph, morph_default_config, plot_morph from tests.test_morphstretch import heaviside @@ -19,7 +19,7 @@ def test_plot_morph(): x_morph = x_target.copy() y_morph = heaviside(x_target, lb * (1 + stretch), ub * (1 + stretch)) cfg = morph_default_config(stretch=0.1) # off init - morph_rv = pdfmorph(x_morph, y_morph, x_target, y_target, verbose=True, **cfg) + morph_rv = morph(x_morph, y_morph, x_target, y_target, verbose=True, **cfg) chain = morph_rv["morph_chain"] fig, ax = plt.subplots() l_list = plot_morph(chain, ax) diff --git a/tests/test_refine.py b/tests/test_refine.py index 345d3593..d9cbd6d4 100644 --- a/tests/test_refine.py +++ b/tests/test_refine.py @@ -6,13 +6,13 @@ import numpy import pytest -from diffpy.pdfmorph.morph_helpers.transformpdftordf import TransformXtalPDFtoRDF -from diffpy.pdfmorph.morph_helpers.transformrdftopdf import TransformXtalRDFtoPDF -from diffpy.pdfmorph.morphs.morphchain import MorphChain -from diffpy.pdfmorph.morphs.morphscale import MorphScale -from diffpy.pdfmorph.morphs.morphsmear import MorphSmear -from diffpy.pdfmorph.morphs.morphstretch import MorphStretch -from diffpy.pdfmorph.refine import Refiner +from diffpy.morph.morph_helpers.transformpdftordf import TransformXtalPDFtoRDF +from diffpy.morph.morph_helpers.transformrdftopdf import TransformXtalRDFtoPDF +from diffpy.morph.morphs.morphchain import MorphChain +from diffpy.morph.morphs.morphscale import MorphScale +from diffpy.morph.morphs.morphsmear import MorphSmear +from diffpy.morph.morphs.morphstretch import MorphStretch +from diffpy.morph.refine import Refiner # useful variables thisfile = locals().get("__file__", "file.py") @@ -37,7 +37,9 @@ def test_refine_morph(self, setup): } mscale = MorphScale(config) - refiner = Refiner(mscale, self.x_morph, self.y_morph, self.x_target, self.y_target) + refiner = Refiner( + mscale, self.x_morph, self.y_morph, self.x_target, self.y_target + ) refiner.refine() x_morph, y_morph, x_target, y_target = mscale.xyallout @@ -60,7 +62,9 @@ def test_refine_chain(self, setup): mstretch = MorphStretch(config) chain = MorphChain(config, mscale, mstretch) - refiner = Refiner(chain, self.x_morph, self.y_morph, self.x_target, self.y_target) + refiner = Refiner( + chain, self.x_morph, self.y_morph, self.x_target, self.y_target + ) res = refiner.refine() # Compare the morph to the target. Note that due to @@ -82,9 +86,13 @@ class TestRefineUC: @pytest.fixture def setup(self): morph_file = os.path.join(testdata_dir, "nickel_ss0.01.cgr") - self.x_morph, self.y_morph = numpy.loadtxt(morph_file, unpack=True, skiprows=8) + self.x_morph, self.y_morph = numpy.loadtxt( + morph_file, unpack=True, skiprows=8 + ) target_file = os.path.join(testdata_dir, "nickel_ss0.02_eps0.002.cgr") - self.x_target, self.y_target = numpy.loadtxt(target_file, unpack=True, skiprows=8) + self.x_target, self.y_target = numpy.loadtxt( + target_file, unpack=True, skiprows=8 + ) self.y_target *= 1.5 return @@ -105,7 +113,9 @@ def test_refine(self, setup): chain.append(MorphSmear()) chain.append(TransformXtalRDFtoPDF()) - refiner = Refiner(chain, self.x_morph, self.y_morph, self.x_target, self.y_target) + refiner = Refiner( + chain, self.x_morph, self.y_morph, self.x_target, self.y_target + ) # Do this as two-stage fit. First refine amplitude parameters, and then # position parameters. diff --git a/tests/test_tools.py b/tests/test_tools.py index 7bf79e13..f0a5b497 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -10,7 +10,7 @@ import numpy import pytest -import diffpy.pdfmorph.tools as tools +import diffpy.morph.tools as tools # useful variables thisfile = locals().get("__file__", "file.py") @@ -77,13 +77,16 @@ def test_field_sort(self, setup): path_sequence = [] for file in absolute_sf: path_sequence.append(Path(file).absolute()) - sorted_path_sequence, fvs = tools.field_sort(path_sequence, "temperature", get_field_values=True) + sorted_path_sequence, fvs = tools.field_sort( + path_sequence, "temperature", get_field_values=True + ) sorted_sequence = [] for path in sorted_path_sequence: print(path) sorted_sequence.append(path.name) - # Temperature sort should produce same result as alphanumerical if leading character is removed + # Temperature sort should produce same result as alphanumerical if + # leading character is removed sequence_files.sort(key=lambda entry: entry[2:]) assert sequence_files == sorted_sequence @@ -91,7 +94,9 @@ def test_field_sort(self, setup): assert fvs == [174, 180, 186, 192, 198, 204, 210] # Now reverse the sort - reversed_path_sequence = tools.field_sort(path_sequence, "temperature", reverse=True) + reversed_path_sequence = tools.field_sort( + path_sequence, "temperature", reverse=True + ) reversed_sequence = [] for path in reversed_path_sequence: reversed_sequence.append(path.name) @@ -100,9 +105,14 @@ def test_field_sort(self, setup): sequence_files.sort() assert sequence_files == reversed_sequence - # Check we get the same sequence when we load header information from a serial file - serial_file = os.path.join(testdata_dir, "testsequence_serialfile.json") - metadata_path_sequence = tools.field_sort(path_sequence, "temperature", serfile=serial_file, reverse=True) + # Check we get the same sequence when we load header information from + # a serial file + serial_file = os.path.join( + testdata_dir, "testsequence_serialfile.json" + ) + metadata_path_sequence = tools.field_sort( + path_sequence, "temperature", serfile=serial_file, reverse=True + ) metadata_sequence = [] for path in metadata_path_sequence: metadata_sequence.append(path.name) @@ -132,7 +142,12 @@ def test_get_values_from_dictionary_collection(self): # Check get_values_from_dictionary_collection output gives target for collection_type in all_types: - assert tools.get_values_from_dictionary_collection(collection_type, target_key="target") == target_list + assert ( + tools.get_values_from_dictionary_collection( + collection_type, target_key="target" + ) + == target_list + ) # End of class TestTools diff --git a/tests/test_version.py b/tests/test_version.py index db948e0e..230d27c2 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,10 +1,11 @@ """Unit tests for __version__.py """ -import diffpy.pdfmorph +import diffpy.morph def test_package_version(): - """Ensure the package version is defined and not set to the initial placeholder.""" - assert hasattr(diffpy.pdfmorph, "__version__") - assert diffpy.pdfmorph.__version__ != "0.0.0" + """Ensure the package version is defined and not set to the initial + placeholder.""" + assert hasattr(diffpy.morph, "__version__") + assert diffpy.morph.__version__ != "0.0.0" diff --git a/tests/testdata/testsaving/succinct/Morph_Reference_Table.txt b/tests/testdata/testsaving/succinct/Morph_Reference_Table.txt index 43d11f71..9759b83d 100644 --- a/tests/testdata/testsaving/succinct/Morph_Reference_Table.txt +++ b/tests/testdata/testsaving/succinct/Morph_Reference_Table.txt @@ -1,4 +1,4 @@ -# Data generated by pdfmorph +# Data generated by diffpy.morph # from morphing FILE # with target directory DIRECTORY @@ -6,6 +6,8 @@ # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Labels: [Target] [Temperature] [Pearson] [Rw] f_180K.gr 180.0 0.999810 0.020141 diff --git a/tests/testdata/testsaving/succinct/Morphs/mwt_a.cgr b/tests/testdata/testsaving/succinct/Morphs/mwt_a.cgr index 6a17632f..c1b2cb4c 100644 --- a/tests/testdata/testsaving/succinct/Morphs/mwt_a.cgr +++ b/tests/testdata/testsaving/succinct/Morphs/mwt_a.cgr @@ -1,4 +1,4 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Labels: [r] [gr] diff --git a/tests/testdata/testsaving/succinct/Morphs/mwt_b.cgr b/tests/testdata/testsaving/succinct/Morphs/mwt_b.cgr index 6a17632f..c1b2cb4c 100644 --- a/tests/testdata/testsaving/succinct/Morphs/mwt_b.cgr +++ b/tests/testdata/testsaving/succinct/Morphs/mwt_b.cgr @@ -1,4 +1,4 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Labels: [r] [gr] diff --git a/tests/testdata/testsaving/succinct/Morphs/mwt_c.cgr b/tests/testdata/testsaving/succinct/Morphs/mwt_c.cgr index 6a17632f..c1b2cb4c 100644 --- a/tests/testdata/testsaving/succinct/Morphs/mwt_c.cgr +++ b/tests/testdata/testsaving/succinct/Morphs/mwt_c.cgr @@ -1,4 +1,4 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Labels: [r] [gr] diff --git a/tests/testdata/testsaving/succinct/Morphs/mwt_d.cgr b/tests/testdata/testsaving/succinct/Morphs/mwt_d.cgr index 6a17632f..c1b2cb4c 100644 --- a/tests/testdata/testsaving/succinct/Morphs/mwt_d.cgr +++ b/tests/testdata/testsaving/succinct/Morphs/mwt_d.cgr @@ -1,4 +1,4 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Labels: [r] [gr] diff --git a/tests/testdata/testsaving/succinct/Morphs/mwt_e.cgr b/tests/testdata/testsaving/succinct/Morphs/mwt_e.cgr index 6a17632f..c1b2cb4c 100644 --- a/tests/testdata/testsaving/succinct/Morphs/mwt_e.cgr +++ b/tests/testdata/testsaving/succinct/Morphs/mwt_e.cgr @@ -1,4 +1,4 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Labels: [r] [gr] diff --git a/tests/testdata/testsaving/succinct/Morphs/mwt_f.cgr b/tests/testdata/testsaving/succinct/Morphs/mwt_f.cgr index 6a17632f..c1b2cb4c 100644 --- a/tests/testdata/testsaving/succinct/Morphs/mwt_f.cgr +++ b/tests/testdata/testsaving/succinct/Morphs/mwt_f.cgr @@ -1,4 +1,4 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Labels: [r] [gr] diff --git a/tests/testdata/testsaving/succinct/single_succinct_morph.cgr b/tests/testdata/testsaving/succinct/single_succinct_morph.cgr index 6a17632f..c1b2cb4c 100644 --- a/tests/testdata/testsaving/succinct/single_succinct_morph.cgr +++ b/tests/testdata/testsaving/succinct/single_succinct_morph.cgr @@ -1,4 +1,4 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Labels: [r] [gr] diff --git a/tests/testdata/testsaving/verbose/Morph_Reference_Table.txt b/tests/testdata/testsaving/verbose/Morph_Reference_Table.txt index 175787a7..c849e35f 100644 --- a/tests/testdata/testsaving/verbose/Morph_Reference_Table.txt +++ b/tests/testdata/testsaving/verbose/Morph_Reference_Table.txt @@ -1,4 +1,4 @@ -# Data generated by pdfmorph +# Data generated by diffpy.morph # from morphing FILE # with target directory DIRECTORY @@ -6,6 +6,8 @@ # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Target: f_180K.gr # Optimized morphing parameters: diff --git a/tests/testdata/testsaving/verbose/Morphs/mwt_a.cgr b/tests/testdata/testsaving/verbose/Morphs/mwt_a.cgr index 84e9d75e..d4ab20ff 100644 --- a/tests/testdata/testsaving/verbose/Morphs/mwt_a.cgr +++ b/tests/testdata/testsaving/verbose/Morphs/mwt_a.cgr @@ -1,10 +1,12 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Input morphing parameters: # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Optimized morphing parameters: # rmin = 0.000000 diff --git a/tests/testdata/testsaving/verbose/Morphs/mwt_b.cgr b/tests/testdata/testsaving/verbose/Morphs/mwt_b.cgr index 2c58d1b3..fd039d17 100644 --- a/tests/testdata/testsaving/verbose/Morphs/mwt_b.cgr +++ b/tests/testdata/testsaving/verbose/Morphs/mwt_b.cgr @@ -1,10 +1,12 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Input morphing parameters: # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Optimized morphing parameters: # rmin = 0.000000 diff --git a/tests/testdata/testsaving/verbose/Morphs/mwt_c.cgr b/tests/testdata/testsaving/verbose/Morphs/mwt_c.cgr index 3c357184..0321f80e 100644 --- a/tests/testdata/testsaving/verbose/Morphs/mwt_c.cgr +++ b/tests/testdata/testsaving/verbose/Morphs/mwt_c.cgr @@ -1,10 +1,12 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Input morphing parameters: # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Optimized morphing parameters: # rmin = 0.000000 diff --git a/tests/testdata/testsaving/verbose/Morphs/mwt_d.cgr b/tests/testdata/testsaving/verbose/Morphs/mwt_d.cgr index ea201276..fa694073 100644 --- a/tests/testdata/testsaving/verbose/Morphs/mwt_d.cgr +++ b/tests/testdata/testsaving/verbose/Morphs/mwt_d.cgr @@ -1,10 +1,12 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Input morphing parameters: # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Optimized morphing parameters: # rmin = 0.000000 diff --git a/tests/testdata/testsaving/verbose/Morphs/mwt_e.cgr b/tests/testdata/testsaving/verbose/Morphs/mwt_e.cgr index 78aa564e..5ea8d806 100644 --- a/tests/testdata/testsaving/verbose/Morphs/mwt_e.cgr +++ b/tests/testdata/testsaving/verbose/Morphs/mwt_e.cgr @@ -1,10 +1,12 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Input morphing parameters: # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Optimized morphing parameters: # rmin = 0.000000 diff --git a/tests/testdata/testsaving/verbose/Morphs/mwt_f.cgr b/tests/testdata/testsaving/verbose/Morphs/mwt_f.cgr index 53eb1971..1cf81646 100644 --- a/tests/testdata/testsaving/verbose/Morphs/mwt_f.cgr +++ b/tests/testdata/testsaving/verbose/Morphs/mwt_f.cgr @@ -1,10 +1,12 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Input morphing parameters: # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Optimized morphing parameters: # rmin = 0.000000 diff --git a/tests/testdata/testsaving/verbose/single_verbose_morph.cgr b/tests/testdata/testsaving/verbose/single_verbose_morph.cgr index 84e9d75e..d4ab20ff 100644 --- a/tests/testdata/testsaving/verbose/single_verbose_morph.cgr +++ b/tests/testdata/testsaving/verbose/single_verbose_morph.cgr @@ -1,10 +1,12 @@ -# PDF created by pdfmorph +# PDF created by diffpy.morph # from PATH # Input morphing parameters: # scale = None # stretch = None # smear = None +# hshift = None +# vshift = None # Optimized morphing parameters: # rmin = 0.000000 diff --git a/tutorial/PDFmorph_manual.pdf b/tutorial/PDFmorph_manual.pdf deleted file mode 100644 index 7239202c..00000000 Binary files a/tutorial/PDFmorph_manual.pdf and /dev/null differ diff --git a/tutorial/additionalData.zip b/tutorial/additionalData.zip index 8a87c8aa..66520ccf 100644 Binary files a/tutorial/additionalData.zip and b/tutorial/additionalData.zip differ diff --git a/tutorial/diffpy.morph_manual.pdf b/tutorial/diffpy.morph_manual.pdf new file mode 100644 index 00000000..779e49f4 Binary files /dev/null and b/tutorial/diffpy.morph_manual.pdf differ