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

code and unit test for salt bridge tool #121

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e6f086d
added some rough code draft for salt bridge
brittyscience Mar 27, 2024
f1ee5ce
Merge branch 'main' into saltbridge_code
brittyscience Mar 31, 2024
0ff52c6
Prompting change in CNT
Jgmedina95 Apr 1, 2024
8ed1734
merging CNT_rdf to saltbridge_code
brittyscience Apr 3, 2024
3d90dd3
Merge branch 'main' into saltbridge_code
brittyscience Apr 10, 2024
9228c2a
pushing latest updates. Unit test and codes WIP
brittyscience Apr 12, 2024
f67e42b
Another changes updates to my GT and Unit test. WIP
brittyscience Apr 15, 2024
1103b11
latest changes. pytest is still not working.
brittyscience Apr 15, 2024
679f61a
updating my saltbridge tool code
brittyscience Apr 22, 2024
562d164
Just another update of my changes from two days ago.
brittyscience Apr 25, 2024
92ad2aa
more updates. agent should have my tools now
brittyscience Apr 25, 2024
4be782f
fixed init bug
brittyscience May 7, 2024
0df1242
added arg schema, making code more flexible, path registery
brittyscience May 11, 2024
5655b4d
unit test now works and salt bridge tool revision update
brittyscience May 13, 2024
86e333d
Merge branch 'main' of https://github.com/ur-whitelab/md-agent into s…
brittyscience May 13, 2024
e6d792c
pre commit hiccup fix
brittyscience Jun 9, 2024
2daed31
updating with changes from main
brittyscience Jun 9, 2024
8833cf7
resolved merging conflict
brittyscience Jun 12, 2024
f154a69
moved my saltbridge unit test to test_analysis folder
brittyscience Jun 13, 2024
5ddbba2
added ID to my description in line 67 and 70
brittyscience Jun 20, 2024
012c3b4
Merge branch 'main' of https://github.com/ur-whitelab/md-agent into s…
brittyscience Jun 26, 2024
e94f81a
M)erge branch 'main' of https://github.com/ur-whitelab/md-agent into …
brittyscience Jul 10, 2024
694de51
Update mdagent/tools/base_tools/analysis_tools/salt_bridge_tool.py
brittyscience Jul 10, 2024
e058c77
Update tests/test_analysis/test_saltbridge_tools.py
brittyscience Jul 10, 2024
0d6fe0d
Update tests/test_analysis/test_saltbridge_tools.py
brittyscience Jul 10, 2024
46580ff
adjustments based on feedback
brittyscience Jul 10, 2024
6841574
merging changes from remote branch
brittyscience Jan 23, 2025
ed756af
salt bridge code update
brittyscience Jan 23, 2025
41c75ed
merged from main to saltbridge_code
qcampbel Jan 24, 2025
2be80a8
added salt bridge counts and new unit tests
qcampbel Jan 29, 2025
2c36751
added neutral ph warning & refactored a bit
qcampbel Feb 5, 2025
670aca1
fixed code error (line 68)
qcampbel Feb 5, 2025
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
104 changes: 104 additions & 0 deletions mdagent/tools/base_tools/analysis_tools/salt_bridge_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import os
from typing import Optional

import matplotlib.pyplot as plt
import mdtraj as md
import numpy as np
from langchain.tools import BaseTool

from mdagent.utils import FileType, PathRegistry


class SaltBridgeFunction: #this class defines a method called find_salt_bridge
#using MD traj and top files and threshold distance default, residue pair list
# used to account for salt bridge analysis
brittyscience marked this conversation as resolved.
Show resolved Hide resolved
def __init__(self, path_registry):
brittyscience marked this conversation as resolved.
Show resolved Hide resolved
self.path_registry = path_registry
self.includes_top = [".h5", ".lh5", ".pdb"] # those are the files we need for this analysis
self.paired_salt_bridges=[] #stores paired salt bridges
self.unpaired_residues=set() #store unpaired residues

def find_salt_bridges(self, traj_file, top_file , threshold_distance=0.4, residue_pairs=None):
salt_bridges = []
# load trajectory using MDTraj
traj = md.load(traj_file, top= top_file)
qcampbel marked this conversation as resolved.
Show resolved Hide resolved
if residue_pairs is None:
residue_pairs = [("ARG", "ASP"), ("ARG", "GLU"), ("LYS", "ASP"), ("LYS", "GLU")]

for pair in residue_pairs:
donor_residues = traj.topology.select(f'residue_name == "{pair[0]}"')
acceptor_residues = traj.topology.select(f'residue_name == "{pair[1]}"')

for donor_idx in donor_residues:
for acceptor_idx in acceptor_residues:
distances = md.compute_distances(traj, [[donor_idx, acceptor_idx]])
if any(d <= threshold_distance for d in distances):
salt_bridges.append((donor_idx, acceptor_idx))

