Skip to content

Commit 7d14b91

Browse files
authored
Merge pull request #198 from StollLab/molsys_point
Add point keyword to MolSys object for selection atoms neara point in space
2 parents c19459b + 6157178 commit 7d14b91

3 files changed

Lines changed: 256 additions & 141 deletions

File tree

src/chilife/MolSys.py

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -233,24 +233,24 @@ class MolSys(MolecularSystemBase):
233233
Name of molecular system
234234
"""
235235
def __init__(
236-
self,
237-
record_types: np.ndarray,
238-
atomids: np.ndarray,
239-
names: np.ndarray,
240-
altlocs: np.ndarray,
241-
resnames: np.ndarray,
242-
resnums: np.ndarray,
243-
icodes: np.ndarray,
244-
chains: np.ndarray,
245-
trajectory: np.ndarray,
246-
occupancies: np.ndarray,
247-
bs: np.ndarray,
248-
segs: np.ndarray,
249-
atypes: np.ndarray,
250-
charges: np.ndarray,
251-
bonds: ArrayLike = None,
252-
name: str = 'Noname_MolSys'
253-
236+
self,
237+
record_types: np.ndarray,
238+
atomids: np.ndarray,
239+
names: np.ndarray,
240+
altlocs: np.ndarray,
241+
resnames: np.ndarray,
242+
resnums: np.ndarray,
243+
icodes: np.ndarray,
244+
chains: np.ndarray,
245+
trajectory: np.ndarray,
246+
occupancies: np.ndarray,
247+
bs: np.ndarray,
248+
segs: np.ndarray,
249+
atypes: np.ndarray,
250+
charges: np.ndarray,
251+
chiral: ArrayLike =None,
252+
bonds: ArrayLike = None,
253+
name: str = "Noname_MolSys",
254254
):
255255

256256
self.molsys = self
@@ -268,6 +268,7 @@ def __init__(
268268
self.segs = segs.copy()
269269
self.atypes = atypes.copy()
270270
self.charges = charges.copy()
271+
self.chiral = chiral.copy() if chiral is not None else None
271272
self._fname = name
272273

273274
self.ix = np.arange(len(self.atomids))
@@ -310,6 +311,7 @@ def __init__(
310311
'chain': self.chains,
311312
'occupancies': self.occupancies,
312313
'b': self.bs,
314+
'chiral': self.chiral,
313315
'segid': self.chains,
314316
'type': self.atypes,
315317
'charges': self.charges,
@@ -341,7 +343,8 @@ def __init__(
341343
'!=': operator.ne,
342344
'byres': partial(byres, molsys=self.molsys),
343345
'within': update_wrapper(partial(within, molsys=self.molsys), within),
344-
'around': update_wrapper(partial(within, molsys=self.molsys), within)}
346+
'around': update_wrapper(partial(within, molsys=self.molsys), within),
347+
'point': update_wrapper(partial(point, molsys=self.molsys), point),}
345348

346349

347350
# Aliases
@@ -783,6 +786,12 @@ def process_statement(statement, logickws, subjectkws):
783786
continue
784787

785788
stat_split = stat.split()
789+
if stat_split[0] not in logickws and stat_split[0] not in subjectkws:
790+
raise ValueError(
791+
f"Invalid selection keyword: {stat_split[0]}. All selections statements and substatements must start "
792+
f"with a valid selection keyword"
793+
)
794+
786795
subject = None
787796
values = []
788797
while len(stat_split) > 0:
@@ -810,6 +819,9 @@ def process_statement(statement, logickws, subjectkws):
810819
mask = operation(mask, tmp) if operation else tmp
811820
operation = None
812821

822+
elif not values and hasattr(operation, 'novals'):
823+
mask = operation(mask, np.ones_like(mask, dtype=bool))
824+
813825
return mask
814826

815827

@@ -879,7 +891,7 @@ def check_operation(operation, stat_split, logickws):
879891
operation : callable
880892
A simplified version of the provided operation now accounting for the user provided parameters.
881893
"""
882-
advanced_operators = (logickws['within'], logickws['around'], logickws['byres'])
894+
advanced_operators = (logickws['within'], logickws['around'], logickws['byres'], logickws['point'])
883895
if operation in advanced_operators:
884896
outer_operation = logickws['and']
885897
args = [stat_split.pop(i) for i in range(1, 1 + operation.nargs)]
@@ -915,7 +927,7 @@ def build_operator(stat_split, logickws):
915927
operation = logickws['and']
916928
unary_operators = (logickws['not'], logickws['byres'])
917929
binary_operators = (logickws['and'], logickws['or'])
918-
advanced_operators = (logickws['within'], logickws['around'])
930+
advanced_operators = (logickws['within'], logickws['around'], logickws['point'])
919931

