├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── lint.yml │ ├── pin_requirements.yml │ ├── pypi_main.yml │ ├── pypi_test.yml │ └── pytest.yml ├── .gitignore ├── CITATION.cff ├── CONTRIBUTING.rst ├── DESCRIPTION.rst ├── HISTORY.rst ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── codecov.yml ├── dependabot.yml ├── dev └── gui │ ├── dev_gui_secB.py │ ├── dev_mwe_app.py │ ├── dev_rfu_app.py │ ├── errorbar_app.yaml │ ├── mwe_app.yaml │ ├── readme │ ├── rfu_headless.py │ └── test_data │ └── secb │ ├── 1qyn.pdb │ ├── 20211116_2207_PyHDX_session.zip │ ├── dG_fits.csv │ ├── ddG_comparison.csv │ ├── peptides.csv │ ├── rates.csv │ └── rfu_residues.csv ├── docs ├── _docs_notes.md ├── assets │ └── pyhdx_inverted_button_only.png ├── citing.md ├── configuration.md ├── css │ ├── codehilite.css │ └── style.css ├── examples │ ├── 01_basic_usage.ipynb │ ├── 02_coverage_and_d_uptake_fit.ipynb │ ├── 03_fitting.ipynb │ └── 04_plot_output.ipynb ├── gen_ref_pages.py ├── index.md ├── installation.md ├── introduction.md ├── javascript │ └── mathjax.js ├── refs.bib ├── web_app.md └── web_app_autodoc.md ├── images ├── PXL_20220703_130658046.jpg ├── PyHDX_rgb.png ├── pyhdx_header.png ├── pyhdx_inverted.afphoto ├── pyhdx_inverted.jpg ├── pyhdx_inverted_button.afdesign ├── pyhdx_inverted_button_only.png ├── pyhdx_inverted_button_white_bg.png ├── readme_img.png ├── screenshot_pyhdx040b5.png └── screenshot_pyhdx043.png ├── mkdocs.yml ├── pyhdx ├── __init__.py ├── __version__.py ├── _versioneer.py ├── alignment.py ├── batch_processing.py ├── cli.py ├── config.py ├── config.yaml ├── datasets.py ├── fileIO.py ├── fit_models.py ├── fitting.py ├── fitting_torch.py ├── legacy.py ├── local_cluster.py ├── models.py ├── output.py ├── plot.py ├── process.py ├── support.py ├── tol_colors.py └── web │ ├── __init__.py │ ├── apps.py │ ├── apps │ ├── peptide_app.yaml │ ├── peptide_faq.md │ ├── pyhdx_app.yaml │ └── rfu_app.yaml │ ├── base.py │ ├── cache.py │ ├── constructor.py │ ├── controllers.py │ ├── jinja_base.html │ ├── log.py │ ├── main_controllers.py │ ├── opts.py │ ├── pane.py │ ├── paramdoc.py │ ├── readme │ ├── holoviews.png │ ├── molstar.png │ ├── pyhdx_diagram.py │ ├── pyhdx_main_application.png │ ├── pyhdx_rfu.png │ ├── readme.rst │ └── tables_list.txt │ ├── serve.py │ ├── sources.py │ ├── static │ ├── extendedgoldentemplate │ │ └── default.css │ ├── index.html │ └── pyhdx_header.png │ ├── template.py │ ├── theme.py │ ├── tools.py │ ├── transforms.py │ ├── utils.py │ ├── views.py │ └── widgets.py ├── pyproject.toml ├── pyrightconfig.json ├── readthedocs.yml ├── requirements ├── requirements-macOS-latest-3.9.txt ├── requirements-macos-latest-3.10.txt ├── requirements-macos-latest-3.11.txt ├── requirements-macos-latest-3.12.txt ├── requirements-ubuntu-latest-3.10.txt ├── requirements-ubuntu-latest-3.11.txt ├── requirements-ubuntu-latest-3.12.txt ├── requirements-ubuntu-latest-3.9.txt ├── requirements-windows-latest-3.10.txt ├── requirements-windows-latest-3.11.txt ├── requirements-windows-latest-3.12.txt └── requirements-windows-latest-3.9.txt ├── templates ├── 01_load_secb_data.py ├── 01a_load_ppix_data.py ├── 02_load_from_yaml.py ├── 03_initial_guesses.py ├── 04_SecB_fit_d_uptake.py ├── 05_SecB_fit_dG.py ├── 07_SecB_aligned_fit.py ├── 08_load_fitresult.py ├── 09_figure_output.ipynb ├── 09_figure_output.py ├── archive │ ├── README.md │ ├── SecB_batch_fit_and_checkpoint.py │ ├── fit_report_pdf.py │ ├── fitting_with_logs.py │ ├── individual_plots.py │ ├── load_fitresult_web.py │ └── plot_output.py └── yaml_files │ └── SecB.yaml └── tests ├── check_test_fit_result.py ├── gen_docs_example_result.py ├── generate_test_fit_results.py ├── test_config.py ├── test_data ├── input │ ├── PpiA_folding.csv │ ├── PpiB_folding.csv │ ├── PpiX_states.yaml │ ├── PyHDX_state_spec_202207140958.yaml │ ├── data_states.yaml │ ├── data_states_deltas.yaml │ ├── data_states_red.yaml │ ├── ecSecB_apo.csv │ ├── ecSecB_apo_red.csv │ ├── ecSecB_dimer.csv │ ├── ecSecB_dimer_deltaC.csv │ ├── ecSecB_dimer_deltaN.csv │ ├── ecSecB_dimer_red.csv │ ├── fit_settings.yaml │ └── jobfile.yaml └── output │ ├── attributes │ ├── X.txt │ └── Z.txt │ ├── ecSecB_batch.csv │ ├── ecSecB_batch.txt │ ├── ecSecB_batch_aligned.csv │ ├── ecSecB_batch_aligned.txt │ ├── ecSecB_batch_loss.csv │ ├── ecSecB_batch_peptide_mse.csv │ ├── ecSecB_batch_residue_mse.csv │ ├── ecSecB_d_uptake.csv │ ├── ecSecB_data.csv │ ├── ecSecB_data.txt │ ├── ecSecB_guess.csv │ ├── ecSecB_guess.txt │ ├── ecSecB_info.csv │ ├── ecSecB_info.txt │ ├── ecSecB_rates.txt │ ├── ecSecB_reduced_guess.csv │ ├── ecSecB_reduced_guess.txt │ ├── ecSecB_rfu_per_exposure.csv │ ├── ecSecB_rfu_per_exposure.txt │ ├── ecSecB_torch_fit.csv │ ├── ecSecB_torch_fit.txt │ ├── ecSecB_torch_fit_epochs_20000.csv │ ├── ecSecB_torch_fit_epochs_20000.txt │ ├── ecsecb_delta_batch │ ├── HDXMeasurements.csv │ ├── fit_result.csv │ ├── fit_result.txt │ ├── log.txt │ ├── losses.csv │ └── losses.txt │ ├── ecsecb_reduced │ ├── HDXMeasurements.csv │ ├── fit_result.csv │ ├── fit_result.txt │ ├── log.txt │ ├── losses.csv │ └── losses.txt │ ├── ecsecb_reduced_batch │ ├── HDXMeasurements.csv │ ├── fit_result.csv │ ├── fit_result.txt │ ├── log.txt │ ├── losses.csv │ └── losses.txt │ ├── ecsecb_tetramer_dimer │ ├── HDXMeasurements.csv │ ├── fit_result.csv │ ├── fit_result.txt │ ├── log.txt │ ├── losses.csv │ └── losses.txt │ ├── main_web │ ├── peptides.csv │ ├── rfu.csv │ └── rfu_colors.csv │ ├── rfu_web │ ├── peptides.csv │ └── rfu.csv │ └── web │ ├── 1qyn.pdb │ ├── dG.csv │ ├── d_calc.csv │ ├── d_uptake.csv │ ├── ddG_comparison.csv │ ├── loss.csv │ ├── peptide_mse.csv │ ├── peptides.csv │ ├── rates.csv │ └── rfu.csv ├── test_datasets.py ├── test_fileIO.py ├── test_fitting.py ├── test_gui.py ├── test_models.py ├── test_support.py └── test_templates.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | pyhdx/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | format: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - uses: actions/setup-python@v5 10 | with: 11 | python-version: "3.10" 12 | - uses: astral-sh/ruff-action@v3 13 | -------------------------------------------------------------------------------- /.github/workflows/pin_requirements.yml: -------------------------------------------------------------------------------- 1 | name: Generate Requirements Files 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | generate-requirements: 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [ubuntu-latest, macos-latest, windows-latest] 13 | python-version: ["3.10", "3.11", "3.12"] 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | - name: Install uv 19 | uses: astral-sh/setup-uv@v3 20 | with: 21 | # Install a specific version of uv. 22 | version: "0.5.4" 23 | 24 | - name: Generate requirements file 25 | run: uv pip compile --all-extras --python-version ${{ matrix.python-version }} pyproject.toml -o requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt 26 | 27 | - name: Upload requirements file 28 | uses: actions/upload-artifact@v4 29 | with: 30 | name: req-artifact-${{ matrix.os }}-${{ matrix.python-version }} 31 | path: requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt 32 | merge: 33 | runs-on: ubuntu-latest 34 | needs: generate-requirements 35 | steps: 36 | - name: Merge Artifacts 37 | uses: actions/upload-artifact/merge@v4 38 | with: 39 | name: all-requirements 40 | pattern: req-artifact-* -------------------------------------------------------------------------------- /.github/workflows/pypi_main.yml: -------------------------------------------------------------------------------- 1 | name: PyPi distribute main release 2 | on: [release] 3 | 4 | jobs: 5 | build-n-publish: 6 | name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - name: Set up Python 3.10 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: "3.10" 18 | - name: Install Hatch 19 | run: pip install hatch 20 | - name: Build 21 | run: hatch build 22 | - name: Publish distribution 📦 to PyPI 23 | uses: pypa/gh-action-pypi-publish@release/v1 24 | with: 25 | password: ${{ secrets.PYPI_API_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/pypi_test.yml: -------------------------------------------------------------------------------- 1 | name: PyPi distribute test push 2 | on: [push] 3 | 4 | jobs: 5 | build-n-publish: 6 | name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Set up Python 3.10 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: "3.10" 19 | 20 | - name: Configure Git 21 | run: | 22 | git config --global user.email "github-actions@github.com" 23 | git config --global user.name "GitHub Actions" 24 | 25 | - name: Create test version tag 26 | run: | 27 | # Get number of commits in current branch 28 | COMMIT_COUNT=$(git rev-list --count HEAD) 29 | # Get short SHA 30 | SHA=$(git rev-parse --short HEAD) 31 | # Create a PEP 440 compliant version number 32 | VERSION="0.2.1.dev${COMMIT_COUNT}" 33 | # Create and push tag 34 | git tag -a "v${VERSION}" -m "Test release ${VERSION}" 35 | echo "Created tag v${VERSION}" 36 | 37 | - name: Install Hatch 38 | run: pip install hatch 39 | 40 | - name: Build 41 | run: hatch build 42 | 43 | - name: Publish distribution 📦 to Test PyPI 44 | uses: pypa/gh-action-pypi-publish@release/v1 45 | with: 46 | password: ${{ secrets.TEST_PYPI_API_TOKEN }} 47 | repository-url: https://test.pypi.org/legacy/ -------------------------------------------------------------------------------- /.github/workflows/pytest.yml: -------------------------------------------------------------------------------- 1 | name: Testing 2 | on: 3 | push: 4 | pull_request: 5 | 6 | jobs: 7 | test: 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ "ubuntu-latest", "macos-latest", "windows-latest"] 12 | python-version: [ "3.10", "3.11" ] 13 | 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - name: Check out repository 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - name: Set up python ${{ matrix.python-version }} 21 | id: setup-python 22 | uses: actions/setup-python@v5 23 | - name: Install uv 24 | uses: astral-sh/setup-uv@v5 25 | with: 26 | # Install a specific version of uv. 27 | version: "0.5.4" 28 | enable-cache: true 29 | cache-dependency-glob: requirements/requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt 30 | - name: Install dependencies 31 | shell: bash 32 | run: | 33 | uv venv -p ${{ matrix.python-version }} 34 | if [ "${{ matrix.os }}" == "windows-latest" ]; then 35 | source .venv/Scripts/activate 36 | else 37 | source .venv/bin/activate 38 | fi 39 | echo PATH=$PATH >> $GITHUB_ENV 40 | uv pip install -r requirements/requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt 41 | uv pip install -e .[test] 42 | # - name: Install test requirements 43 | # run: 44 | - name: Run tests 45 | run: | 46 | uv run pytest tests/ 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv* 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | .idea 104 | 105 | 106 | # Dask 107 | dask-worker-space/ 108 | 109 | # Hatch-vcs generated version file 110 | _version.py 111 | 112 | #vscode 113 | .vscode/ 114 | 115 | # local development files 116 | dev/ 117 | templates/guesses 118 | templates/output 119 | templates/fit -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Smit" 5 | given-names: "Jochem H." 6 | orcid: "https://orcid.org/0000-0002-3597-9429" 7 | title: "PyHDX" 8 | version: 0.4.0b5 9 | doi: 10.5281/zenodo.4062031 10 | date-released: 2021-12-01 11 | url: "https://github.com/Jhsmit/PyHDX" 12 | preferred-citation: 13 | type: article 14 | authors: 15 | - family-names: "Smit" 16 | given-names: "Jochem H." 17 | orcid: "https://orcid.org/0000-0002-3597-9429" 18 | - family-names: "Krishnamurthy" 19 | given-names: "Srinath" 20 | orcid: "https://orcid.org/0000-0001-5492-4450" 21 | - family-names: "Srinivasu" 22 | given-names: "Bindu Y." 23 | orcid: "https://orcid.org/0000-0003-0875-2680" 24 | - family-names: "Parakra" 25 | given-names: "Rinky" 26 | orcid: "https://orcid.org/0000-0001-8690-7073" 27 | - family-names: "Karamanou" 28 | given-names: "Spyridoula" 29 | orcid: "https://orcid.org/0000-0002-8803-1404" 30 | - family-names: "Economou" 31 | given-names: "Anastassios" 32 | orcid: "https://orcid.org/0000-0002-1770-507X" 33 | doi: "10.1021/acs.analchem.1c02155" 34 | journal: "Analytical Chemistry" 35 | month: 9 36 | start: 12840 # First page number 37 | end: 12847 # Last page number 38 | title: "Probing Universal Protein Dynamics Using Hydrogen–Deuterium Exchange Mass Spectrometry-Derived Residue-Level Gibbs Free Energy" 39 | number: 38 40 | volume: 93 41 | year: 2021 42 | url: "https://doi.org/10.1021/acs.analchem.1c02155" 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Contributing 5 | ============ 6 | 7 | Contributions are welcome, and they are greatly appreciated! 8 | 9 | Types of Contributions 10 | ---------------------- 11 | 12 | Report Bugs 13 | ~~~~~~~~~~~ 14 | 15 | Report bugs at https://github.com/Jhsmit/pyhdx/issues. 16 | 17 | If you are reporting a bug, when running PyHDX locally, please include: 18 | 19 | * Your operating system name and version. 20 | * Any details about your local setup that might be helpful in troubleshooting. 21 | * Detailed steps to reproduce the bug, possibly include a minimal dataset which reproduces the bug. 22 | 23 | If you are reporting a bug when using the hosted PyHDX web interface, please include: 24 | 25 | * The version of PyHDX as shown in the top header 26 | * Which web component you were using and steps to reproduce the bug. 27 | * Possibly include input/output data which helps reproduce the bug. 28 | 29 | Submit Feedback 30 | ~~~~~~~~~~~~~~~ 31 | 32 | The best way to send feedback is to file an issue at https://github.com/Jhsmit/pyhdx/issues. 33 | 34 | If you are proposing a feature: 35 | 36 | * Explain in detail how it would work. 37 | * Keep the scope as narrow as possible, to make it easier to implement. 38 | 39 | Pull Requests 40 | ~~~~~~~~~~~~~ 41 | 42 | You can contribute code by submitting a pull request. If you contribute new features describe in your 43 | PR what the new feature does, possible references to literature and how it should be used. 44 | 45 | The PR should add new tests for the new feature and all current tests should pass. New functions and 46 | classes should have docstrings and ideally code examples and documentation should be added. 47 | 48 | Documentation 49 | ~~~~~~~~~~~~~ 50 | 51 | Docstrings use `google `__ style headers. 52 | Parameters for `__init__` are documented at the `class` level. `intersphinx `__ 53 | is used to link to classes of external packages. 54 | Some guidelines for how to refer to objects or entities: 55 | 56 | * Variable, module function and class names: ```numpy``` 57 | * Boolean values: ````True```` 58 | * Python objects: ``:obj:`int```, ``:obj:`bool`` 59 | * Numpy arrays: ``:class:`~numpy.ndarray``` 60 | * Pandas dataframe: ``:class:`~pandas.DataFrame``` 61 | * Classes within the same module: ``:class:.HDXMeasurement`` 62 | * Default values: ``copy : :obj:`bool`, default: ``True```` 63 | * Refer to arguments / parameters with single ticks: ```param1``` 64 | 65 | 66 | .. code:: 67 | class Coverage(object): 68 | """Single docstring line description. 69 | 70 | Args: 71 | data: Dataframe with input peptides. 72 | c_term: Residue index number of the C-terminal residue (where first residue in index number 1). 73 | * args: Additional arguments 74 | **metadata: kwargs named metadata. 75 | 76 | 77 | """ 78 | 79 | pahtlike should be: Pathlike[str] or Union[str, Pathlike[str]] 80 | 81 | Properties: 82 | 83 | .. code:: 84 | *args : :obj:`tuple` 85 | Additional arguments should be passed as keyword arguments 86 | **kwargs : :obj:`dict`, optional 87 | Extra arguments to `somefunction`. 88 | 89 | Additional resources: 90 | 91 | `Sphinx docstrings `__ 92 | `Mypy cheatsheet __ 93 | `Google styleguild __ -------------------------------------------------------------------------------- /DESCRIPTION.rst: -------------------------------------------------------------------------------- 1 | PyHDX is software tool to derive Derive ΔG for single residues from HDX-MS data. 2 | 3 | It can be used in scripts/jupyter notebooks or accessed through a web interface. 4 | 5 | `bioRxiv Paper`_ 6 | `Analytical Chemistry Paper`_ 7 | `GitHub page`_ 8 | `Documentation`_ 9 | 10 | .. _bioRxiv Paper: https://doi.org/10.1101/2020.09.30.320887 11 | .. _Analytical Chemistry Paper: https://doi.org/10.1021/acs.analchem.1c02155 12 | .. _GitHub page: https://github.com/Jhsmit/PyHDX 13 | .. _Documentation: https://pyhdx.readthedocs.io/en/latest/ -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | History 3 | ======= 4 | 5 | 0.1.0 (2019-09-06) 6 | ------------------ 7 | 8 | * First release on PyPI. 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jochem Smit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include CONTRIBUTING.rst 3 | include HISTORY.rst 4 | include LICENSE.txt 5 | include README.rst 6 | 7 | recursive-include tests * 8 | recursive-exclude * __pycache__ 9 | recursive-exclude * *.py[co] 10 | recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif 11 | 12 | include versioneer.py 13 | include pyhdx/_version.py 14 | include pyhdx/web/*.html 15 | include pyhdx/web/apps/*.yaml 16 | include pyhdx/config.yaml 17 | 18 | graft pyhdx/web/static -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyHDX 2 | 3 | [![zenodo](https://zenodo.org/badge/206772076.svg)](https://zenodo.org/badge/latestdoi/206772076) 4 | [![biorxiv](https://img.shields.io/badge/bioRxiv-v2-%23be2635)](https://www.biorxiv.org/content/10.1101/2020.09.30.320887v2) 5 | [![license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 6 | [![docs](https://readthedocs.org/projects/pyhdx/badge/?version=latest)](https://pyhdx.readthedocs.io/en/latest/?badge=latest) 7 | 8 | 9 | 10 | PyHDX is python project which can be used to derive Gibbs free energy from HDX-MS data. 11 | 12 | [PyHDX web](http://pyhdx.jhsmit.org) 13 | 14 | [PyHDX latest documentation](https://pyhdx.readthedocs.io/en/latest/) 15 | 16 | [PyHDX on YouTube](https://www.youtube.com/channel/UCTro6Iv1BhvjUPYZNu5TJWg) 17 | 18 | [![screenshot](images/screenshot_pyhdx043.png)](http://pyhdx.jhsmit.org/) 19 | 20 | ## Installation 21 | 22 | Installation of the latest stable beta with `pip`: 23 | 24 | ```bash 25 | $ pip install pyhdx 26 | ``` 27 | 28 | Installation with web interface extra: 29 | 30 | ```bash 31 | $ pip install pyhdx[web] 32 | ``` 33 | 34 | Conda install (includes web interface extra): 35 | 36 | ```bash 37 | $ conda install pyhdx 38 | ``` 39 | 40 | # Run PyHDX 41 | 42 | Most up-to-date code examples are in the directory `pyhdx/templates` 43 | 44 | To run the web server: 45 | 46 | ```bash 47 | $ pyhdx serve 48 | ``` 49 | 50 | Please refer to the [docs](https://pyhdx.readthedocs.io/en/stable/) for more details on how to run PyHDX. 51 | 52 | ## Web Application 53 | 54 | The PyHDX web application is currently hosted at: 55 | http://pyhdx.jhsmit.org 56 | 57 | A test file can be downloaded from [here](https://raw.githubusercontent.com/Jhsmit/PyHDX/master/tests/test_data/input/ecSecB_apo.csv) and [here](https://raw.githubusercontent.com/Jhsmit/PyHDX/master/tests/test_data/input/ecSecB_dimer.csv>) (right click, save as). 58 | 59 | A beta version might be available at: 60 | http://pyhdx-beta.jhsmit.org 61 | 62 | 63 | ## Publication 64 | 65 | Our Analytical Chemistry Publication describing PyHDX can be found [here](https://doi.org/10.1021/acs.analchem.1c02155) 66 | 67 | The latest version (v2) of our biorxiv paper: https://doi.org/10.1101/2020.09.30.320887 68 | 69 | Python code for analysis and generation the figures in the paper are here: https://github.com/Jhsmit/PyHDX-paper 70 | 71 | ## Other 72 | 73 | HDX MS datasets repository and format: 74 | https://github.com/Jhsmit/HDX-MS-datasets 75 | 76 | HDXMS datasets python bindings: 77 | https://github.com/Jhsmit/hdxms-datasets 78 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - setup.py 3 | 4 | codecov: 5 | disable_default_path_fixes: false 6 | 7 | comment: off 8 | -------------------------------------------------------------------------------- /dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | requirements: 8 | - file: "requirements/requirements-*.txt" -------------------------------------------------------------------------------- /dev/gui/dev_mwe_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Runs a MWE app example 3 | 4 | """ 5 | 6 | import sys 7 | from pathlib import Path 8 | 9 | import panel as pn 10 | import yaml 11 | from pyhdx.web.base import STATIC_DIR 12 | from pyhdx.web.template import GoldenElvis, ExtendedGoldenTemplate 13 | from pyhdx.web.theme import ExtendedGoldenDarkTheme, ExtendedGoldenDefaultTheme 14 | from pyhdx.web.utils import load_state 15 | from pyhdx.web.constructor import AppConstructor 16 | from dev_controllers import * 17 | 18 | sys._excepthook = sys.excepthook 19 | 20 | import traceback as tb 21 | 22 | 23 | # Prevents failures without tracebacks 24 | def my_exception_hook(exctype, value, traceback): 25 | # Print the error and traceback 26 | # https://stackoverflow.com/questions/43039048/pyqt5-fails-with-cryptic-message/43039363#43039363 27 | tb.print_tb(traceback, file=sys.stdout) 28 | print(exctype, value, traceback) 29 | 30 | tb.print_stack() 31 | print(traceback.format_exc()) 32 | # or 33 | print(sys.exc_info()[2]) 34 | # Call the normal Exception hook after 35 | sys._excepthook(exctype, value, traceback) 36 | sys.exit(1) 37 | 38 | 39 | # Set the exception hook to our wrapping function 40 | sys.excepthook = my_exception_hook 41 | 42 | cwd = Path(__file__).parent.resolve() 43 | yaml_dict = yaml.safe_load((cwd / "mwe_app.yaml").read_text(encoding="utf-8")) 44 | 45 | ctr = AppConstructor() 46 | ctrl = ctr.parse(yaml_dict) 47 | 48 | fmt = {"accent_base_color": "#1d417a"} 49 | 50 | views_names = ["xy_scatter", "xy_line"] 51 | 52 | elvis = GoldenElvis(ctrl, ExtendedGoldenTemplate, ExtendedGoldenDefaultTheme, title="test") 53 | 54 | 55 | views = {v: ctrl.views[v] for v in views_names} 56 | # [v.update() for v in views.values()] 57 | 58 | tmpl = elvis.compose(elvis.row(elvis.view("xy_scatter"), elvis.view("xy_line"))) 59 | 60 | 61 | def reload_tables(): 62 | df = pd.DataFrame( 63 | { 64 | "x": np.random.normal(loc=3, scale=2, size=100), 65 | "y": np.random.normal(loc=2, scale=0.3, size=100), 66 | } 67 | ) 68 | 69 | src = ctrl.sources["main"] 70 | src.tables["test_data"] = df 71 | src.param.trigger("updated") 72 | 73 | 74 | pn.state.onload(reload_tables) 75 | 76 | if __name__ == "__main__": 77 | pn.serve(tmpl, show=True, static_dirs={"pyhdx": STATIC_DIR}) 78 | 79 | elif __name__.startswith("bokeh_app"): 80 | tmpl.servable() 81 | 82 | # ctrl.template.servable() 83 | 84 | 85 | # panel serve --show --autoreload --static-dirs pyhdx=C:\Users\jhsmi\pp\PyHDX\pyhdx\web\static 86 | -------------------------------------------------------------------------------- /dev/gui/dev_rfu_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Launch RFU app and preload data 3 | Run local_cluster.py in anothor thread 4 | 5 | """ 6 | import sys 7 | from pathlib import Path 8 | 9 | import pandas as pd 10 | import panel as pn 11 | import yaml 12 | 13 | from pyhdx.batch_processing import StateParser 14 | from pyhdx.fileIO import csv_to_dataframe, load_fitresult 15 | from pyhdx.web.apps import rfu_app 16 | from pyhdx.web.base import STATIC_DIR 17 | from pyhdx.web.utils import load_state, fix_multiindex_dtypes 18 | from pyhdx.config import cfg, reset_config 19 | 20 | reset_config() 21 | 22 | sys._excepthook = sys.excepthook 23 | 24 | import traceback as tb 25 | 26 | 27 | def my_exception_hook(exctype, value, traceback): 28 | # Print the error and traceback 29 | # https://stackoverflow.com/questions/43039048/pyqt5-fails-with-cryptic-message/43039363#43039363 30 | tb.print_tb(traceback, file=sys.stdout) 31 | print(exctype, value, traceback) 32 | 33 | tb.print_stack() 34 | print(traceback.format_exc()) 35 | # or 36 | print(sys.exc_info()[2]) 37 | # Call the normal Exception hook after 38 | sys._excepthook(exctype, value, traceback) 39 | sys.exit(1) 40 | 41 | 42 | # Set the exception hook to our wrapping function 43 | sys.excepthook = my_exception_hook 44 | 45 | 46 | ctrl, tmpl = rfu_app() 47 | 48 | cwd = Path(__file__).parent 49 | root_dir = cwd.parent.parent 50 | data_dir = root_dir / "tests" / "test_data" / "input" 51 | 52 | 53 | # batch_fname = 'data_states.yaml' # standard secb apo / dimer dataset 54 | # batch_fname = 'data_states_red.yaml' # reduced number of pepties 55 | batch_fname = "PpiX_states.yaml" # secb apo / dimer but artificial delta C/N tail 56 | hdx_spec = yaml.safe_load(Path(data_dir / batch_fname).read_text()) 57 | 58 | 59 | def init_dashboard(): 60 | n = 2 # change this to control the number of HDX measurements added 61 | # states = ['PpiA_Folding'] 62 | # states = ['PpiB_Folding'] 63 | states = ["PpiA_Folding", "PpiB_Folding"] 64 | input_control = ctrl.control_panels["PeptideFileInputControl"] 65 | load_state(input_control, hdx_spec, data_dir=data_dir, states=states) 66 | 67 | input_control._action_load_datasets() 68 | 69 | # if n > 1: 70 | # diff = ctrl.control_panels['DifferentialControl'] 71 | # diff._action_add_comparison() 72 | 73 | 74 | # pn.state.onload(reload_dashboard) 75 | # pn.state.onload(reload_tables) 76 | pn.state.onload(init_dashboard) 77 | # pn.state.onload(init_batch) 78 | 79 | 80 | print(__name__) 81 | 82 | if __name__ == "__main__": 83 | Path(cfg.assets_dir).mkdir(exist_ok=True, parents=True) 84 | pn.serve( 85 | tmpl, 86 | show=True, 87 | static_dirs={"pyhdx": STATIC_DIR, "assets": str(cfg.assets_dir)}, 88 | ) 89 | 90 | elif __name__.startswith("bokeh_app"): 91 | Path(cfg.assets_dir).mkdir(exist_ok=True, parents=True) 92 | tmpl.servable() 93 | 94 | # panel serve dev_gui_secB.py --show --autoreload --port 5076 --static-dirs pyhdx=C:/Users/jhsmi/pp/PyHDX/pyhdx/web/static assets=C:/Users/jhsmi/.pyhdx/assets 95 | -------------------------------------------------------------------------------- /dev/gui/errorbar_app.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | title: MWE test app 3 | 4 | main_controller: 5 | type: base 6 | #kwargs: ... 7 | 8 | sources: 9 | main: 10 | type: table 11 | 12 | filters: 13 | base_src: 14 | type: table_source 15 | source: main 16 | table: test_data 17 | 18 | tools: 19 | coverage: 20 | type: hover 21 | tooltips: 22 | - ["x", '@x'] 23 | - ['y', '@y'] 24 | 25 | opts: 26 | scatter: 27 | type: generic 28 | tools: 29 | - coverage 30 | size: 10 31 | hooks: 32 | - handle: xaxis 33 | attr: axis_label_text_color 34 | value: red 35 | - handle: yaxis 36 | attr: axis_label_text_color 37 | value: blue 38 | another_hook: 39 | type: generic 40 | hooks: 41 | - handle: yaxis 42 | attr: axis_label_text_color 43 | value: blue 44 | errorbar: 45 | type: generic 46 | apply_ranges: False 47 | hooks: 48 | - handle: glyph 49 | attr: upper_head.line_color 50 | value: green 51 | 52 | views: 53 | xy_scatter: 54 | type: scatter 55 | source: base_src 56 | x: index 57 | y: y 58 | opts: 59 | - scatter 60 | - another_hook 61 | xy_scatter_errorbars: 62 | type: errorbars 63 | source: base_src 64 | pos: index 65 | value: y 66 | err: yerr 67 | opts: 68 | - errorbar 69 | scatter_overlay: 70 | type: overlay 71 | views: 72 | - xy_scatter 73 | - xy_scatter_errorbars 74 | 75 | controllers: 76 | - mwe 77 | -------------------------------------------------------------------------------- /dev/gui/mwe_app.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | title: MWE test app 3 | 4 | main_controller: 5 | type: base 6 | #kwargs: ... 7 | 8 | sources: 9 | main: 10 | type: table 11 | 12 | transforms: 13 | base_src: 14 | type: table_source 15 | source: main 16 | table: test_data 17 | 18 | tools: 19 | coverage: 20 | type: hover 21 | tooltips: 22 | - ["x", '@x'] 23 | - ['y', '@y'] 24 | 25 | opts: 26 | scatter: 27 | type: generic 28 | tools: 29 | - coverage 30 | size: 10 31 | 32 | views: 33 | xy_scatter: 34 | type: scatter 35 | source: base_src 36 | opts: 37 | - scatter 38 | xy_line: 39 | type: scatter 40 | source: base_src 41 | opts: 42 | - scatter 43 | 44 | controllers: 45 | - dev 46 | - async 47 | -------------------------------------------------------------------------------- /dev/gui/readme: -------------------------------------------------------------------------------- 1 | Use dev_gui_secB to test/develop the web app 2 | 3 | Make sure to run `local_cluster` in another thread first -------------------------------------------------------------------------------- /dev/gui/rfu_headless.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from logging import StreamHandler 3 | from pathlib import Path 4 | 5 | import panel as pn 6 | 7 | from pyhdx.web.apps import rfu_app 8 | import yaml 9 | 10 | from pyhdx.web.utils import load_state 11 | 12 | # %% 13 | 14 | sys._excepthook = sys.excepthook 15 | 16 | import traceback as tb 17 | 18 | 19 | def my_exception_hook(exctype, value, traceback): 20 | # Print the error and traceback 21 | # https://stackoverflow.com/questions/43039048/pyqt5-fails-with-cryptic-message/43039363#43039363 22 | tb.print_tb(traceback, file=sys.stdout) 23 | print(exctype, value, traceback) 24 | 25 | tb.print_stack() 26 | print(traceback.format_exc()) 27 | # or 28 | print(sys.exc_info()[2]) 29 | # Call the normal Exception hook after 30 | sys._excepthook(exctype, value, traceback) 31 | sys.exit(1) 32 | 33 | 34 | # Set the exception hook to our wrapping function 35 | sys.excepthook = my_exception_hook 36 | 37 | 38 | # %% 39 | 40 | cwd = Path(__file__).parent 41 | root_dir = cwd.parent.parent 42 | data_dir = root_dir / "tests" / "test_data" / "input" 43 | 44 | batch_fname = "PpiX_states.yaml" # secb apo / dimer but artificial delta C/N tail 45 | hdx_spec = yaml.safe_load(Path(data_dir / batch_fname).read_text()) 46 | 47 | # %% 48 | # create controller / app and load data 49 | ctrl, tmpl = rfu_app() 50 | 51 | file_input = ctrl.control_panels["PeptideRFUFileInputControl"] 52 | # states = ['PpiA_Folding'] 53 | # states = ['PpiB_Folding'] 54 | states = ["PpiA_Folding", "PpiB_Folding"] 55 | # load_state_rfu(file_input, hdx_spec, data_dir = data_dir, states=states) 56 | # 57 | # file_input._action_load_datasets() 58 | 59 | print(file_input.nd_control) 60 | # %% 61 | 62 | # len(file_input.src.hdxm_objects) 63 | 64 | 65 | # %% 66 | 67 | # file_input._add_single_dataset_spec() 68 | # %% 69 | 70 | # input_files = [data_dir / f_dict['filename'] for f_dict in hdx_spec["data_files"].values()] 71 | # f_bytes = [f.read_bytes() for f in input_files] 72 | # file_input.widgets["input_files"].filename = [f.name for f in input_files] 73 | # file_input.input_files = f_bytes 74 | # file_input._read_files() 75 | # 76 | # file_input.data_files 77 | # 78 | # 79 | 80 | # 81 | # #%% 82 | # file_input.widgets["input_files"].filename 83 | # 84 | # #%% 85 | # # state_spec = hdx_spec['states'] 86 | # states = states or list(state_spec.keys()) 87 | # names = names or states 88 | -------------------------------------------------------------------------------- /dev/gui/test_data/secb/20211116_2207_PyHDX_session.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/dev/gui/test_data/secb/20211116_2207_PyHDX_session.zip -------------------------------------------------------------------------------- /docs/_docs_notes.md: -------------------------------------------------------------------------------- 1 | Material web element icons: 2 | 3 | :material-form-textbox: 4 | :material-button-cursor: 5 | :material-form-dropdown: 6 | :material-expand-all: -------------------------------------------------------------------------------- /docs/assets/pyhdx_inverted_button_only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/docs/assets/pyhdx_inverted_button_only.png -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | Basic configuration of PyHDX is done through a `.yaml` configuration file. By default, the configuration 4 | file in the user home directory is used (`~/.pyhdx/config.yaml`). 5 | Individual or all config entries can be overwritten by loading (partial) config files. 6 | 7 | In `'myconfigfile.yaml`: 8 | 9 | ```yaml 10 | cluster: 11 | n_workers: 4 12 | fitting: 13 | dtype: float32 14 | device: cuda 15 | ``` 16 | Then to load the config: 17 | 18 | ```python 19 | from pyhdx.config import cfg 20 | cfg.load_config('myconfigfile.yaml') 21 | cfg.TORCH_DTYPE 22 | >>> torch.float32 23 | ``` 24 | 25 | ## Default configuration 26 | 27 | The default configuration file is shown below, and can be found in PyHDX source at `pyhdx/config.yaml`. 28 | 29 | ```yaml 30 | --8<-- 31 | ./pyhdx/config.yaml 32 | --8<-- 33 | ``` 34 | 35 | ### Cluster 36 | Settings related to the `dask` cluster for computation of $\Delta G$ values. 37 | 38 | - **scheduler_address**: The address for the `dask` scheduler. If scheduler is found at this address, 39 | a new cluster is created. 40 | - **n_workers**: Number of workers for the cluster. 41 | 42 | 43 | ### Server 44 | Settings related to the `panel` web application. 45 | 46 | - **assets_dir**: Directory for static assets. Uploaded `.pdb` files for visualization are stored here. 47 | - **log_dir**: Directory for log files. 48 | 49 | ### Fitting 50 | Settings related to $\Delta G$ fitting. 51 | 52 | - **dtype**: Data type for fitting. Can be `float32` or `float64`. 53 | - **device**: Device for fitting. Can be `cpu` or `cuda` (GPU), if `cuda` is available. 54 | 55 | ### Analysis 56 | Settings related to analysis of HDX-MS data. 57 | 58 | - **drop_first**: Number of N-terminal residues to consider as completely back-exchanging. 59 | - **weight_exponent**: Exponent for calculating residue-level RFU values by weighted averaging. 60 | 61 | ### Plotting 62 | Settings related to output format of plots generated by the web interface. These include widths and 63 | aspect ratios of figures and plots, as well as the number of columns of plots to place into one figure. -------------------------------------------------------------------------------- /docs/css/codehilite.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .codehilite .hll { background-color: #ffffcc } 7 | .codehilite { background: #f8f8f8; } 8 | .codehilite .c { color: #3D7B7B; font-style: italic } /* Comment */ 9 | .codehilite .err { border: 1px solid #FF0000 } /* Error */ 10 | .codehilite .k { color: #008000; font-weight: bold } /* Keyword */ 11 | .codehilite .o { color: #666666 } /* Operator */ 12 | .codehilite .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ 13 | .codehilite .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ 14 | .codehilite .cp { color: #9C6500 } /* Comment.Preproc */ 15 | .codehilite .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ 16 | .codehilite .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ 17 | .codehilite .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ 18 | .codehilite .gd { color: #A00000 } /* Generic.Deleted */ 19 | .codehilite .ge { font-style: italic } /* Generic.Emph */ 20 | .codehilite .gr { color: #E40000 } /* Generic.Error */ 21 | .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .codehilite .gi { color: #008400 } /* Generic.Inserted */ 23 | .codehilite .go { color: #717171 } /* Generic.Output */ 24 | .codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 25 | .codehilite .gs { font-weight: bold } /* Generic.Strong */ 26 | .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .codehilite .gt { color: #0044DD } /* Generic.Traceback */ 28 | .codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 29 | .codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 30 | .codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 31 | .codehilite .kp { color: #008000 } /* Keyword.Pseudo */ 32 | .codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 33 | .codehilite .kt { color: #B00040 } /* Keyword.Type */ 34 | .codehilite .m { color: #666666 } /* Literal.Number */ 35 | .codehilite .s { color: #BA2121 } /* Literal.String */ 36 | .codehilite .na { color: #687822 } /* Name.Attribute */ 37 | .codehilite .nb { color: #008000 } /* Name.Builtin */ 38 | .codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 39 | .codehilite .no { color: #880000 } /* Name.Constant */ 40 | .codehilite .nd { color: #AA22FF } /* Name.Decorator */ 41 | .codehilite .ni { color: #717171; font-weight: bold } /* Name.Entity */ 42 | .codehilite .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ 43 | .codehilite .nf { color: #0000FF } /* Name.Function */ 44 | .codehilite .nl { color: #767600 } /* Name.Label */ 45 | .codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 46 | .codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */ 47 | .codehilite .nv { color: #19177C } /* Name.Variable */ 48 | .codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 49 | .codehilite .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .codehilite .mb { color: #666666 } /* Literal.Number.Bin */ 51 | .codehilite .mf { color: #666666 } /* Literal.Number.Float */ 52 | .codehilite .mh { color: #666666 } /* Literal.Number.Hex */ 53 | .codehilite .mi { color: #666666 } /* Literal.Number.Integer */ 54 | .codehilite .mo { color: #666666 } /* Literal.Number.Oct */ 55 | .codehilite .sa { color: #BA2121 } /* Literal.String.Affix */ 56 | .codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */ 57 | .codehilite .sc { color: #BA2121 } /* Literal.String.Char */ 58 | .codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */ 59 | .codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 60 | .codehilite .s2 { color: #BA2121 } /* Literal.String.Double */ 61 | .codehilite .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ 62 | .codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */ 63 | .codehilite .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ 64 | .codehilite .sx { color: #008000 } /* Literal.String.Other */ 65 | .codehilite .sr { color: #A45A77 } /* Literal.String.Regex */ 66 | .codehilite .s1 { color: #BA2121 } /* Literal.String.Single */ 67 | .codehilite .ss { color: #19177C } /* Literal.String.Symbol */ 68 | .codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */ 69 | .codehilite .fm { color: #0000FF } /* Name.Function.Magic */ 70 | .codehilite .vc { color: #19177C } /* Name.Variable.Class */ 71 | .codehilite .vg { color: #19177C } /* Name.Variable.Global */ 72 | .codehilite .vi { color: #19177C } /* Name.Variable.Instance */ 73 | .codehilite .vm { color: #19177C } /* Name.Variable.Magic */ 74 | .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ 75 | -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | p { 2 | text-align: justify; 3 | text-justify: inter-word; 4 | } -------------------------------------------------------------------------------- /docs/gen_ref_pages.py: -------------------------------------------------------------------------------- 1 | """Generate the code reference pages.""" 2 | from pathlib import Path 3 | 4 | import mkdocs_gen_files 5 | 6 | ROOT_DIR = "pyhdx" 7 | nav = mkdocs_gen_files.Nav() 8 | 9 | # open_func = open # for debugging 10 | open_func = mkdocs_gen_files.open 11 | 12 | # %% 13 | skip = ["pyhdx_diagram"] 14 | for path in sorted(Path(ROOT_DIR).rglob("*.py")): # 15 | module_path = path.relative_to(ROOT_DIR).with_suffix("") # 16 | doc_path = path.relative_to(ROOT_DIR).with_suffix(".md") # 17 | 18 | full_doc_path = Path("reference", doc_path) # 19 | 20 | parts = list(module_path.parts) 21 | if module_path.stem.startswith("_"): 22 | continue 23 | if module_path.stem in skip: 24 | continue 25 | 26 | nav[parts] = doc_path.as_posix() 27 | 28 | with open_func(full_doc_path, "w") as fd: # 29 | identifier = ".".join(parts) # 30 | print("::: " + identifier, file=fd) # 31 | 32 | mkdocs_gen_files.set_edit_path(full_doc_path, path) # 33 | 34 | 35 | with open_func("reference/SUMMARY.md", "w") as nav_file: # 36 | nav_file.writelines(nav.build_literate_nav()) 37 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome the PyHDX documentation! 2 | 3 | 4 | ![pyhdx_image](assets/pyhdx_inverted_button_only.png) -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Stable release 4 | 5 | Installation of the latest stable release version. 6 | 7 | With `conda`: 8 | 9 | ```bash 10 | conda install -c conda-forge pyhdx 11 | ``` 12 | 13 | With `pip`: 14 | 15 | ```bash 16 | pip install pyhdx 17 | ``` 18 | 19 | To install with web application: 20 | 21 | ```bash 22 | pip install pyhdx[web] 23 | ``` 24 | 25 | To install with pdf output: 26 | ```bash 27 | pip install pyhdx[pdf] 28 | ``` 29 | 30 | Installation via `conda` automatically installs all extras. 31 | 32 | ## Running the web server 33 | 34 | 35 | PyHDX web application can be launched from the command line using the `pyhdx` cli command with below options, 36 | 37 | To run PyHDX server using default settings on your local server: 38 | 39 | ```bash 40 | pyhdx serve 41 | ``` 42 | 43 | To run PyHDX server using the IP address and port number of your dask cluster: 44 | 45 | ```bash 46 | pyhdx serve --scheduler_address : 47 | ``` 48 | 49 | If no dask cluster is found at the specified address, a LocalCluster will be started (on localhost) using the 50 | specified port number. 51 | 52 | To start a dask cluster separately, open another terminal tab and run: 53 | 54 | ```bash 55 | python local_cluster.py 56 | ``` 57 | 58 | This will start a Dask cluster on the scheduler address as specified in the PyHDX config. 59 | 60 | ## Downloading datasets 61 | 62 | You can download public datasets from the HDX-MS database to directly load into PyHDX web. 63 | 64 | To download up to 5 available datasets from the repository: 65 | 66 | ```bash 67 | pyhdx datasets fetch 5 68 | ``` 69 | 70 | 71 | ## Install from source 72 | 73 | 74 | Create a new conda environment in a local `.venv` folder: 75 | 76 | ```bash 77 | conda create --prefix ./.venv python=3.9 78 | conda activate ./.venv 79 | ``` 80 | 81 | Clone the GitHub repository: 82 | ```bash 83 | git clone https://github.com/Jhsmit/PyHDX 84 | cd PyHDX 85 | ``` 86 | 87 | You can install the dependencies from the pinned requirement files for your OS with pip: 88 | 89 | ```bash 90 | $ pip install -r requirements/requirements--3.9.txt 91 | ``` 92 | 93 | Then to editable install PyHDX: 94 | 95 | ```bash 96 | $ pip install -e . 97 | ``` 98 | 99 | 100 | ### Running from source 101 | 102 | To run the web application: 103 | 104 | ```bash 105 | python pyhdx/web/serve.py 106 | ``` 107 | 108 | This runs the pyhx web application without a Dask cluster to submit jobs to, so 109 | submitting a fitting job will give an error. 110 | 111 | To start a dask cluster separately, open another terminal tab and run: 112 | 113 | ```bash 114 | python pyhdx/local_cluster.py 115 | ``` 116 | 117 | ## Configuration 118 | 119 | A configuration file is located in the `.pyhdx` folder in the user home directory. This file 120 | is used by default and can be edited to change PyHDX default settings. 121 | 122 | Alternatively, users can create additional `.yaml` configuration files in this directory, after 123 | which the scripts ``local_cluster.py`` and ``serve.py`` prompt the user for which file to use. 124 | 125 | The section ``server`` configures the panel server settings. In this section the additional keys 126 | ``port`` and ``websocket_origin`` can be added, which are passed to ``panel.serve``. See the panel 127 | [Deploy and Export](https://panel.holoviz.org/user_guide/Deploy_and_Export.html) deploy section for 128 | more information. 129 | 130 | -------------------------------------------------------------------------------- /docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | PyHDX is software to extract H/D exchange kinetics from HDX-MS data sets in terms of Gibbs free energy of exchange (ΔG) 4 | at the residue level. 5 | 6 | An interactive web server is available where users can upload HDX-MS data and obtain ΔG values. Please refer to the 7 | [GitHub](https://github.com/Jhsmit/PyHDX) page for up-to-date links to the web server. 8 | How to use the web app is described on the [PyHDX web applicaton](web_app.md) page. 9 | 10 | PyHDX analysis can also be run headless from python scripts. The 'templates' directory on GitHub lists several examples. 11 | Additional examples can be found in the _Examples_ section. 12 | 13 | 14 | For more information, please have a look at [publication](https://doi.org/10.1021/acs.analchem.1c02155) in Analytical Chemistry. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/javascript/mathjax.js: -------------------------------------------------------------------------------- 1 | window.MathJax = { 2 | tex: { 3 | inlineMath: [["\\(", "\\)"]], 4 | displayMath: [["\\[", "\\]"]], 5 | processEscapes: true, 6 | processEnvironments: true 7 | }, 8 | options: { 9 | ignoreHtmlClass: ".*|", 10 | processHtmlClass: "arithmatex" 11 | } 12 | }; 13 | 14 | document$.subscribe(() => { 15 | MathJax.typesetPromise() 16 | }) 17 | -------------------------------------------------------------------------------- /docs/web_app_autodoc.md: -------------------------------------------------------------------------------- 1 | # Work in progress -------------------------------------------------------------------------------- /images/PXL_20220703_130658046.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/PXL_20220703_130658046.jpg -------------------------------------------------------------------------------- /images/PyHDX_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/PyHDX_rgb.png -------------------------------------------------------------------------------- /images/pyhdx_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/pyhdx_header.png -------------------------------------------------------------------------------- /images/pyhdx_inverted.afphoto: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/pyhdx_inverted.afphoto -------------------------------------------------------------------------------- /images/pyhdx_inverted.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/pyhdx_inverted.jpg -------------------------------------------------------------------------------- /images/pyhdx_inverted_button.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/pyhdx_inverted_button.afdesign -------------------------------------------------------------------------------- /images/pyhdx_inverted_button_only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/pyhdx_inverted_button_only.png -------------------------------------------------------------------------------- /images/pyhdx_inverted_button_white_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/pyhdx_inverted_button_white_bg.png -------------------------------------------------------------------------------- /images/readme_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/readme_img.png -------------------------------------------------------------------------------- /images/screenshot_pyhdx040b5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/screenshot_pyhdx040b5.png -------------------------------------------------------------------------------- /images/screenshot_pyhdx043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/images/screenshot_pyhdx043.png -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: PyHDX 2 | site_description: Loading and parsing of HDX-MS datasets and associated metadata 3 | 4 | repo_url: https://github.com/jhsmit/pyhdx/ 5 | edit_uri: edit/master/docs/ 6 | 7 | 8 | theme: 9 | name: "material" 10 | 11 | markdown_extensions: 12 | - codehilite 13 | - markdown.extensions.codehilite: 14 | guess_lang: false 15 | - attr_list 16 | - pymdownx.snippets 17 | - pymdownx.caret # A^T^ for superscript 18 | - pymdownx.mark # mark with ==text== 19 | - pymdownx.tilde # H~2~O for subscript 20 | - pymdownx.emoji: 21 | emoji_index: !!python/name:materialx.emoji.twemoji 22 | emoji_generator: !!python/name:materialx.emoji.to_svg 23 | - pymdownx.arithmatex: 24 | generic: true 25 | 26 | nav: 27 | - Overview: index.md 28 | - Introduction: introduction.md 29 | - Installation: installation.md 30 | - Web Application: web_app.md 31 | - Configuration: configuration.md 32 | - Examples: 33 | - Basic Usage: examples/01_basic_usage.ipynb 34 | - Coverage and D-uptake fit: examples/02_coverage_and_d_uptake_fit.ipynb 35 | - Fitting of ΔGs: examples/03_fitting.ipynb 36 | # - Plotting output: examples/04_plot_output.ipynb (outdated) 37 | - Citing and Resources: citing.md 38 | - API Reference: reference/ 39 | 40 | 41 | watch: 42 | - pyhdx 43 | 44 | plugins: 45 | - search 46 | - gen-files: 47 | scripts: 48 | - docs/gen_ref_pages.py 49 | - literate-nav: 50 | nav_file: SUMMARY.md 51 | - mkdocs-jupyter 52 | - mkdocstrings: 53 | default_handler: python 54 | handlers: 55 | python: 56 | options: 57 | enable_inventory: true 58 | show_signature_annotations: false 59 | import: 60 | - https://docs.python.org/3/objects.inv 61 | - https://numpy.org/doc/stable/objects.inv 62 | - https://docs.scipy.org/doc/scipy/objects.inv 63 | - https://pandas.pydata.org/docs/objects.inv 64 | paths: [pyhdx] 65 | 66 | extra_css: 67 | - css/codehilite.css 68 | - css/style.css 69 | 70 | extra_javascript: 71 | - javascript/mathjax.js 72 | - https://polyfill.io/v3/polyfill.min.js?features=es6 73 | - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js -------------------------------------------------------------------------------- /pyhdx/__init__.py: -------------------------------------------------------------------------------- 1 | from pyhdx.__version__ import __version__ 2 | from pyhdx.datasets import read_dynamx 3 | from pyhdx.fitting_torch import TorchFitResult, TorchFitResultSet 4 | from pyhdx.models import ( 5 | Coverage, 6 | HDXMeasurement, 7 | HDXMeasurementSet, 8 | HDXTimepoint, 9 | ) 10 | 11 | VERSION_STRING = f"PyHDX {__version__}" 12 | 13 | try: 14 | from pyhdx.output import FitReport 15 | except ModuleNotFoundError: 16 | pass 17 | 18 | 19 | __all__ = [ 20 | "HDXTimepoint", 21 | "HDXMeasurement", 22 | "Coverage", 23 | "HDXMeasurementSet", 24 | "read_dynamx", 25 | "TorchFitResult", 26 | "TorchFitResultSet", 27 | "FitReport", 28 | ] 29 | -------------------------------------------------------------------------------- /pyhdx/__version__.py: -------------------------------------------------------------------------------- 1 | # Adapted from: https://github.com/maresb/hatch-vcs-footgun-example 2 | # Define the variable '__version__': 3 | try: 4 | # If we are in an editable install, the _versioneer file exist and we can use it to find the version 5 | from pyhdx._versioneer import get_versions 6 | 7 | # This will fail with LookupError if the package is not installed in 8 | # editable mode or if Git is not installed. 9 | __version__ = get_versions()["version"] 10 | except ImportError: 11 | # If the project build with hatch, there should be a _version.py file 12 | try: 13 | from pyhdx._version import __version__ # noqa: F401 # type: ignore 14 | except ModuleNotFoundError: 15 | # The user is probably trying to run this without having installed 16 | # the package, so complain. 17 | raise RuntimeError("PyHDX is not correctly installed. Please install it with pip.") 18 | -------------------------------------------------------------------------------- /pyhdx/alignment.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import re 4 | import numpy as np 5 | import pandas as pd 6 | 7 | 8 | def parse_clustal_string(s: str, num_proteins: int, whitelines: int = 2, offset: int = 0) -> dict: 9 | """Takes input Clustal result and parses it to a dictionary. 10 | 11 | Keys in the output dict are IDs of the protein as input into clustal. Values are aligned 12 | (containing '-' for gaps) and concatenated FASTA sequences. 13 | 14 | Args: 15 | s: Input Clustal string. 16 | num_proteins: Number of aligned proteins in the clustal result. 17 | whitelines: Number of white lines between each block of aligned proteins. Default 18 | value is 2. 19 | offset: Number of lines before alignment information starts. 20 | 21 | Returns: 22 | Dictionary with concatenated aligned result 23 | 24 | """ 25 | spacing = num_proteins + whitelines 26 | lines = s.split("\n") 27 | results = [ 28 | "".join( 29 | [ 30 | re.search(r"(?<=\s{3})(.*)(?=\t)", line)[0].strip() 31 | for line in lines[i + offset :: spacing] 32 | ] 33 | ) 34 | for i in range(num_proteins) 35 | ] 36 | names = [re.search(r"(.*)(?=\s{3})", lines[offset + i])[0].strip() for i in range(num_proteins)] 37 | 38 | alignment = {name: result for name, result in zip(names, results)} 39 | 40 | return alignment 41 | 42 | 43 | # TODO should take dicts only 44 | def align_dataframes(dataframes, alignment, first_r_numbers=None): 45 | """ 46 | Aligned dataframes based on an alignment. 47 | 48 | The supplied dataframes should have the residue number as index. The returned dataframes are reindex and their 49 | residue numbers are moved to the 'r_number' columns. 50 | 51 | Parameters 52 | ---------- 53 | dataframes : :obj:`list` or :obj:`dict` 54 | Input dataframes 55 | alignment : :obj:`list` or :obj:`dict` 56 | Alignment as list or dict, (type must be equal to `dataframes`, values are strings with single-letter amino 57 | acids codes where gaps are '-'. 58 | first_r_numbers: :obj:`list` 59 | List of residue numbers corresponding to the first residue in the alignment sequences. Use for N-terminally 60 | truncated proteins or proteins with fused purification tags. 61 | Returns 62 | ------- 63 | 64 | dataframe: :class:`pd.Dataframe` 65 | Aligned and concatenated dataframe. If 'alignment' is given as a dict, the returned dataframe is a column 66 | multiindex dataframe. 67 | 68 | """ 69 | 70 | # todo add option to merge/include sequence information in output dataframes 71 | # todo add dict-like input for dataframes (multiindex dataframe) 72 | 73 | assert len(alignment) == len(dataframes), "Length of dataframes and alignments does not match" 74 | 75 | if isinstance(alignment, dict): 76 | if not isinstance(dataframes, dict): 77 | raise TypeError("'alignment' and 'dataframes' must either both be `dict` or `list`") 78 | align_list = list(alignment.values()) 79 | df_list = list(dataframes.values()) 80 | assert alignment.keys() == dataframes.keys(), "Keys of input dicts to not match" 81 | elif isinstance(alignment, list): 82 | if not isinstance(alignment, list): 83 | raise TypeError("'alignment' and 'dataframes' must either both be `dict` or `list`") 84 | align_list = alignment 85 | df_list = dataframes 86 | else: 87 | raise TypeError("Invalid data type for 'alignment'") 88 | 89 | first_r_numbers = first_r_numbers if first_r_numbers is not None else [1] * len(dataframes) 90 | assert len(first_r_numbers) == len( 91 | df_list 92 | ), "Length of first residue number list does not match number of dataframes" 93 | 94 | dfs = [] 95 | for df, align, r_offset in zip(df_list, align_list, first_r_numbers): 96 | align_array = np.array(list(align)) 97 | r_number = np.cumsum(align_array != "-").astype(float) 98 | r_number[align_array == "-"] = np.nan 99 | r_number += r_offset - 1 100 | index = pd.Index(r_number, name="r_number") 101 | 102 | result = df.reindex(index).reset_index().astype({"r_number": "Int32"}) 103 | dfs.append(result) 104 | 105 | keys = alignment.keys() if isinstance(alignment, dict) else None 106 | output = pd.concat(dfs, axis=1, keys=keys) 107 | return output 108 | -------------------------------------------------------------------------------- /pyhdx/cli.py: -------------------------------------------------------------------------------- 1 | import time 2 | from ipaddress import ip_address 3 | from pathlib import Path 4 | from typing import Optional 5 | 6 | import typer 7 | from omegaconf import OmegaConf 8 | from tqdm.auto import tqdm 9 | 10 | from pyhdx.config import cfg 11 | from pyhdx.datasets import DataVault 12 | 13 | app = typer.Typer() 14 | 15 | 16 | @app.command() 17 | def serve( 18 | scheduler_address: Optional[str] = typer.Option(None, help="Address for dask scheduler to use"), 19 | config: Optional[Path] = typer.Option( 20 | None, exists=True, help="Optional PyHDX .yaml config file to use" 21 | ), 22 | ): 23 | """Launch the PyHDX web application""" 24 | 25 | from pyhdx.config import cfg 26 | from pyhdx.local_cluster import default_cluster, verify_cluster 27 | 28 | if config is not None: 29 | conf = OmegaConf.create(config.read_text()) 30 | cfg.set_config(conf) 31 | 32 | if scheduler_address is not None: 33 | ip, port = scheduler_address.split(":") 34 | if not ip_address(ip): 35 | print("Invalid IP Address") 36 | return 37 | elif not 0 <= int(port) < 2**16: 38 | print("Invalid port, must be 0-65535") 39 | return 40 | cfg.cluster.scheduler_address = scheduler_address 41 | 42 | scheduler_address = cfg.cluster.scheduler_address 43 | if not verify_cluster(scheduler_address): 44 | # Start a new local cluster if none is found 45 | client = default_cluster() 46 | _, ip, port = client.scheduler_address.split(":") 47 | ip = ip.strip("/") 48 | scheduler_address = f"{ip}:{port}" 49 | print(f"Started new Dask LocalCluster at {scheduler_address}") 50 | 51 | # Start the PyHDX web application 52 | from pyhdx.web import serve as serve_pyhdx 53 | 54 | serve_pyhdx.run_apps() 55 | 56 | loop = True 57 | while loop: 58 | try: 59 | time.sleep(1) 60 | except KeyboardInterrupt: 61 | print("Interrupted") 62 | loop = False 63 | 64 | 65 | datasets_app = typer.Typer(help="Manage HDX datasets") 66 | 67 | 68 | @datasets_app.command() 69 | def fetch(num: int = typer.Option(10, min=1, help="Maximum number of datasets to download")): 70 | """Update the datasets from the PyHDX repository""" 71 | vault = DataVault(cache_dir=cfg.database_dir) 72 | missing_datasets = list(set(vault.remote_index) - set(vault.datasets)) 73 | missing_datasets = [data_id for data_id in missing_datasets if data_id] 74 | 75 | failed = [] 76 | success = [] 77 | if missing_datasets: 78 | todo = list(missing_datasets)[:num] 79 | for data_id in tqdm(todo): 80 | try: 81 | vault.fetch_dataset(data_id) 82 | success.append(data_id) 83 | except Exception: 84 | failed.append(data_id) 85 | else: 86 | print("All datasets already downloaded") 87 | 88 | if failed: 89 | print(f"Failed to download: {', '.join(failed)}") 90 | if success: 91 | print(f"Downloaded: {', '.join(success)}") 92 | 93 | 94 | @datasets_app.command() 95 | def clear(): 96 | """Clear the local dataset cache""" 97 | vault = DataVault(cache_dir=cfg.database_dir) 98 | vault.clear_cache() 99 | 100 | 101 | app.add_typer(datasets_app, name="datasets") 102 | 103 | 104 | @app.callback() 105 | def callback(): 106 | pass 107 | 108 | 109 | if __name__ == "__main__": 110 | app() 111 | -------------------------------------------------------------------------------- /pyhdx/config.yaml: -------------------------------------------------------------------------------- 1 | cluster: 2 | scheduler_address: "127.0.0.1:52123" 3 | n_workers: 10 4 | 5 | server: 6 | assets_dir: ~/.pyhdx/assets 7 | log_dir: ~/.pyhdx/logs 8 | database_dir : ~/.hdxms_datasets/datasets 9 | 10 | fitting: 11 | dtype: float64 12 | device: cpu 13 | 14 | analysis: 15 | drop_first: 2 16 | weight_exponent: 1.0 17 | 18 | plotting: 19 | # Sizes are in mm 20 | ncols: 2 21 | page_width: 160 22 | cbar_width: 2.5 23 | peptide_coverage_aspect: 3 24 | peptide_mse_aspect: 3 25 | residue_scatter_aspect: 3 26 | dG_aspect: 2.5 27 | linear_bars_aspect: 30 28 | loss_aspect: 2.5 29 | rainbowclouds_aspect: 4 30 | -------------------------------------------------------------------------------- /pyhdx/datasets.py: -------------------------------------------------------------------------------- 1 | from hdxms_datasets import * # noqa F403 2 | -------------------------------------------------------------------------------- /pyhdx/legacy.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from pyhdx import HDXMeasurement, PeptideMasterTable 4 | from pyhdx.batch_processing import StateParser, time_factors, temperature_offsets 5 | 6 | 7 | def legacy_parsers(version): 8 | loader = LOADER_VERSIONS[version] 9 | parser = type(f"StateParser_{version}", (StateParser,), {"load_hdxm": loader}) 10 | 11 | return parser 12 | 13 | 14 | def load_hdxm_v041(self, state: str, **kwargs: Any) -> HDXMeasurement: 15 | """Read a single protein state to :class:`~pyhdx.models.HDXMeasurement`. 16 | 17 | Args: 18 | state: Name of the protein state to read. 19 | **kwargs: Additional keyword arguments passed to :class:`~pyhdx.models.HDXMeasurement`. 20 | 21 | Returns: 22 | The requested :class:`~pyhdx.models.HDXMeasurement`. 23 | 24 | """ 25 | 26 | state_dict = self.state_spec[state] 27 | 28 | filenames = state_dict["filenames"] 29 | df = self.load_data(*filenames) 30 | 31 | pmt = PeptideMasterTable( 32 | df, 33 | drop_first=state_dict.get("drop_first", 1), 34 | d_percentage=state_dict["d_percentage"], 35 | ) 36 | 37 | if "control" in state_dict.keys(): # Use a FD control for back exchange correction 38 | # todo control should be set from an external file 39 | control_state = state_dict["control"]["state"] 40 | exposure_value = state_dict["control"]["exposure"]["value"] 41 | exposure_units = state_dict["control"]["exposure"]["unit"] 42 | control_exposure = exposure_value * time_factors[exposure_units] 43 | 44 | pmt.set_control((control_state, control_exposure)) 45 | elif "be_percent" in state_dict.keys(): # Flat back exchange percentage for all peptides\ 46 | pmt.set_backexchange(state_dict["be_percent"]) 47 | else: 48 | raise ValueError("No valid back exchange control method specified") 49 | 50 | temperature = state_dict["temperature"]["value"] 51 | try: 52 | t_offset = temperature_offsets[state_dict["temperature"]["unit"]] 53 | except KeyError: 54 | t_offset = temperature_offsets[state_dict["temperature"]["unit"].lower()] 55 | 56 | temperature += t_offset 57 | 58 | sequence = state_dict.get("sequence", "") 59 | c_term = state_dict.get("c_term") 60 | n_term = state_dict.get("n_term", 1) 61 | 62 | if not (c_term or sequence): 63 | raise ValueError("Must specify either 'c_term' or 'sequence'") 64 | 65 | state_data = pmt.get_state(state_dict["state"]) 66 | for flt in self.data_filters: 67 | state_data = flt(state_data) 68 | 69 | if "name" not in kwargs: 70 | kwargs["name"] = state 71 | 72 | hdxm = HDXMeasurement( 73 | state_data, 74 | temperature=temperature, 75 | pH=state_dict["pH"], 76 | sequence=sequence, 77 | n_term=n_term, 78 | c_term=c_term, 79 | **kwargs, 80 | ) 81 | 82 | return hdxm 83 | 84 | 85 | LOADER_VERSIONS = {"041": load_hdxm_v041} 86 | -------------------------------------------------------------------------------- /pyhdx/local_cluster.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import argparse 4 | import asyncio 5 | import time 6 | from asyncio import Future 7 | from typing import Callable, Iterable, Any 8 | 9 | from dask.distributed import LocalCluster, Client 10 | from distributed import connect 11 | 12 | from pyhdx.config import cfg 13 | from pyhdx.support import select_config 14 | 15 | 16 | class DummyClient(object): 17 | """Object to use as dask Client-like object for doing local operations with 18 | the dask Client API. 19 | """ 20 | 21 | @staticmethod 22 | def submit(func: Callable, *args: Any, **kwargs) -> Future: 23 | future = Future() 24 | future.set_result(func(*args)) 25 | return future 26 | 27 | @staticmethod 28 | def map(func: Callable, *iterables: Iterable, **kwargs) -> list[Future]: 29 | futures = [] 30 | for items in zip(*iterables): 31 | result = func(*items) 32 | future = Future() 33 | future.set_result(result) 34 | futures.append(future) 35 | 36 | return futures 37 | 38 | @staticmethod 39 | def gather(futures) -> list[Any]: 40 | return [future.result() for future in futures] 41 | 42 | 43 | def default_client(timeout="2s", **kwargs): 44 | """Return Dask client at scheduler adress as defined by the global config""" 45 | scheduler_address = cfg.cluster.scheduler_address 46 | try: 47 | client = Client(scheduler_address, timeout=timeout, **kwargs) 48 | return client 49 | except (TimeoutError, IOError): 50 | print(f"No valid Dask scheduler found at specified address: '{scheduler_address}'") 51 | return False 52 | 53 | 54 | def default_cluster(**kwargs): 55 | """Start a dask LocalCluster at the scheduler port given by the config 56 | 57 | kwargs: override defaults 58 | 59 | """ 60 | 61 | scheduler_address = cfg.cluster.scheduler_address 62 | port = int(scheduler_address.split(":")[-1]) 63 | 64 | settings = { 65 | "scheduler_port": port, 66 | "n_workers": cfg.cluster.n_workers, 67 | } 68 | settings.update(kwargs) 69 | cluster = LocalCluster(**settings) 70 | 71 | return cluster 72 | 73 | 74 | def verify_cluster(scheduler_address, timeout="2s"): 75 | """Check if a valid dask scheduler is running at the provided scheduler_address""" 76 | try: 77 | asyncio.run(connect(scheduler_address, timeout=timeout)) 78 | return True 79 | except (TimeoutError, OSError): 80 | return False 81 | 82 | 83 | def verify_cluster_async(scheduler_address, timeout="2s"): 84 | """Check if a valid dask scheduler is running at the provided scheduler_address""" 85 | try: 86 | asyncio.run(connect(scheduler_address, timeout=timeout)) 87 | return True 88 | except (TimeoutError, OSError): 89 | return False 90 | 91 | 92 | def blocking_cluster(): 93 | """Start a dask LocalCluster and block until iterrupted""" 94 | parser = argparse.ArgumentParser(description="Start a new Dask local cluster") 95 | parser.add_argument("-p", "--port", help="Port to use for the Dask local cluster", dest="port") 96 | 97 | args = parser.parse_args() 98 | 99 | if args.port: 100 | port = int(args.port) 101 | else: 102 | scheduler_address = cfg.cluster.scheduler_address 103 | port = int(scheduler_address.split(":")[-1]) 104 | try: 105 | n_workers = cfg.cluster.n_workers 106 | local_cluster = LocalCluster(scheduler_port=port, n_workers=n_workers) 107 | print(f"Started local cluster at {local_cluster.scheduler_address}") 108 | except OSError: 109 | print(f"Could not start local cluster with at port: {port}") 110 | raise 111 | try: 112 | loop = True 113 | while loop: 114 | try: 115 | time.sleep(2) 116 | except KeyboardInterrupt: 117 | print("Interrupted") 118 | loop = False 119 | finally: 120 | local_cluster.close() 121 | 122 | 123 | if __name__ == "__main__": 124 | select_config() 125 | blocking_cluster() 126 | -------------------------------------------------------------------------------- /pyhdx/web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/pyhdx/web/__init__.py -------------------------------------------------------------------------------- /pyhdx/web/apps/peptide_app.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | title: PyHDX app 3 | 4 | main_controller: 5 | type: peptide 6 | 7 | sources: 8 | main: 9 | type: table 10 | 11 | transforms: 12 | d_uptake_src: 13 | type: table_source 14 | source: main 15 | table: d_uptake 16 | k_int_src: 17 | type: table_source 18 | source: main 19 | table: k_int 20 | k_int_replace: 21 | type: generic 22 | source: k_int_src 23 | pd_function: replace 24 | kwargs: 25 | to_replace: 0 26 | value: .NAN 27 | 28 | opts: 29 | base: 30 | type: generic 31 | responsive: True 32 | framewise: True 33 | show_grid: True 34 | d_uptake: 35 | type: generic 36 | logx: True 37 | xlabel: Time (s) 38 | ylabel: D uptake 39 | tools: 40 | - hover 41 | k_int: 42 | type: generic 43 | logy: True 44 | xlabel: Amino acid 45 | ylabel: Intrinsic rate (s⁻¹) 46 | tools: 47 | - count 48 | 49 | 50 | tools: 51 | hover: 52 | type: hover 53 | tooltips: 54 | - ["Time (s)", '$x'] 55 | - ['D-uptake', '$y'] 56 | count: 57 | type: hover 58 | tooltips: 59 | - [ "Amino acid", '@aa' ] 60 | - [ "Intrinsic rate", '@k_int' ] 61 | 62 | views: 63 | peptide_uptake: 64 | type: curve 65 | source: d_uptake_src 66 | opts: 67 | - base 68 | - d_uptake 69 | 70 | x: time 71 | y: sum 72 | 73 | aa_uptake: 74 | type: curve 75 | source: d_uptake_src 76 | opts: 77 | - base 78 | - d_uptake 79 | x: time 80 | y: aa_0 81 | y_objects: !regexp aa_\d 82 | 83 | k_int: 84 | type: bars 85 | source: k_int_replace 86 | opts: 87 | - base 88 | - k_int 89 | x: index 90 | y: k_int 91 | # vdims: 92 | # - aa 93 | 94 | controllers: 95 | peptide: 96 | -------------------------------------------------------------------------------- /pyhdx/web/apps/peptide_faq.md: -------------------------------------------------------------------------------- 1 | FAQ 2 | === 3 | 4 | ### What does this app do? 5 | 6 | This app calculates the D-uptake of a peptide, given the parameters of the Linderstrøm-lang 7 | model, $$k_{open}$$ and $$k_{close}$$, as well as the experimental parameters pH and temperature. 8 | This allows HDX-MS users to find which $$\Delta G_{EX}$$ (Hereafter $$\Delta G$$) values can be probed, 9 | given time range, pH and temperature. 10 | 11 | For example, with pH 6.5 and a temperature of 5°C, if $$\Delta G$$ for an amino 12 | acid is $$\gt 35$$ kJ/mol, no exchange is expected before $$10^5$$ seconds (~ 1 day) of exchange. 13 | 14 | ### What is this 'dependent variable' in peptide controls? 15 | 16 | The parameters for the model are $$k_{open}$$ and $$k_{close}$$, which relate to $$\Delta G$$ 17 | via $$\tfrac{k_{close}}{k_{open}} = e^{\frac{\Delta G}{RT}} $$. Therefore, if two out of three 18 | quantities are known, the third can be calculated. The 'dependent variable' settings lets you 19 | choose which one is calculated, while the other two can be set by the user. 20 | 21 | ### How do you know what values for $$k_{open}$$ are? 22 | 23 | We don't, typically only the ratio of $$k_{open}$$ and $$k_{close}$$ can be determined from 24 | HDX experiments. Literature information on opening and closing rates is scarce, so for I'm 25 | only aware of a paper from 1993 ([Pederson](https://doi.org/10.1006/jmbi.1993.1176)) and a 26 | more recent work from 2021 ([Peacock](https://10.1021/acs.biochem.1c00277)). 27 | 28 | The former finds $$k_{open}}$$ rates ranging from ~$$10^{-3}$$ to $$10^{-8} s^{-1}$$. However, 29 | with such a slow opening rate reaction, to satisfy typical $$\Delta G$$ values of 10-50 kJ/mol, 30 | the corresponding $$k_{close}$$ rates would be 0.1-100 $$s^{-1}$$, and given that typical 31 | values of $$k_{int}$$ (Intrinsic/chemical rate of exchange) are similar or even lower, this 32 | would place most of these HD kinetics outside the EX2 regime (where $$k_{close} \gg k_{int}$$), 33 | contrary to observation. 34 | 35 | The latter work by Peacock at al finds $$k_{open} \approx 10^2 s^{-1}$$, and for reasonably 36 | flexible proteins/residues with $$\Delta G$$ = 15 kJ/mol, this would mean $$k_{close}$$ of 37 | ~$$10^{5} s^{-1}$$. The protein reported is likely more flexible than $$\Delta G$$ = 15 kJ/mol, 38 | and assuming flexibility is reduced by a decrease in $$k_{open}$$, rates for $$k_{close}$$ 39 | are will be slower than $$10^{5} s^{-1}$$. 40 | 41 | 42 | ### What are the timescales involved? 43 | 44 | If we do an experiment at 20 °C, pH 7.5 with a middle-of-the road protein flexibility of 45 | $$\Delta G$$ = 20 kJ/mol D-uptake is expected to start at about 100s of D-exposure, where 46 | ~95% D is reached at roughly 1000s. If we assume $$k_{open}$$ is $$10^{2} s^{-1}$$, $$k_{close}$$ 47 | is about $$10^6 s^{-1}$$. This means that the lifetime of the open state is on the order of 48 | microseconds, and the opening event will occur about 10000 times before we start to see a 49 | measurable amount of D-uptake, after 100 seconds of D-exposure. 50 | 51 | 52 | ### What about EX1 vs EX2? 53 | 54 | In this simulation we know both $$k_{open}$$ and $$k_{close}}$$, so there is no need to make 55 | mathematical approximations depending on the kinetics regime. D-uptake is calculated from: 56 | 57 | $$ 58 | k_{ex} = \frac{1}{2} (k_{open} + k_{close} + k_{int} - \sqrt{((k_{open} + k_{close} + k_{int})^2 - 4 k_{open} k_{int}}) 59 | $$ 60 | 61 | As described in the Pederson paper, and is accurate for at least the $$ \Delta G \gt 10 $$ kJ/mol regime. 62 | 63 | Since in EX2 the exchange only depends on the ratio of the closing and opening rates, and 64 | not their absolute values, we can probe in which regime we are by playing with the sliders. 65 | Typically, with $$\Delta G$$ at 30 kJ/mol, and 'Dependent variable' set to $$k_{close}$$, 66 | we see no effect in the uptake curve (lower panel for per-residue uptake curve) when changing 67 | the rate of $$k_{open}$$ over 6 order of magnitude from $$10^{-2}$$ to $$10^4 s^{-1}$$. 68 | However, at about 15 kJ/mol, changes in the D-uptake curve begin to show as function of 69 | $$k_{open}$$, with about a 2-fold difference in the half-life of D-uptake. These differences 70 | become more pronounced at 10 kJ/mol (up to 10-fold) and relate to accuracy of $$\Delta G$$ values 71 | found by PyHDX in these low $$\Delta G$$ / high flexibility regimes given that PyHDX assumes 72 | EX2 kinetics. 73 | 74 | ### The first amino acid is missing! 75 | 76 | For simplicity, we are only considering 'middle' amino acids, so when calculating intrinsic 77 | rates of exchange no N-terminal or C-terminal effects are taken into account. However, since 78 | these rates do also depend on the chemical nature of the amino acid on the N-terminal side of 79 | the amide in question, we need to know which amino acid this is. So to model exchange for 80 | the peptide 'LGPLTAGHH', find in your protein which amino acid is N-terminal to this 81 | peptide, and then for example enter 'KLGPLTAGHH'. 82 | 83 | ### What about back exchange? 84 | 85 | There is no back exchange modelled here. 86 | 87 | ### Why do the sliders look funny/colored or move janky? 88 | 89 | There are some bugs with the sliders and possibly the way they are coupled to eachother is 90 | not ideal, we're looking at improving this at some point. 91 | 92 | ### Can I import my data and compare to simulation? 93 | 94 | No sorry, at the moment this is not possible. We always welcome feature requests through the 95 | [Issues](https://github.com/Jhsmit/PyHDX/issues) page on GitHub if you any suggestions to improve PyHDX. 96 | 97 | ### Can I change the limits of the time axis or slider values? 98 | 99 | Same as the previous question, although this one is a little higher on the priority list. 100 | -------------------------------------------------------------------------------- /pyhdx/web/cache.py: -------------------------------------------------------------------------------- 1 | import param 2 | import pandas as pd 3 | 4 | 5 | class Cache(param.Parameterized): 6 | def __getitem__(self, item): 7 | return None 8 | 9 | def __setitem__(self, key, value): 10 | pass 11 | 12 | def __contains__(self, item): 13 | return False 14 | 15 | 16 | class MemoryCache(Cache): 17 | _cache = param.Dict(default={}) 18 | 19 | max_items = param.Integer(None, doc="Maximum number of items allowed in the cache") 20 | 21 | def __getitem__(self, item): 22 | return self._cache.__getitem__(item) 23 | 24 | def __setitem__(self, key, value): 25 | if self.max_items is not None and len(self._cache) >= self.max_items: 26 | self._cache.popitem() 27 | 28 | self._cache[key] = value 29 | 30 | def __contains__(self, item): 31 | return item in self._cache 32 | 33 | 34 | class HybridHDFCache(Cache): 35 | """ 36 | 37 | Hybrid HDFStore / Memory cache 38 | 39 | Sometimes there are errors depending on the dtypes of dataframes stored 40 | 41 | """ 42 | 43 | file_path = param.String() 44 | 45 | _store = param.ClassSelector(class_=pd.HDFStore) 46 | 47 | _cache = param.Dict(default={}) 48 | 49 | bytes_threshold = param.Integer(default=int(1e8)) 50 | 51 | def __init__(self, **params): 52 | super().__init__(**params) 53 | if self.file_path is not None: 54 | self._store = pd.HDFStore(self.file_path) 55 | 56 | def __getitem__(self, item): 57 | key = str(item) 58 | try: 59 | return self._cache.__getitem__(key) 60 | except KeyError: 61 | return self._store.__getitem__(key) 62 | 63 | def _store_put(self, key, value): 64 | try: 65 | self._store[key] = value 66 | 67 | # Check if reading back the dataframe works 68 | try: 69 | _value = self._store[key] 70 | except AttributeError: 71 | del self._store[key] 72 | self._cache[key] = value 73 | 74 | except ( 75 | NotImplementedError, 76 | TypeError, 77 | ): # pytables does not support categorical dtypes 78 | self._cache[key] = value 79 | 80 | def __setitem__(self, key, value): 81 | key = str(key) 82 | if isinstance(value, pd.DataFrame) and value.memory_usage().sum() > self.bytes_threshold: 83 | self._store_put(key, value) 84 | elif isinstance(value, pd.Series) and value.memory_usage() > self.bytes_threshold: 85 | self._store_put(key, value) 86 | else: 87 | self._cache[str(key)] = value 88 | 89 | def __contains__(self, item): 90 | return str(item) in self._cache.keys() | self._store.keys() 91 | 92 | # todo with statement for creating caches? 93 | # def __exit__(self): 94 | # pass 95 | -------------------------------------------------------------------------------- /pyhdx/web/log.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | import sys 4 | 5 | 6 | def get_default_handler(stream=None): 7 | sh = logging.StreamHandler(stream) 8 | formatter = logging.Formatter("%(asctime)s [%(levelname)s]: %(message)s") 9 | sh.setFormatter(formatter) 10 | 11 | return sh 12 | 13 | 14 | # https://stackoverflow.com/questions/7621897/python-logging-module-globally 15 | def setup_custom_logger(name): 16 | formatter = logging.Formatter(fmt="%(asctime)s - %(levelname)s - %(module)s - %(message)s") 17 | 18 | handler = logging.StreamHandler() 19 | handler.setFormatter(formatter) 20 | 21 | logger = logging.getLogger(name) 22 | logger.setLevel(logging.DEBUG) 23 | logger.addHandler(handler) 24 | return logger 25 | 26 | 27 | def setup_md_log(name, md_window, log_level=logging.DEBUG): 28 | logger = logging.getLogger(name) 29 | sh = logging.StreamHandler(md_window) 30 | 31 | formatter = logging.Formatter("%(asctime)s [%(levelname)s]: %(message)s") 32 | sh.setFormatter(formatter) 33 | sh.setLevel(log_level) 34 | logger.addHandler(sh) 35 | 36 | 37 | class StreamToLogger(object): 38 | """ 39 | Fake file-like stream object that redirects writes to a logger instance. 40 | https://stackoverflow.com/questions/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python 41 | """ 42 | 43 | def __init__(self, logger, level): 44 | self.logger = logger 45 | self.level = level 46 | self.linebuf = "" 47 | 48 | def write(self, buf): 49 | for line in buf.rstrip().splitlines(): 50 | self.logger.log(self.level, line.rstrip()) 51 | 52 | def flush(self): 53 | pass 54 | 55 | 56 | def logger(root_name): 57 | def decorator(function): 58 | def wrapper(*args, **kwargs): 59 | wrapper.calls += 1 60 | dt = datetime.datetime.now().strftime("%Y%m%d") 61 | logger = logging.getLogger(f"{root_name}.{function.__name__}.{dt}_{wrapper.calls}") 62 | logger.setLevel(logging.DEBUG) 63 | sys.stderr = StreamToLogger(logger, logging.DEBUG) 64 | wrapper.logger = logger 65 | return function(*args, **kwargs) 66 | 67 | wrapper.calls = 0 68 | return wrapper 69 | 70 | return decorator 71 | -------------------------------------------------------------------------------- /pyhdx/web/paramdoc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Adaptation of Pyviz' nbsite paramdoc: https://github.com/pyviz-dev/nbsite/blob/master/nbsite/paramdoc.py 3 | See Also: https://github.com/Jhsmit/param_docs 4 | 5 | 6 | """ 7 | import inspect 8 | from functools import partial 9 | 10 | try: 11 | from numpydoc.numpydoc import DEDUPLICATION_TAG 12 | except ImportError: 13 | DEDUPLICATION_TAG = "" 14 | 15 | import param 16 | 17 | from param.parameterized import label_formatter 18 | 19 | param.parameterized.docstring_signature = False 20 | param.parameterized.docstring_describe_params = False 21 | 22 | # Parameter attributes which are never shown 23 | IGNORED_ATTRS = [ 24 | "precedence", 25 | "check_on_set", 26 | "instantiate", 27 | "pickle_default_value", 28 | "watchers", 29 | "compute_default_fn", 30 | "doc", 31 | "owner", 32 | "per_instance", 33 | "constant", 34 | "is_instance", 35 | "name", 36 | "allow_None", 37 | "time_fn", 38 | "time_dependent", 39 | "label", 40 | "inclusive_bounds", 41 | ] 42 | 43 | # Default parameter attribute values (value not shown if it matches defaults) 44 | DEFAULT_VALUES = {"allow_None": False, "readonly": False} 45 | 46 | 47 | def print_lines(app, what, name, obj, options, lines): 48 | print(lines) 49 | 50 | 51 | def param_format_basic(app, what, name, obj, options, lines): 52 | # if what == 'module': 53 | # lines = ["start"] 54 | 55 | if what == "class" and isinstance(obj, param.parameterized.ParameterizedMetaclass): 56 | lines += [ 57 | "..", 58 | DEDUPLICATION_TAG, 59 | ] # Prevent numpydoc from mangling the docstring 60 | 61 | try: 62 | lines.insert(0, "") 63 | lines.insert(0, f"**{obj.header}**") 64 | except AttributeError: 65 | pass 66 | 67 | parameters = ["name"] 68 | mro = obj.mro()[::-1] 69 | inherited = [] 70 | for cls in mro[:-1]: 71 | if not issubclass(cls, param.Parameterized) or cls is param.Parameterized: 72 | continue 73 | cls_params = [ 74 | p for p in cls.param if p not in parameters and cls.param[p] == obj.param[p] 75 | ] 76 | if not cls_params: 77 | continue 78 | parameters += cls_params 79 | cname = cls.__name__ 80 | module = cls.__module__ 81 | params = ", ".join([f"**{p}**" for p in cls_params]) 82 | inherited.extend( 83 | [ 84 | "", 85 | " :class:`{module}.{name}`: {params}".format( 86 | name=cname, module=module, params=params 87 | ), 88 | ] 89 | ) 90 | 91 | params = [p for p in obj.param if p not in parameters] 92 | for child in params: 93 | pobj = obj.param[child] 94 | if child in ["print_level", "name"]: 95 | continue 96 | if pobj.precedence and pobj.precedence < 0: 97 | continue 98 | 99 | label = label_formatter(pobj.name) 100 | doc = pobj.doc or "" 101 | members = inspect.getmembers(pobj) 102 | params_str = "" 103 | for m in members: 104 | # This formats default='apple', objects=['apple', 'pear', 'banana'] etc 105 | if ( 106 | m[0][0] != "_" 107 | and m[0] not in IGNORED_ATTRS 108 | and not inspect.ismethod(m[1]) 109 | and not inspect.isfunction(m[1]) 110 | and m[1] is not None 111 | and DEFAULT_VALUES.get(m[0]) != m[1] 112 | and (m[0] != "label" or pobj.label != label) 113 | ): 114 | subtitutions = {"objects": "options"} 115 | key = subtitutions.get(m[0], m[0]) 116 | 117 | params_str += "%s=%s, " % (key, repr(m[1])) 118 | params_str = params_str[:-2] 119 | ptype = pobj.__class__.__name__ 120 | 121 | display_name = pobj.label or " ".join(s.capitalize() for s in child.split("_")) 122 | if params_str.lstrip(): 123 | lines.extend([f"| **{display_name}** (*{ptype}*, {params_str})"]) 124 | else: 125 | lines.extend([f"| **{display_name}** (*{ptype}*)"]) 126 | lines.extend(["| " + doc]) 127 | lines.append("") 128 | 129 | if inherited: 130 | lines.extend(["Additional GUI elements on: "] + inherited) 131 | 132 | lines.append("|") 133 | 134 | 135 | def param_skip(app, what, name, obj, skip, options): 136 | if what == "class" and not skip: 137 | return ( 138 | getattr(obj, "__qualname__", "").startswith("Parameters.deprecate") 139 | or ( 140 | isinstance(obj, partial) 141 | and obj.args 142 | and isinstance(obj.args[0], param.Parameterized) 143 | ) 144 | or ( 145 | getattr(obj, "__qualname__", "").startswith("Parameterized.") 146 | and getattr(obj, "__class__", str).__name__ == "function" 147 | ) 148 | ) 149 | -------------------------------------------------------------------------------- /pyhdx/web/readme/holoviews.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/pyhdx/web/readme/holoviews.png -------------------------------------------------------------------------------- /pyhdx/web/readme/molstar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/pyhdx/web/readme/molstar.png -------------------------------------------------------------------------------- /pyhdx/web/readme/pyhdx_main_application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/pyhdx/web/readme/pyhdx_main_application.png -------------------------------------------------------------------------------- /pyhdx/web/readme/pyhdx_rfu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/pyhdx/web/readme/pyhdx_rfu.png -------------------------------------------------------------------------------- /pyhdx/web/readme/readme.rst: -------------------------------------------------------------------------------- 1 | README 2 | ------ 3 | 4 | This folder contains information on the stucture of the pyhdx web applications 5 | 6 | tables_list: 7 | list of tables -------------------------------------------------------------------------------- /pyhdx/web/readme/tables_list.txt: -------------------------------------------------------------------------------- 1 | 2 | peptides 3 | index: peptide_id 4 | columns: state, exposure, quantity 5 | 6 | 7 | rfu 8 | index: r_number 9 | columns: state, exposure, quantity 10 | cmap opt: rfu_cmap 11 | 12 | d_uptake 13 | index: r_number 14 | columns: D_uptake_fit_ID, state, exposure, quantity 15 | cmap opt: d_uptake_cmap 16 | 17 | rates 18 | index: r_number 19 | columns: guess_ID, state, quantity 20 | 21 | dG 22 | index: r_number 23 | columns: fit_ID, state, quantity 24 | cmap opt: dG_cmap 25 | 26 | ddG 27 | index: r_number 28 | columns: comparison_name, comparison_state, quantity 29 | cmap opt: ddG_cmap 30 | 31 | drfu 32 | index: r_number 33 | columns: comparison_name, comparison_state, exposure, quantity 34 | cmap opt: drfu_cmap 35 | 36 | d_calc 37 | index: exposure 38 | columns: fit_ID, state, peptide_id, quantity 39 | 40 | loss 41 | index: epoch 42 | columns: fit_ID, loss_type 43 | 44 | peptide_mse 45 | index: peptide_id 46 | columns: fit_ID, state, quantity 47 | 48 | d_calc 49 | peptide_mse (has colormap but not user configurable) 50 | -------------------------------------------------------------------------------- /pyhdx/web/serve.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | from pathlib import Path 4 | 5 | import numpy as np 6 | import panel as pn 7 | import torch 8 | from omegaconf import ListConfig 9 | 10 | from pyhdx.config import cfg 11 | from pyhdx.support import select_config 12 | from pyhdx.web.apps import main_app, rfu_app, peptide_app 13 | from pyhdx.web.base import STATIC_DIR 14 | 15 | APP_DICT = { 16 | "main": lambda: main_app()[1], 17 | "rfu": lambda: rfu_app()[1], 18 | "peptide": lambda: peptide_app()[1], 19 | } 20 | 21 | 22 | def run_apps(): 23 | np.random.seed(43) 24 | torch.manual_seed(43) 25 | 26 | # Checking clusters like this interferes with starting the server somehow 27 | # scheduler_address = cfg.cluster.scheduler_address 28 | # if not verify_cluster(scheduler_address): 29 | # print( 30 | # f"No valid Dask scheduler found at specified address: '{scheduler_address}'" 31 | # ) 32 | 33 | log_root_dir = cfg.log_dir 34 | log_dir = log_root_dir / datetime.datetime.now().strftime("%Y%m%d") 35 | log_dir.mkdir(parents=True, exist_ok=True) 36 | root_log = logging.getLogger("pyhdx") 37 | root_log.setLevel(logging.DEBUG) 38 | 39 | fh = logging.FileHandler(log_dir / "pyhdx_logs.txt") 40 | formatter = logging.Formatter( 41 | "%(asctime)s %(name)s [%(levelname)s]: %(message)s", "%Y-%m-%d %H:%M:%S" 42 | ) 43 | fh.setFormatter(formatter) 44 | fh.setLevel(logging.DEBUG) 45 | root_log.addHandler(fh) 46 | root_log.info("Starting PyHDX server") 47 | 48 | tornado_logger = logging.getLogger("tornado.application") 49 | fh = logging.FileHandler(log_dir / "tornado_logs.txt") 50 | formatter = logging.Formatter( 51 | "%(asctime)s %(name)s [%(levelname)s]: %(message)s", "%Y-%m-%d %H:%M:%S" 52 | ) 53 | fh.setFormatter(formatter) 54 | fh.setLevel(10) 55 | tornado_logger.addHandler(fh) 56 | 57 | # TODO Clean assets dir from pdb files 58 | Path(cfg.assets_dir).mkdir(exist_ok=True, parents=True) 59 | 60 | print("Welcome to the PyHDX server!") 61 | ws = cfg.server.get("websocket_origin") 62 | ws = list(ws) if isinstance(ws, ListConfig) else ws 63 | pn.serve( 64 | APP_DICT, 65 | port=cfg.server.get("port", 0), 66 | websocket_origin=ws, 67 | static_dirs={"pyhdx": STATIC_DIR, "assets": str(cfg.assets_dir)}, 68 | index=str(STATIC_DIR / "index.html"), 69 | ) 70 | 71 | 72 | if __name__ == "__main__": 73 | cfg_file = Path().cwd() / "pyhdx.yaml" 74 | if (cfg_file := Path().cwd() / "pyhdx.yaml").exists(): 75 | cfg.load_config(cfg_file) 76 | print("Loading local config file pyhdx.yaml") 77 | else: 78 | select_config() 79 | run_apps() 80 | -------------------------------------------------------------------------------- /pyhdx/web/static/extendedgoldentemplate/default.css: -------------------------------------------------------------------------------- 1 | @import url("https://golden-layout.com/assets/css/goldenlayout-light-theme.css"); 2 | 3 | #header { 4 | background-color: #00aa41; 5 | } 6 | 7 | .bk.bk-input-group { 8 | color: black; 9 | } 10 | 11 | .lm_content { 12 | background-color: #ffffff; 13 | } 14 | 15 | .lm_header .lm_tab { 16 | background-color: #efefef; 17 | padding-top: 3px; 18 | padding-bottom: 4px !important; 19 | height: 30px !important; 20 | line-height: 28px; 21 | font-size: 17px; 22 | } 23 | 24 | .lm_header .lm_tab.lm_active { 25 | padding-top: 0px; 26 | border-top: 3px #52BDEC solid; 27 | background-color: #ffffff; 28 | padding-bottom: 3px !important; 29 | } 30 | 31 | .lm_header { 32 | background-color: white; 33 | } 34 | 35 | /* works but shows up briefly */ 36 | .lm_splitter { 37 | background: #116E8A; 38 | opacity: .001; 39 | transition: opacity 1000ms ease 40 | } 41 | 42 | .lm_splitter:hover { 43 | opacity: 1; 44 | background: #00407A !important; 45 | } -------------------------------------------------------------------------------- /pyhdx/web/static/pyhdx_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jhsmit/PyHDX/5d4c83d6eb0eff25b6dcda70ccb084698ef8bf43/pyhdx/web/static/pyhdx_header.png -------------------------------------------------------------------------------- /pyhdx/web/theme.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | import panel as pn 4 | import param 5 | 6 | from pyhdx.web.template import ExtendedGoldenTemplate 7 | 8 | from panel.template.fast.theme import DEFAULT_STYLE, DARK_STYLE 9 | from bokeh.themes import Theme as BkTheme 10 | 11 | custom_default_json_theme = DEFAULT_STYLE.create_bokeh_theme() 12 | custom_default_json_theme["attrs"]["ColorBar"]["background_fill_alpha"] = 0 13 | custom_dark_json_theme = DARK_STYLE.create_bokeh_theme() 14 | custom_dark_json_theme["attrs"]["ColorBar"]["background_fill_alpha"] = 0 15 | 16 | 17 | class ExtendedGoldenDefaultTheme(pn.template.golden.GoldenDefaultTheme): 18 | css = param.Filename( 19 | default=pathlib.Path(__file__).parent / "static" / "extendedgoldentemplate" / "default.css" 20 | ) 21 | 22 | _template = ExtendedGoldenTemplate 23 | 24 | bokeh_theme = param.ClassSelector( 25 | class_=(BkTheme, str), default=BkTheme(json=custom_default_json_theme) 26 | ) 27 | 28 | 29 | class ExtendedGoldenDarkTheme(pn.template.golden.GoldenDarkTheme): 30 | css = param.Filename( 31 | default=pathlib.Path(__file__).parent / "static" / "extendedgoldentemplate" / "dark.css" 32 | ) 33 | 34 | _template = ExtendedGoldenTemplate 35 | 36 | bokeh_theme = param.ClassSelector( 37 | class_=(BkTheme, str), default=BkTheme(json=custom_dark_json_theme) 38 | ) 39 | -------------------------------------------------------------------------------- /pyhdx/web/tools.py: -------------------------------------------------------------------------------- 1 | from bokeh.models.tools import * # noqa F403 2 | 3 | supported_tools = { 4 | "pan": PanTool, # noqa F405 5 | "wheel_pan": WheelPanTool, # noqa F405 6 | "wheel_zoom": WheelZoomTool, # noqa F405 7 | "zoom_in": ZoomInTool, # noqa F405 8 | "zoom_out": ZoomOutTool, # noqa F405 9 | "tap": TapTool, # noqa F405 10 | "crosshair": CrosshairTool, # noqa F405 11 | "box_select": BoxSelectTool, # noqa F405 12 | "poly_select": PolySelectTool, # noqa F405 13 | "lasso_select": LassoSelectTool, # noqa F405 14 | "box_zoom": BoxZoomTool, # noqa F405 15 | "save": SaveTool, # noqa F405 16 | "undo": UndoTool, # noqa F405 17 | "redo": RedoTool, # noqa F405 18 | "reset": ResetTool, # noqa F405 19 | "help": HelpTool, # noqa F405 20 | "box_edit": BoxEditTool, # noqa F405 21 | "line_edit": LineEditTool, # noqa F405 22 | "point_draw": PointDrawTool, # noqa F405 23 | "poly_draw": PolyDrawTool, # noqa F405 24 | "poly_edit": PolyEditTool, # noqa F405 25 | "freehand_draw": FreehandDrawTool, # noqa F405 26 | "hover": HoverTool, # noqa F405 27 | } 28 | -------------------------------------------------------------------------------- /pyhdx/web/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pathlib import Path 4 | from typing import Optional, Dict, List, TYPE_CHECKING 5 | 6 | import pandas as pd 7 | 8 | from pyhdx.support import multiindex_set_categories, multiindex_astype 9 | 10 | if TYPE_CHECKING: 11 | from pyhdx.web.controllers import PeptideFileInputControl 12 | 13 | # todo merge with batch_processing 14 | time_factors = {"s": 1, "m": 60.0, "min": 60.0, "h": 3600, "d": 86400} 15 | temperature_offsets = {"c": 273.15, "celsius": 273.15, "k": 0, "kelvin": 0} 16 | 17 | 18 | def get_view( 19 | widget, 20 | ): # widget is viewable or widget or custom somthing with view method 21 | """If the widget object has a `view` attribute, return this, otherwise return the widget""" 22 | if hasattr(widget, "view"): 23 | return widget.view 24 | else: 25 | return widget 26 | 27 | 28 | def load_state( 29 | file_input: PeptideFileInputControl, 30 | hdx_spec: Dict, 31 | data_dir: Path, 32 | states: Optional[List[str]] = None, 33 | names: Optional[List[str]] = None, 34 | ) -> None: 35 | """Loader method for `PeptideFileInputControl` from a HDX-MS state specification.""" 36 | 37 | input_files = [data_dir / f_dict["filename"] for f_dict in hdx_spec["data_files"].values()] 38 | file_input.widgets["input_files"].filename = [f.name for f in input_files] 39 | f_bytes = [f.read_bytes() for f in input_files] 40 | file_input.input_files = f_bytes 41 | 42 | # file_input._read_files() # triggered by setting input files 43 | state_spec = hdx_spec["states"] 44 | states = states or list(state_spec.keys()) 45 | names = names or states 46 | 47 | for state, name in zip(states, names): 48 | peptide_spec = state_spec[state]["peptides"] 49 | data_spec = hdx_spec["data_files"] 50 | 51 | file_input.fd_file = data_spec[peptide_spec["FD_control"]["data_file"]]["filename"] 52 | file_input.fd_state = peptide_spec["FD_control"]["state"] 53 | file_input.fd_exposure = ( 54 | peptide_spec["FD_control"]["exposure"]["value"] 55 | * time_factors[peptide_spec["FD_control"]["exposure"]["unit"]] 56 | ) 57 | 58 | if "ND_control" in peptide_spec: 59 | file_input.nd_file = data_spec[peptide_spec["ND_control"]["data_file"]]["filename"] 60 | file_input.nd_state = peptide_spec["ND_control"]["state"] 61 | file_input.nd_exposure = ( 62 | peptide_spec["ND_control"]["exposure"]["value"] 63 | * time_factors[peptide_spec["ND_control"]["exposure"]["unit"]] 64 | ) 65 | 66 | file_input.exp_file = data_spec[peptide_spec["experiment"]["data_file"]]["filename"] 67 | file_input.exp_state = peptide_spec["experiment"]["state"] 68 | 69 | # Set experiment exposures if specified, otherwise leave default selection 70 | try: 71 | exp_vals = peptide_spec["experiment"]["exposure"]["values"] 72 | f = time_factors[peptide_spec["experiment"]["exposure"]["unit"]] 73 | file_input.exp_exposures = [v * f for v in exp_vals] 74 | except KeyError: 75 | pass 76 | 77 | file_input.measurement_name = name 78 | 79 | metadata = state_spec[state]["metadata"] 80 | file_input.pH = metadata["pH"] 81 | 82 | file_input.temperature = ( 83 | metadata["temperature"]["value"] 84 | + temperature_offsets[metadata["temperature"]["unit"].lower()] 85 | ) 86 | 87 | file_input.d_percentage = metadata["d_percentage"] 88 | 89 | if "n_term" in metadata: 90 | file_input.n_term = metadata["n_term"] 91 | if "c_term" in metadata: 92 | file_input.c_term = metadata["c_term"] 93 | if "sequence" in metadata: 94 | file_input.sequence = metadata["sequence"] 95 | 96 | file_input._add_single_dataset_spec() 97 | 98 | 99 | def fix_multiindex_dtypes(index: pd.MultiIndex) -> pd.MultiIndex: 100 | """Assigns correct dtypes to (column) multiindex""" 101 | for level, name in enumerate(index.names): 102 | if name in [ 103 | "state", 104 | "D_uptake_fit_ID", 105 | "guess_ID", 106 | "fit_ID", 107 | "comparison_name", 108 | "comparison_state", 109 | ]: 110 | index = multiindex_astype(index, level, "category") 111 | categories = list(index.unique(level=level)) 112 | index = multiindex_set_categories(index, level, categories, ordered=True) 113 | elif name in ["exposure"]: 114 | index = multiindex_astype(index, level, "float") 115 | 116 | return index 117 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling", "hatch-vcs"] 3 | build-backend = "hatchling.build" 4 | 5 | 6 | [project] 7 | name = "pyhdx" 8 | description = "Derive ΔG for single residues from HDX-MS data" 9 | authors = [{ name = "Jochem Smit", email = "jhsmit@gmail.com" }] 10 | license = "MIT" 11 | readme = "README.md" 12 | classifiers = [ 13 | "Development Status :: 4 - Beta", 14 | "Intended Audience :: Science/Research", 15 | "Natural Language :: English", 16 | "Programming Language :: Python :: 3.9", 17 | ] 18 | requires-python = ">3.9,<=3.11" 19 | dependencies = [ 20 | "torch", 21 | "hdxrate", 22 | "numpy==1.25.2", 23 | "ultraplot", 24 | "matplotlib", 25 | "colorcet", 26 | "pandas", 27 | "scikit-image", 28 | "scipy", 29 | "symfit", 30 | "tqdm", 31 | "typer", 32 | "dask", 33 | "distributed", 34 | "packaging", 35 | "param<2", 36 | "pyyaml", 37 | "omegaconf", 38 | "hdxms-datasets>=0.1.3", 39 | ] 40 | dynamic = ["version"] 41 | 42 | [project.optional-dependencies] 43 | web = [ 44 | "panel==0.14.4", 45 | "bokeh==2.4.3", 46 | "holoviews==1.17.1", 47 | "colorcet", 48 | "hvplot==0.8.4", 49 | "param<2", 50 | ] 51 | pdf = ["pylatex", "ultraplot"] 52 | docs = [ 53 | "mkdocs", 54 | "mkdocstrings[python]", 55 | "mkdocs-material", 56 | "pygments", 57 | "mkdocs-gen-files", 58 | "mkdocs-literate-nav", 59 | "mkdocs-jupyter", 60 | ] 61 | dev = ["black[jupyter]"] 62 | test = ["pytest>=7.2.0"] 63 | 64 | [project.scripts] 65 | pyhdx = 'pyhdx.cli:app' 66 | 67 | [project.urls] 68 | Source = "https://github.com/Jhsmit/PyHDX/" 69 | Documentation = "https://pyhdx.readthedocs.io/en/stable/" 70 | 71 | [tool.hatch.build] 72 | exclude = ["_versioneer.py"] 73 | 74 | [tool.hatch.version] 75 | source = "vcs" 76 | 77 | [tool.hatch.build.hooks.vcs] 78 | version-file = "pyhdx/_version.py" 79 | 80 | [tool.flake8] 81 | max-line-length = 100 82 | ignore = "D203" 83 | exclude = [".git", "__pycache__", "build", "dist", "docs"] 84 | max-complexity = 10 85 | 86 | [tool.black] 87 | line-length = 100 88 | 89 | [tool.ruff] 90 | line-length = 100 91 | target-version = "py310" 92 | exclude = ["docs/examples/04_plot_output.ipynb"] 93 | -------------------------------------------------------------------------------- /pyrightconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": [ 3 | "**/node_modules", 4 | "**/__pycache__", 5 | ".venv*", 6 | 7 | ], 8 | 9 | } -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | mkdocs: 9 | configuration: mkdocs.yml 10 | 11 | # Optionally build your docs in additional formats such as PDF 12 | 13 | formats: 14 | - pdf 15 | 16 | 17 | build: 18 | os: "ubuntu-22.04" 19 | tools: 20 | python: "3.9" 21 | jobs: 22 | post_create_environment: 23 | - pip install .[docs] 24 | 25 | python: 26 | install: 27 | - requirements: requirements/requirements-ubuntu-latest-3.9.txt 28 | -------------------------------------------------------------------------------- /requirements/requirements-macOS-latest-3.9.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.9 3 | # by the following command: 4 | # 5 | # pip-compile --extra=web --output-file=requirements-macOS-latest-3.9.txt pyproject.toml 6 | # 7 | antlr4-python3-runtime==4.9.3 8 | # via omegaconf 9 | bleach==6.0.0 10 | # via panel 11 | bokeh==2.4.3 12 | # via 13 | # hvplot 14 | # panel 15 | # pyhdx (pyproject.toml) 16 | certifi==2024.7.4 17 | # via requests 18 | charset-normalizer==3.2.0 19 | # via requests 20 | click==8.1.7 21 | # via 22 | # dask 23 | # distributed 24 | # typer 25 | cloudpickle==2.2.1 26 | # via 27 | # dask 28 | # distributed 29 | colorcet==3.0.1 30 | # via 31 | # holoviews 32 | # hvplot 33 | # pyhdx (pyproject.toml) 34 | cycler==0.11.0 35 | # via matplotlib 36 | dask==2023.8.1 37 | # via 38 | # distributed 39 | # pyhdx (pyproject.toml) 40 | distributed==2023.8.1 41 | # via pyhdx (pyproject.toml) 42 | filelock==3.12.2 43 | # via torch 44 | fsspec==2023.6.0 45 | # via dask 46 | hdxms-datasets==0.1.3 47 | # via pyhdx (pyproject.toml) 48 | hdxrate==0.2.0 49 | # via pyhdx (pyproject.toml) 50 | holoviews==1.17.1 51 | # via 52 | # hvplot 53 | # pyhdx (pyproject.toml) 54 | hvplot==0.8.4 55 | # via pyhdx (pyproject.toml) 56 | idna==3.7 57 | # via requests 58 | imageio==2.31.1 59 | # via scikit-image 60 | importlib-metadata==6.8.0 61 | # via 62 | # dask 63 | # markdown 64 | jinja2==3.1.5 65 | # via 66 | # bokeh 67 | # distributed 68 | # torch 69 | kiwisolver==1.4.5 70 | # via matplotlib 71 | lazy-loader==0.3 72 | # via scikit-image 73 | locket==1.0.0 74 | # via 75 | # distributed 76 | # partd 77 | markdown==3.4.4 78 | # via panel 79 | markupsafe==2.1.3 80 | # via jinja2 81 | matplotlib==3.4.3 82 | # via 83 | # proplot 84 | # pyhdx (pyproject.toml) 85 | mpmath==1.3.0 86 | # via sympy 87 | msgpack==1.0.5 88 | # via distributed 89 | networkx==3.1 90 | # via 91 | # scikit-image 92 | # torch 93 | numpy==1.25.2 94 | # via 95 | # bokeh 96 | # contourpy 97 | # hdxrate 98 | # holoviews 99 | # hvplot 100 | # imageio 101 | # matplotlib 102 | # pandas 103 | # pyhdx (pyproject.toml) 104 | # pywavelets 105 | # scikit-image 106 | # scipy 107 | # symfit 108 | # tifffile 109 | omegaconf==2.3.0 110 | # via 111 | # hdxms-datasets 112 | # pyhdx (pyproject.toml) 113 | packaging==23.1 114 | # via 115 | # bokeh 116 | # dask 117 | # distributed 118 | # hdxms-datasets 119 | # holoviews 120 | # hvplot 121 | # pyhdx (pyproject.toml) 122 | # scikit-image 123 | pandas==2.0.3 124 | # via 125 | # hdxms-datasets 126 | # holoviews 127 | # hvplot 128 | # pyhdx (pyproject.toml) 129 | panel==0.14.4 130 | # via 131 | # holoviews 132 | # hvplot 133 | # pyhdx (pyproject.toml) 134 | param==1.13.0 135 | # via 136 | # holoviews 137 | # hvplot 138 | # panel 139 | # pyct 140 | # pyhdx (pyproject.toml) 141 | # pyviz-comms 142 | partd==1.4.0 143 | # via dask 144 | pillow==10.3.0 145 | # via 146 | # bokeh 147 | # imageio 148 | # matplotlib 149 | # scikit-image 150 | proplot==0.9.7 151 | # via pyhdx (pyproject.toml) 152 | psutil==5.9.5 153 | # via distributed 154 | pyct==0.5.0 155 | # via 156 | # colorcet 157 | # panel 158 | pyparsing==3.1.1 159 | # via matplotlib 160 | python-dateutil==2.8.2 161 | # via 162 | # matplotlib 163 | # pandas 164 | pytz==2023.3 165 | # via pandas 166 | pyviz-comms==3.0.0 167 | # via 168 | # holoviews 169 | # panel 170 | pywavelets==1.4.1 171 | # via scikit-image 172 | pyyaml==6.0.1 173 | # via 174 | # bokeh 175 | # dask 176 | # distributed 177 | # hdxms-datasets 178 | # omegaconf 179 | # pyhdx (pyproject.toml) 180 | requests==2.32.0 181 | # via 182 | # hdxms-datasets 183 | # panel 184 | scikit-image==0.21.0 185 | # via pyhdx (pyproject.toml) 186 | scipy==1.11.2 187 | # via 188 | # pyhdx (pyproject.toml) 189 | # scikit-image 190 | # symfit 191 | six==1.16.0 192 | # via 193 | # bleach 194 | # python-dateutil 195 | sortedcontainers==2.4.0 196 | # via distributed 197 | symfit==0.5.6 198 | # via pyhdx (pyproject.toml) 199 | sympy==1.12 200 | # via 201 | # symfit 202 | # torch 203 | tblib==2.0.0 204 | # via distributed 205 | tifffile==2023.8.12 206 | # via scikit-image 207 | toolz==0.12.0 208 | # via 209 | # dask 210 | # distributed 211 | # partd 212 | toposort==1.10 213 | # via symfit 214 | torch==2.0.1 215 | # via pyhdx (pyproject.toml) 216 | tornado==6.4.2 217 | # via 218 | # bokeh 219 | # distributed 220 | tqdm==4.66.3 221 | # via 222 | # panel 223 | # pyhdx (pyproject.toml) 224 | typer==0.9.0 225 | # via pyhdx (pyproject.toml) 226 | typing-extensions==4.7.1 227 | # via 228 | # bokeh 229 | # panel 230 | # torch 231 | # typer 232 | tzdata==2023.3 233 | # via pandas 234 | urllib3==2.2.2 235 | # via 236 | # distributed 237 | # requests 238 | webencodings==0.5.1 239 | # via bleach 240 | zict==3.0.0 241 | # via distributed 242 | zipp==3.19.1 243 | # via 244 | # importlib-metadata 245 | # importlib-resources 246 | 247 | # The following packages are considered to be unsafe in a requirements file: 248 | # setuptools 249 | -------------------------------------------------------------------------------- /requirements/requirements-windows-latest-3.9.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.9 3 | # by the following command: 4 | # 5 | # pip-compile --extra=web --output-file=requirements-windows-latest-3.9.txt pyproject.toml 6 | # 7 | antlr4-python3-runtime==4.9.3 8 | # via omegaconf 9 | bleach==6.0.0 10 | # via panel 11 | bokeh==2.4.3 12 | # via 13 | # hvplot 14 | # panel 15 | # pyhdx (pyproject.toml) 16 | certifi==2024.7.4 17 | # via requests 18 | charset-normalizer==3.2.0 19 | # via requests 20 | click==8.1.7 21 | # via 22 | # dask 23 | # distributed 24 | # typer 25 | cloudpickle==2.2.1 26 | # via 27 | # dask 28 | # distributed 29 | colorama==0.4.6 30 | # via 31 | # click 32 | # tqdm 33 | colorcet==3.0.1 34 | # via 35 | # holoviews 36 | # hvplot 37 | # pyhdx (pyproject.toml) 38 | cycler==0.11.0 39 | # via matplotlib 40 | dask==2023.8.1 41 | # via 42 | # distributed 43 | # pyhdx (pyproject.toml) 44 | distributed==2023.8.1 45 | # via pyhdx (pyproject.toml) 46 | filelock==3.12.2 47 | # via torch 48 | fsspec==2023.6.0 49 | # via dask 50 | hdxms-datasets==0.1.3 51 | # via pyhdx (pyproject.toml) 52 | hdxrate==0.2.0 53 | # via pyhdx (pyproject.toml) 54 | holoviews==1.17.1 55 | # via 56 | # hvplot 57 | # pyhdx (pyproject.toml) 58 | hvplot==0.8.4 59 | # via pyhdx (pyproject.toml) 60 | idna==3.7 61 | # via requests 62 | imageio==2.31.1 63 | # via scikit-image 64 | importlib-metadata==6.8.0 65 | # via 66 | # dask 67 | # markdown 68 | jinja2==3.1.5 69 | # via 70 | # bokeh 71 | # distributed 72 | # torch 73 | kiwisolver==1.4.5 74 | # via matplotlib 75 | lazy-loader==0.3 76 | # via scikit-image 77 | locket==1.0.0 78 | # via 79 | # distributed 80 | # partd 81 | markdown==3.4.4 82 | # via panel 83 | markupsafe==2.1.3 84 | # via jinja2 85 | matplotlib==3.4.3 86 | # via 87 | # proplot 88 | # pyhdx (pyproject.toml) 89 | mpmath==1.3.0 90 | # via sympy 91 | msgpack==1.0.5 92 | # via distributed 93 | networkx==3.1 94 | # via 95 | # scikit-image 96 | # torch 97 | numpy==1.25.2 98 | # via 99 | # bokeh 100 | # contourpy 101 | # hdxrate 102 | # holoviews 103 | # hvplot 104 | # imageio 105 | # matplotlib 106 | # pandas 107 | # pyhdx (pyproject.toml) 108 | # pywavelets 109 | # scikit-image 110 | # scipy 111 | # symfit 112 | # tifffile 113 | omegaconf==2.3.0 114 | # via 115 | # hdxms-datasets 116 | # pyhdx (pyproject.toml) 117 | packaging==23.1 118 | # via 119 | # bokeh 120 | # dask 121 | # distributed 122 | # hdxms-datasets 123 | # holoviews 124 | # hvplot 125 | # pyhdx (pyproject.toml) 126 | # scikit-image 127 | pandas==2.0.3 128 | # via 129 | # hdxms-datasets 130 | # holoviews 131 | # hvplot 132 | # pyhdx (pyproject.toml) 133 | panel==0.14.4 134 | # via 135 | # holoviews 136 | # hvplot 137 | # pyhdx (pyproject.toml) 138 | param==1.13.0 139 | # via 140 | # holoviews 141 | # hvplot 142 | # panel 143 | # pyct 144 | # pyhdx (pyproject.toml) 145 | # pyviz-comms 146 | partd==1.4.0 147 | # via dask 148 | pillow==10.3.0 149 | # via 150 | # bokeh 151 | # imageio 152 | # matplotlib 153 | # scikit-image 154 | proplot==0.9.7 155 | # via pyhdx (pyproject.toml) 156 | psutil==5.9.5 157 | # via distributed 158 | pyct==0.5.0 159 | # via 160 | # colorcet 161 | # panel 162 | pyparsing==3.1.1 163 | # via matplotlib 164 | python-dateutil==2.8.2 165 | # via 166 | # matplotlib 167 | # pandas 168 | pytz==2023.3 169 | # via pandas 170 | pyviz-comms==3.0.0 171 | # via 172 | # holoviews 173 | # panel 174 | pywavelets==1.4.1 175 | # via scikit-image 176 | pyyaml==6.0.1 177 | # via 178 | # bokeh 179 | # dask 180 | # distributed 181 | # hdxms-datasets 182 | # omegaconf 183 | # pyhdx (pyproject.toml) 184 | requests==2.32.0 185 | # via 186 | # hdxms-datasets 187 | # panel 188 | scikit-image==0.21.0 189 | # via pyhdx (pyproject.toml) 190 | scipy==1.11.2 191 | # via 192 | # pyhdx (pyproject.toml) 193 | # scikit-image 194 | # symfit 195 | six==1.16.0 196 | # via 197 | # bleach 198 | # python-dateutil 199 | sortedcontainers==2.4.0 200 | # via distributed 201 | symfit==0.5.6 202 | # via pyhdx (pyproject.toml) 203 | sympy==1.12 204 | # via 205 | # symfit 206 | # torch 207 | tblib==2.0.0 208 | # via distributed 209 | tifffile==2023.8.12 210 | # via scikit-image 211 | toolz==0.12.0 212 | # via 213 | # dask 214 | # distributed 215 | # partd 216 | toposort==1.10 217 | # via symfit 218 | torch==2.0.1 219 | # via pyhdx (pyproject.toml) 220 | tornado==6.4.2 221 | # via 222 | # bokeh 223 | # distributed 224 | tqdm==4.66.3 225 | # via 226 | # panel 227 | # pyhdx (pyproject.toml) 228 | typer==0.9.0 229 | # via pyhdx (pyproject.toml) 230 | typing-extensions==4.7.1 231 | # via 232 | # bokeh 233 | # panel 234 | # torch 235 | # typer 236 | tzdata==2023.3 237 | # via pandas 238 | urllib3==2.2.2 239 | # via 240 | # distributed 241 | # requests 242 | webencodings==0.5.1 243 | # via bleach 244 | zict==3.0.0 245 | # via distributed 246 | zipp==3.19.1 247 | # via 248 | # importlib-metadata 249 | # importlib-resources 250 | 251 | # The following packages are considered to be unsafe in a requirements file: 252 | # setuptools 253 | -------------------------------------------------------------------------------- /templates/01_load_secb_data.py: -------------------------------------------------------------------------------- 1 | """Load a HDX-MS dataset (peptide list with D-uptake per peptide, csv format)""" 2 | # %% 3 | from pathlib import Path 4 | 5 | import numpy as np 6 | 7 | from pyhdx.datasets import filter_peptides 8 | from pyhdx.fileIO import read_dynamx 9 | from pyhdx.models import HDXMeasurement 10 | from pyhdx.process import apply_control, correct_d_uptake 11 | 12 | # %% 13 | 14 | current_dir = Path(__file__).parent 15 | output_dir = current_dir / "output" 16 | output_dir.mkdir(exist_ok=True) 17 | np.random.seed(43) 18 | 19 | # %% 20 | 21 | fpath = current_dir.parent / "tests" / "test_data" / "input" / "ecSecB_apo.csv" 22 | 23 | # Load the full .csv file 24 | df = read_dynamx(fpath) 25 | 26 | fd = {"state": "Full deuteration control", "exposure": {"value": 0.167, "unit": "min"}} 27 | 28 | # Filter out peptides for the full deuteration control 29 | fd_df = filter_peptides(df, **fd) 30 | 31 | # filter out peptides for the experiment with state "SecB WT apo" 32 | peptides = filter_peptides(df, state="SecB WT apo") 33 | 34 | # Apply FD control, returns only peptides in both FD control and experiment 35 | peptides_control = apply_control(peptides, fd_df) 36 | 37 | # Correct for N-terminal back exchanging residues and deuterium percentage of the buffer 38 | peptides_corrected = correct_d_uptake(peptides_control, drop_first=1, d_percentage=90.0) 39 | 40 | sequence = "MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPYARECITSMVSRGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA" 41 | temperature, pH = 273.15 + 30, 8.0 42 | 43 | # %% 44 | 45 | # Create HDX Measurement object with addtional experimental metadata (sequence, pH, temperature) 46 | hdxm = HDXMeasurement(peptides_corrected, sequence=sequence, pH=pH, temperature=temperature) 47 | -------------------------------------------------------------------------------- /templates/01a_load_ppix_data.py: -------------------------------------------------------------------------------- 1 | """Load a HDX-MS dataset with both a FD and ND control""" 2 | 3 | from pathlib import Path 4 | 5 | import numpy as np 6 | 7 | from pyhdx import read_dynamx 8 | from pyhdx.process import apply_control, filter_peptides 9 | 10 | # %% 11 | 12 | root_dir = Path(__file__).parent.parent 13 | np.random.seed(43) 14 | 15 | fpath = root_dir / "tests" / "test_data" / "input" / "PpiA_folding.csv" 16 | 17 | # Load the full .csv file 18 | df = read_dynamx(fpath) 19 | 20 | # %% 21 | 22 | fd_spec = {"state": "Native", "exposure": {"value": 86400, "unit": "s"}} 23 | nd_spec = {"state": "FD", "exposure": {"value": 0.6, "unit": "s"}} 24 | 25 | # Filter out peptides for the full deuteration control 26 | fd_df = filter_peptides(df, **fd_spec) 27 | 28 | # Filter out peptides for the non-deuterated control 29 | nd_df = filter_peptides(df, **nd_spec) 30 | 31 | # Filter out peptides for the experiment with state "Folding" 32 | peptides = filter_peptides(df, state="Folding") 33 | 34 | # %% 35 | # Apply the FD and ND controls. This adds the columns 'rfu' and 'rfu_sd' (Relative Fractional Uptake) 36 | # as well as 'fd_uptake' and 'nd_uptake', and their standard deviations. 37 | peptides_control = apply_control(peptides, fd_control=fd_df, nd_control=nd_df) 38 | peptides_control.columns 39 | 40 | # %% 41 | -------------------------------------------------------------------------------- /templates/02_load_from_yaml.py: -------------------------------------------------------------------------------- 1 | """Load a HDX-MS dataset from a .yaml HDX specification file""" 2 | # %% 3 | from pathlib import Path 4 | import yaml 5 | 6 | from pyhdx.datasets import HDXDataSet 7 | 8 | from pyhdx import HDXMeasurement, HDXMeasurementSet 9 | 10 | # %% 11 | 12 | current_dir = Path(__file__).parent 13 | output_dir = current_dir / "output" 14 | output_dir.mkdir(exist_ok=True) 15 | 16 | yaml_stream = Path(current_dir / "yaml_files" / "SecB.yaml").read_text() 17 | hdx_spec = yaml.safe_load(yaml_stream) 18 | input_dir = current_dir.parent / "tests" / "test_data" / "input" 19 | 20 | # %% 21 | 22 | dataset = HDXDataSet.from_spec( 23 | hdx_spec, 24 | data_dir=input_dir, 25 | ) 26 | 27 | print(dataset.describe()) 28 | 29 | # %% 30 | # Load an HDX measurement by state name 31 | hdxm = HDXMeasurement.from_dataset(dataset, state="SecB_tetramer") 32 | print(hdxm) 33 | print(hdxm.timepoints) 34 | 35 | # # Load an HDX measurement by index of states in the spec 36 | 37 | hdxm = HDXMeasurement.from_dataset(dataset, state=1) 38 | print(str(hdxm)) 39 | print(hdxm.timepoints) 40 | 41 | 42 | # %% 43 | hdxm_set = HDXMeasurementSet.from_dataset(dataset) 44 | print(hdxm_set) 45 | -------------------------------------------------------------------------------- /templates/03_initial_guesses.py: -------------------------------------------------------------------------------- 1 | """Load HDX-MS data from yaml spec and perform initial guess of exchange rates""" 2 | from pyhdx import HDXMeasurement 3 | from pyhdx.datasets import HDXDataSet 4 | from pathlib import Path 5 | from pyhdx.fitting import fit_rates_weighted_average 6 | import yaml 7 | from pyhdx.local_cluster import default_client 8 | from pyhdx.fileIO import dataframe_to_file 9 | 10 | current_dir = Path(__file__).parent 11 | output_dir = current_dir / "guesses" 12 | output_dir.mkdir(exist_ok=True) 13 | data_dir = current_dir.parent / "tests" / "test_data" / "input" 14 | yaml_stream = Path(current_dir / "yaml_files" / "SecB.yaml").read_text() 15 | hdx_spec = yaml.safe_load(yaml_stream) 16 | 17 | dataset = HDXDataSet.from_spec( 18 | hdx_spec, 19 | data_dir=data_dir, 20 | ) 21 | 22 | # Requires local_cluster.py to be running (or other Dask client on default address in config) 23 | client = default_client() 24 | 25 | for name in dataset.states: 26 | print(name) 27 | hdxm = HDXMeasurement.from_dataset(dataset, state=name) 28 | 29 | # Save sequence info + intrinsic rates 30 | dataframe_to_file( 31 | output_dir / f"{name}_intrinsic_rates.csv", hdxm.coverage.protein, fmt="pprint" 32 | ) 33 | 34 | fr = fit_rates_weighted_average(hdxm, client=client) 35 | dataframe_to_file(output_dir / f"{name}_rates_guess.csv", fr.output) 36 | -------------------------------------------------------------------------------- /templates/04_SecB_fit_d_uptake.py: -------------------------------------------------------------------------------- 1 | # %% 2 | 3 | from pathlib import Path 4 | import yaml 5 | 6 | from pyhdx import HDXMeasurementSet 7 | from pyhdx.datasets import HDXDataSet 8 | from pyhdx.fitting import fit_d_uptake, DUptakeFitResultSet 9 | 10 | 11 | # %% 12 | 13 | current_dir = Path(__file__).parent 14 | output_dir = current_dir / "output" 15 | output_dir.mkdir(exist_ok=True) 16 | yaml_stream = Path(current_dir / "yaml_files" / "SecB.yaml").read_text() 17 | hdx_spec = yaml.safe_load(yaml_stream) 18 | 19 | # %% 20 | # Modify the HDX spec to Limit the data to < 40. seconds exposure 21 | for state, state_spec in hdx_spec["states"].items(): 22 | peptide_spec = state_spec["peptides"] 23 | peptide_spec["experiment"]["query"] = ["exposure < 40."] 24 | 25 | input_dir = current_dir.parent / "tests" / "test_data" / "input" 26 | dataset = HDXDataSet.from_spec(hdx_spec, data_dir=input_dir) 27 | 28 | # load all hdx measurements, fit individually the D-uptake and then combine into one fit result 29 | hdxm_set = HDXMeasurementSet.from_dataset(dataset) 30 | results = [] 31 | for hdxm in hdxm_set: 32 | print(hdxm) 33 | 34 | result = fit_d_uptake(hdxm, repeats=2) 35 | results.append(result) 36 | 37 | results 38 | 39 | # %% 40 | 41 | fr = DUptakeFitResultSet(results) 42 | fr.output 43 | -------------------------------------------------------------------------------- /templates/05_SecB_fit_dG.py: -------------------------------------------------------------------------------- 1 | """Load SecB HDX-MS data and guesses, perform global fit of gibbs free energy, save results to directory""" 2 | 3 | # %% 4 | from pathlib import Path 5 | 6 | import numpy as np 7 | import ultraplot as uplt 8 | import yaml 9 | 10 | from pyhdx.datasets import DataSet as HDXDataSet 11 | from pyhdx.fileIO import csv_to_dataframe, save_fitresult 12 | from pyhdx.fitting import fit_gibbs_global, fit_rates_weighted_average 13 | from pyhdx.local_cluster import default_client 14 | from pyhdx.models import HDXMeasurement 15 | 16 | # %% 17 | 18 | guess = False # Set to True to redo initial guesses 19 | fit_kwargs = {"epochs": 200000, "lr": 1e4, "stop_loss": 1e-6} 20 | 21 | # %% 22 | current_dir = Path(__file__).parent 23 | output_dir = current_dir / "output" 24 | output_dir.mkdir(exist_ok=True) 25 | yaml_stream = Path(current_dir / "yaml_files" / "SecB.yaml").read_text() 26 | hdx_spec = yaml.safe_load(yaml_stream) 27 | 28 | # %% 29 | 30 | input_dir = current_dir.parent / "tests" / "test_data" / "input" 31 | dataset = HDXDataSet.from_spec(hdx_spec, data_dir=input_dir) 32 | 33 | # %% 34 | hdxm = HDXMeasurement.from_dataset(dataset, state="SecB_tetramer") 35 | print(hdxm.timepoints) 36 | 37 | # %% 38 | 39 | if guess: 40 | client = default_client() 41 | wt_avg_result = fit_rates_weighted_average(hdxm, client=client) 42 | init_guess = wt_avg_result.output 43 | else: 44 | init_guess = csv_to_dataframe( 45 | current_dir.parent / "tests" / "test_data" / "output" / "ecSecB_guess.csv" 46 | ) 47 | 48 | gibbs_guess = hdxm.guess_deltaG(init_guess["rate"]) 49 | 50 | # %% 51 | 52 | fr_torch = fit_gibbs_global(hdxm, gibbs_guess, **fit_kwargs) 53 | 54 | # Human readable output 55 | fr_torch.to_file(output_dir / "SecB_fit_result.txt", fmt="pprint") 56 | 57 | # Machine readable output 58 | fr_torch.to_file(output_dir / "SecB_fit_result.csv", fmt="csv") 59 | 60 | save_fitresult(output_dir / "SecB_fit", fr_torch) 61 | 62 | # %% 63 | # evaluate the fit, plot measured and fitted D-uptake for a few peptides 64 | NUM_EVAL_POINTS = 100 65 | all_timepoints = np.concatenate([hdxm.timepoints for hdxm in fr_torch.hdxm_set]) 66 | 67 | elem = all_timepoints[np.nonzero(all_timepoints)] 68 | start = np.log10(elem.min()) 69 | end = np.log10(elem.max()) 70 | pad = (end - start) * 0.1 71 | time = np.logspace(start - pad, end + pad, num=NUM_EVAL_POINTS, endpoint=True) 72 | 73 | # %% 74 | # evaluate the fitted model at timepoints 75 | d_calc = fr_torch(time) # Ns x Np x Nt 76 | d_calc 77 | # %% 78 | # choose which sample to plot 79 | # here we plot the first one (= SecB tetramer) 80 | sample_idx = 0 81 | hdxm = fr_torch.hdxm_set.hdxm_list[sample_idx] 82 | d_calc_s = d_calc[sample_idx] 83 | 84 | # %% 85 | # make a subplot grid to plot the first 24 peptides 86 | fig, axes = uplt.subplots(ncols=4, nrows=6) 87 | for i, ax in enumerate(axes): 88 | ax.plot(time, d_calc_s[i], color="r") 89 | ax.scatter(hdxm.timepoints, hdxm.d_exp.iloc[i], color="k") 90 | 91 | axes.format(xscale="log", xlabel="Time (s)", ylabel="Corrected D-uptake", ylim=(0, None)) 92 | # %% 93 | -------------------------------------------------------------------------------- /templates/07_SecB_aligned_fit.py: -------------------------------------------------------------------------------- 1 | """Load two HDX-MS datasets and guesses and perform fitting in batch with a second regualizer and mock alignment 2 | 3 | OUTDATED EXAMPLE 4 | """ 5 | 6 | 7 | from pathlib import Path 8 | 9 | import yaml 10 | 11 | from pyhdx.batch_processing import StateParser 12 | from pyhdx.fileIO import csv_to_protein 13 | from pyhdx.fitting import fit_gibbs_global_batch_aligned 14 | 15 | mock_alignment = { 16 | "dimer": "MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVY--------------EVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGA----YCPNILFPAARECIASMVARGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA-----------------", 17 | "apo": "MSEQNNTEMTFQIQRIYTKDI------------SFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLG-------------------EETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPYARECITSMVSRG----TFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA", 18 | } 19 | 20 | current_dir = Path(__file__).parent 21 | output_dir = current_dir / "output" 22 | output_dir.mkdir(exist_ok=True) 23 | yaml_stream = Path(current_dir / "yaml_files" / "SecB.yaml").read_text() 24 | hdx_spec = yaml.safe_load(yaml_stream) 25 | 26 | 27 | test_data_dir = current_dir.parent / "tests" / "test_data" 28 | guess = csv_to_protein(test_data_dir / "output" / "ecSecB_guess.csv") 29 | 30 | input_dir = test_data_dir / "input" 31 | parser = StateParser(hdx_spec, input_dir) 32 | 33 | 34 | hdx_set = hdxm = parser.load_hdxmset() 35 | gibbs_guess = hdx_set[0].guess_deltaG(guess["rate"]) 36 | 37 | hdx_set.add_alignment(list(mock_alignment.values())) 38 | result = fit_gibbs_global_batch_aligned(hdx_set, gibbs_guess, r1=2, r2=5, epochs=1000) 39 | 40 | # Human readable output 41 | result.to_file(output_dir / "Batch_aligned_fit_result.txt", fmt="pprint") 42 | 43 | # Machine readable output 44 | result.to_file(output_dir / "Batch_aligned_fit_result.csv", fmt="csv") 45 | -------------------------------------------------------------------------------- /templates/08_load_fitresult.py: -------------------------------------------------------------------------------- 1 | """Load a fit result from a directory where a fit result was saved with save_fitresult""" 2 | from pyhdx.fileIO import load_fitresult 3 | from pathlib import Path 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | 7 | # %% 8 | 9 | current_dir = Path(__file__).parent 10 | print(current_dir.parent / "tests" / "test_data" / "output" / "ecsecb_tetramer_dimer") 11 | 12 | fit_result = load_fitresult( 13 | current_dir.parent / "tests" / "test_data" / "output" / "ecsecb_tetramer_dimer" 14 | ) 15 | 16 | # %% 17 | 18 | mse_df = fit_result.get_peptide_mse() 19 | 20 | 21 | s = 0 # index of protein state to view 22 | p = 20 # index of the peptide to view 23 | 24 | timepoints = fit_result.hdxm_set[s].timepoints 25 | timepoints = timepoints[np.nonzero(timepoints)] 26 | tmin = np.log10(timepoints.min()) 27 | tmax = np.log10(timepoints.max()) 28 | 29 | pad = 0.1 * (tmax - tmin) 30 | 31 | time = np.logspace(tmin - pad, tmax + pad, num=100) 32 | 33 | d_calc = fit_result(time) 34 | d_exp = fit_result.hdxm_set.d_exp 35 | 36 | fit_result.losses[["mse_loss", "reg_1"]].plot() 37 | 38 | fig, ax = plt.subplots() 39 | ax.scatter(fit_result.hdxm_set[s].timepoints, d_exp[s, p, :], color="k") 40 | ax.plot(time, d_calc[s, p, :], color="r") 41 | ax.set_xscale("log") 42 | ax.set_xlabel("Time (s)") 43 | ax.set_ylabel("Corrected D-uptake (Da)") 44 | ax.set_title(f"Peptide index: {p}") 45 | 46 | output = fit_result.output 47 | protein_name = output.columns.unique(level=0)[0] 48 | 49 | fig, ax = plt.subplots() 50 | ax.set_aspect(2) 51 | ax.scatter(fit_result.output.index, fit_result.output[protein_name]["dG"] * 1e-3) 52 | ax.set_xlabel("Residue number") 53 | ax.set_ylabel("ΔG (kJ/mol)") 54 | plt.show() 55 | -------------------------------------------------------------------------------- /templates/09_figure_output.py: -------------------------------------------------------------------------------- 1 | # %% 2 | from pathlib import Path 3 | 4 | import pandas as pd 5 | import ultraplot as uplt 6 | from pymol import cmd 7 | 8 | from pyhdx.config import cfg 9 | from pyhdx.fileIO import csv_to_dataframe 10 | from pyhdx.plot import ( 11 | CMAP_NORM_DEFAULTS, 12 | ddG_scatter_figure, 13 | dG_scatter_figure, 14 | linear_bars_figure, 15 | ) 16 | from pyhdx.support import apply_cmap, color_pymol 17 | 18 | # %% 19 | cwd = Path(__file__).parent 20 | root_dir = cwd.parent 21 | web_data_dir = root_dir / "tests" / "test_data" / "output" / "web" 22 | 23 | 24 | # %% 25 | dG_df = csv_to_dataframe(web_data_dir / "dG.csv") 26 | plot_data = dG_df.xs("Gibbs_fit_1", axis=1, level=0) 27 | 28 | # %% 29 | 30 | width = cfg.plotting.page_width 31 | aspect = cfg.plotting.dG_aspect 32 | figure_kwargs = { 33 | "width": width, 34 | "refaspect": aspect, 35 | } 36 | 37 | figure_kwargs["ncols"] = 2 38 | 39 | # %% 40 | protein_states = plot_data.columns.get_level_values(0).unique() 41 | protein_states 42 | 43 | # %% 44 | 45 | fig, axes, cbars = dG_scatter_figure(plot_data) # , **figure_kwargs) 46 | uplt.show() 47 | 48 | # %% 49 | # %% 50 | linear_bars_figure(dG_df, groupby="fit_ID") 51 | uplt.show() 52 | 53 | # %% 54 | 55 | linear_bars_figure(dG_df, groupby="fit_ID", reference="SecB_tetramer") 56 | uplt.show() 57 | 58 | # %% 59 | protein_states = plot_data.columns.get_level_values(0).unique() 60 | ddG_scatter_figure(plot_data, reference=protein_states[0]) 61 | 62 | # %% 63 | 64 | # Creating a colored structure 65 | cmd.load("https://files.rcsb.org/download/1QYN.pdb") 66 | cmd.set("antialias", 2) 67 | cmd.set("fog", 0) 68 | 69 | # %% 70 | cmd.remove("resn HOH") # This removes only water molecules 71 | cmd.ipython_image() 72 | # %% 73 | # take dG values for SecB tetramer and reindex (pad with nan) 74 | # such that these regions are correctly colored as no coverage 75 | dG_values = plot_data[("SecB_tetramer", "dG")].reindex(pd.RangeIndex(1, 160)) 76 | 77 | # %% 78 | # create a pandas Series with hexadeicmal codes 79 | cmap, norm = CMAP_NORM_DEFAULTS["dG"] 80 | colors = apply_cmap(dG_values, cmap, norm) 81 | colors 82 | 83 | # %% 84 | # apply the colors to the pymol structure 85 | color_pymol(colors, cmd) 86 | cmd.ipython_image() 87 | 88 | # %% 89 | 90 | # save the output 91 | cmd.png( 92 | "SecB_dG_render.png", 93 | width="10cm", 94 | dpi=300, 95 | ray=1, 96 | ) 97 | 98 | 99 | # %% 100 | # rotate for a different view 101 | cmd.rotate("y", 90) 102 | cmd.ipython_image() 103 | 104 | # %% 105 | -------------------------------------------------------------------------------- /templates/archive/README.md: -------------------------------------------------------------------------------- 1 | Archive 2 | ======= 3 | 4 | 5 | This directory has old scripts which need to be updated and likely do not run out-of-the-box anymore. -------------------------------------------------------------------------------- /templates/archive/SecB_batch_fit_and_checkpoint.py: -------------------------------------------------------------------------------- 1 | """Load two HDX-MS datasets and guesses and perform fitting in batch with a second regualizer 2 | 3 | Example of using checkpoints to save model history 4 | 5 | """ 6 | # %% 7 | 8 | from pathlib import Path 9 | 10 | import matplotlib as mpl 11 | import matplotlib.pyplot as plt 12 | import numpy as np 13 | import pandas as pd 14 | 15 | from pyhdx.fileIO import ( 16 | csv_to_protein, 17 | read_dynamx, 18 | dataframe_to_file, 19 | save_fitresult, 20 | ) 21 | from pyhdx.fitting import fit_gibbs_global_batch 22 | from pyhdx.fitting_torch import CheckPoint 23 | from pyhdx.models import PeptideMasterTable, HDXMeasurement, HDXMeasurementSet 24 | 25 | 26 | # %% 27 | 28 | # Pycharm scientific mode 29 | if "__file__" not in locals(): 30 | __file__ = Path().cwd() / "templates" / "script.py" 31 | 32 | current_dir = Path(__file__).parent 33 | output_dir = current_dir / "output" 34 | output_dir.mkdir(exist_ok=True) 35 | data_dir = current_dir.parent / "tests" / "test_data" 36 | 37 | # %% 38 | 39 | data = read_dynamx(data_dir / "input" / "ecSecB_apo.csv", data_dir / "input" / "ecSecB_dimer.csv") 40 | pmt = PeptideMasterTable(data) 41 | pmt.set_control(("Full deuteration control", 0.167 * 60)) 42 | 43 | dimer = HDXMeasurement(pmt.get_state("SecB his dimer apo"), pH=8, temperature=273.15 + 30) 44 | apo = HDXMeasurement(pmt.get_state("SecB WT apo"), pH=8, temperature=273.15 + 30) 45 | 46 | hdx_set = HDXMeasurementSet([dimer, apo]) 47 | guess = csv_to_protein(data_dir / "output" / "ecSecB_guess.csv") 48 | 49 | rates_df = pd.DataFrame({name: guess["rate"] for name in hdx_set.names}) 50 | gibbs_guess = hdx_set.guess_deltaG(rates_df) 51 | 52 | 53 | # %% 54 | # Example fit with only 5000 epochs and high learning rate 55 | # Checkpoint stores model history every `epoch_step` epochs 56 | checkpoint = CheckPoint(epoch_step=250) 57 | result = fit_gibbs_global_batch( 58 | hdx_set, gibbs_guess, r1=0.5, r2=0.1, epochs=5000, lr=1e5, callbacks=[checkpoint] 59 | ) 60 | print( 61 | f"MSE loss: {result.mse_loss:.2f}, " 62 | f"Reg loss: {result.reg_loss:.2f}, " 63 | f"Reg percent: {result.regularization_percentage:.0f}%" 64 | ) 65 | 66 | 67 | df = checkpoint.to_dataframe(hdx_set.names) 68 | dataframe_to_file(output_dir / "model_history.csv", df) 69 | dataframe_to_file(output_dir / "model_history.txt", df, fmt="pprint") 70 | 71 | 72 | # Checkpoint history scatter plot 73 | # Note that these are raw dG values including interpolated values in regions of no coverage 74 | history = checkpoint.model_history 75 | num = len(history) 76 | cmap = mpl.cm.get_cmap("winter") 77 | norm = mpl.colors.Normalize(vmin=1, vmax=num * checkpoint.epoch_step) 78 | colors = iter(cmap(np.linspace(0, 1, num=num))) 79 | 80 | fig, ax = plt.subplots() 81 | for key, val in history.items(): 82 | n = len(val["dG"].numpy().squeeze()) 83 | ax.scatter(hdx_set.coverage.index, val["dG"].numpy().squeeze()[0], color=next(colors)) 84 | 85 | fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap, norm=norm), label="Epochs") 86 | plt.show() 87 | 88 | # Human readable output 89 | result.to_file(output_dir / "Batch_fit_result.txt", fmt="pprint") 90 | 91 | # Machine readable output 92 | result.to_file(output_dir / "Batch_fit_result.csv", fmt="csv") 93 | 94 | # Save full fitresult 95 | save_fitresult(output_dir / "SecB_tetramer_dimer_batch", result) 96 | -------------------------------------------------------------------------------- /templates/archive/fit_report_pdf.py: -------------------------------------------------------------------------------- 1 | """Generate a pdf output with all peptide fits. Requires pdflatex""" 2 | from pyhdx.output import FitReport 3 | from pyhdx.fileIO import load_fitresult 4 | from pathlib import Path 5 | from concurrent import futures 6 | import proplot as pplt 7 | 8 | 9 | current_dir = Path().cwd() 10 | fit_result = load_fitresult(current_dir / "output" / "SecB_tetramer_dimer_batch") 11 | 12 | tmp_dir = Path(__file__).parent / "temp" 13 | tmp_dir.mkdir(exist_ok=True) 14 | 15 | if __name__ == "__main__": 16 | report = FitReport(fit_result, temp_dir=tmp_dir) 17 | report.add_standard_figure("peptide_coverage_figure") 18 | report.add_standard_figure("residue_time_scatter_figure") 19 | report.add_standard_figure("residue_scatter_figure") 20 | report.add_standard_figure("dG_scatter_figure", ncols=1, aspect=3) 21 | report.add_standard_figure("ddG_scatter_figure", ncols=1, reference=0) 22 | report.add_standard_figure( 23 | "linear_bars", cmap="viridis", norm=pplt.Norm("linear", 15e3, 35e3) 24 | ) # todo name from kwargs 25 | report.add_standard_figure("rainbowclouds") 26 | 27 | report.add_peptide_uptake_curves() 28 | 29 | executor = futures.ProcessPoolExecutor(max_workers=10) 30 | report.generate_figures(executor=executor) 31 | 32 | report.generate_latex() 33 | report.generate_pdf(current_dir / "output" / "fit_report") 34 | -------------------------------------------------------------------------------- /templates/archive/fitting_with_logs.py: -------------------------------------------------------------------------------- 1 | """Perform fitting with a range of regularizers""" 2 | from pyhdx.batch_processing import StateParser 3 | from pathlib import Path 4 | from pyhdx.fitting import fit_gibbs_global_batch 5 | import yaml 6 | import time 7 | from datetime import datetime 8 | from pyhdx.fileIO import csv_to_protein 9 | from pyhdx import VERSION_STRING 10 | 11 | current_dir = Path(__file__).parent 12 | test_data_dir = current_dir.parent / "tests" / "test_data" 13 | input_dir = test_data_dir / "input" 14 | yaml_stream = Path(current_dir / "yaml_files" / "SecB.yaml").read_text() 15 | data_dict = yaml.safe_load(yaml_stream) 16 | 17 | output_dir = current_dir / "fit" 18 | output_dir.mkdir(exist_ok=True) 19 | 20 | parser = StateParser(data_dict, data_src=input_dir) 21 | hdx_set = parser.load_hdxmset() 22 | 23 | rates_list = [ 24 | csv_to_protein(current_dir / "guesses" / f"{name}_rates_guess.csv")["rate"] 25 | for name in data_dict.keys() 26 | ] 27 | gibbs_guess = hdx_set.guess_deltaG(rates_list) 28 | 29 | log_file = output_dir / "fitting_log.txt" 30 | now = datetime.now() 31 | date = f'# {now.strftime("%Y/%m/%d %H:%M:%S")} ({int(now.timestamp())})' 32 | 33 | lines = [VERSION_STRING, date] 34 | 35 | r2 = 0.5 36 | for r1 in [0, 0.01, 0.25, 0.5, 1]: 37 | t0 = time.time() 38 | result = fit_gibbs_global_batch(hdx_set, gibbs_guess, epochs=1000, r1=r1, r2=r2) 39 | t1 = time.time() 40 | 41 | block = "--------------------------" 42 | regularizers = f"Regualizer 1: {r1} Regualizer 2: {r2}" 43 | loss = ( 44 | f"Total_loss {result.total_loss:.2f}, mse_loss {result.mse_loss:.2f}, reg_loss {result.reg_loss:.2f}" 45 | f"({result.regularization_percentage:.2f}%)" 46 | ) 47 | time_elapsed = f"Time elapsed: {(t1 - t0):.2f} s" 48 | epochs = f"Number of epochs: {result.metadata['epochs_run']}" 49 | 50 | result.output.to_csv(output_dir / f"fit_output_r1_{r1}_r2_{r2}.csv") # , na_rep='NaN') 51 | result.output.to_file( 52 | output_dir / f"fit_output_r1_{r1}_r2_{r2}.txt", fmt="pprint", na_rep="NaN" 53 | ) 54 | 55 | lines += ["", block, regularizers, loss, epochs, time_elapsed, block] 56 | 57 | log_file.write_text("\n".join(lines)) 58 | -------------------------------------------------------------------------------- /templates/archive/individual_plots.py: -------------------------------------------------------------------------------- 1 | """ 2 | Automagically plot all available figures from a fit result 3 | """ 4 | 5 | from pyhdx.fileIO import load_fitresult 6 | from pyhdx.plot import linear_bars_figure 7 | from pathlib import Path 8 | import pandas as pd 9 | import matplotlib.pyplot as plt 10 | 11 | # %% 12 | 13 | __file__ = Path().cwd() / "templates" / "script.py" # Uncomment for PyCharm scientific mode 14 | 15 | cwd = Path(__file__).parent 16 | output_dir = cwd / "output" / "single_figures" 17 | output_dir.mkdir(exist_ok=True) 18 | fit_result = load_fitresult(cwd / "output" / "SecB_tetramer_dimer_batch") 19 | 20 | # rfus = fit_result.hdxm_set.rfu_residues 21 | # %% 22 | # self = fit_result.hdxm_set 23 | # [hdxm.rfu_residues for hdxm in self] 24 | # rfus = pd.concat([hdxm.rfu_residues for hdxm in self], 25 | # keys=self.names, names=['state', 'exposure'], axis=1) 26 | # add quantity multiindex level 27 | 28 | rfus = fit_result.hdxm_set.rfu_residues 29 | columns = pd.MultiIndex.from_tuples( 30 | [(*tup, "rfu") for tup in rfus.columns], names=[*rfus.columns.names, "quantity"] 31 | ) 32 | rfus.columns = columns 33 | 34 | 35 | # %% 36 | 37 | # RFUs grouped by state 38 | name = "linear_bars_rfu_by_state" 39 | fig, axes, cbar = linear_bars_figure(rfus, field="rfu") 40 | plt.savefig(output_dir / f"{name}.png") 41 | plt.savefig(output_dir / f"{name}.pdf") 42 | 43 | # %% 44 | 45 | # RFUs grouped by exposure 46 | name = "linear_bars_rfu_by_exposure" 47 | fig, axes, cbar = linear_bars_figure(rfus, field="rfu", groupby="exposure") 48 | plt.savefig(output_dir / f"{name}.png") 49 | plt.savefig(output_dir / f"{name}.pdf") 50 | 51 | # %% 52 | 53 | # dRFUs grouped by exposure, wrt first sample (test - reference, positive dRFU is more flexible) 54 | name = "linear_bars_drfu_by_exposure" 55 | fig, axes, cbar = linear_bars_figure(rfus, field="rfu", groupby="exposure", reference=0) 56 | plt.savefig(output_dir / f"{name}.png") 57 | plt.savefig(output_dir / f"{name}.pdf") 58 | 59 | 60 | # %% 61 | 62 | # dGs 63 | name = "linear_bars_dG" 64 | fig, axes, cbar = linear_bars_figure(fit_result.output, field="dG") 65 | plt.savefig(output_dir / f"{name}.png") 66 | plt.savefig(output_dir / f"{name}.pdf") 67 | 68 | # %% 69 | 70 | # ddGs 71 | name = "linear_bars_ddG" 72 | fig, axes, cbar = linear_bars_figure(fit_result.output, field="dG", reference=0) 73 | plt.savefig(output_dir / f"{name}.png") 74 | plt.savefig(output_dir / f"{name}.pdf") 75 | -------------------------------------------------------------------------------- /templates/archive/load_fitresult_web.py: -------------------------------------------------------------------------------- 1 | """Load a fit result from a directory directly ino the web interface""" 2 | from pyhdx.fileIO import load_fitresult 3 | from pathlib import Path 4 | from pyhdx.web.apps import main_app 5 | from pyhdx.web.base import STATIC_DIR 6 | import panel as pn 7 | 8 | current_dir = Path(__file__).parent 9 | fit_result = load_fitresult( 10 | current_dir.parent / "tests" / "test_data" / "output" / "ecsecb_tetramer_dimer" 11 | ) 12 | 13 | ctrl, tmpl = main_app() 14 | 15 | src = ctrl.sources["main"] 16 | for hdxm in fit_result.hdxm_set: 17 | src.add(hdxm, hdxm.name) 18 | src.add(fit_result, "fitresult_1") 19 | 20 | pn.serve(tmpl, show=True, static_dirs={"pyhdx": STATIC_DIR}) 21 | -------------------------------------------------------------------------------- /templates/archive/plot_output.py: -------------------------------------------------------------------------------- 1 | """ 2 | Automagically plot all available figures from a fit result 3 | """ 4 | 5 | from pyhdx.fileIO import load_fitresult 6 | from pyhdx.plot import FitResultPlot 7 | from pathlib import Path 8 | 9 | # %% 10 | 11 | # __file__ = Path().cwd() / 'templates'/ 'script.py' # Uncomment for PyCharm scientific mode 12 | 13 | 14 | cwd = Path(__file__).parent 15 | output_dir = cwd / "output" / "figures" 16 | output_dir.mkdir(exist_ok=True) 17 | fit_result = load_fitresult(cwd / "output" / "SecB_tetramer_dimer_batch") 18 | 19 | fr_plot = FitResultPlot(fit_result, output_path=output_dir) 20 | 21 | kwargs = { 22 | "residue_scatter": {"cmap": "BuGn"}, # change default colormap 23 | "ddG_scatter": { 24 | "reference": 1 25 | }, # Set reference for ΔΔG to the second (index 1 state) (+ APO state (tetramer)) 26 | } 27 | 28 | fr_plot.plot_all(**kwargs) 29 | -------------------------------------------------------------------------------- /templates/yaml_files/SecB.yaml: -------------------------------------------------------------------------------- 1 | data_files: 2 | data_apo: 3 | filename: ecSecB_apo.csv 4 | format: DynamX 5 | 6 | data_dimer: 7 | filename: ecSecB_dimer.csv 8 | format: DynamX 9 | 10 | states: 11 | SecB_tetramer: 12 | peptides: 13 | experiment: 14 | data_file: data_apo 15 | state: SecB WT apo 16 | query: 17 | - exposure != 0 18 | FD_control: 19 | data_file: data_apo 20 | state: Full deuteration control 21 | exposure: 22 | value: 0.167 23 | unit: min 24 | metadata: 25 | pH: 8. 26 | d_percentage: 90. 27 | temperature: 28 | value: 30. 29 | unit: C 30 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPYARECITSMVSRGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 31 | n_term: 1 32 | c_term: 155 33 | author: Foo Bar 34 | 35 | SecB_dimer: 36 | peptides: 37 | experiment: 38 | data_file: data_dimer 39 | state: 'SecB his dimer apo' 40 | exposure: 41 | values: 42 | - 0.167 43 | - 0.5 44 | - 1. 45 | - 10. 46 | - 100.000008 47 | unit: min 48 | FD_control: 49 | data_file: data_apo 50 | state: Full deuteration control 51 | exposure: 52 | value: 0.167 53 | unit: min 54 | metadata: 55 | pH: 8. 56 | d_percentage: 90. 57 | temperature: 58 | value: 30. 59 | unit: C 60 | temperature_unit: Celsius 61 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPAARECIASMVARGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 62 | n_term: 1 63 | c_term: 155 64 | author: Foo Bar -------------------------------------------------------------------------------- /tests/check_test_fit_result.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script checks 'fixed' (stored) values of dG fitting and compares to new results generated in 3 | `generate_test_fit_results.py' 4 | """ 5 | 6 | import pandas as pd 7 | from pathlib import Path 8 | from pyhdx.fileIO import csv_to_dataframe 9 | 10 | test_data_dir = Path(__file__).parent / "test_data" 11 | 12 | comparisons = {} 13 | 14 | new_result = csv_to_dataframe(test_data_dir / "output" / "ecSecB_torch_fit.csv") 15 | fixed_result = csv_to_dataframe(test_data_dir / "ecSecB_torch_fit_fixed.csv").rename( 16 | columns={"deltaG": "dG"} 17 | ) 18 | comparisons["single_fit"] = (new_result, fixed_result) 19 | 20 | new_result = csv_to_dataframe(test_data_dir / "output" / "ecSecB_torch_fit_epochs_20000.csv") 21 | fixed_result = csv_to_dataframe(test_data_dir / "ecSecB_torch_fit_epochs_20000_fixed.csv").rename( 22 | columns={"deltaG": "dG"} 23 | ) 24 | comparisons["single_20k"] = (new_result, fixed_result) 25 | 26 | new_result = csv_to_dataframe(test_data_dir / "output" / "ecSecB_batch.csv") 27 | fixed_result = csv_to_dataframe(test_data_dir / "ecSecB_batch_fixed.csv").rename( 28 | columns={"deltaG": "dG"} 29 | ) 30 | comparisons["batch"] = (new_result, fixed_result) 31 | 32 | new_result = csv_to_dataframe(test_data_dir / "output" / "ecSecB_batch_aligned.csv") 33 | fixed_result = csv_to_dataframe(test_data_dir / "ecSecB_batch_aligned_fixed.csv").rename( 34 | columns={"deltaG": "dG"} 35 | ) 36 | comparisons["batch_aligned"] = (new_result, fixed_result) 37 | 38 | 39 | for k, (new_result, fixed_result) in comparisons.items(): 40 | print(f"Checking {k}") 41 | bools = new_result.columns.get_level_values(new_result.columns.nlevels - 1) == "dG" 42 | new = new_result.xs("dG", level=-1, axis=1).dropna(how="any") 43 | 44 | bools = fixed_result.columns.get_level_values(fixed_result.columns.nlevels - 1) == "dG" 45 | old = fixed_result.xs("dG", level=-1, axis=1).dropna(how="any") 46 | 47 | abs_diffs = (new - old).abs() 48 | rel_diffs = ((new - old).abs() / old) * 100 49 | 50 | if new.equals(old): 51 | print("No differences") 52 | else: 53 | print(f"Sizes: {len(new)} ({len(old)}):") 54 | print("Differences") 55 | for s in ["mean", "median", "min", "max", "std"]: 56 | value = getattr(abs_diffs, s)() 57 | perc = getattr(rel_diffs, s)() 58 | if isinstance(value, pd.Series): 59 | vs = ", ".join([f"{v:.2f}" for v in value]) 60 | ps = ", ".join([f"{p:.1f}%" for p in perc]) 61 | print(f"{s}: {vs} ({ps})") 62 | else: 63 | print(f"{s}: {value:.2f} ({perc:.2f})%") 64 | -------------------------------------------------------------------------------- /tests/gen_docs_example_result.py: -------------------------------------------------------------------------------- 1 | """Obtain ΔG for ecSecB tetramer and dimer""" 2 | from pathlib import Path 3 | from pyhdx.batch_processing import StateParser 4 | from pyhdx.fileIO import csv_to_dataframe, save_fitresult 5 | from pyhdx.fitting import fit_gibbs_global_batch 6 | import yaml 7 | 8 | cwd = Path(__file__).parent 9 | 10 | data_dir = cwd / "test_data" / "input" 11 | output_dir = cwd / "test_data" / "output" 12 | 13 | yaml_dict = yaml.safe_load(Path(data_dir / "data_states.yaml").read_text()) 14 | 15 | parser = StateParser(yaml_dict, data_dir) 16 | hdx_set = parser.load_hdxmset() 17 | 18 | initial_guess_rates = csv_to_dataframe(output_dir / "ecSecB_guess.csv") 19 | 20 | guesses = hdx_set[0].guess_deltaG(initial_guess_rates["rate"]) 21 | fit_kwargs = yaml.safe_load(Path(data_dir / "fit_settings.yaml").read_text()) 22 | 23 | fr = fit_gibbs_global_batch(hdx_set, guesses, **fit_kwargs) 24 | save_fitresult(output_dir / "ecsecb_tetramer_dimer", fr) 25 | -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import yaml 3 | from pyhdx.config import ( 4 | PyHDXConfig, 5 | ) 6 | from omegaconf import OmegaConf 7 | 8 | directory = Path(__file__).parent 9 | 10 | 11 | class TestConfig(object): 12 | def test_cfg_singleton(self, tmp_path): 13 | cfg = PyHDXConfig() 14 | scheduler_address = "127.0.0.1:00000" 15 | cfg.cluster.scheduler_address = scheduler_address 16 | 17 | assert cfg.cluster.scheduler_address == scheduler_address 18 | assert cfg.conf.cluster.scheduler_address == scheduler_address 19 | 20 | cfg2 = PyHDXConfig() 21 | assert id(cfg) == id(cfg2) 22 | assert cfg.cluster.scheduler_address == scheduler_address 23 | 24 | s = """ 25 | foo: 26 | - bar 27 | - apple 28 | - banana 29 | value: 3 30 | """ 31 | 32 | Path(tmp_path / "configfile.yaml").write_text(yaml.dump(s)) 33 | conf = OmegaConf.load(tmp_path / "configfile.yaml") 34 | 35 | merged = OmegaConf.merge(cfg.conf, conf) 36 | 37 | cfg.set_config(merged) 38 | 39 | assert "bar" in cfg.foo 40 | assert cfg.value == 3 41 | assert cfg2.value == 3 42 | -------------------------------------------------------------------------------- /tests/test_data/input/PpiX_states.yaml: -------------------------------------------------------------------------------- 1 | 2 | data_files: 3 | PpiA_folding: 4 | filename: PpiA_folding.csv 5 | format: DynamX 6 | 7 | PpiB_folding: 8 | filename: PpiB_folding.csv 9 | format: DynamX 10 | 11 | states: 12 | PpiA_Folding: 13 | peptides: 14 | experiment: 15 | data_file: PpiA_folding 16 | state: Folding 17 | exposure: 18 | values: 19 | - 4.98 20 | - 10.020000000000001 21 | - 19.98 22 | - 30.0 23 | - 40.02 24 | - 60.0 25 | - 150.0 26 | - 300.0 27 | - 600.0 28 | - 900.0 29 | - 1200.0 30 | - 1800.00012 31 | - 2700.0 32 | - 3600.0 33 | - 10800.0 34 | - 57600.0 35 | - 86400.0 36 | unit: s 37 | FD_control: 38 | data_file: PpiA_folding 39 | state: Native 40 | exposure: 41 | value: 86400 42 | unit: s 43 | ND_control: 44 | data_file: PpiA_folding 45 | state: FD 46 | exposure: 47 | value: 0.6 48 | unit: s 49 | metadata: 50 | pH: 8. 51 | d_percentage: 90. 52 | temperature: 53 | value: 25. 54 | unit: Celsius 55 | n_term: 0 56 | c_term: 163 57 | author: Foo Bar 58 | 59 | PpiB_Folding: 60 | peptides: 61 | experiment: 62 | data_file: PpiB_folding 63 | state: Folding 64 | exposure: 65 | values: 66 | - 4.98 67 | - 10.020000000000001 68 | - 19.98 69 | - 30.0 70 | - 40.02 71 | - 60.0 72 | - 150.0 73 | - 300.0 74 | - 600.0 75 | - 900.0 76 | - 1200.0 77 | - 1800.0 78 | unit: s 79 | FD_control: 80 | data_file: PpiB_folding 81 | state: Native 82 | exposure: 83 | value: 1800 84 | unit: s 85 | ND_control: 86 | data_file: PpiB_folding 87 | state: FD 88 | exposure: 89 | value: 0.6 90 | unit: s 91 | FD_control: 92 | state: Native 93 | exposure: 94 | value: 1800 95 | unit: s 96 | metadata: 97 | pH: 8. 98 | d_percentage: 90. 99 | temperature: 100 | value: 25. 101 | unit: Celsius 102 | n_term: 1 103 | c_term: 173 104 | author: Foo Bar -------------------------------------------------------------------------------- /tests/test_data/input/PyHDX_state_spec_202207140958.yaml: -------------------------------------------------------------------------------- 1 | # PyHDX v0.4.1+59.gf3e3e9c.dirty 2 | SecB WT apo: 3 | filenames: 4 | - ecSecB_apo.csv 5 | - ecSecB_dimer.csv 6 | FD_control: 7 | state: Full deuteration control 8 | exposure: 9 | value: 10.020000000000001 10 | unit: s 11 | experiment: 12 | state: SecB WT apo 13 | exposure: 14 | values: 15 | - 10.020000000000001 16 | - 30.0 17 | - 60.0 18 | - 300.0 19 | - 600.0 20 | - 6000.00048 21 | unit: s 22 | pH: 7.5 23 | temperature: 24 | value: 293.15 25 | unit: K 26 | d_percentage: 90.0 27 | n_term: 1 28 | c_term: 156 29 | SecB his dimer apo: 30 | filenames: 31 | - ecSecB_apo.csv 32 | - ecSecB_dimer.csv 33 | FD_control: 34 | state: Full deuteration control 35 | exposure: 36 | value: 10.020000000000001 37 | unit: s 38 | experiment: 39 | state: SecB his dimer apo 40 | exposure: 41 | values: 42 | - 10.020000000000001 43 | - 30.0 44 | - 60.0 45 | - 300.0 46 | - 600.0 47 | - 6000.00048 48 | unit: s 49 | pH: 7.5 50 | temperature: 51 | value: 293.15 52 | unit: K 53 | d_percentage: 90.0 54 | n_term: 1 55 | c_term: 156 56 | -------------------------------------------------------------------------------- /tests/test_data/input/data_states.yaml: -------------------------------------------------------------------------------- 1 | 2 | data_files: 3 | data_apo: 4 | filename: ecSecB_apo.csv 5 | format: DynamX 6 | 7 | data_dimer: 8 | filename: ecSecB_dimer.csv 9 | format: DynamX 10 | 11 | states: 12 | SecB_tetramer: 13 | peptides: 14 | experiment: 15 | data_file: data_apo 16 | state: SecB WT apo 17 | FD_control: 18 | data_file: data_apo 19 | state: Full deuteration control 20 | exposure: 21 | value: 0.167 22 | unit: min 23 | metadata: 24 | pH: 8. 25 | d_percentage: 90. 26 | temperature: 27 | value: 30. 28 | unit: C 29 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPYARECITSMVSRGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 30 | n_term: 1 31 | c_term: 155 32 | author: Foo Bar 33 | 34 | SecB_dimer: 35 | peptides: 36 | experiment: 37 | data_file: data_dimer 38 | state: 'SecB his dimer apo' 39 | FD_control: 40 | data_file: data_apo 41 | state: Full deuteration control 42 | exposure: 43 | value: 0.167 44 | unit: min 45 | metadata: 46 | pH: 8. 47 | d_percentage: 90. 48 | temperature: 49 | value: 30. 50 | unit: C 51 | temperature_unit: Celsius 52 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPAARECIASMVARGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 53 | n_term: 1 54 | c_term: 155 55 | author: Foo Bar -------------------------------------------------------------------------------- /tests/test_data/input/data_states_deltas.yaml: -------------------------------------------------------------------------------- 1 | # YAML file for loading HDX-MS data; PyHDX v0.4.1 2 | 3 | data_files: 4 | data_apo: 5 | filename: ecSecB_apo.csv 6 | format: DynamX 7 | 8 | data_dimer: 9 | filename: ecSecB_dimer.csv 10 | format: DynamX 11 | 12 | data_dimer_deltaC: 13 | filename: ecSecB_dimer_deltaC.csv 14 | format: DynamX 15 | 16 | data_dimer_deltaN: 17 | filename: ecSecB_dimer_deltaN.csv 18 | format: DynamX 19 | 20 | states: 21 | SecB_tetramer: 22 | peptides: 23 | experiment: 24 | data_file: data_apo 25 | state: SecB WT apo 26 | FD_control: 27 | data_file: data_apo 28 | state: Full deuteration control 29 | exposure: 30 | value: 0.167 31 | unit: min 32 | metadata: 33 | pH: 8. 34 | d_percentage: 90. 35 | temperature: 36 | value: 30. 37 | unit: C 38 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPYARECITSMVSRGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 39 | n_term: 1 40 | c_term: 155 41 | author: Foo Bar 42 | 43 | SecB_dimer: 44 | peptides: 45 | experiment: 46 | data_file: data_dimer 47 | state: 'SecB his dimer apo' 48 | FD_control: 49 | data_file: data_apo 50 | state: Full deuteration control 51 | exposure: 52 | value: 0.167 53 | unit: min 54 | metadata: 55 | pH: 8. 56 | d_percentage: 90. 57 | temperature: 58 | value: 30. 59 | unit: C 60 | temperature_unit: Celsius 61 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPAARECIASMVARGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 62 | n_term: 1 63 | c_term: 155 64 | author: Foo Bar 65 | 66 | SecB_dimer_deltaC: 67 | peptides: 68 | experiment: 69 | data_file: data_dimer_deltaC 70 | state: dimer_deltaC 71 | FD_control: 72 | data_file: data_apo 73 | state: Full deuteration control 74 | exposure: 75 | value: 0.167 76 | unit: min 77 | metadata: 78 | pH: 8. 79 | d_percentage: 90. 80 | temperature: 81 | value: 30. 82 | unit: Celsius 83 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPAARECIASMVARGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 84 | n_term: 1 85 | c_term: 155 86 | author: Foo Bar 87 | 88 | SecB_dimer_deltaN: 89 | peptides: 90 | experiment: 91 | data_file: data_dimer_deltaN 92 | state: dimer_deltaN 93 | FD_control: 94 | data_file: data_apo 95 | state: Full deuteration control 96 | exposure: 97 | value: 0.167 98 | unit: min 99 | metadata: 100 | pH: 8. 101 | d_percentage: 90. 102 | temperature: 103 | value: 30. 104 | unit: Celsius 105 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPAARECIASMVARGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 106 | n_term: 1 107 | c_term: 155 108 | author: Foo Bar -------------------------------------------------------------------------------- /tests/test_data/input/data_states_red.yaml: -------------------------------------------------------------------------------- 1 | # YAML file for loading HDX-MS data; PyHDX v0.4.0b3 2 | 3 | data_files: 4 | data_apo: 5 | filename: ecSecB_apo_red.csv 6 | format: DynamX 7 | 8 | data_dimer: 9 | filename: ecSecB_dimer_red.csv 10 | format: DynamX 11 | 12 | states: 13 | SecB_tetramer: 14 | peptides: 15 | experiment: 16 | data_file: data_apo 17 | state: SecB WT apo 18 | FD_control: 19 | data_file: data_apo 20 | state: Full deuteration control 21 | exposure: 22 | value: 0.167 23 | unit: min 24 | metadata: 25 | pH: 8. 26 | d_percentage: 90. 27 | temperature: 28 | value: 30. 29 | unit: C 30 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPYARECITSMVSRGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 31 | n_term: 1 32 | c_term: 155 33 | author: Foo Bar 34 | 35 | SecB_dimer: 36 | peptides: 37 | experiment: 38 | data_file: data_dimer 39 | state: 'SecB his dimer apo' 40 | FD_control: 41 | data_file: data_apo 42 | state: Full deuteration control 43 | exposure: 44 | value: 0.167 45 | unit: min 46 | metadata: 47 | pH: 8. 48 | d_percentage: 90. 49 | temperature: 50 | value: 30. 51 | unit: C 52 | temperature_unit: Celsius 53 | sequence: MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQMAHCLGAYCPNILFPAARECIASMVARGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA 54 | n_term: 1 55 | c_term: 155 56 | author: Foo Bar 57 | -------------------------------------------------------------------------------- /tests/test_data/input/fit_settings.yaml: -------------------------------------------------------------------------------- 1 | r1: 1 2 | r2: 1 3 | epochs: 200000 4 | stop_loss: 1.e-6 5 | patience: 100 6 | optimizer: SGD 7 | lr: 1.e+4 8 | momentum: 0.5 9 | nesterov: True 10 | -------------------------------------------------------------------------------- /tests/test_data/input/jobfile.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: load_hdxm_set 3 | name: load_data 4 | state_file: data_states.yaml 5 | - task: estimate_rates 6 | name: rates 7 | hdxm_set: $(load_data.out) 8 | - task: create_guess 9 | name: guess 10 | rates_df: $(rates.out.output) 11 | hdxm_set: $(load_data.out) 12 | - task: fit_global_batch 13 | name: global_fit 14 | hdxm_set: $(load_data.out) 15 | initial_guess: $(guess.out) 16 | kwargs: 17 | epochs: 100 18 | stop_loss: 1.e-6 19 | - task: save_fit_result 20 | fit_result: $(global_fit.out) 21 | output_dir: fit_result_output_1 22 | -------------------------------------------------------------------------------- /tests/test_data/output/ecSecB_batch_peptide_mse.csv: -------------------------------------------------------------------------------- 1 | # {"comment": "#", "header": [0, 1], "index_col": 0} 2 | state,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo,SecB WT apo,SecB WT apo,SecB WT apo,SecB WT apo 3 | quantity,start,end,sequence,peptide_mse,start,end,sequence,peptide_mse 4 | peptide_id,,,,,,,, 5 | 0,9.0,17.0,xTFQIQRIY,1.3751750386233206,9,17,xTFQIQRIY,2.681230989571072 6 | 1,11.0,17.0,xQIQRIY,0.8824435982018982,11,17,xQIQRIY,1.7938367007868818 7 | 2,18.0,32.0,xKDISFEApNApHVF,7.579634846425569,18,32,xKDISFEApNApHVF,10.635127227204972 8 | 3,18.0,33.0,xKDISFEApNApHVFQ,7.28761191415292,18,33,xKDISFEApNApHVFQ,9.34978502012464 9 | 4,18.0,34.0,xKDISFEApNApHVFQK,7.116876292719556,18,34,xKDISFEApNApHVFQK,9.663266418294581 10 | 5,21.0,32.0,xSFEApNApHVF,2.1242659095152776,18,41,xKDISFEApNApHVFQKDWQpEVK,20.528563872838124 11 | 6,21.0,33.0,xSFEApNApHVFQ,2.663969238219398,21,32,xSFEApNApHVF,4.027324532176286 12 | 7,21.0,34.0,xSFEApNApHVFQK,2.9515449468690744,21,33,xSFEApNApHVFQ,4.154699460820283 13 | 8,21.0,41.0,xSFEApNApHVFQKDWQpEVK,13.829940815185807,21,34,xSFEApNApHVFQK,4.445608540291414 14 | 9,21.0,42.0,xSFEApNApHVFQKDWQpEVKL,8.857381522687511,21,41,xSFEApNApHVFQKDWQpEVK,11.248875055900893 15 | 10,24.0,32.0,xApNApHVF,0.0640053118017636,21,42,xSFEApNApHVFQKDWQpEVKL,8.98927075047278 16 | 11,24.0,34.0,xApNApHVFQK,0.2592222300804079,21,43,xSFEApNApHVFQKDWQpEVKLD,17.406144978998903 17 | 12,24.0,41.0,xApNApHVFQKDWQpEVK,4.674816748896731,24,32,xApNApHVF,0.19184105182862804 18 | 13,24.0,42.0,xApNApHVFQKDWQpEVKL,2.530471732682495,24,34,xApNApHVFQK,0.3546076181449297 19 | 14,33.0,43.0,xKDWQpEVKLD,2.346791507630496,24,41,xApNApHVFQKDWQpEVK,2.868647827346262 20 | 15,35.0,41.0,xWQpEVK,0.7880150840285244,24,42,xApNApHVFQKDWQpEVKL,2.062838591052175 21 | 16,37.0,46.0,xxEVKLDLDT,0.7081072256283335,25,41,xxNApHVFQKDWQpEVK,2.9002610043758517 22 | 17,42.0,51.0,xDLDTASSQL,7.150666459980955,33,43,xKDWQpEVKLD,2.405655756050938 23 | 18,42.0,54.0,xDLDTASSQLADD,6.9384416262230015,35,41,xWQpEVK,0.8376293658151879 24 | 19,42.0,56.0,xDLDTASSQLADDVY,14.488617452021568,37,46,xxEVKLDLDT,0.9579395708359816 25 | 20,42.0,57.0,xDLDTASSQLADDVYE,17.673422902699663,42,51,xDLDTASSQL,8.703532050464782 26 | 21,43.0,51.0,xLDTASSQL,6.869201149467188,42,54,xDLDTASSQLADD,9.011744900660124 27 | 22,45.0,51.0,xTASSQL,4.203912358763735,42,56,xDLDTASSQLADDVY,18.902527076155515 28 | 23,61.0,68.0,xVTVTASL,0.2884223715277684,42,57,xDLDTASSQLADDVYE,22.038404268159876 29 | 24,61.0,74.0,xVTVTASLGEETAF,2.60114162930759,43,51,xLDTASSQL,8.408034882145474 30 | 25,61.0,75.0,xVTVTASLGEETAFL,5.312648986308367,45,51,xTASSQL,5.091246653582819 31 | 26,62.0,68.0,xTVTASL,0.30022878244467677,61,68,xVTVTASL,1.693057037081713 32 | 27,64.0,75.0,xTASLGEETAFL,2.6290239976622383,61,71,xVTVTASLGEE,2.6703331412099938 33 | 28,66.0,74.0,xSLGEETAF,1.5742480485492802,61,74,xVTVTASLGEETAF,5.186110099581527 34 | 29,66.0,75.0,xSLGEETAFL,2.7407085942023195,61,75,xVTVTASLGEETAFL,9.209058787167718 35 | 30,75.0,84.0,xCEVQQGGIF,5.075625755692763,62,68,xTVTASL,1.2394846901085583 36 | 31,76.0,84.0,xEVQQGGIF,4.210361298145018,64,75,xTASLGEETAFL,4.765613814290385 37 | 32,78.0,84.0,xQQGGIF,2.8833180159881846,66,74,xSLGEETAF,2.0042327770725112 38 | 33,85.0,93.0,xIAGIEGTQ,0.2516448047726945,66,75,xSLGEETAFL,4.283646927983283 39 | 34,99.0,106.0,xAYCpNIL,8.11938545529461,75,84,xCEVQQGGIF,1.6290568696562555 40 | 35,99.0,107.0,xAYCpNILF,13.931830513619477,76,84,xEVQQGGIF,1.189696657568241 41 | 36,99.0,112.0,xAYCpNILFpAARE,35.274770401466604,78,84,xQQGGIF,0.9814176050814422 42 | 37,99.0,113.0,xAYCpNILFpAAREC,44.87501506788697,85,93,xIAGIEGTQ,0.6716707393042912 43 | 38,101.0,107.0,xCpNILF,2.7355203140263122,85,94,xIAGIEGTQM,0.6180562213357316 44 | 39,114.0,126.0,xASMVARGTFpQL,4.472685358599387,99,106,xAYCpNIL,0.9713086917523283 45 | 40,114.0,133.0,xASMVARGTFpQLNLApVNF,4.880912214139024,99,107,xAYCpNILF,1.268776580848291 46 | 41,116.0,126.0,xMVARGTFpQL,4.873186607778398,99,112,xAYCpNILFpYARE,1.7319961284074492 47 | 42,117.0,126.0,xVARGTFpQL,4.437070760105464,99,113,xAYCpNILFpYAREC,2.525203692428428 48 | 43,118.0,125.0,xARGTFpQ,4.609083200103305,101,107,xCpNILF,0.3689620533183186 49 | 44,118.0,126.0,xARGTFpQL,5.81567038142031,101,112,xCpNILFpYARE,0.8578455940470995 50 | 45,118.0,133.0,xARGTFpQLNLApVNF,5.4781328522205905,114,126,xTSMVSRGTFpQL,4.212414748188352 51 | 46,127.0,133.0,xLApVNF,0.17894497932587536,114,133,xTSMVSRGTFpQLNLApVNF,6.086455819132598 52 | 47,137.0,155.0,xMNYLQQQAGEGTEEHQDA,0.12585840866547843,116,126,xMVSRGTFpQL,2.755346931363691 53 | 48,138.0,151.0,xNYLQQQAGEGTEE,0.009361636913066239,117,126,xVSRGTFpQL,2.5875317111649787 54 | 49,138.0,155.0,xNYLQQQAGEGTEEHQDA,0.1555886250402446,118,125,xSRGTFpQ,1.533917148815356 55 | 50,139.0,155.0,xYLQQQAGEGTEEHQDA,0.2590375663524784,118,126,xSRGTFpQL,2.7109655159112824 56 | 51,140.0,155.0,xLQQQAGEGTEEHQDA,0.11409917805175773,118,128,xSRGTFpQLNL,3.764872023739733 57 | 52,141.0,155.0,xQQQAGEGTEEHQDA,0.07080386698555634,118,133,xSRGTFpQLNLApVNF,3.374964450399014 58 | 53,,,,,127,133,xLApVNF,0.02902108316587682 59 | 54,,,,,137,151,xMNYLQQQAGEGTEE,0.02064578909476151 60 | 55,,,,,137,154,xMNYLQQQAGEGTEEHQD,0.22306988880657402 61 | 56,,,,,137,155,xMNYLQQQAGEGTEEHQDA,0.05018210568209243 62 | 57,,,,,138,151,xNYLQQQAGEGTEE,0.003644960163298655 63 | 58,,,,,138,155,xNYLQQQAGEGTEEHQDA,0.03329872491606257 64 | 59,,,,,139,155,xYLQQQAGEGTEEHQDA,0.05907059280246908 65 | 60,,,,,140,155,xLQQQAGEGTEEHQDA,0.05856327624059291 66 | 61,,,,,141,154,xQQQAGEGTEEHQD,0.16597569842217466 67 | 62,,,,,141,155,xQQQAGEGTEEHQDA,0.07196088400726616 68 | -------------------------------------------------------------------------------- /tests/test_data/output/ecSecB_batch_residue_mse.csv: -------------------------------------------------------------------------------- 1 | # {"comment": "#", "header": [0, 1], "index_col": 0} 2 | state,SecB his dimer apo,SecB WT apo 3 | quantity,residue_mse,residue_mse 4 | r_number,, 5 | 10,1.3751750386233206,2.681230989571072 6 | 11,1.3751750386233206,2.681230989571072 7 | 12,1.093614215525365,2.1741485388372492 8 | 13,1.093614215525365,2.1741485388372492 9 | 14,1.093614215525365,2.1741485388372492 10 | 15,1.093614215525365,2.1741485388372492 11 | 16,1.093614215525365,2.1741485388372492 12 | 17,1.093614215525365,2.1741485388372492 13 | 18,, 14 | 19,7.34000997093161,11.793800024219964 15 | 20,7.34000997093161,11.793800024219964 16 | 21,7.34000997093161,11.793800024219964 17 | 22,5.827824678117211,8.880122089417332 18 | 23,5.827824678117211,8.880122089417332 19 | 24,5.827824678117211,8.880122089417332 20 | 25,4.010410325064295,6.001954561965798 21 | 26,, 22 | 27,4.010410325064295,5.812204558178852 23 | 28,4.010410325064295,5.812204558178852 24 | 29,, 25 | 30,4.010410325064295,5.812204558178852 26 | 31,4.010410325064295,5.812204558178852 27 | 32,4.010410325064295,5.812204558178852 28 | 33,4.805502436195869,6.630173365191569 29 | 34,4.421481216146383,6.111141578584361 30 | 35,5.690466934691248,7.247599092811707 31 | 36,3.9507300453407583,5.522495088096794 32 | 37,3.9507300453407583,5.522495088096794 33 | 38,, 34 | 39,3.3620916101544798,4.8652630973145135 35 | 40,3.3620916101544798,4.8652630973145135 36 | 41,3.3620916101544798,4.8652630973145135 37 | 42,2.818724321679263,4.7207848391328815 38 | 43,6.920098351380591,9.400871482409867 39 | 44,7.781469807543606,9.798803914657205 40 | 45,7.781469807543606,9.798803914657205 41 | 46,6.985614866607407,8.751571896833454 42 | 47,8.242712709524385,10.3122802138641 43 | 48,8.242712709524385,10.3122802138641 44 | 49,8.242712709524385,10.3122802138641 45 | 50,8.242712709524385,10.3122802138641 46 | 51,8.242712709524385,10.3122802138641 47 | 52,12.606019588753147,16.124324766841358 48 | 53,12.606019588753147,16.124324766841358 49 | 54,12.606019588753147,16.124324766841358 50 | 55,16.026109738555817,20.416398824019687 51 | 56,16.026109738555817,20.416398824019687 52 | 57,17.673422902699663,22.038404268159876 53 | 58,, 54 | 59,, 55 | 60,, 56 | 61,, 57 | 62,2.131686816830887,4.002006344964441 58 | 63,1.4650360923143066,3.176696198569545 59 | 64,1.4650360923143066,3.176696198569545 60 | 65,1.657856904304589,3.3993387634167394 61 | 66,1.657856904304589,3.3993387634167394 62 | 67,1.797831875599847,3.3133040035984016 63 | 68,1.797831875599847,3.3133040035984016 64 | 69,2.776498248703551,4.316308242048487 65 | 70,2.776498248703551,4.316308242048487 66 | 71,2.776498248703551,4.316308242048487 67 | 72,2.776498248703551,4.6625582565478645 68 | 73,2.776498248703551,4.6625582565478645 69 | 74,2.776498248703551,4.6625582565478645 70 | 75,3.3754047410478303,5.730463819101896 71 | 76,5.075625755692763,1.6290568696562555 72 | 77,4.617544572285134,1.396454404433189 73 | 78,4.617544572285134,1.396454404433189 74 | 79,3.8999335834726025,1.2247150391841903 75 | 80,3.8999335834726025,1.2247150391841903 76 | 81,3.8999335834726025,1.2247150391841903 77 | 82,3.8999335834726025,1.2247150391841903 78 | 83,3.8999335834726025,1.2247150391841903 79 | 84,3.8999335834726025,1.2247150391841903 80 | 85,, 81 | 86,0.2516448047726945,0.646440377907322 82 | 87,0.2516448047726945,0.646440377907322 83 | 88,0.2516448047726945,0.646440377907322 84 | 89,0.2516448047726945,0.646440377907322 85 | 90,0.2516448047726945,0.646440377907322 86 | 91,0.2516448047726945,0.646440377907322 87 | 92,0.2516448047726945,0.646440377907322 88 | 93,0.2516448047726945,0.646440377907322 89 | 94,,0.6180562213357316 90 | 95,, 91 | 96,, 92 | 97,, 93 | 98,, 94 | 99,, 95 | 100,21.270344209985755,1.4697727210403748 96 | 101,21.270344209985755,1.4697727210403748 97 | 102,15.848952225222687,1.1072588836531383 98 | 103,, 99 | 104,15.848952225222687,1.1072588836531383 100 | 105,15.848952225222687,1.1072588836531383 101 | 106,15.848952225222687,1.1072588836531383 102 | 107,18.34027345286799,1.1433269832615995 103 | 108,, 104 | 109,39.86619176366764,1.6232636160254748 105 | 110,39.86619176366764,1.6232636160254748 106 | 111,39.86619176366764,1.6232636160254748 107 | 112,39.86619176366764,1.6232636160254748 108 | 113,44.87501506788697,2.525203692428428 109 | 114,, 110 | 115,4.633060194704245,4.94864516891645 111 | 116,4.633060194704245,4.94864516891645 112 | 117,4.735346388883896,4.014370065448646 113 | 118,4.638715420022663,3.5521238767557843 114 | 119,4.933641042801854,3.0466661459855287 115 | 120,4.933641042801854,3.0466661459855287 116 | 121,4.933641042801854,3.0466661459855287 117 | 122,4.933641042801854,3.0466661459855287 118 | 123,4.933641042801854,3.0466661459855287 119 | 124,, 120 | 125,4.933641042801854,3.0466661459855287 121 | 126,5.022958801247209,3.3984346670404806 122 | 127,5.219337242385245,4.196581688382982 123 | 128,2.2168394253067527,2.3313076011244713 124 | 129,2.2168394253067527,1.8568875091838108 125 | 130,, 126 | 131,2.2168394253067527,1.8568875091838108 127 | 132,2.2168394253067527,1.8568875091838108 128 | 133,2.2168394253067527,1.8568875091838108 129 | 134,, 130 | 135,, 131 | 136,, 132 | 137,, 133 | 138,0.12585840866547843,0.09356101079722573 134 | 139,0.08815651441509362,0.06102696961661181 135 | 140,0.13023679326905652,0.060708593900600946 136 | 141,0.12687970852752128,0.06039127742380658 137 | 142,0.11665913229214869,0.07532816377271163 138 | 143,0.11665913229214869,0.07532816377271163 139 | 144,0.11665913229214869,0.07532816377271163 140 | 145,0.11665913229214869,0.07532816377271163 141 | 146,0.11665913229214869,0.07532816377271163 142 | 147,0.11665913229214869,0.07532816377271163 143 | 148,0.11665913229214869,0.07532816377271163 144 | 149,0.11665913229214869,0.07532816377271163 145 | 150,0.11665913229214869,0.07532816377271163 146 | 151,0.11665913229214869,0.07532816377271163 147 | 152,0.1428633428780997,0.09622796202148952 148 | 153,0.1428633428780997,0.09622796202148952 149 | 154,0.1428633428780997,0.09622796202148952 150 | 155,0.1428633428780997,0.05550559399309567 151 | -------------------------------------------------------------------------------- /tests/test_data/output/ecSecB_rates.txt: -------------------------------------------------------------------------------- 1 | state,SecB WT apo,SecB his dimer apo 2 | quantity,rate,rate 3 | r_number,, 4 | 10,3.267321962268033,4.238219132510516 5 | 11,3.267321962268033,4.238219132510516 6 | 12,2.118045529024262,4.303282130011018 7 | 13,2.118045529024262,4.303282130011018 8 | 14,2.118045529024262,4.303282130011018 9 | 15,2.118045529024262,4.303282130011018 10 | 16,2.118045529024262,4.303282130011018 11 | 17,2.118045529024262,4.303282130011018 12 | 18,, 13 | 19,1.6763547851499292,1.7154838269504966 14 | 20,1.6763547851499292,1.7154838269504966 15 | 21,1.6763547851499292,1.7154838269504966 16 | 22,2.4305128857568232,2.5735437820742058 17 | 23,2.4305128857568232,2.5735437820742058 18 | 24,2.4305128857568232,2.5735437820742058 19 | 25,4.212490519940495,4.286549878719669 20 | 26,, 21 | 27,4.2760359342425955,4.286549878719669 22 | 28,4.2760359342425955,4.286549878719669 23 | 29,, 24 | 30,4.2760359342425955,4.286549878719669 25 | 31,4.2760359342425955,4.286549878719669 26 | 32,4.2760359342425955,4.286549878719669 27 | 33,4.525139018912599,4.4483659654604475 28 | 34,4.870725575053343,4.8071404076724145 29 | 35,5.034955970997727,4.912754627308401 30 | 36,5.070054609055126,4.9793737928698665 31 | 37,5.070054609055126,4.9793737928698665 32 | 38,, 33 | 39,5.210901706827179,5.173209628460079 34 | 40,5.210901706827179,5.173209628460079 35 | 41,5.210901706827179,5.173209628460079 36 | 42,5.4831826010032945,5.504937286586671 37 | 43,4.950153455856195,4.924708532366507 38 | 44,4.6463068460769525,4.620268231828498 39 | 45,4.6463068460769525,4.620268231828498 40 | 46,4.479182585929411,4.398212003962994 41 | 47,4.164969160701725,2.4294341286942 42 | 48,4.164969160701725,2.4294341286942 43 | 49,4.164969160701725,2.4294341286942 44 | 50,4.164969160701725,2.4294341286942 45 | 51,4.164969160701725,2.4294341286942 46 | 52,4.388474738359397,4.392186161933458 47 | 53,4.388474738359397,4.392186161933458 48 | 54,4.388474738359397,4.392186161933458 49 | 55,4.183734368714358,4.177326917291435 50 | 56,4.183734368714358,4.177326917291435 51 | 57,3.3618124020390834,3.6028548890412817 52 | 58,, 53 | 59,, 54 | 60,, 55 | 61,, 56 | 62,0.21055264467241958,0.19024744434281735 57 | 63,0.18902053317644257,0.22852806766732778 58 | 64,0.18902053317644257,0.22852806766732778 59 | 65,0.250660450102236,0.28770562858674914 60 | 66,0.250660450102236,0.28770562858674914 61 | 67,0.8016487214445653,0.7516759519400016 62 | 68,0.8016487214445653,0.7516759519400016 63 | 69,1.537316109597103,1.5005561088739905 64 | 70,1.537316109597103,1.5005561088739905 65 | 71,1.537316109597103,1.5005561088739905 66 | 72,1.7935017019550232,1.5005561088739905 67 | 73,1.7935017019550232,1.5005561088739905 68 | 74,1.7935017019550232,1.5005561088739905 69 | 75,1.3251526274301664,1.2313605443113373 70 | 76,0.006931471251081753,0.03156371282121241 71 | 77,0.006931471251081753,0.03292505956303548 72 | 78,0.006931471251081753,0.03292505956303548 73 | 79,0.006931471251081753,0.037353774371392715 74 | 80,0.006931471251081753,0.037353774371392715 75 | 81,0.006931471251081753,0.037353774371392715 76 | 82,0.006931471251081753,0.037353774371392715 77 | 83,0.006931471251081753,0.037353774371392715 78 | 84,0.006931471251081753,0.037353774371392715 79 | 85,, 80 | 86,6.203240784420741,6.198336711841038 81 | 87,6.203240784420741,6.198336711841038 82 | 88,6.203240784420741,6.198336711841038 83 | 89,6.203240784420741,6.198336711841038 84 | 90,6.203240784420741,6.198336711841038 85 | 91,6.203240784420741,6.198336711841038 86 | 92,6.203240784420741,6.198336711841038 87 | 93,6.203240784420741,6.198336711841038 88 | 94,6.3120841337641345, 89 | 95,, 90 | 96,, 91 | 97,, 92 | 98,, 93 | 99,, 94 | 100,0.00803738154606326,0.7985445512041474 95 | 101,0.00803738154606326,0.7985445512041474 96 | 102,0.006931471251081753,0.4148807879697755 97 | 103,, 98 | 104,0.006931471251081753,0.4148807879697755 99 | 105,0.006931471251081753,0.4148807879697755 100 | 106,0.006931471251081753,0.4148807879697755 101 | 107,0.006931471251081753,0.34593759266419416 102 | 108,, 103 | 109,0.006931471251081753,0.7417767768558982 104 | 110,0.006931471251081753,0.7417767768558982 105 | 111,0.006931471251081753,0.7417767768558982 106 | 112,0.006931471251081753,0.7417767768558982 107 | 113,0.006931471251081753,0.5518561145812007 108 | 114,, 109 | 115,0.026357606833986358,0.478686066119727 110 | 116,0.026357606833986358,0.478686066119727 111 | 117,0.04481349387549377,0.7153137342383413 112 | 118,0.07463337540357394,0.8212316403779023 113 | 119,0.19700950083298574,1.6439226999728318 114 | 120,0.19700950083298574,1.6439226999728318 115 | 121,0.19700950083298574,1.6439226999728318 116 | 122,0.19700950083298574,1.6439226999728318 117 | 123,0.19700950083298574,1.6439226999728318 118 | 124,, 119 | 125,0.19700950083298574,1.6439226999728318 120 | 126,0.20800241716935366,1.519448147842392 121 | 127,4.4163874489324915,5.00328052997896 122 | 128,5.805475025848523,7.0999335990651895 123 | 129,6.242559747500323,7.0999335990651895 124 | 130,, 125 | 131,6.242559747500323,7.0999335990651895 126 | 132,6.242559747500323,7.0999335990651895 127 | 133,6.242559747500323,7.0999335990651895 128 | 134,, 129 | 135,, 130 | 136,, 131 | 137,, 132 | 138,8.269633870545356,8.365005033248018 133 | 139,8.240750458213792,8.253171983583803 134 | 140,8.216473942637808,8.210952102579745 135 | 141,8.20825672934682,8.239544888724444 136 | 142,8.196291006710247,8.231363874959749 137 | 143,8.196291006710247,8.231363874959749 138 | 144,8.196291006710247,8.231363874959749 139 | 145,8.196291006710247,8.231363874959749 140 | 146,8.196291006710247,8.231363874959749 141 | 147,8.196291006710247,8.231363874959749 142 | 148,8.196291006710247,8.231363874959749 143 | 149,8.196291006710247,8.231363874959749 144 | 150,8.196291006710247,8.231363874959749 145 | 151,8.196291006710247,8.231363874959749 146 | 152,8.187618090918777,8.203937114150273 147 | 153,8.187618090918777,8.203937114150273 148 | 154,8.187618090918777,8.203937114150273 149 | 155,8.129137434003207,8.203937114150273 150 | -------------------------------------------------------------------------------- /tests/test_data/output/ecSecB_reduced_guess.csv: -------------------------------------------------------------------------------- 1 | # {"comment": "#", "header": [0], "index_col": 0} 2 | r_number,rate,k1,k2,r 3 | 10,0.06452680645034409,0.0014710299877289668,0.18628978677434427,0.4296314385241624 4 | 11,0.06452680645034409,0.0014710299877289668,0.18628978677434427,0.4296314385241624 5 | 12,0.05503298884769727,0.001572986663459952,0.1906938295764471,0.46014025612901505 6 | 13,0.05503298884769727,0.001572986663459952,0.1906938295764471,0.46014025612901505 7 | 14,0.05503298884769727,0.001572986663459952,0.1906938295764471,0.46014025612901505 8 | 15,0.05503298884769727,0.001572986663459952,0.1906938295764471,0.46014025612901505 9 | 16,0.05503298884769727,0.001572986663459952,0.1906938295764471,0.46014025612901505 10 | 17,0.05503298884769727,0.001572986663459952,0.1906938295764471,0.46014025612901505 11 | 18,,,, 12 | 19,0.038132669452901134,0.00014425642137672057,0.08675100637144606,0.37101304403079105 13 | 20,0.038132669452901134,0.00014425642137672057,0.08675100637144606,0.37101304403079105 14 | 21,0.038132669452901134,0.00014425642137672057,0.08675100637144606,0.37101304403079105 15 | 22,0.04484867448709209,0.00015370010713668157,0.09120458204251146,0.339476890426073 16 | 23,0.04484867448709209,0.00015370010713668157,0.09120458204251146,0.339476890426073 17 | 24,0.04484867448709209,0.00015370010713668157,0.09120458204251146,0.339476890426073 18 | 25,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 19 | 26,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 20 | 27,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 21 | 28,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 22 | 29,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 23 | 30,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 24 | 31,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 25 | 32,0.054910372357786924,0.00019806275290812358,0.08735955987693989,0.2524932285792707 26 | 33,0.06276138507334675,0.00018032624091279814,0.10267615530579,0.2635791235318248 27 | 34,0.08139132911517155,0.001250781785435029,0.13923552607793077,0.2843823613186683 28 | -------------------------------------------------------------------------------- /tests/test_data/output/ecSecB_reduced_guess.txt: -------------------------------------------------------------------------------- 1 | rate k1 k2 r 2 | r_number 3 | 10 0.064527 0.001471 0.186290 0.429631 4 | 11 0.064527 0.001471 0.186290 0.429631 5 | 12 0.055033 0.001573 0.190694 0.460140 6 | 13 0.055033 0.001573 0.190694 0.460140 7 | 14 0.055033 0.001573 0.190694 0.460140 8 | 15 0.055033 0.001573 0.190694 0.460140 9 | 16 0.055033 0.001573 0.190694 0.460140 10 | 17 0.055033 0.001573 0.190694 0.460140 11 | 18 NaN NaN NaN NaN 12 | 19 0.038133 0.000144 0.086751 0.371013 13 | 20 0.038133 0.000144 0.086751 0.371013 14 | 21 0.038133 0.000144 0.086751 0.371013 15 | 22 0.044849 0.000154 0.091205 0.339477 16 | 23 0.044849 0.000154 0.091205 0.339477 17 | 24 0.044849 0.000154 0.091205 0.339477 18 | 25 0.054910 0.000198 0.087360 0.252493 19 | 26 0.054910 0.000198 0.087360 0.252493 20 | 27 0.054910 0.000198 0.087360 0.252493 21 | 28 0.054910 0.000198 0.087360 0.252493 22 | 29 0.054910 0.000198 0.087360 0.252493 23 | 30 0.054910 0.000198 0.087360 0.252493 24 | 31 0.054910 0.000198 0.087360 0.252493 25 | 32 0.054910 0.000198 0.087360 0.252493 26 | 33 0.062761 0.000180 0.102676 0.263579 27 | 34 0.081391 0.001251 0.139236 0.284382 -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_delta_batch/log.txt: -------------------------------------------------------------------------------- 1 | # 2022/10/12 17:44:16 (1665589456) 2 | PyHDX v0.4.1+101.gdc82a4a.dirty 3 | Total_loss 3.88, mse_loss 3.65, reg_loss 0.23(5.90%) 4 | Number of epochs: 200 -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_reduced/fit_result.csv: -------------------------------------------------------------------------------- 1 | # PyHDX v0.4.1+101.gdc82a4a.dirty 2 | # 2022/10/12 17:43:23 (1665589403) 3 | # {"r1": 2, "epochs": 1000, "patience": 50, "stop_loss": 5e-06, "optimizer": "SGD", "lr": 10000.0, "momentum": 0.5, "nesterov": true, "model_name": "DeltaGFit", "total_loss": 3.9010414755569216, "mse_loss": 3.5819585237492655, "reg_loss": 0.3190829518076561, "regularization_percentage": 8.179429872944457, "epochs_run": 1000} 4 | # {"comment": "#", "header": [0, 1], "index_col": 0} 5 | state,SecB WT apo,SecB WT apo,SecB WT apo,SecB WT apo,SecB WT apo,SecB WT apo 6 | quantity,sequence,_dG,dG,pfact,k_obs,covariance 7 | r_number,,,,,, 8 | 10,T,19757.28120448772,19757.28120448772,2536.5100423285808,0.06486969207192071,1568.4311197731636 9 | 11,F,19757.556765270547,19757.556765270547,2536.7873653853158,0.05395036350296078,1517.2109906978967 10 | 12,Q,20628.92369164594,20628.92369164594,3584.4547549200574,0.05519571449870551,965.9769593309643 11 | 13,I,17589.484845313964,17589.484845313964,1073.2947758493633,0.04124071083140861,941.0551583140535 12 | 14,Q,19313.523881525016,19313.523881525016,2127.041279354457,0.04769463812438258,945.8464803545261 13 | 15,R,21557.76814426851,21557.76814426851,5181.6214679973145,0.05519504812892025,965.901530674116 14 | 16,I,17551.395460996762,17551.395460996762,1057.1974443604804,0.04384124691974164,941.8995433391453 15 | 17,Y,17551.182007804015,17551.182007804015,1057.1079184775172,0.04486623653053851,942.6607191506314 16 | 18,T,19414.645220775754,,,, 17 | 19,K,22846.842847935965,22846.842847935965,8641.258081824544,0.02510859286372127,1351.3401539958081 18 | 20,D,22847.04574198739,22847.04574198739,8641.953701748349,0.028834821091745896,1195.3335939905312 19 | 21,I,17021.208490840247,17021.208490840247,856.6498814684534,0.021545932923336886,1528.3819378960889 20 | 22,S,22454.0878832421,22454.0878832421,7394.4208032638635,0.02802119906827844,807.4496687439739 21 | 23,F,22416.36162887612,22416.36162887612,7284.568295534334,0.023658443696466976,902.794053296021 22 | 24,E,21778.513968681127,21778.513968681127,5655.888330144271,0.02366065984107018,902.751080127011 23 | 25,A,20448.89886122653,20448.89886122653,3337.3695939818886,0.031858372003408686,709.0579631878827 24 | 26,P,20323.914305793125,,,, 25 | 27,N,22345.8815860305,22345.8815860305,7083.695828939716,0.037681518594450264,669.0738044048961 26 | 28,A,22345.813560209754,22345.813560209754,7083.504651748488,0.04427311875282795,655.1703391352725 27 | 29,P,20145.97013210899,,,, 28 | 30,H,19478.505817858277,19478.505817858277,2270.924952096237,0.03708872903547614,671.9142566570022 29 | 31,V,18462.295592320334,18462.295592320334,1517.4235740214503,0.030715383756997287,721.247237338592 30 | 32,F,18727.769836338583,18727.769836338583,1685.9659736269123,0.037097383801222,671.9605629129414 31 | 33,Q,20048.446554362472,20048.446554362472,2847.1163567977937,0.06948513059455876,979.6544804437572 32 | 34,K,19827.157245276103,19827.157245276103,2607.8129018678096,0.08317765503410189,1793.6301487085734 33 | -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_reduced/fit_result.txt: -------------------------------------------------------------------------------- 1 | PyHDX v0.4.1+101.gdc82a4a.dirty 2 | 2022/10/12 17:43:23 (1665589403) 3 | 4 | Metadata 5 | -------- 6 | r1: 2 7 | epochs: 1000 8 | patience: 50 9 | stop_loss: 5.0e-06 10 | optimizer: SGD 11 | lr: 10000.0 12 | momentum: 0.5 13 | nesterov: true 14 | model_name: DeltaGFit 15 | total_loss: 3.9010414755569216 16 | mse_loss: 3.5819585237492655 17 | reg_loss: 0.3190829518076561 18 | regularization_percentage: 8.179429872944457 19 | epochs_run: 1000 20 | 21 | state SecB WT apo 22 | quantity sequence _dG dG pfact k_obs covariance 23 | r_number 24 | 10 T 19757.281204 19757.281204 2536.510042 0.064870 1568.431120 25 | 11 F 19757.556765 19757.556765 2536.787365 0.053950 1517.210991 26 | 12 Q 20628.923692 20628.923692 3584.454755 0.055196 965.976959 27 | 13 I 17589.484845 17589.484845 1073.294776 0.041241 941.055158 28 | 14 Q 19313.523882 19313.523882 2127.041279 0.047695 945.846480 29 | 15 R 21557.768144 21557.768144 5181.621468 0.055195 965.901531 30 | 16 I 17551.395461 17551.395461 1057.197444 0.043841 941.899543 31 | 17 Y 17551.182008 17551.182008 1057.107918 0.044866 942.660719 32 | 18 T 19414.645221 NaN NaN NaN NaN 33 | 19 K 22846.842848 22846.842848 8641.258082 0.025109 1351.340154 34 | 20 D 22847.045742 22847.045742 8641.953702 0.028835 1195.333594 35 | 21 I 17021.208491 17021.208491 856.649881 0.021546 1528.381938 36 | 22 S 22454.087883 22454.087883 7394.420803 0.028021 807.449669 37 | 23 F 22416.361629 22416.361629 7284.568296 0.023658 902.794053 38 | 24 E 21778.513969 21778.513969 5655.888330 0.023661 902.751080 39 | 25 A 20448.898861 20448.898861 3337.369594 0.031858 709.057963 40 | 26 P 20323.914306 NaN NaN NaN NaN 41 | 27 N 22345.881586 22345.881586 7083.695829 0.037682 669.073804 42 | 28 A 22345.813560 22345.813560 7083.504652 0.044273 655.170339 43 | 29 P 20145.970132 NaN NaN NaN NaN 44 | 30 H 19478.505818 19478.505818 2270.924952 0.037089 671.914257 45 | 31 V 18462.295592 18462.295592 1517.423574 0.030715 721.247237 46 | 32 F 18727.769836 18727.769836 1685.965974 0.037097 671.960563 47 | 33 Q 20048.446554 20048.446554 2847.116357 0.069485 979.654480 48 | 34 K 19827.157245 19827.157245 2607.812902 0.083178 1793.630149 -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_reduced/log.txt: -------------------------------------------------------------------------------- 1 | # 2022/10/12 17:43:23 (1665589403) 2 | PyHDX v0.4.1+101.gdc82a4a.dirty 3 | Total_loss 3.90, mse_loss 3.58, reg_loss 0.32(8.18%) 4 | Number of epochs: 1000 -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_reduced_batch/fit_result.csv: -------------------------------------------------------------------------------- 1 | # PyHDX v0.4.1+101.gdc82a4a.dirty 2 | # 2022/10/12 17:44:14 (1665589454) 3 | # {"r1": 1, "r2": 1, "r2_reference": false, "epochs": 1000, "patience": 50, "stop_loss": 5e-06, "optimizer": "SGD", "lr": 10000.0, "momentum": 0.5, "nesterov": true, "model_name": "DeltaGFit", "total_loss": 3.4407049854708287, "mse_loss": 3.253551544614695, "reg_loss": 0.18715344085613372, "regularization_percentage": 5.439392265434914, "epochs_run": 1000} 4 | # {"comment": "#", "header": [0, 1], "index_col": 0} 5 | state,SecB WT apo,SecB WT apo,SecB WT apo,SecB WT apo,SecB WT apo,SecB WT apo,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo 6 | quantity,sequence,_dG,dG,pfact,k_obs,covariance,sequence,_dG,dG,pfact,k_obs,covariance 7 | r_number,,,,,,,,,,,, 8 | 10,T,19785.840335928344,19785.840335928344,2565.413683518494,0.06413911215186027,1453.55527945289,T,19785.833626368087,19785.833626368087,2565.4068544867932,0.06413928282161568,1711.9199174185992 9 | 11,F,19449.428120762062,19449.428120762062,2244.87731362424,0.06096261359656187,1436.7818361161487,F,19449.43087859832,19449.43087859832,2244.879769857275,0.06096254692407681,1691.5333323565417 10 | 12,Q,20696.962872864176,20696.962872864176,3682.531328154074,0.05372609036551021,839.7983493183048,Q,20696.886230249063,20696.886230249063,3682.4193538416052,0.05372772361479637,1066.0270900087658 11 | 13,I,17096.487363613596,17096.487363613596,882.6207356291535,0.05013992815249046,826.7464187580648,I,17096.382619796037,17096.382619796037,882.5840579587729,0.05014200946636334,1060.1129245483212 12 | 14,Q,19100.419278991096,19100.419278991096,1954.5976852221734,0.05190032668760706,832.8697053011177,Q,19100.386802719448,19100.386802719448,1954.5725009740202,0.05190099507024482,1062.179704777046 13 | 15,R,21625.792367882354,21625.792367882354,5323.367556872738,0.053725637515547715,839.7262632668292,R,21625.709546290545,21625.709546290545,5323.192640229678,0.05372740257342513,1065.947536722467 14 | 16,I,17198.085031788352,17198.085031788352,918.9242187263515,0.050430996927419466,827.7965912244715,I,17198.042325280814,17198.042325280814,918.9086490954218,0.05043185048174831,1060.4165249309663 15 | 17,Y,17198.141555641116,17198.141555641116,918.944826191127,0.051604529743162035,832.2273828506213,Y,17198.08753660646,17198.08753660646,918.9251319230863,0.05160563452158977,1062.1819940036255 16 | 18,T,19414.645220775754,,,,,T,19414.645220775754,,,, 17 | 19,K,22388.68779130164,22388.68779130164,7205.02574919617,0.030112984209630798,1075.0622984134422,K,22388.722907697982,22388.722907697982,7205.126131404816,0.030112564732118367,1136.1467771722969 18 | 20,D,22647.87667366772,22647.87667366772,7985.36054935843,0.031205456121584322,1040.2447001002345,D,22647.792244333374,22647.792244333374,7985.093070867806,0.031206501287002326,1097.2895913659015 19 | 21,I,16263.755741349283,16263.755741349283,634.2961331265222,0.0290870128972546,1112.6404266771697,I,16263.756438665847,16263.756438665847,634.2963086076218,0.029087004862860356,1178.100987880697 20 | 22,S,22050.254447956646,22050.254447956646,6299.73810516643,0.03288956866053879,691.6413022726151,S,21971.471359804735,21971.471359804735,6105.87545743891,0.0339336474054854,716.8865709055923 21 | 23,F,21683.217636634155,21683.217636634155,5446.04208462351,0.031643817807693236,709.2164365753976,F,21603.306147287632,21603.306147287632,5276.087783892502,0.032662941071926486,733.8169149048754 22 | 24,E,21045.437771640267,21045.437771640267,4228.53258102465,0.0316455088060907,709.2071243783034,E,20965.539724751558,20965.539724751558,4096.594872422821,0.03266445676200365,733.8151474492041 23 | 25,A,19780.251733770365,19780.251733770365,2559.731863583739,0.04153305620257319,589.3393601654221,A,19643.33868232643,19643.33868232643,2424.3982429239522,0.04385053906929797,623.7446488862537 24 | 26,P,20236.414305793376,,,,,P,20236.414305793376,,,, 25 | 27,N,21998.198725996404,21998.198725996404,6170.965863954781,0.04325398155121294,584.5429353790894,N,21863.821886580798,21863.821886580798,5850.589490340698,0.045622150708776524,621.1387444397075 26 | 28,A,22301.528972868073,22301.528972868073,6960.137529910511,0.04505773868195002,581.2163711106231,A,22169.87957422773,22169.87957422773,6605.934295766728,0.04747332146965505,620.047236660706 27 | 29,P,20145.97013210899,,,,,P,20145.97013210899,,,, 28 | 30,H,19090.09978762621,19090.09978762621,1946.6115772288915,0.043264688874526815,584.6755640498185,H,18955.849402275366,18955.849402275366,1845.6424608829109,0.04563027804361596,621.3249656015865 29 | 31,V,17700.20845823553,17700.20845823553,1121.4941528081606,0.041549403767549994,589.5060242574051,V,17563.48176936872,17563.48176936872,1062.279035120057,0.04386333336900048,623.9929809057962 30 | 32,F,18339.57228818997,18339.57228818997,1445.3107976115793,0.04327010784029749,584.7428065623648,F,18205.385907803287,18205.385907803287,1370.3785321388555,0.04563439102815351,621.4193331201452 31 | 33,Q,20377.02175321505,20377.02175321505,3243.5430886714785,0.06099525621699391,768.349286897496,Q,20376.513134276378,20376.513134276378,3242.8886384427483,0.061007561929005596,817.523946361823 32 | 34,K,19858.075688694582,19858.075688694582,2639.999026209591,0.08216396047351444,1335.492143945354,K,19858.009898648445,19858.009898648445,2639.9301187035085,0.08216610430669125,1498.4200507288879 33 | -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_reduced_batch/fit_result.txt: -------------------------------------------------------------------------------- 1 | PyHDX v0.4.1+101.gdc82a4a.dirty 2 | 2022/10/12 17:44:14 (1665589454) 3 | 4 | Metadata 5 | -------- 6 | r1: 1 7 | r2: 1 8 | r2_reference: false 9 | epochs: 1000 10 | patience: 50 11 | stop_loss: 5.0e-06 12 | optimizer: SGD 13 | lr: 10000.0 14 | momentum: 0.5 15 | nesterov: true 16 | model_name: DeltaGFit 17 | total_loss: 3.4407049854708287 18 | mse_loss: 3.253551544614695 19 | reg_loss: 0.18715344085613372 20 | regularization_percentage: 5.439392265434914 21 | epochs_run: 1000 22 | 23 | state SecB WT apo SecB his dimer apo 24 | quantity sequence _dG dG pfact k_obs covariance sequence _dG dG pfact k_obs covariance 25 | r_number 26 | 10 T 19785.840336 19785.840336 2565.413684 0.064139 1453.555279 T 19785.833626 19785.833626 2565.406854 0.064139 1711.919917 27 | 11 F 19449.428121 19449.428121 2244.877314 0.060963 1436.781836 F 19449.430879 19449.430879 2244.879770 0.060963 1691.533332 28 | 12 Q 20696.962873 20696.962873 3682.531328 0.053726 839.798349 Q 20696.886230 20696.886230 3682.419354 0.053728 1066.027090 29 | 13 I 17096.487364 17096.487364 882.620736 0.050140 826.746419 I 17096.382620 17096.382620 882.584058 0.050142 1060.112925 30 | 14 Q 19100.419279 19100.419279 1954.597685 0.051900 832.869705 Q 19100.386803 19100.386803 1954.572501 0.051901 1062.179705 31 | 15 R 21625.792368 21625.792368 5323.367557 0.053726 839.726263 R 21625.709546 21625.709546 5323.192640 0.053727 1065.947537 32 | 16 I 17198.085032 17198.085032 918.924219 0.050431 827.796591 I 17198.042325 17198.042325 918.908649 0.050432 1060.416525 33 | 17 Y 17198.141556 17198.141556 918.944826 0.051605 832.227383 Y 17198.087537 17198.087537 918.925132 0.051606 1062.181994 34 | 18 T 19414.645221 NaN NaN NaN NaN T 19414.645221 NaN NaN NaN NaN 35 | 19 K 22388.687791 22388.687791 7205.025749 0.030113 1075.062298 K 22388.722908 22388.722908 7205.126131 0.030113 1136.146777 36 | 20 D 22647.876674 22647.876674 7985.360549 0.031205 1040.244700 D 22647.792244 22647.792244 7985.093071 0.031207 1097.289591 37 | 21 I 16263.755741 16263.755741 634.296133 0.029087 1112.640427 I 16263.756439 16263.756439 634.296309 0.029087 1178.100988 38 | 22 S 22050.254448 22050.254448 6299.738105 0.032890 691.641302 S 21971.471360 21971.471360 6105.875457 0.033934 716.886571 39 | 23 F 21683.217637 21683.217637 5446.042085 0.031644 709.216437 F 21603.306147 21603.306147 5276.087784 0.032663 733.816915 40 | 24 E 21045.437772 21045.437772 4228.532581 0.031646 709.207124 E 20965.539725 20965.539725 4096.594872 0.032664 733.815147 41 | 25 A 19780.251734 19780.251734 2559.731864 0.041533 589.339360 A 19643.338682 19643.338682 2424.398243 0.043851 623.744649 42 | 26 P 20236.414306 NaN NaN NaN NaN P 20236.414306 NaN NaN NaN NaN 43 | 27 N 21998.198726 21998.198726 6170.965864 0.043254 584.542935 N 21863.821887 21863.821887 5850.589490 0.045622 621.138744 44 | 28 A 22301.528973 22301.528973 6960.137530 0.045058 581.216371 A 22169.879574 22169.879574 6605.934296 0.047473 620.047237 45 | 29 P 20145.970132 NaN NaN NaN NaN P 20145.970132 NaN NaN NaN NaN 46 | 30 H 19090.099788 19090.099788 1946.611577 0.043265 584.675564 H 18955.849402 18955.849402 1845.642461 0.045630 621.324966 47 | 31 V 17700.208458 17700.208458 1121.494153 0.041549 589.506024 V 17563.481769 17563.481769 1062.279035 0.043863 623.992981 48 | 32 F 18339.572288 18339.572288 1445.310798 0.043270 584.742807 F 18205.385908 18205.385908 1370.378532 0.045634 621.419333 49 | 33 Q 20377.021753 20377.021753 3243.543089 0.060995 768.349287 Q 20376.513134 20376.513134 3242.888638 0.061008 817.523946 50 | 34 K 19858.075689 19858.075689 2639.999026 0.082164 1335.492144 K 19858.009899 19858.009899 2639.930119 0.082166 1498.420051 -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_reduced_batch/log.txt: -------------------------------------------------------------------------------- 1 | # 2022/10/12 17:44:14 (1665589454) 2 | PyHDX v0.4.1+101.gdc82a4a.dirty 3 | Total_loss 3.44, mse_loss 3.25, reg_loss 0.19(5.44%) 4 | Number of epochs: 1000 -------------------------------------------------------------------------------- /tests/test_data/output/ecsecb_tetramer_dimer/log.txt: -------------------------------------------------------------------------------- 1 | # 2022/10/12 17:59:00 (1665590340) 2 | PyHDX v0.4.1+101.gdc82a4a.dirty 3 | Total_loss 0.71, mse_loss 0.39, reg_loss 0.32(45.34%) 4 | Number of epochs: 200000 -------------------------------------------------------------------------------- /tests/test_data/output/web/ddG_comparison.csv: -------------------------------------------------------------------------------- 1 | # PyHDX v0.4.1+103.g6c72a90.dirty 2 | # 2022/08/16 11:20:44 (1660641644) 3 | # {"comment": "#", "header": [0, 1, 2], "index_col": 0} 4 | comparison_name,comparison_1,comparison_1 5 | comparison_state,SecB_dimer,SecB_dimer 6 | quantity,ddG,covariance 7 | r_number,, 8 | 11,324.14839345396103,3819.1811770211743 9 | 12,324.1585464441596,4770.569086557881 10 | 13,-1641.5326539498637,2161.4534533108135 11 | 14,-1020.1261517554922,2614.0270689533063 12 | 15,-1219.4348711696475,6011.914373015615 13 | 16,-1483.9306435773433,2103.373914141971 14 | 17,-1483.9315110323187,2109.2752670910954 15 | 18,, 16 | 19,, 17 | 20,523.0436837190464,4261.827954938101 18 | 21,-1536.738967826288,2066.8512206134537 19 | 22,-599.6365649617946,4975.245569349427 20 | 23,-2472.9585601593135,20027.81758161017 21 | 24,-2472.9700291323898,14400.420510225385 22 | 25,-2472.9497222232458,18635.410914390653 23 | 26,, 24 | 27,106.86791092367275,3614.5467839424214 25 | 28,106.86853387436713,3408.1100433837323 26 | 29,, 27 | 30,-541.3071397027416,6119.555998701294 28 | 31,-6418.823621004991,3940.978341837918 29 | 32,-993.1849071471224,4081.5662320280435 30 | 33,425.0333272626085,3770.732956093258 31 | 34,425.0154017102068,4505.60581124256 32 | 35,1168.7018626754616,2962.607240213756 33 | 36,-117.45274746965151,3742.973569576989 34 | 37,1755.7858361354993,9394.447488133052 35 | 38,, 36 | 39,3353.5842308286265,4966.979496402648 37 | 40,-1450.9518970542485,2082.5756201895983 38 | 41,690.430476861271,2054.297780853146 39 | 42,476.24340968047727,3888.2071155934323 40 | 43,607.3432394363608,5975.986720674321 41 | 44,-1035.088622983756,3724.1484235093417 42 | 45,-232.30597075126025,2754.7934827225777 43 | 46,-19106.676353745148,3757.1264600311174 44 | 47,-1440.3456860372244,6733.141839361974 45 | 48,-1867.2120547882514,7378.88416428242 46 | 49,13776.534629153019,3991.443438849754 47 | 50,-2726.6315504146623,8849.43937526878 48 | 51,-3616.2155869576964,3733.9575511703324 49 | 52,1085.9465501218147,6018.2898200926575 50 | 53,552.586065936568,3587.7417560943504 51 | 54,552.6033952434991,4421.814300417166 52 | 55,41.42863822059735,1354.2540664558774 53 | 56,41.41499386869327,1712.5872277967894 54 | 57,-189.82590579926546,3291.8016587902953 55 | 58,, 56 | 59,, 57 | 60,, 58 | 61,, 59 | 62,, 60 | 63,-3592.587623149255,4639.977711586462 61 | 64,-4173.822659381378,4074.720479755051 62 | 65,-5804.507018784723,13096.566160956263 63 | 66,-2946.9414211742624,7988.212001202946 64 | 67,-2946.933515615092,6703.053449765075 65 | 68,9262.370806922274,4080.9053533214687 66 | 69,4826.196854162543,2143.1234760975676 67 | 70,1105.1394863162168,2802.7528498409965 68 | 71,3027.2816860087514,2220.504737053217 69 | 72,-2623.9844262656406,3187.0738755030043 70 | 73,-1136.2600748691184,2825.05228618972 71 | 74,-9676.726383460013,3566.3615435755896 72 | 75,-4677.733220754319,2994.897621631992 73 | 76,, 74 | 77,-4330.058992478218,7890.759853847792 75 | 78,-7010.934544585209,6870.372531748587 76 | 79,-6022.297292906434,5619.2670058946915 77 | 80,-6522.418364543162,16571.846625006896 78 | 81,-6522.379298603166,7256.541227364457 79 | 82,-6522.411864688645,8399.647546895825 80 | 83,-6747.413827268931,6804.9170663574705 81 | 84,-6747.432892143246,8027.198751964717 82 | 85,, 83 | 86,, 84 | 87,-1914.8166506372982,3160.5798086143645 85 | 88,-31.302964186699683,5535.285297660964 86 | 89,-648.634570787337,3322.032402888714 87 | 90,-648.6324348984781,2796.775139348815 88 | 91,-506.86017027324124,3182.958911926744 89 | 92,867.6890554265156,4792.881401165835 90 | 93,1597.1544323996604,4179.393730105658 91 | 94,, 92 | 95,, 93 | 96,, 94 | 97,, 95 | 98,, 96 | 99,, 97 | 100,, 98 | 101,-12460.430247394223,4307.029405459188 99 | 102,-11238.664831268019,3570.2613890210196 100 | 103,, 101 | 104,-9979.750510905858,4024.142466884978 102 | 105,-7569.39983319446,6651.910197932905 103 | 106,-8207.220785032914,7201.850766431775 104 | 107,-8493.287568640528,7863.708492708247 105 | 108,, 106 | 109,-10932.81003406813,7956.970963311015 107 | 110,-14110.436062998942,8100.780001022508 108 | 111,-14284.528440641414,8074.645567737245 109 | 112,-14335.016306096873,8073.3340885818825 110 | 113,-10461.522413739232,9025.385830278909 111 | 114,, 112 | 115,, 113 | 116,-4706.135481133897,5501.607803053111 114 | 117,-4651.988284844832,2994.535267859268 115 | 118,-3248.014227751046,3939.297901788915 116 | 119,-3472.810070481217,3474.4592719980687 117 | 120,-3487.3909323547377,2227.740750736499 118 | 121,-11215.721655218324,2024.0982551612885 119 | 122,-10693.283891574163,1844.5974606687573 120 | 123,-9297.96235412865,1753.7338955751277 121 | 124,, 122 | 125,4794.912279871292,1906.7063587653133 123 | 126,2991.560226519585,2382.50881442652 124 | 127,1205.4697947178356,4425.190618199888 125 | 128,618.7342448756845,4449.44121670189 126 | 129,618.7559058252536,14704.095720970865 127 | 130,, 128 | 131,-780.4175344730447,4016.7323157375067 129 | 132,-188.61821637966568,12239.885898847302 130 | 133,-188.60356792013044,9998.23143813858 131 | 134,, 132 | 135,, 133 | 136,, 134 | 137,, 135 | 138,, 136 | 139,514.5201555720741,13725.451008058466 137 | 140,742.8716343379347,13096.190011244302 138 | 141,583.7844858177814,12044.453923745035 139 | 142,320.6914024044054,24858.835725880363 140 | 143,531.4847114791792,4090.5768676818184 141 | 144,531.5070984882714,4090.5328843035777 142 | 145,531.5004210819643,3581.1829302442948 143 | 146,442.1278785224131,3017.1754228371487 144 | 147,442.1599050528457,3412.558578327914 145 | 148,442.1598546139885,6676.33931581649 146 | 149,442.1643200659146,3672.1332402753164 147 | 150,442.17658193036914,3600.831281384174 148 | 151,442.1945445668498,12539.145422796491 149 | 152,442.22903262722866,6538.998967167178 150 | 153,13.993962513821316,3349.068960897598 151 | 154,-88.48495479859048,3691.776781509257 152 | 155,-11584.969787070699,2168.0951002058528 153 | -------------------------------------------------------------------------------- /tests/test_data/output/web/loss.csv: -------------------------------------------------------------------------------- 1 | # PyHDX v0.4.0b8+11.ga9afe73 2 | # 2022/04/01 19:02:10 (1648832530) 3 | # {"comment": "#", "header": [0, 1], "index_col": 0} 4 | fit_ID,testfit_1,testfit_1,testfit_1 5 | loss_type,mse_loss,reg_1,reg_2 6 | epoch,,, 7 | 1,6.920279968095333,0.19799864651673482,0.10806670982379456 8 | 2,6.919951147194551,0.1979982881796757,0.1080679907206308 9 | 3,6.919568537622394,0.19799786655457707,0.10806933129247444 10 | 4,6.919158740116222,0.19799741444769908,0.10807070984391343 11 | 5,6.9187353816001975,0.19799694708055746,0.10807217162117731 12 | 6,6.918305277401368,0.19799647206364313,0.10807361157445321 13 | 7,6.917871835724202,0.19799599320197397,0.1080750918036468 14 | 8,6.9174367546755615,0.19799551239802918,0.10807655146918058 15 | 9,6.917000890535733,0.19799503060293244,0.10807803405467542 16 | 10,6.916564660156683,0.19799454829227525,0.1080795080071103 17 | -------------------------------------------------------------------------------- /tests/test_data/output/web/peptide_mse.csv: -------------------------------------------------------------------------------- 1 | # PyHDX v0.4.0b8+11.ga9afe73 2 | # 2022/04/01 19:02:10 (1648832530) 3 | # {"comment": "#", "header": [0, 1, 2], "index_col": 0} 4 | fit_ID,testfit_1,testfit_1,testfit_1,testfit_1,testfit_1,testfit_1,testfit_1,testfit_1 5 | state,testname_123,testname_123,testname_123,testname_123,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo,SecB his dimer apo 6 | quantity,start,end,sequence,peptide_mse,start,end,sequence,peptide_mse 7 | peptide_id,,,,,,,, 8 | 0,10,18,xTFQIQRIY,3.947739266922723,10,18,xTFQIQRIY,3.5193569910950804 9 | 1,12,18,xQIQRIY,2.3717045511496093,12,18,xQIQRIY,2.1384184221582268 10 | 2,19,33,xKDISFEApNApHVF,16.61492220641772,19,33,xKDISFEApNApHVF,12.20196135659875 11 | 3,19,34,xKDISFEApNApHVFQ,15.742157716336303,19,34,xKDISFEApNApHVFQ,12.647634413447747 12 | 4,19,35,xKDISFEApNApHVFQK,16.678306686824104,19,35,xKDISFEApNApHVFQK,12.836832215010304 13 | 5,19,42,xKDISFEApNApHVFQKDWQpEVK,34.359808118009354,22,33,xSFEApNApHVF,4.511914792438984 14 | 6,22,33,xSFEApNApHVF,7.430998035446716,22,34,xSFEApNApHVFQ,5.753469481956724 15 | 7,22,34,xSFEApNApHVFQ,8.037460246821254,22,35,xSFEApNApHVFQK,6.494110749150896 16 | 8,22,35,xSFEApNApHVFQK,8.815910899820494,22,42,xSFEApNApHVFQKDWQpEVK,24.13162507034316 17 | 9,22,42,xSFEApNApHVFQKDWQpEVK,20.91487443108386,22,43,xSFEApNApHVFQKDWQpEVKL,17.76373147551497 18 | 10,22,43,xSFEApNApHVFQKDWQpEVKL,18.468902248171627,25,33,xApNApHVF,0.3609703853208824 19 | 11,22,44,xSFEApNApHVFQKDWQpEVKLD,30.37234096055923,25,35,xApNApHVFQK,0.8908170074942291 20 | 12,25,33,xApNApHVF,0.8514561150894192,25,42,xApNApHVFQKDWQpEVK,10.066012360077496 21 | 13,25,35,xApNApHVFQK,1.1807655813307292,25,43,xApNApHVFQKDWQpEVKL,7.15653718893671 22 | 14,25,42,xApNApHVFQKDWQpEVK,7.233739139519347,34,44,xKDWQpEVKLD,4.266314302161917 23 | 15,25,43,xApNApHVFQKDWQpEVKL,6.181929755301458,36,42,xWQpEVK,1.264247404139059 24 | 16,27,42,xxNApHVFQKDWQpEVK,7.151348635895382,39,47,xxEVKLDLDT,1.6794665381701455 25 | 17,34,44,xKDWQpEVKLD,4.703232366176631,43,52,xDLDTASSQL,9.5487580902498 26 | 18,36,42,xWQpEVK,1.3908454166752364,43,55,xDLDTASSQLADD,10.95902965703607 27 | 19,39,47,xxEVKLDLDT,2.257673293494475,43,57,xDLDTASSQLADDVY,21.024491284270947 28 | 20,43,52,xDLDTASSQL,12.976575682111333,43,58,xDLDTASSQLADDVYE,25.302209400825973 29 | 21,43,55,xDLDTASSQLADD,15.153234266100844,44,52,xLDTASSQL,8.646015633649975 30 | 22,43,57,xDLDTASSQLADDVY,28.95221878023521,46,52,xTASSQL,5.043814840434739 31 | 23,43,58,xDLDTASSQLADDVYE,33.543359441905494,62,69,xVTVTASL,2.386582805241739 32 | 24,44,52,xLDTASSQL,12.088562484921596,62,75,xVTVTASLGEETAF,9.66592792871135 33 | 25,46,52,xTASSQL,7.30630191360729,62,76,xVTVTASLGEETAFL,14.185153732471818 34 | 26,62,69,xVTVTASL,4.488219456639876,63,69,xTVTASL,1.39370551838038 35 | 27,62,72,xVTVTASLGEE,7.160288764612365,65,76,xTASLGEETAFL,6.27369706758093 36 | 28,62,75,xVTVTASLGEETAF,13.580273538802913,67,75,xSLGEETAF,2.9554955192397796 37 | 29,62,76,xVTVTASLGEETAFL,19.81239500581753,67,76,xSLGEETAFL,4.277780490037302 38 | 30,63,69,xTVTASL,3.0903548932725755,76,85,xCEVQQGGIF,0.5302477228070371 39 | 31,65,76,xTASLGEETAFL,9.418588562865208,77,85,xEVQQGGIF,0.5100701074307675 40 | 32,67,75,xSLGEETAF,3.589399169298512,79,85,xQQGGIF,0.4788242817039919 41 | 33,67,76,xSLGEETAFL,6.416518742742589,86,94,xIAGIEGTQ,1.0427810505298838 42 | 34,76,85,xCEVQQGGIF,0.881308931618412,100,107,xAYCpNIL,0.6610351683174646 43 | 35,77,85,xEVQQGGIF,0.6543923425482588,100,108,xAYCpNILF,0.5838612701876289 44 | 36,79,85,xQQGGIF,0.7148396627982425,100,113,xAYCpNILFpAARE,1.9557524662004802 45 | 37,86,94,xIAGIEGTQ,1.5948877139049824,100,114,xAYCpNILFpAAREC,1.6563698503449367 46 | 38,86,95,xIAGIEGTQM,1.8490772369424857,102,108,xCpNILF,1.3249179768843675 47 | 39,100,107,xAYCpNIL,0.9908224715415389,115,127,xASMVARGTFpQL,10.729795408234082 48 | 40,100,108,xAYCpNILF,1.3089738281896703,115,134,xASMVARGTFpQLNLApVNF,20.959691605553846 49 | 41,100,113,xAYCpNILFpYARE,1.6338361965386057,117,127,xMVARGTFpQL,4.455643252434257 50 | 42,100,114,xAYCpNILFpYAREC,1.793940240489376,118,127,xVARGTFpQL,2.8443541533485788 51 | 43,102,108,xCpNILF,0.1338577036748709,119,126,xARGTFpQ,0.42118679619568206 52 | 44,102,113,xCpNILFpYARE,0.5361558662268714,119,127,xARGTFpQL,0.958645011952863 53 | 45,115,127,xTSMVSRGTFpQL,6.796974200420517,119,134,xARGTFpQLNLApVNF,5.7126670245823625 54 | 46,115,134,xTSMVSRGTFpQLNLApVNF,13.478856161431368,128,134,xLApVNF,0.8330615808458443 55 | 47,117,127,xMVSRGTFpQL,3.9252946709072325,138,156,xMNYLQQQAGEGTEEHQDA,11.110015403919283 56 | 48,118,127,xVSRGTFpQL,3.382150817649157,139,152,xNYLQQQAGEGTEE,5.599360709620225 57 | 49,119,126,xSRGTFpQ,1.8182754466494158,139,156,xNYLQQQAGEGTEEHQDA,9.529616980086237 58 | 50,119,127,xSRGTFpQL,2.949626372810016,140,156,xYLQQQAGEGTEEHQDA,9.082120839840083 59 | 51,119,129,xSRGTFpQLNL,4.234278419950299,141,156,xLQQQAGEGTEEHQDA,7.732288541629598 60 | 52,119,134,xSRGTFpQLNLApVNF,6.331156410461599,142,156,xQQQAGEGTEEHQDA,6.5932775993787525 61 | 53,128,134,xLApVNF,0.55048232978919,,,, 62 | 54,138,152,xMNYLQQQAGEGTEE,6.263180073254058,,,, 63 | 55,138,155,xMNYLQQQAGEGTEEHQD,9.762034081811764,,,, 64 | 56,138,156,xMNYLQQQAGEGTEEHQDA,10.194681389135782,,,, 65 | 57,139,152,xNYLQQQAGEGTEE,5.57541894966696,,,, 66 | 58,139,156,xNYLQQQAGEGTEEHQDA,9.059184211291472,,,, 67 | 59,140,156,xYLQQQAGEGTEEHQDA,8.14991015267399,,,, 68 | 60,141,156,xLQQQAGEGTEEHQDA,7.376786738625948,,,, 69 | 61,142,155,xQQQAGEGTEEHQD,6.1479782283383715,,,, 70 | 62,142,156,xQQQAGEGTEEHQDA,6.409730210979206,,,, 71 | -------------------------------------------------------------------------------- /tests/test_datasets.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import numpy as np 4 | import yaml 5 | 6 | from pyhdx.datasets import DataSet as HDXDataSet 7 | from pyhdx.models import HDXMeasurement, HDXMeasurementSet 8 | 9 | cwd = Path(__file__).parent 10 | input_dir = cwd / "test_data" / "input" 11 | output_dir = cwd / "test_data" / "output" 12 | 13 | np.random.seed(43) 14 | 15 | 16 | def test_load_from_yaml(): 17 | yaml_pth = Path(input_dir / "data_states.yaml") 18 | hdx_spec = yaml.safe_load(yaml_pth.read_text()) 19 | 20 | dataset = HDXDataSet.from_spec(hdx_spec, data_dir=input_dir) 21 | 22 | hdxm = HDXMeasurement.from_dataset(dataset, state="SecB_tetramer") 23 | assert isinstance(hdxm, HDXMeasurement) 24 | 25 | assert ( 26 | hdxm.temperature 27 | == hdx_spec["states"]["SecB_tetramer"]["metadata"]["temperature"]["value"] + 273.15 28 | ) 29 | 30 | assert hdxm.name == "SecB_tetramer" 31 | assert hdxm.state == "SecB WT apo" 32 | 33 | hdxm_set = HDXMeasurementSet.from_dataset(dataset) 34 | assert isinstance(hdxm_set, HDXMeasurementSet) 35 | assert hdxm_set.names == list(hdx_spec["states"].keys()) 36 | -------------------------------------------------------------------------------- /tests/test_fileIO.py: -------------------------------------------------------------------------------- 1 | import pyhdx 2 | from pyhdx import TorchFitResult 3 | from pyhdx.fileIO import ( 4 | csv_to_dataframe, 5 | dataframe_to_stringio, 6 | dataframe_to_file, 7 | save_fitresult, 8 | load_fitresult, 9 | ) 10 | 11 | from pathlib import Path 12 | from io import StringIO 13 | import numpy as np 14 | import pandas as pd 15 | import pytest 16 | 17 | from pyhdx.models import HDXMeasurement, HDXMeasurementSet 18 | from pyhdx.fitting import fit_gibbs_global 19 | from pyhdx.datasets import read_dynamx, filter_peptides 20 | from pyhdx.process import apply_control, correct_d_uptake 21 | 22 | cwd = Path(__file__).parent 23 | input_dir = cwd / "test_data" / "input" 24 | output_dir = cwd / "test_data" / "output" 25 | 26 | 27 | @pytest.fixture() 28 | def hdxm() -> HDXMeasurement: 29 | fpath = input_dir / "ecSecB_apo.csv" 30 | df = read_dynamx(fpath) 31 | 32 | fd = { 33 | "state": "Full deuteration control", 34 | "exposure": {"value": 0.167, "unit": "min"}, 35 | } 36 | 37 | fd_df = filter_peptides(df, **fd) 38 | peptides = filter_peptides(df, state="SecB WT apo") # , query=["exposure != 0."]) 39 | peptides_control = apply_control(peptides, fd_df) 40 | peptides_corrected = correct_d_uptake(peptides_control) 41 | 42 | temperature, pH = 273.15 + 30, 8.0 43 | hdxm = HDXMeasurement(peptides_corrected, temperature=temperature, pH=pH, c_term=155) 44 | 45 | return hdxm 46 | 47 | 48 | @pytest.fixture() 49 | def fit_result(hdxm) -> TorchFitResult: 50 | initial_rates = csv_to_dataframe(output_dir / "ecSecB_guess.csv") 51 | gibbs_guess = hdxm.guess_deltaG(initial_rates["rate"]) 52 | 53 | fit_result = fit_gibbs_global(hdxm, gibbs_guess, epochs=100, r1=2) 54 | return fit_result 55 | 56 | 57 | def test_read_dynamx(): 58 | fpath = input_dir / "ecSecB_apo.csv" 59 | df = read_dynamx(fpath) 60 | 61 | assert df.shape[0] == 567 62 | assert df["start"][0] == 9 63 | assert df["end"][0] == 17 64 | assert df["stop"][0] == 18 65 | 66 | with open(fpath, mode="r") as f: 67 | df = read_dynamx(StringIO(f.read())) 68 | assert df.shape[0] == 567 69 | 70 | 71 | def test_read_write_tables(tmp_path): 72 | # Single-index columns 73 | df = pd.DataFrame(np.random.randn(25, 4), columns=list("ABCD")) 74 | df.index.name = "singlecolumnindex" 75 | 76 | sio = StringIO() 77 | dataframe_to_stringio(df, sio) 78 | sio.seek(0) 79 | df_read = csv_to_dataframe(sio) 80 | pd.testing.assert_frame_equal(df, df_read) 81 | 82 | fpath = Path(tmp_path) / "single_index.csv" 83 | dataframe_to_file(fpath, df) 84 | csv_to_dataframe(fpath) 85 | pd.testing.assert_frame_equal(df, df_read) 86 | 87 | # multi-index column 88 | cols = pd.MultiIndex.from_product([("a", "b"), ("x", "y")]) 89 | df = pd.DataFrame(np.random.randn(25, 4), columns=cols) 90 | df.index.name = "multicolumnindex" 91 | 92 | sio = StringIO() 93 | dataframe_to_stringio(df, sio) 94 | sio.seek(0) 95 | df_read = csv_to_dataframe(sio) 96 | pd.testing.assert_frame_equal(df, df_read) 97 | 98 | fpath = Path(tmp_path) / "multi_index.csv" 99 | dataframe_to_file(fpath, df) 100 | df_read = csv_to_dataframe(fpath) 101 | pd.testing.assert_frame_equal(df, df_read) 102 | 103 | metadata = { 104 | "instrumuent": "LCMS", 105 | "settings": {"pressure": "5 kPa", "temperature": "400K"}, 106 | } 107 | 108 | df.attrs["metadata"] = metadata 109 | 110 | fpath = Path(tmp_path) / "multi_index_with_metadata.csv" 111 | dataframe_to_file(fpath, df) 112 | df_read = csv_to_dataframe(fpath) 113 | pd.testing.assert_frame_equal(df, df_read) 114 | 115 | assert df_read.attrs["metadata"] == metadata 116 | 117 | fpath = Path(tmp_path) / "multi_index_with_metadata.txt" 118 | dataframe_to_file(fpath, df, fmt="pprint", include_version=True) 119 | lines = Path(fpath).read_text().split("\n") 120 | assert len(lines) == 38 121 | assert lines[0].strip() == pyhdx.VERSION_STRING 122 | 123 | 124 | def test_load_save_fitresult(tmp_path, fit_result: TorchFitResult, hdxm: HDXMeasurement): 125 | # todo missing read batch result test 126 | 127 | fpath = Path(tmp_path) / "fit_result_single.csv" 128 | fit_result.to_file(fpath) 129 | df = csv_to_dataframe(fpath) 130 | assert df.attrs["metadata"] == fit_result.metadata 131 | fit_result_dir = Path(tmp_path) / "fit_result" 132 | 133 | save_fitresult(fit_result_dir, fit_result, log_lines=["test123"]) 134 | 135 | log_lines = Path(fit_result_dir / "log.txt").read_text().split("\n") 136 | assert log_lines[-1] == "test123" 137 | 138 | fit_result_loaded = load_fitresult(fit_result_dir) 139 | assert isinstance(fit_result_loaded.losses, pd.DataFrame) 140 | assert isinstance(fit_result_loaded.hdxm_set, HDXMeasurementSet) 141 | 142 | timepoints = np.linspace(0, 30 * 60, num=100) 143 | d_calc = fit_result_loaded(timepoints) 144 | assert d_calc.shape == (1, hdxm.Np, len(timepoints)) 145 | 146 | losses = csv_to_dataframe(fit_result_dir / "losses.csv") 147 | fr_load_with_hdxm_and_losses = load_fitresult(fit_result_dir) 148 | assert len(fr_load_with_hdxm_and_losses.losses) == 100 149 | 150 | assert fr_load_with_hdxm_and_losses.metadata["total_loss"] == losses.iloc[-1].sum() 151 | -------------------------------------------------------------------------------- /tests/test_models.py: -------------------------------------------------------------------------------- 1 | import tempfile 2 | from pathlib import Path 3 | 4 | import numpy as np 5 | import pandas as pd 6 | from pandas.testing import assert_frame_equal 7 | 8 | from pyhdx import HDXMeasurement 9 | from pyhdx.datasets import read_dynamx 10 | from pyhdx.fileIO import csv_to_dataframe, csv_to_hdxm 11 | from pyhdx.models import Coverage 12 | from pyhdx.process import apply_control, correct_d_uptake, filter_peptides 13 | 14 | cwd = Path(__file__).parent 15 | input_dir = cwd / "test_data" / "input" 16 | output_dir = cwd / "test_data" / "output" 17 | 18 | 19 | class TestHDXMeasurement(object): 20 | @classmethod 21 | def setup_class(cls): 22 | fpath = input_dir / "ecSecB_apo.csv" 23 | df = read_dynamx(fpath) 24 | 25 | fd = { 26 | "state": "Full deuteration control", 27 | "exposure": {"value": 0.167, "unit": "min"}, 28 | } 29 | 30 | fd_df = filter_peptides(df, **fd) 31 | peptides = filter_peptides(df, state="SecB WT apo") # , query=["exposure != 0."]) 32 | peptides_control = apply_control(peptides, fd_df) 33 | peptides_corrected = correct_d_uptake(peptides_control) 34 | 35 | cls.temperature, cls.pH = 273.15 + 30, 8.0 36 | cls.hdxm = HDXMeasurement( 37 | peptides_corrected, temperature=cls.temperature, pH=cls.pH, c_term=155 38 | ) 39 | 40 | def test_dim(self): 41 | assert self.hdxm.Nt == len(self.hdxm.data["exposure"].unique()) 42 | 43 | def test_guess(self): 44 | pass 45 | 46 | def test_tensors(self): 47 | # tensors = self.hdxm.get_tensors() 48 | # assert ... 49 | pass 50 | 51 | def test_rfu(self): 52 | rfu_residues = self.hdxm.rfu_residues 53 | compare = csv_to_dataframe(output_dir / "ecSecB_rfu_per_exposure.csv") 54 | compare.columns = compare.columns.astype(float) 55 | compare.columns.name = "exposure" 56 | assert_frame_equal(rfu_residues, compare) 57 | 58 | def test_to_file(self): 59 | with tempfile.TemporaryDirectory() as tempdir: 60 | fpath = Path(tempdir) / "hdxm.csv" 61 | self.hdxm.to_file(fpath) 62 | hdxm_read = csv_to_hdxm(fpath) 63 | k1 = self.hdxm.coverage["k_int"] 64 | k2 = hdxm_read.coverage["k_int"] 65 | pd.testing.assert_series_equal(k1, k2) 66 | 67 | assert self.hdxm.metadata == hdxm_read.metadata 68 | 69 | 70 | class TestCoverage(object): 71 | @classmethod 72 | def setup_class(cls): 73 | fpath = input_dir / "ecSecB_apo.csv" 74 | df = read_dynamx(fpath) 75 | 76 | fd = { 77 | "state": "Full deuteration control", 78 | "exposure": {"value": 0.167, "unit": "min"}, 79 | } 80 | 81 | fd_df = filter_peptides(df, **fd) 82 | peptides = filter_peptides(df, state="SecB WT apo") # , query=["exposure != 0."]) 83 | peptides_control = apply_control(peptides, fd_df) 84 | peptides_corrected = correct_d_uptake(peptides_control) 85 | 86 | cls.hdxm = HDXMeasurement(peptides_corrected, c_term=155) 87 | cls.sequence = ( 88 | "MSEQNNTEMTFQIQRIYTKDISFEAPNAPHVFQKDWQPEVKLDLDTASSQLADDVYEVVLRVTVTASLGEETAFLCEVQQGGIFSIAGIEGTQM" 89 | "AHCLGAYCPNILFPYARECITSMVSRGTFPQLNLAPVNFDALFMNYLQQQAGEGTEEHQDA" 90 | ) 91 | 92 | def test_sequence(self): 93 | data = self.hdxm[0].data 94 | cov = Coverage(data, c_term=155) 95 | 96 | for r, s in zip(cov.r_number, cov["sequence"]): 97 | if s != "X": 98 | assert self.sequence[r - 1] == s 99 | 100 | assert cov.protein.index.max() == 155 101 | cov_seq = Coverage(data, sequence=self.sequence) 102 | assert cov_seq.protein.index.max() == len(self.sequence) 103 | 104 | for r, s in zip(cov_seq.r_number, cov_seq["sequence"]): 105 | assert self.sequence[r - 1] == s 106 | 107 | def test_dim(self): 108 | cov = self.hdxm.coverage 109 | assert cov.Np == len(np.unique(cov.data["sequence"])) 110 | assert cov.Nr == len(cov.r_number) 111 | 112 | assert cov.Np == 63 113 | assert cov.Nr == 146 114 | 115 | def test_XZ(self): 116 | test_X = np.genfromtxt(output_dir / "attributes" / "X.txt") 117 | assert np.allclose(self.hdxm.coverage.X, test_X) 118 | 119 | test_Z = np.genfromtxt(output_dir / "attributes" / "Z.txt") 120 | assert np.allclose(self.hdxm.coverage.Z, test_Z) 121 | -------------------------------------------------------------------------------- /tests/test_support.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib as mpl 3 | from pyhdx.support import rgb_to_hex 4 | 5 | 6 | class TestSupportFunctions(object): 7 | def test_color_converions(self): 8 | all_rbg = np.arange(256**3, dtype=np.uint32).view(np.uint8).reshape(256**3, -1) 9 | 10 | selected_rgb = all_rbg[np.random.randint(0, 1000) :: 1000] 11 | hex_mpl = np.array([mpl.colors.to_hex(rgba).upper() for rgba in selected_rgb / 255]) 12 | 13 | hex_pyhdx = rgb_to_hex(selected_rgb) 14 | assert np.all(hex_pyhdx == hex_mpl) 15 | -------------------------------------------------------------------------------- /tests/test_templates.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import sys 3 | 4 | 5 | class TestTemplates(object): 6 | def test_templates_converions(self): 7 | # import templates and run 8 | template_dir = Path(__file__).parent.parent / "templates" # / something 9 | print(template_dir) 10 | sys.path.append(str(template_dir)) 11 | 12 | scripts = [ 13 | f.stem for f in template_dir.iterdir() if f.stem[:2].isdigit() and f.suffix == ".py" 14 | ] 15 | for s in scripts: 16 | pass 17 | # doesnt work with some scripts requiring a cluster 18 | # print(s) 19 | # importlib.import_module(s) 20 | --------------------------------------------------------------------------------