Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
75ab998
Add install_requires to setup.py
cabb99 Mar 30, 2026
d357d86
Removes empty decorator causing errors
cabb99 Mar 31, 2026
e5a6ea3
Changes separator delimiter for compatibility with pandas 3.0
cabb99 Mar 31, 2026
c47f5b9
Sets default directory for cleaned structures to /tmp
cabb99 Mar 31, 2026
678d1f6
Moves test output to temp
cabb99 Mar 31, 2026
2a96631
Add automated PyPI publish workflow with TestPyPI validation
cabb99 Mar 31, 2026
63acca7
Use conda in publish test job for openmm/pdbfixer
cabb99 Mar 31, 2026
9e32e14
Merge pull request #22 from cabb99/main
cabb99 Mar 31, 2026
be36e98
Fixes numpy installation for tests
cabb99 Mar 31, 2026
7c59968
Clarifies DCA as an energy model.
cabb99 Mar 31, 2026
2325d8f
Updates README to include pip and conda installation
cabb99 Mar 31, 2026
d75539d
Clarifies DCA as an energy model
cabb99 Mar 31, 2026
fd2838b
Vectorizes mutational_decoy_energy_fluctuation
cabb99 Mar 31, 2026
2d7f240
Adds numba functions to cache
cabb99 Mar 31, 2026
689dfae
Reduces tests for compute_region_means
cabb99 Mar 31, 2026
a8b8e5d
Using functools for jit compilation cache
cabb99 Mar 31, 2026
2e71b4d
Creates awsem model only once
cabb99 Mar 31, 2026
f027897
Fixes edge case in inner product.
cabb99 Apr 1, 2026
3a4330c
Fixes pydca tests
cabb99 Apr 1, 2026
0faffc2
skip slow tests in CI
cabb99 Apr 2, 2026
e2ac7f7
Adds marker for stochastic tests
cabb99 Apr 2, 2026
edfe05c
Fixes compatibility for python 3.7 to 3.13
cabb99 Apr 2, 2026
489e840
Fixes diff_mean_inner_product_2_by_2 test to avoid negative repetitions
cabb99 Apr 3, 2026
3b6a65b
Fixes negative values in repetitions
cabb99 Apr 3, 2026
48aca43
Adds all python versions by default to pytest
cabb99 Apr 3, 2026
fa5cbfd
skip memory heavy tests
cabb99 Apr 3, 2026
d0beac0
marks memory heavy tests
cabb99 Apr 3, 2026
c3418d2
Update CI workflow to exclude Python 3.14
cabb99 Apr 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 12 additions & 27 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
#os: [macOS-latest, ubuntu-latest, windows-latest]
#python-version: [3.8, 3.9, "3.10"]
os: [ubuntu-latest]
python-version: [3.8, 3.9, "3.10", "3.11"]
python-version: ["3.12", "3.13"]
steps:
- uses: actions/checkout@v1

Expand Down Expand Up @@ -92,44 +92,29 @@ jobs:

#Install numpy first
- name: Install numpy
shell: bash -l {0}
run: |
conda install "numpy<2"
conda install "numpy"

#Install pydca
- name: Check out pydca
uses: actions/checkout@v3
with:
repository: cabb99/pydca
path: pydca_dir
# Install pyDCA from GitHub (optional – tests that need it will be skipped if this fails)
- name: Install pydca
continue-on-error: true
shell: bash -l {0}
run: |
cd pydca_dir
echo "numpy<2" > constraints.txt
conda run -n test pip install -r requirements.txt -c constraints.txt
conda run -n test python -m pip install . --no-deps
conda list
cd ..
pwd
conda run -n test python -c "import sys; print(sys.executable); print('\n'.join(sys.path))"
conda run -n test python -c "import pydca; print('pydca is installed and importable')"
conda run -n test pip freeze | grep pydca
python -m pip install "git+https://github.com/cabb99/pydca.git"
python -c "import pydca; print('pydca is installed and importable')"
pip freeze | grep pydca

- name: Install package

# conda setup requires this special shell
shell: bash -l {0}
run: |
conda install -c conda-forge --file requirements.txt
pip install -r requirements.txt
conda install -c conda-forge pdbfixer
python -m pip install . --no-deps
conda list

- name: Install pdbfixer

# conda setup requires this special shell
shell: bash -l {0}
run: |
conda install -c conda-forge pdbfixer

- name: Run tests

# conda setup requires this special shell
Expand All @@ -139,7 +124,7 @@ jobs:
python -c "import sys; print(sys.executable); print('\n'.join(sys.path))"
python -c "import pydca; print('pydca is installed and importable')"
pip freeze | grep pydca
pytest -v --cov=frustratometer --cov-report=xml --color=yes tests/
pytest -v --skip-slow --skip-memory-heavy --cov=frustratometer --cov-report=xml --color=yes tests/

