Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attract init #1072

Draft
wants to merge 11 commits into
base: attract-module
Choose a base branch
from
8 changes: 8 additions & 0 deletions integration_tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pytest


if "CNS_EXEC" in os.environ:
CNS_EXEC = os.environ["CNS_EXEC"]
else:
Expand All @@ -17,3 +18,10 @@

tests_path = Path(__file__).resolve().parents[0]
GOLDEN_DATA = Path(tests_path, "golden_data")

# Placeholder decorator to check if ATTRACT is installed
# TODO: Implement
ATTRACT_IS_INSTALLED = False
has_attract = pytest.mark.skipif(
not ATTRACT_IS_INSTALLED, reason="ATTRACT not installed"
)
89 changes: 89 additions & 0 deletions integration_tests/golden_data/rna.pdb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
ATOM 666 O5' G B 87 -27.630 38.463 -14.109 1.00 42.87 O
ATOM 667 C5' G B 87 -28.602 38.855 -13.151 1.00 42.27 C
ATOM 668 C4' G B 87 -29.933 39.139 -13.799 1.00 48.64 C
ATOM 669 O4' G B 87 -29.779 40.178 -14.803 1.00 43.31 O
ATOM 670 C3' G B 87 -30.564 37.982 -14.561 1.00 44.60 C
ATOM 671 O3' G B 87 -31.242 37.065 -13.721 1.00 56.31 O
ATOM 672 C2' G B 87 -31.474 38.697 -15.548 1.00 41.13 C
ATOM 673 O2' G B 87 -32.656 39.153 -14.903 1.00 47.54 O
ATOM 674 C1' G B 87 -30.621 39.913 -15.906 1.00 44.86 C
ATOM 675 N9 G B 87 -29.775 39.659 -17.088 1.00 45.69 N
ATOM 676 C8 G B 87 -28.407 39.541 -17.117 1.00 44.82 C
ATOM 677 N7 G B 87 -27.938 39.311 -18.313 1.00 52.84 N
ATOM 678 C5 G B 87 -29.064 39.272 -19.124 1.00 47.42 C
ATOM 679 C6 G B 87 -29.183 39.057 -20.521 1.00 42.22 C
ATOM 680 O6 G B 87 -28.286 38.852 -21.348 1.00 61.13 O
ATOM 681 N1 G B 87 -30.512 39.100 -20.935 1.00 45.00 N
ATOM 682 C2 G B 87 -31.588 39.318 -20.108 1.00 54.13 C
ATOM 683 N2 G B 87 -32.796 39.322 -20.693 1.00 54.04 N
ATOM 684 N3 G B 87 -31.490 39.520 -18.804 1.00 50.61 N
ATOM 685 C4 G B 87 -30.207 39.484 -18.382 1.00 56.23 C
ATOM 686 P G B 88 -31.281 35.502 -14.096 1.00 53.68 P
ATOM 687 OP1 G B 88 -31.907 34.794 -12.951 1.00 54.68 O
ATOM 688 OP2 G B 88 -29.910 35.116 -14.509 1.00 39.00 O
ATOM 689 O5' G B 88 -32.256 35.463 -15.355 1.00 57.66 O
ATOM 690 C5' G B 88 -33.614 35.868 -15.241 1.00 57.35 C
ATOM 691 C4' G B 88 -34.327 35.784 -16.567 1.00 58.96 C
ATOM 692 O4' G B 88 -33.726 36.709 -17.509 1.00 56.45 O
ATOM 693 C3' G B 88 -34.258 34.440 -17.273 1.00 57.94 C
ATOM 694 O3' G B 88 -35.199 33.508 -16.775 1.00 73.51 O
ATOM 695 C2' G B 88 -34.483 34.816 -18.732 1.00 63.36 C
ATOM 696 O2' G B 88 -35.863 35.029 -18.990 1.00 43.75 O
ATOM 697 C1' G B 88 -33.761 36.163 -18.812 1.00 50.86 C
ATOM 698 N9 G B 88 -32.375 36.021 -19.299 1.00 55.68 N
ATOM 699 C8 G B 88 -31.223 35.986 -18.549 1.00 49.61 C
ATOM 700 N7 G B 88 -30.145 35.851 -19.273 1.00 46.05 N
ATOM 701 C5 G B 88 -30.611 35.792 -20.580 1.00 52.57 C
ATOM 702 C6 G B 88 -29.908 35.650 -21.807 1.00 55.72 C
ATOM 703 O6 G B 88 -28.688 35.544 -21.994 1.00 50.01 O
ATOM 704 N1 G B 88 -30.778 35.638 -22.894 1.00 55.41 N
ATOM 705 C2 G B 88 -32.146 35.747 -22.816 1.00 63.24 C
ATOM 706 N2 G B 88 -32.812 35.713 -23.980 1.00 54.57 N
ATOM 707 N3 G B 88 -32.811 35.881 -21.680 1.00 54.18 N
ATOM 708 C4 G B 88 -31.987 35.895 -20.612 1.00 56.47 C
ATOM 709 P U B 89 -34.726 32.337 -15.783 1.00 72.44 P
ATOM 710 OP1 U B 89 -35.625 32.374 -14.602 1.00 52.31 O
ATOM 711 OP2 U B 89 -33.266 32.503 -15.573 1.00 82.19 O
ATOM 712 O5' U B 89 -35.004 31.025 -16.635 1.00 80.12 O
ATOM 713 C5' U B 89 -35.939 30.056 -16.196 1.00 65.85 C
ATOM 714 C4' U B 89 -36.896 29.668 -17.291 1.00 76.34 C
ATOM 715 O4' U B 89 -37.915 28.785 -16.746 1.00 63.91 O
ATOM 716 C3' U B 89 -37.686 30.811 -17.917 1.00 82.48 C
ATOM 717 O3' U B 89 -36.974 31.513 -18.934 1.00 99.92 O
ATOM 718 C2' U B 89 -38.956 30.123 -18.405 1.00 89.18 C
ATOM 719 O2' U B 89 -38.721 29.436 -19.626 1.00 91.14 O
ATOM 720 C1' U B 89 -39.175 29.087 -17.304 1.00 88.15 C
ATOM 721 N1 U B 89 -40.055 29.598 -16.225 1.00 82.95 N
ATOM 722 C2 U B 89 -41.420 29.461 -16.387 1.00 61.44 C
ATOM 723 O2 U B 89 -41.925 28.945 -17.370 1.00 78.44 O
ATOM 724 N3 U B 89 -42.177 29.953 -15.353 1.00 62.09 N
ATOM 725 C4 U B 89 -41.727 30.556 -14.197 1.00 67.62 C
ATOM 726 O4 U B 89 -42.543 30.948 -13.359 1.00 75.18 O
ATOM 727 C5 U B 89 -40.304 30.660 -14.099 1.00 60.41 C
ATOM 728 C6 U B 89 -39.541 30.188 -15.090 1.00 48.05 C
ATOM 729 P A B 90 -35.937 30.752 -19.910 1.00110.96 P
ATOM 730 OP1 A B 90 -36.589 29.495 -20.359 1.00102.79 O
ATOM 731 OP2 A B 90 -34.637 30.668 -19.201 1.00 98.18 O
ATOM 732 O5' A B 90 -35.811 31.758 -21.135 1.00 85.89 O
ATOM 733 C5' A B 90 -36.772 31.757 -22.177 1.00101.33 C
ATOM 734 C4' A B 90 -36.131 31.497 -23.516 1.00 89.35 C
ATOM 735 O4' A B 90 -35.196 32.562 -23.827 1.00 82.67 O
ATOM 736 C3' A B 90 -35.288 30.236 -23.620 1.00 85.67 C
ATOM 737 O3' A B 90 -36.061 29.062 -23.805 1.00 81.70 O
ATOM 738 C2' A B 90 -34.369 30.554 -24.789 1.00 88.06 C
ATOM 739 O2' A B 90 -35.057 30.404 -26.023 1.00 84.48 O
ATOM 740 C1' A B 90 -34.107 32.043 -24.563 1.00 74.25 C
ATOM 741 N9 A B 90 -32.865 32.273 -23.800 1.00 69.68 N
ATOM 742 C8 A B 90 -32.700 32.486 -22.452 1.00 66.88 C
ATOM 743 N7 A B 90 -31.446 32.652 -22.096 1.00 64.42 N
ATOM 744 C5 A B 90 -30.739 32.537 -23.287 1.00 65.35 C
ATOM 745 C6 A B 90 -29.369 32.614 -23.602 1.00 49.71 C
ATOM 746 N6 A B 90 -28.396 32.835 -22.714 1.00 56.28 N
ATOM 747 N1 A B 90 -29.018 32.453 -24.895 1.00 67.75 N
ATOM 748 C2 A B 90 -29.971 32.230 -25.806 1.00 61.21 C
ATOM 749 N3 A B 90 -31.286 32.138 -25.636 1.00 68.13 N
ATOM 750 C4 A B 90 -31.605 32.302 -24.341 1.00 75.90 C
ATOM 751 P A B 91 -35.474 27.632 -23.362 1.00 83.80 P
ATOM 752 OP1 A B 91 -36.622 26.694 -23.287 1.00 91.65 O
ATOM 753 OP2 A B 91 -34.652 27.853 -22.146 1.00 84.63 O