920932
while _io := logickws.get(stat_split[0], False):
921933

@@ -936,6 +948,9 @@ def toperation(a, b, operation, _io):
936948

937949
operation = partial(toperation, operation=operation, _io=_io)
938950

951+
if hasattr(_io.func, 'novals'):
952+
operation.novals = _io.func.novals
953+
939954
elif _io in binary_operators:
940955
if operation != logickws['and']:
941956
raise RuntimeError('Cannot have two binary logical operators in succession')
@@ -1323,7 +1338,7 @@ def unot(mask):
13231338

13241339
def within(distance, mask, molsys):
13251340
"""
1326-
Advanced logic operator to identify atoms within a user defined distance.
1341+
Advanced logic operator to identify atoms within a user defined distance of another selection.
13271342
13281343
Parameters
13291344
----------
@@ -1355,6 +1370,44 @@ def within(distance, mask, molsys):
13551370
within.nargs = 1
13561371

13571372

1373+
def point(x, y, z, distance, mask, molsys):
1374+
"""
1375+
Advanced logic operator to identify atoms within a user defined distance of a point in cartesian space.
1376+
1377+
Parameters
1378+
----------
1379+
x : float
1380+
The x coordinate of the point in cartesian space.
1381+
y : float
1382+
The y coordinate of the point in cartesian space.
1383+
z : float
1384+
The z coordinate of the point in cartesian space.
1385+
distance : float
1386+
The distance window defining which atoms will be included in the selection.
1387+
mask : np.ndarray
1388+
A boolean array defining a subset of atoms of a :class:`~MolSys` from which the distance cutoff will be
1389+
measured by.
1390+
molsys : :class:`~MolSys`
1391+
A chiLife :class:`~MolSys` from which to select atoms from .
1392+
1393+
Returns
1394+
-------
1395+
out_mask : np.ndarray
1396+
A boolean array defining a subset of atoms of a :class:`~MolSys` that are within the user defined distance
1397+
of the user defined selection.
1398+
"""
1399+
1400+
tree1 = cKDTree(molsys.coords[mask])
1401+
args = tree1.query_ball_point(np.array([x, y, z], dtype=float), distance)
1402+
out_mask = np.zeros_like(mask, dtype=bool)
1403+
out_mask[args] = True
1404+
1405+
return out_mask
1406+
1407+
1408+
point.nargs = 4
1409+
point.novals = True
1410+
13581411
def concat_molsys(systems):
13591412
"""
13601413
Function to concatenate two or more :class:`~MolSys` objects into a single :class:`~MolSys` object. Atoms will be

src/chilife/io.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Tuple, Dict, Union, BinaryIO, TextIO, Protocol
1+
from typing import Tuple, Dict, Union, BinaryIO, TextIO
22
import warnings
33
import os
44
import urllib
@@ -9,7 +9,7 @@
99
from pathlib import Path
1010
import pickle
1111
import shutil
12-
from io import StringIO, BytesIO
12+
from io import StringIO
1313
import zipfile
1414

1515
import numpy as np
@@ -507,8 +507,8 @@ def fetch(accession_number: str, save: bool = False) -> MDAnalysis.Universe:
507507
pdb_name = accession_number + '.pdb'
508508

509509
if accession_number.startswith('AF-'):
510-
print(f"https://alphafold.ebi.ac.uk/files/{accession_number}-F1-model_v3.pdb")
511-
urllib.request.urlretrieve(f"https://alphafold.ebi.ac.uk/files/{accession_number}-F1-model_v3.pdb", pdb_name)
510+
print(f"https://alphafold.ebi.ac.uk/files/{accession_number}-F1-model_v6.pdb")
511+
urllib.request.urlretrieve(f"https://alphafold.ebi.ac.uk/files/{accession_number}-F1-model_v6.pdb", pdb_name)
512512
else:
513513
urllib.request.urlretrieve(f"http://files.rcsb.org/download/{pdb_name}", pdb_name)
514514

@@ -631,7 +631,7 @@ def write_frame(pdb_file: TextIO, atoms, frame=None, coords=None):
631631
pdb_file.write("TER\n")
632632

633633
if frame is not None:
634-
pdb_file.write(f"ENDMDL\n")
634+
pdb_file.write("ENDMDL\n")
635635

636636

637637
def write_ic(pdb_file: TextIO,

0 commit comments

Comments
 (0)