- name: CodeCov
uses: codecov/codecov-action@v3
Expand Down
124 changes: 124 additions & 0 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: Publish to PyPI

on:
release:
types: [published]

jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0 # versioneer needs full history

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"

- name: Install build dependencies
run: python3 -m pip install build --user

- name: Build wheel and source tarball
run: python3 -m build

- name: Store distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/

publish-to-testpypi:
name: Publish to TestPyPI
needs: build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/frustratometer
permissions:
id-token: write # mandatory for trusted publishing

steps:
- name: Download distributions
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/

- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

test-from-testpypi:
name: Test install from TestPyPI
needs: publish-to-testpypi
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
with:
persist-credentials: false

- uses: conda-incubator/setup-miniconda@v2
with:
python-version: ${{ matrix.python-version }}
channels: conda-forge,defaults
activate-environment: test-publish
auto-update-conda: false
auto-activate-base: false

- name: Install conda dependencies
shell: bash -l {0}
run: |
conda install -c conda-forge openmm pdbfixer pytest

- name: Wait for TestPyPI to index the package
shell: bash -l {0}
run: sleep 30

- name: Install frustratometer from TestPyPI
shell: bash -l {0}
run: |
pip install --pre \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
frustratometer

- name: Verify import and version
shell: bash -l {0}
run: |
python -c "import frustratometer; print(frustratometer.__version__)"

- name: Run tests
shell: bash -l {0}
run: |
python -m pytest tests/ \
--ignore=tests/test_dca_frustratometer.py \
-x --tb=short

publish-to-pypi:
name: Publish to PyPI
needs: test-from-testpypi
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/frustratometer
permissions:
id-token: write # mandatory for trusted publishing

steps:
- name: Download distributions
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,25 @@ The frustratometer is based on the principle of minimal frustration postulated b

Under most circunstances amino acids are minimally frustrated in the protein, but pockets where amino acids are of interest for protein function can remain frustrated, as they become minimally frustrated when achieving the corresponding function. In short, a frustrated residue or interaction indicates that the particular amino acid would be minimized under a different environment, which reveals possible competing evolutionary pressures on its selection. We have identified that these pockets usually correspond to regions of functional importance, for example, that may form part of a catalitic domain, a hinge domain, or a binding region.

This principle of minimum frustration has been shown using the AWSEM forcefield but can be extended to any other forcefield, including atomistic forcefields, or a pseudo-forcefield like DCA, as implemented here.
This principle of minimum frustration has been shown using the AWSEM forcefield but can be extended to any other forcefield, including atomistic forcefields, or energy models derived from coevolutionary analysis, such as DCA-based Potts models, as implemented here.

## Installation

To install this modules please clone this repository and install it using the following commands.
### Using conda (recommended)

git clone HanaJaafari/Frustratometer
conda install -c conda-forge frustratometer

### Using pip

pip install frustratometer

Note: `openmm` and `pdbfixer` are not available on PyPI and must be installed separately via conda:

conda install -c conda-forge openmm pdbfixer

### From source

git clone https://github.com/HanaJaafari/Frustratometer
cd Frustratometer
conda install -c conda-forge --file requirements.txt
pip install -e .
Expand Down
35 changes: 35 additions & 0 deletions devtools/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
ARG PYTHON_VERSION=3.12
FROM condaforge/miniforge3:latest