66 changes: 66 additions & 0 deletions integration_tests/test_attract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import shutil
import tempfile
from pathlib import Path

import pytest

from haddock.libs.libontology import PDBFile
from haddock.modules.sampling.attract import \
DEFAULT_CONFIG as DEFAULT_ATTRACT_CONFIG
from haddock.modules.sampling.attract import HaddockModule as AttractModule

from . import GOLDEN_DATA, has_attract


class MockPreviousIO:
"""Mock the previous IO module."""

def __init__(self, path):
self.path = path

def retrieve_models(self):
"""Mock the retrieval of some models"""
shutil.copy(
Path(GOLDEN_DATA, "prot.pdb"),
Path(self.path, "prot.pdb"),
)

shutil.copy(
Path(GOLDEN_DATA, "rna.pdb"),
Path(self.path, "rna.pdb"),
)

return [
PDBFile(file_name="prot.pdb", path=self.path),
PDBFile(file_name="rna.pdb", path=self.path),
]

def output(self) -> None:
"""Mock the output"""
return None


@pytest.fixture(name="attract_module")
def fixture_attract_module():
"""Initialize the attract module"""
with tempfile.TemporaryDirectory() as tempdir:
os.chdir(tempdir)
yield AttractModule(
order=0, path=Path("."), initial_params=DEFAULT_ATTRACT_CONFIG
)