# Check if the donor and acceptor form a salt bridge
if any(d <= threshold_distance for d in distances):
# If yes, remove them from the unpaired set
self.unpaired_residues.discard(donor_idx) # Remove donor from unpaired residues set
self.unpaired_residues.discard(acceptor_idx) # Remove acceptor from unpaired residues set
else:
# If not, add them to the unpaired set
self.unpaired_residues.add(donor_idx) # Add donor to unpaired residues set
self.unpaired_residues.add(acceptor_idx) # Add acceptor to unpaired residues set
print("Salt bridges found:")
for bridge in salt_bridges:
print(
f"Residue {traj.topology.atom(bridge[0]).residue.index + 1} ({traj.topology.atom(bridge[0]).residue.name}) - "
f"Residue {traj.topology.atom(bridge[1]).residue.index + 1} ({traj.topology.atom(bridge[1]).residue.name})"
)

#Print unpaired residues
print("Unpaired_residues:")
salt_bridge_function = salt_bridge_tool.salt_bridge_function
for residue_idx in salt_bridge_function.unpaired_residues:
print(f"Residue {traj.topology.atom(residue_idx).residue.index + 1} ({traj.topology.atom(residue_idx).residue.name})")

return salt_bridges, list(self.unpaired_residues), list(residue_pairs)



class SaltBridgeTool(BaseTool):
name = "salt_bridge_tool"
description = "A tool to find salt bridge in a protein trajectory"

def __init__(self, path_registry):
self.salt_bridge_function = SaltBridgeFunction(path_registry)
qcampbel marked this conversation as resolved.
Show resolved Hide resolved

def _run(self, traj_file, top_file, threshold_distance=0.4, residue_pairs=None):
# Load trajectory using MDTraj
traj = md.load(traj_file, top=top_file)
qcampbel marked this conversation as resolved.
Show resolved Hide resolved
#calls the salt bridge function
salt_bridges = [self.salt_bridge_function.find_salt_bridges(traj_file, top_file,
threshold_distance, residue_pairs)]
return salt_bridges

def _agg_result(self, result):
return result

def _call__(self, traj_file, top_file, threshold_distance=0.4, residue_pairs=None):
result = self._run(traj_file, top_file, threshold_distance, residue_pairs)
return self._agg_result(result)
qcampbel marked this conversation as resolved.
Show resolved Hide resolved




# Print identified salt bridges
qcampbel marked this conversation as resolved.
Show resolved Hide resolved

# this need to be moved under class somewhere. move it

print("Salt bridges found:")
for bridge in salt_bridges:
print(
f"Residue {traj.topology.atom(bridge[0]).residue.index + 1} ({traj.topology.atom(bridge[0]).residue.name}) - "
f"Residue {traj.topology.atom(bridge[1]).residue.index + 1} ({traj.topology.atom(bridge[1]).residue.name})"
)

#Print unpaired residues
print("Unpaired_residues:")
salt_bridge_function = salt_bridge_tool.salt_bridge_function
for residue_idx in salt_bridge_function.unpaired_residues:
print(f"Residue {traj.topology.atom(residue_idx).residue.index + 1} ({traj.topology.atom(residue_idx).residue.name})")
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This is an empty __init__.py file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete this file, we don't need tests to be a module

90 changes: 90 additions & 0 deletions tests/test_saltbridge_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from unittest.mock import Mock
import mdtraj as md
import pytest

from mdagent.tools.base_tools.analysis_tools.salt_bridge_tool import SaltBridgeFunction
from mdagent.utils import PathRegistry

# define test data

traj_file = "test_trajectory.dcd"
top_file = "test_topology.pdb"

#Test saltbridgefunction class

def test_salt_bridge_function():
#initalize SaltBridgeFunction
salt_bridge_function = SaltBridgeFunction(path_registry=None)

#load traj using MDtraj
traj = md.load(traj_file, top=top_file)

#perform salt bridge analysis

salt_bridges, unpaired_residues, residue_pairs = salt_bridge_function.find.salt_bridges(traj, top=top_file)

#check to see if we get a list in our results

assert isinstance(salt_bridges, list)

#Check to make sure residue pairs cant be changed ( tuple)

for pair in residue_pairs:
assert isinstance(pair, tuple)

# check to make sure unpaired residue is assigned a number to identify it by the number (integer)
# to ensure the code works

for residue in unpaired_residues:
assert isinstance( residue, int)

# Finally, run the test
if __name__ == "__main__":
test_salt_bridge_function()



@pytest.fixture
def mock_traj():
# Create a mock trajectory object
traj = Mock()
traj.topology.select.return_value = [1, 2, 3] # Mocking residue selections
return traj


def test_find_salt_bridges(mock_traj):
# Instantiate SaltBridgeFunction
salt_bridge_function = SaltBridgeFunction(path_registry=None)

# Call find_salt_bridges method
traj = md.load(traj_file,top=top_file)
salt_bridges = salt_bridge_function.find_salt_bridges(traj=mock_traj)

# Assert that salt_bridges contain the expected values
expected_salt_bridges = [
(1, 2),
(1, 3),
(2, 1),
(2, 3),
(3, 1),
(3, 2),
] # Example expected output
assert salt_bridges == expected_salt_bridges


import pytest


def test_count_salt_bridges():
salt_bridge_function = SaltBridgeFunction(path_registry=None)
salt_bridges = [
(1, 2),
(1, 3),
(2, 1),
(2, 3),
(3, 1),
(3, 2),
] # Example salt bridges
count = salt_bridge_function.count_salt_bridges(salt_bridges)
assert count == 6 # Example expected count

Loading