# Install OS-level deps
RUN apt-get update -qq \
&& apt-get install -qq -y --no-install-recommends \
gcc g++ gfortran git wget hmmer \
&& rm -rf /var/lib/apt/lists/*

# Create conda env with the requested Python version
ARG PYTHON_VERSION
RUN conda create -n test -y python=${PYTHON_VERSION} && conda clean -afy
SHELL ["conda", "run", "-n", "test", "/bin/bash", "-c"]

WORKDIR /repo

# Install dependencies first (better layer caching)
COPY requirements.txt .
RUN pip install --quiet --no-cache-dir -r requirements.txt
RUN pip install --quiet --no-cache-dir pytest pytest-cov
RUN conda install -n test -y -c conda-forge pdbfixer \
|| echo "WARNING: pdbfixer install failed – pdbfixer tests will be skipped"

# On Python <3.10, old prody needs numpy<2; conda's pdbfixer may have upgraded it
RUN python -c "import sys; sys.exit(0 if sys.version_info < (3,10) else 1)" \
&& conda install -n test -y "numpy<2" \
|| true

# Install pyDCA (optional – continues on failure)
RUN pip install --quiet --no-cache-dir "git+https://github.com/cabb99/pydca.git" \
|| echo "WARNING: pydca install failed – pyDCA tests will be skipped"

# Install the package itself
COPY . .
RUN pip install --quiet --no-cache-dir --no-deps -e .
125 changes: 125 additions & 0 deletions devtools/scripts/smoke_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env bash
# smoke_test.sh – build and run the fast test suite for one or more Python versions.
#
# By default all versions run in PARALLEL. Set PARALLEL=0 to run sequentially.
#
# Usage:
# devtools/scripts/smoke_test.sh [PYTHON_VERSION ...]
#
# Examples:
# devtools/scripts/smoke_test.sh # defaults: 3.12 3.13
# devtools/scripts/smoke_test.sh 3.10 3.11 3.12 3.13 3.14
#
# Environment variables:
# TEST_FLAGS – pytest flags (default: --skip-slow --skip-stochastic --skip-network)
# LOG_DIR – directory for per-version log files (default: devtools/logs)
# PARALLEL – set to 0 to run sequentially (default: 1)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
DOCKERFILE="$REPO_ROOT/devtools/docker/Dockerfile"
TEST_FLAGS="${TEST_FLAGS:---skip-slow --skip-stochastic --skip-network}"
LOG_DIR="${LOG_DIR:-$REPO_ROOT/devtools/logs}"
PARALLEL="${PARALLEL:-1}"

# Default to testing Python 3.7 through 3.14, but allow overriding via command-line args
if [ $# -gt 0 ]; then
VERSIONS=("$@")
else
VERSIONS=(3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14)
fi

mkdir -p "$LOG_DIR"

# ---------- worker function (one per Python version) ----------
run_one() {
local PY="$1"
local LOG="$LOG_DIR/smoke_py${PY}.log"
local IMAGE="frustratometer-test:py${PY}"

{
echo "════════════════════════════════════════"
echo " Python ${PY} – started $(date '+%H:%M:%S')"
echo "════════════════════════════════════════"

echo "--- docker build ---"
if ! docker build \
--build-arg PYTHON_VERSION="${PY}" \
-f "$DOCKERFILE" \
-t "$IMAGE" \
"$REPO_ROOT" ; then
echo "BUILD FAILED for Python ${PY}"
return 1
fi

echo ""
echo "--- pytest ---"
if docker run --rm "$IMAGE" \
conda run -n test --no-capture-output \
python -m pytest tests/ $TEST_FLAGS -v --tb=short --color=no ; then
echo ""
echo "RESULT: PASS (Python ${PY})"
return 0
else
echo ""
echo "RESULT: FAIL (Python ${PY})"
return 1
fi
} > "$LOG" 2>&1
}

# ---------- launch ----------
PIDS=()
for PY in "${VERSIONS[@]}"; do
if [ "$PARALLEL" = "1" ]; then
run_one "$PY" &
PIDS+=("$!:$PY")
else
run_one "$PY" || true
fi
done

# ---------- wait & collect ----------
PASS=()
FAIL=()

if [ "$PARALLEL" = "1" ]; then
for entry in "${PIDS[@]}"; do
PID="${entry%%:*}"
PY="${entry##*:}"
if wait "$PID"; then
PASS+=("$PY")
else
FAIL+=("$PY")
fi
done
else
for PY in "${VERSIONS[@]}"; do
if grep -q "^RESULT: PASS" "$LOG_DIR/smoke_py${PY}.log" 2>/dev/null; then
PASS+=("$PY")
else
FAIL+=("$PY")
fi
done
fi

# ---------- summary ----------
echo ""
echo "════════════════════════════════════════"
echo " Summary"
echo "════════════════════════════════════════"
[ ${#PASS[@]} -gt 0 ] && echo " PASSED: ${PASS[*]}"
[ ${#FAIL[@]} -gt 0 ] && echo " FAILED: ${FAIL[*]}"
echo " Logs: $LOG_DIR/"
echo ""

# Print failures inline for convenience
for PY in "${FAIL[@]+"${FAIL[@]}"}"; do
echo ""
echo "──── FAILURES for Python ${PY} (last 50 lines) ────"
tail -50 "$LOG_DIR/smoke_py${PY}.log"
done

[ ${#FAIL[@]} -eq 0 ] # exit 0 only if nothing failed
2 changes: 1 addition & 1 deletion docs/frustratometer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The frustratometer is based on the principle of minimal frustration postulated b

Under most circumstances, amino acids are minimally frustrated in the protein, but pockets where amino acids are of interest for protein function. In short, a frustrated residue or interaction indicates that the particular amino acid would be minimized under a different configuration, or a different sequence, which reveals possible competing evolutionary pressures on its selection. We have identified that these pockets usually correspond to regions of functional importance, for example, that may form part of a catalytic domain, a hinge domain, or a binding region.

This principle of minimum frustration has been shown using the AWSEM forcefield but can be extended to any other forcefield, including atomistic forcefields, or a pseudo-forcefield like DCA, as implemented here.
This principle of minimum frustration has been shown using the AWSEM forcefield but can be extended to any other forcefield, including atomistic forcefields, or energy models derived from coevolutionary analysis, such as DCA-based Potts models, as implemented here.

In this module, we implement a version of the frustratometer based on Direct Coupling analysis.

Expand Down
Loading
Loading