@has_attract
@pytest.mark.skip(reason="work-in-progress")
def test_attract(attract_module):
"""Integration test for the attract module"""

attract_module.previous_io = MockPreviousIO(path=attract_module.path)

# attract_module.attract_tools = ???
# attract_module.nalib = ???

attract_module.run()

assert len(attract_module.output_models) > 0
1 change: 1 addition & 0 deletions src/fast-rmsdmatrix
Submodule fast-rmsdmatrix added at 4580d1
1 change: 1 addition & 0 deletions src/fcc
Submodule fcc added at 3a1626
16 changes: 8 additions & 8 deletions src/haddock/modules/_template_cat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ To develop your own HADDOCK3 module follow the patterns used in other modules.

1. If your module belongs to a new category, create a folder for that category
under the `modules` folder.
1. Else, create a folder for your module inside its relevant category.
2. Else, create a folder for your module inside its relevant category.
The name of that folder is the name of the module, i.e., the name used to call
the module in the haddock3 configuration files and used throughout the code base.
1. Copy the `__init__.py` file here to the new module's folder and edit it accordingly
3. Copy the `__init__.py` file here to the new module's folder and edit it accordingly
to the instructions there.
1. Do the same for the `defaults.yaml` file.
1. You can then add any extra files needed inside your module's folder in order to
4. Do the same for the `defaults.yaml` file.
5. You can then add any extra files needed inside your module's folder in order to
develop your module fully.
1. If your module requires any extra libraries, describe how to install those libraries
6. If your module requires any extra libraries, describe how to install those libraries
in the `docs/INSTALL.md` file. Unless approved by the Haddock Team, do not add
those dependencies to the `requirements.*` files.
1. HADDOCK3 has already many features related to IO, subprocess run, sending jobs,
7. HADDOCK3 has already many features related to IO, subprocess run, sending jobs,
etc. Please, look around the `libs` folder for pertinent functions, but, above all,
feel welcomed to reach out to us with any doubts.
1. Please write also tests for your module. Our testing machinery already
8. Please write also tests for your module. Our testing machinery already
tests for the common patterns, for example, inspecting the `defaults.yaml` file.
But you should write any additional tests to ensure that your module works properly.
See other examples in the `tests/` folder.
1. Finally, add an example of how to use your module in the `examples/` folder.
9. Finally, add an example of how to use your module in the `examples/` folder.
The example should have a short sampling scheme. Name the config file ending with
`-test.cfg`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__(
) -> None:

# if your module uses CNS you might need to define where the main CNS
# script is localted. See examples in `topoaa`, `emref`.
# script is located. See examples in `topoaa`, `emref`.
# else leave it out.
# cns_script = Path(RECIPE_PATH, "cns", "main.cns")

