Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
34e4094
saving changes
ftclark3 Apr 9, 2025
fa23e34
fixed typo
ftclark3 Jul 8, 2025
fce6e82
added missing 'self' references
ftclark3 Jul 8, 2025
7483959
refactored; basic setup of decoy ensemble is working now
ftclark3 Jul 9, 2025
c9715d7
changed 'AWSEMEnsemble' class name to 'DecoyEnsemble'
ftclark3 Jul 9, 2025
641dfb9
stripped DecoyEnsemble down to the bare minimum
ftclark3 Jul 9, 2025
107d780
updated call to Structure in test_optimization
ftclark3 Jul 10, 2025
2bb3b83
refactored, passing most tests but failing some due to inaccurate ele…
ftclark3 Jul 10, 2025
d0b23f3
tests passing now (except for the dca tests requiring hmmer, which ha…
ftclark3 Jul 10, 2025
9fe70c2
tests still passing (except for that hmmer issue) and AWSEMIndicators…
ftclark3 Jul 10, 2025
d3386c9
got rid of unused classes
ftclark3 Jul 10, 2025
88834d3
updated documentation
ftclark3 Jul 10, 2025
2a88e03
refactored DecoyEnsemble to minimize memory usage and implemented avg…
ftclark3 Jul 11, 2025
790adce
got pdb.py from my amyloid_atlas branch, which allows prody for loadi…
ftclark3 Jul 11, 2025
6068d3c
undid unnecessary modification to structure processing in AWSEM
ftclark3 Jul 11, 2025
6d5586b
changed name of DecoyEnsemble.average to DecoyEnsemble.avg
ftclark3 Jul 11, 2025
1c1a5ef
fix wrong variable used
ftclark3 Jul 11, 2025
3b64349
reinitialize indicator function generator each time attribute is acce…
ftclark3 Jul 11, 2025
3a281d7
take absolute value of gamma (sqrt(gamma**2)) for approximate varianc…
ftclark3 Jul 11, 2025
80612e0
updated parallel tempering writer to write total energy and other ene…
ftclark3 Jul 13, 2025
21f1504
added covariance matrix method to DecoyEnsemble
ftclark3 Jul 14, 2025
002c8cf
saving reindexing array as attribute
ftclark3 Jul 14, 2025
1d2d833
cleaned up decoyensemble class slightly
ftclark3 Jul 14, 2025
3f0dd8d
partial implementation of covariance matrix potts model; still need t…
ftclark3 Jul 27, 2025
3ead94c
temporary md decoys thing for single temperature only
ftclark3 Jul 28, 2025
7f04db5
updated indicator function saving
Jul 31, 2025
7965940
fixed handling of standard deviation calculation
ftclark3 Jul 31, 2025
15ad1e4
fixing std calculation
ftclark3 Jul 31, 2025
d19638a
added code to support alternative md decoy variance calculation but n…
ftclark3 Aug 2, 2025
2d8ff9d
fixed errors
ftclark3 Aug 2, 2025
bac8361
removed total_energies, total_energies_consider, stds, and stds_consider
ftclark3 Aug 2, 2025
3e29dd3
frustration-inspired calculation for group meeting
ftclark3 Aug 6, 2025
5405a14
saving in the state where we can at least calculate the total energy …
ftclark3 Aug 23, 2025
7454c09
brough over Structure.py from amyloid_atlas branch
ftclark3 Sep 17, 2025
e92c350
made biopython residue name list more inclusive
ftclark3 Sep 17, 2025
60c55ca
got fix.py from amyloid_atlas branch, which improves multi chain hand…
ftclark3 Sep 17, 2025
535d370
added option to not call vmd after writing tcl script
ftclark3 Oct 21, 2025
35bc7dc
multi chain support
ftclark3 Oct 21, 2025
f0d2b47
Added comments and edited for testing
ftclark3 Oct 21, 2025
493eb8f
added comments and edits for testing
ftclark3 Oct 21, 2025
0038075
improved comments
ftclark3 Oct 27, 2025
257537f
merging fixed Gamma.py from carlos_main
ftclark3 Nov 7, 2025
4712335
added alt_sigma_wat option
ftclark3 Nov 8, 2025
c7d286c
rearranged AWSEMBase and subclass method calls; fixed bug where masks…
ftclark3 Nov 8, 2025
38e5d27
updating handling of boolean used to determine whether potts model sh…
ftclark3 Nov 8, 2025
0e80f46
add distance matrix-based conformer update capability to AWSEM class
ftclark3 Nov 9, 2025
c1c89cc
fixed missing 'self' in method definition
ftclark3 Nov 9, 2025
f64cbce
small optimization edits; should come back to this soon
Nov 16, 2025
c9579c0
bringing up to date
Nov 16, 2025
1aa9d86
cleaning up tiny unmerged diff (just a newline)
Nov 16, 2025
000c41b
added colorbar scale and increased label fontsize
ftclark3 Nov 26, 2025
f988ff4
syncing with remote
ftclark3 Nov 26, 2025
d81b644
changed name of AWSEMParameters class to Parameters
ftclark3 Dec 5, 2025
f970469
currently passing tests
ftclark3 Dec 6, 2025
c7f3ffd
_AA and gamma refactoring, but burial energy calculation broken (cont…
ftclark3 Dec 6, 2025
ce5c6c7
now passing energy tests
ftclark3 Dec 6, 2025
87d1f80
passing awsem energy and frustration tests; need to check expose indi…
ftclark3 Dec 7, 2025
214f02b
passing awsem energy and frustration tests; need to check expose indi…
ftclark3 Dec 7, 2025
d7d3fe4
always append electrostatic indicators, even if lambda electrostatics…
ftclark3 Dec 7, 2025
42d7db1
finished updating alphabet handling so that it's an object attribute …
ftclark3 Dec 7, 2025
d2b810d
cleaning some things up, including changing AWSEM.indicators to AWSEM…
ftclark3 Dec 8, 2025
86bcc95
moved multiplication of electrostatic mask by indicators to the energ…
ftclark3 Dec 8, 2025
1fdc77f
further cleaned up gamma electrostatics
ftclark3 Dec 8, 2025
d8f0ef3
editing potts model and native energy calculation workflow; passing a…
ftclark3 Dec 8, 2025
7b31a5a
cleaned up a bit more
ftclark3 Dec 8, 2025
ccb42e3
committing numba code and configuring imports
ftclark3 Dec 8, 2025
5a5de5e
reworked numba jit compilation for some functions
ftclark3 Dec 8, 2025
55fece3
factored electrostatic lambda out of electrostatics_gamma
ftclark3 Dec 9, 2025
adf9b95
added seq_index and electrostatics_gamma properties
ftclark3 Dec 9, 2025
0adc1ed
added potts model construction to numba hamiltonian script
ftclark3 Dec 9, 2025
ff71056
now using numba method to compute potts model, but some tests failing
ftclark3 Dec 9, 2025
7eb6610
fixed sign error in numba electrostatic hamiltonian
ftclark3 Dec 9, 2025
08a6b3c
passing tests except for subsequence
ftclark3 Dec 10, 2025
ed94686
restructured AWSEM.py to improve documentation and readability
ftclark3 Dec 10, 2025
e6093a7
fixing error in numba hamiltonian function signature
ftclark3 Dec 11, 2025
db0dc6b
added warnings to AWSEM and renamed potts to potts_option
ftclark3 Dec 11, 2025
ddba96f
adding properties and restructuring hierarchy of attributes in AWSEM;…
ftclark3 Dec 11, 2025
a8ede32
refactored attributes and properties of _AWSEMBase
ftclark3 Dec 12, 2025
5d8a30a
saving changes
ftclark3 Dec 22, 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
1 change: 1 addition & 0 deletions frustratometer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from . import align
from . import frustration
from . import optimization
from . import numba_util

# Handle versioneer
from ._version import get_versions
Expand Down
1,550 changes: 1,342 additions & 208 deletions frustratometer/classes/AWSEM.py

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions frustratometer/classes/DCA.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class DCA(Frustratometer):
# self._decoy_fluctuation = {}
# return self

alphabet = '-ACDEFGHIKLMNPQRSTVWY'

@classmethod
def from_potts_model_file(cls,pdb_structure: object,
potts_model_file: Union[Path,str] = None,
Expand Down Expand Up @@ -146,8 +148,8 @@ def from_potts_model_file(cls,pdb_structure: object,
self.potts_model["J"]= self.potts_model["familycouplings"].reshape(int(len(self.filtered_aligned_sequence)),21,int(len(self.filtered_aligned_sequence)),21).transpose(0,2,1,3)

if self.filtered_aligned_sequence is not None:
self.aa_freq = frustration.compute_aa_freq(self.sequence)
self.contact_freq = frustration.compute_contact_freq(self.sequence)
self.aa_freq = frustration.compute_aa_freq(self.sequence, self.alphabet)
self.contact_freq = frustration.compute_contact_freq(self.sequence, self.alphabet)
else:
self.aa_freq = None
self.contact_freq = None
Expand Down Expand Up @@ -222,8 +224,8 @@ def from_pottsmodel(cls,pdb_structure : object,
self.potts_model["J"]= self.potts_model["familycouplings"].reshape(int(len(self.filtered_aligned_sequence)),21,int(len(self.filtered_aligned_sequence)),21).transpose(0,2,1,3)

if self.filtered_aligned_sequence is not None:
self.aa_freq = frustration.compute_aa_freq(self.sequence)
self.contact_freq = frustration.compute_contact_freq(self.sequence)
self.aa_freq = frustration.compute_aa_freq(self.sequence, self.alphabet)
self.contact_freq = frustration.compute_contact_freq(self.sequence, self.alphabet)
else:
self.aa_freq = None
self.contact_freq = None
Expand Down
58 changes: 41 additions & 17 deletions frustratometer/classes/Frustratometer.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,27 @@ def native_energy(self,sequence:str = None,ignore_couplings_of_gaps:bool=False,i
if sequence is None:
sequence=self.sequence
else:
return frustration.compute_native_energy(sequence, self.potts_model, self.mask,ignore_couplings_of_gaps,ignore_fields_of_gaps)
return frustration.compute_native_energy(sequence, self.potts_model, self.mask, self.alphabet,
ignore_couplings_of_gaps, ignore_fields_of_gaps)
if not self._native_energy:
self._native_energy=frustration.compute_native_energy(sequence, self.potts_model, self.mask,ignore_couplings_of_gaps,ignore_fields_of_gaps)
self._native_energy=frustration.compute_native_energy(sequence, self.potts_model, self.mask, self.alphabet,
ignore_couplings_of_gaps, ignore_fields_of_gaps)
else:
new = frustration.compute_native_energy(
sequence, self.potts_model, self.mask, self.alphabet,
ignore_couplings_of_gaps, ignore_fields_of_gaps)
if not (self._native_energy == new):
raise AssertionError(f"""
It seems that you have changed parameters of an object such that
the native energy of your system is now different from what it was
originally computed to be. Our code probably should prevent this
from happening, but you can prevent it too by not changing the alphabet
or any other parameters after initializing your DCA or AWSEM-family
class (anything that inherits from _AWSEMBase).

Previous value of {self.__class__}._native_energy: {self._native_energy}
New value of {self.__class__}._native_energy: {new}""")

energy_value=self._native_energy
return energy_value

Expand All @@ -89,7 +107,7 @@ def sequences_energies(self, sequences:np.array, split_couplings_and_fields:bool
output (if split_couplings_and_fields==True): np.array
Array containing computed fields and couplings energies of the protein sequences.
"""
output=frustration.compute_sequences_energy(sequences, self.potts_model, self.mask, split_couplings_and_fields)
output=frustration.compute_sequences_energy(sequences, self.potts_model, self.mask, self.alphabet, split_couplings_and_fields)
return output

def fields_energy(self, sequence:str = None, ignore_fields_of_gaps:bool = False) -> float:
Expand All @@ -114,7 +132,7 @@ def fields_energy(self, sequence:str = None, ignore_fields_of_gaps:bool = False)
"""
if sequence is None:
sequence=self.sequence
fields_energy=frustration.compute_fields_energy(sequence, self.potts_model,ignore_fields_of_gaps)
fields_energy=frustration.compute_fields_energy(sequence, self.potts_model, self.alphabet, ignore_fields_of_gaps)
return fields_energy

def couplings_energy(self, sequence:str = None,ignore_couplings_of_gaps:bool = False) -> float:
Expand All @@ -139,7 +157,7 @@ def couplings_energy(self, sequence:str = None,ignore_couplings_of_gaps:bool = F
"""
if sequence is None:
sequence=self.sequence
couplings_energy=frustration.compute_couplings_energy(sequence, self.potts_model, self.mask,ignore_couplings_of_gaps)
couplings_energy=frustration.compute_couplings_energy(sequence, self.potts_model, self.mask, self.alphabet, ignore_couplings_of_gaps)
return couplings_energy

def decoy_fluctuation(self, sequence:str = None,kind:str = 'singleresidue',mask:np.array = None) -> np.array:
Expand Down Expand Up @@ -167,13 +185,13 @@ def decoy_fluctuation(self, sequence:str = None,kind:str = 'singleresidue',mask:
if not isinstance(mask, np.ndarray):
mask=self.mask
if kind == 'singleresidue':
fluctuation = frustration.compute_singleresidue_decoy_energy_fluctuation(sequence, self.potts_model, mask)
fluctuation = frustration.compute_singleresidue_decoy_energy_fluctuation(sequence, self.potts_model, mask, self.alphabet)
elif kind == 'mutational':
fluctuation = frustration.compute_mutational_decoy_energy_fluctuation(sequence, self.potts_model, mask)
fluctuation = frustration.compute_mutational_decoy_energy_fluctuation(sequence, self.potts_model, mask, self.alphabet)
elif kind == 'configurational':
fluctuation = frustration.compute_configurational_decoy_energy_fluctuation(sequence, self.potts_model, mask)
fluctuation = frustration.compute_configurational_decoy_energy_fluctuation(sequence, self.potts_model, mask, self.alphabet)
elif kind == 'contact':
fluctuation = frustration.compute_contact_decoy_energy_fluctuation(sequence, self.potts_model, mask)
fluctuation = frustration.compute_contact_decoy_energy_fluctuation(sequence, self.potts_model, mask, self.alphabet)
else:
raise Exception("Wrong kind of decoy generation selected")
self._decoy_fluctuation[kind] = fluctuation
Expand Down Expand Up @@ -211,7 +229,8 @@ def scores(self):
"""
return frustration.compute_scores(self.potts_model)

def frustration(self, sequence:str = None, kind:str = 'singleresidue', mask:np.array = None, aa_freq:np.array = None, correction:int = 0) -> np.array:
def frustration(self, sequence:str = None, kind:str = 'singleresidue', mask:np.array = None, aa_freq:np.array = None,
correction:int = 0, n_decoys:int = 4000) -> np.array:
"""
Calculates frustration index values.

Expand Down Expand Up @@ -242,9 +261,11 @@ def frustration(self, sequence:str = None, kind:str = 'singleresidue', mask:np.a
frustration_values=frustration.compute_single_frustration(decoy_fluctuation, aa_freq, correction)
return frustration_values
elif kind in ['mutational', 'configurational', 'contact']:
if kind == 'configurational' and 'configurational_frustration' in dir(self):
#TODO: Correct this function for different aa_freq than WT
return self.configurational_frustration(None, correction)
if kind == 'configurational':
if 'configurational_frustration' in dir(self):
return self.configurational_frustration(aa_freq=aa_freq, correction=correction, n_decoys=n_decoys)
else:
raise ValueError("kind='configurational' may only be used on objects implementing self.configurational_frustration")
if aa_freq is None:
aa_freq = self.contact_freq
frustration_values=frustration.compute_pair_frustration(decoy_fluctuation, aa_freq, correction)
Expand All @@ -268,7 +289,7 @@ def plot_decoy_energy(self, sequence:str = None, kind:str = 'singleresidue', met
native_energy = self.native_energy(sequence=sequence)
decoy_energy = self.decoy_energy(kind=kind,sequence=sequence)
if kind == 'singleresidue':
g = frustration.plot_singleresidue_decoy_energy(decoy_energy, native_energy, method)
g = frustration.plot_singleresidue_decoy_energy(decoy_energy, native_energy, method, self.alphabet)
return g

def roc(self):
Expand All @@ -292,6 +313,7 @@ def auc(self):
return frustration.compute_auc(self.roc())

def vmd(self, sequence: str = None, single:Union[str,np.array] = 'singleresidue', pair:Union[str,np.array] = 'mutational',
tcl_script:str = 'frustration.tcl', call_vmd:bool=True,
aa_freq:np.array = None, correction:int = 0, max_connections:Union[int,None] = None, movie_name=None, still_image_name=None):
"""
Calculates frustration indices and superimposes frustration patterns onto PDB structure using the VMD software.
Expand All @@ -317,12 +339,14 @@ def vmd(self, sequence: str = None, single:Union[str,np.array] = 'singleresidue'
from the sequence that was passed to this vmd function. Proceeding further may not\n\
perform the computation that you intend to perform.")


#breakpoint()
tcl_script = frustration.write_tcl_script(self.pdb_file, self.chain, self.mask, self.distance_matrix, self.distance_cutoff,
-self.frustration(kind=single, sequence=sequence, aa_freq=aa_freq),
-self.frustration(kind=pair, sequence=sequence, aa_freq=aa_freq),
max_connections=max_connections, movie_name=movie_name, still_image_name=still_image_name)
frustration.call_vmd(self.pdb_file, tcl_script)
max_connections=max_connections, movie_name=movie_name, still_image_name=still_image_name,
tcl_script=tcl_script,)
if call_vmd:
frustration.call_vmd(self.pdb_file, tcl_script)

def view_pair_frustration(self, sequence:str = None, pair:str = 'mutational', aa_freq:np.array = None):
"""
Expand Down
25 changes: 17 additions & 8 deletions frustratometer/classes/Gamma.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def __init__(self, data, segment_definition=None, description=None, alphabet=Non

self._validate_segments()

@property
def q(self):
return len(self.alphabet)

def _init_from_array(self, gamma_array):
self.gamma_array = gamma_array

Expand Down Expand Up @@ -399,7 +403,7 @@ def correlate_segments(self, other):
return correlations

# Plotting
def plot_gamma(self, new_order=None):
def plot_gamma(self, new_order=None, scale=[-5,5]):
import matplotlib.pyplot as plt
import seaborn as sns
if new_order:
Expand All @@ -408,16 +412,21 @@ def plot_gamma(self, new_order=None):

# Plot setup
f, axes = plt.subplots(2, 2, figsize=(18, 16))
titles = ['Burial Gammas', 'Direct Gammas', 'Water Gammas', 'Protein Gammas']

f.subplots_adjust(hspace=50) # fix overlap between axis ticks of upper subplots and titles of lower subplots
titles = ['Burial Gammas', 'Direct Gammas', 'Protein Gammas', 'Water Gammas']
for i, (title, name) in enumerate(zip(titles, segments)):
ax = axes[i // 2, i % 2]
sns.heatmap(segments[name].reshape(-1, 20), ax=ax, cmap='RdBu_r', center=0)
foo = sns.heatmap(segments[name].reshape(-1, 20), ax=ax, cmap='RdBu_r', center=0, vmin=scale[0], vmax=scale[1])
foo.collections[0].colorbar.ax.tick_params(labelsize=16)
ax.set_title(title)
ax.set_xticks(np.arange(len(self.alphabet)) + 0.5)
ax.set_xticklabels(self.alphabet)
ax.set_yticks(np.arange(segments[name].shape[0] // 20) + 0.5)
ax.set_yticklabels(range(segments[name].shape[0] // 20))
ax.set_xticklabels(self.alphabet, size=16)
if i==0: # burial
ax.set_yticks([0.5,1.5,2.5])
ax.set_yticklabels(['low','medium','high'], rotation=45, size=16)
else: # direct, prot, or wat
ax.set_yticks(np.arange(len(self.alphabet)) + 0.5)
ax.set_yticklabels(self.alphabet, rotation=0, fontsize=16)

plt.tight_layout()
plt.show()
Expand Down Expand Up @@ -648,4 +657,4 @@ class O():

self.gamma1 = Gamma(np.arange(0,1260,1))
self.gamma2 = Gamma(np.arange(0,1260,1)*5+10)
self.gamma3 = Gamma(np.arange(1260,0,-1)*2-4)
self.gamma3 = Gamma(np.arange(1260,0,-1)*2-4)
Loading