Expand Down
135 changes: 135 additions & 0 deletions src/haddock/modules/sampling/attract/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""attract docking module
==================================

This module performs a fragment-based single-stranded (ss) RNA-protein docking
using ATTRACT docking engine. This docking approach was developed to tackle the
flexibility of ssRNA. Its core idea is to split ssRNA chain into overlapping
trinucleotides (fragments), and dock them onto the rigid receptor separately,
assembling the fragments back into the whole chain models afterwards.

#todo
add short description of the protocol, including CG, NAlib, sampling,
scoring (two ways) and assembly + possible restraints.
."""
import os
#import sys
import tempfile
import subprocess
import shutil
import shlex
from pathlib import Path
from haddock.modules.sampling.attract.attractmodule import (
rename_and_coarse_grain,
process_rna_file,
)

from haddock import log
from haddock.core.defaults import MODULE_DEFAULT_YAML
from haddock.core.typing import FilePath, Any
#from haddock.libs import libpdb
#from haddock.libs.libio import working_directory
from haddock.libs.libontology import Format, PDBFile
#from haddock.libs.libutil import check_subprocess
from haddock.modules import BaseHaddockModule

RECIPE_PATH = Path(__file__).resolve().parent
DEFAULT_CONFIG = Path(RECIPE_PATH, MODULE_DEFAULT_YAML)

class HaddockModule(BaseHaddockModule):
"""ATTRACT module."""

name = RECIPE_PATH.name

def __init__(self,
order: int,
path: Path,
initial_params: FilePath = DEFAULT_CONFIG) -> None:
super().__init__(order, path, initial_params)

@classmethod
def confirm_installation(cls) -> None:
"""Confirm that ATTRACT and its environment variables are properly set."""
try:
attract_dir = os.environ['ATTRACTDIR']
attract_tools = os.environ['ATTRACTTOOLS']
nalib = os.environ['LIBRARY']
randsearch = os.environ['RANDSEARCH']
except KeyError as e:
raise EnvironmentError(f"Required environment variable not found: {e}")

attract_exec = Path(attract_dir, 'attract')
result = subprocess.run([str(attract_exec)], capture_output=True, text=True)

if "Too few arguments" not in result.stderr:
raise RuntimeError('ATTRACT is not installed properly')

# pass paths to _run for further use
cls.attract_dir = attract_dir
cls.attract_tools = attract_tools
cls.nalib = nalib

def _run(self) -> None:
"""Execute module.
Currently:
1. Converts protein and RNA in ATTRACT coarse-grain
2. Splits RNA into overlapping fragments
3. Creates required by ATTRACT files: motif.list, boundfrag.list, nalib
4. Passes initial all-atom protein and RNA to next module
"""

# Get the models generated in previous step
models: list[PDBFile] = [
p for p in self.previous_io.output
if p.file_type == Format.PDB ]

# Check that exactly two models are provided
# practically attract needs protein structure and RNA *sequence*
# but at this stage it's more practical to ask for RNA structure
if len(models) !=2 :
_msg = "ATTRACT requires exactly two molecules"
self.finish_with_error(_msg)

# Copy each model to the working directory
for model in models:
src_model = Path(model.path, model.file_name)
dest_model = Path(os.getcwd(), model.file_name)
shutil.copyfile(src_model, dest_model)

# Ensure we have exactly protein and RNA molecules
model_1 = models[0]
model_2 = models[1]

attracttools = self.attract_tools
attrac_reduce_path = Path(attracttools, 'reduce.py')

_, label_1 = rename_and_coarse_grain(model_1.file_name, attrac_reduce_path)
_, label_2 = rename_and_coarse_grain(model_2.file_name, attrac_reduce_path)

if {label_1, label_2} != {'protein', 'rna'}:
_msg = "ATTRACT requires protein and RNA molecules as input"
self.finish_with_error(_msg)

# Add required by ATTRACT files:
log.info("Preparing docking directory")

nalib = self.nalib
cmd = f"ln -s {nalib} nalib"
p = subprocess.run(shlex.split(cmd), capture_output=True)
err = p.stderr.decode('utf-8')

process_rna_file('rna-aar.pdb')

tmp_dir = tempfile.mkdtemp(dir=os.getcwd())
# will be used during the docking
shutil.rmtree(tmp_dir)

list_of_created_models = []
created_models = ['protein-aa.pdb','rna-aa.pdb']
for model in created_models:
pdb_object = PDBFile(Path(model).name, path=".")
list_of_created_models.append(pdb_object)

self.output_models = list_of_created_models
self.export_io_models()


Loading
Loading