Skip to content

Commit f3ef94e

Browse files
authored
Merge branch 'development' into resolve_env_for_develop
2 parents 6aa3335 + 4df899b commit f3ef94e

File tree

16 files changed

+487
-365
lines changed

16 files changed

+487
-365
lines changed

.github/workflows/CI.yaml

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,15 @@ jobs:
5050
auto-update-conda: false
5151
auto-activate-base: false
5252
use-only-tar-bz2: true # IMPORTANT: This needs to be set for caching to work properly!
53-
54-
- name: Install MDSAPT package
55-
# conda setup requires this special shell
53+
54+
- name: Type-check
5655
shell: bash -l {0}
5756
run: |
58-
python -m pip install . --no-deps
59-
conda list
57+
pyright mdsapt
6058
61-
# TODO: possibly integrate this step with meta.yml?
6259
- name: Run tests
63-
# conda setup requires this special shell
6460
shell: bash -l {0}
6561
run: |
66-
pip install pytest-cov
6762
pytest -v ./mdsapt --cov=mdsapt --cov-report=xml
6863
6964
- name: Upload CodeCov
@@ -73,6 +68,12 @@ jobs:
7368
flags: unittests
7469
name: codecov-${{ matrix.os }}-py${{ matrix.python-version }}
7570

71+
- name: Install MDSAPT package
72+
shell: bash -l {0}
73+
run: |
74+
python -m pip install . --no-deps
75+
conda list
76+
7677
package:
7778
name: Packaging py${{ matrix.python-version }}/${{ matrix.os }}
7879
runs-on: ${{ matrix.os }}
@@ -162,3 +163,23 @@ jobs:
162163
shell: bash -l {0}
163164
run: >
164165
python3 -c "import mdsapt"
166+
167+
lint-and-format:
168+
# These checks are much faster and don't actually need to install any packages.
169+
name: Run fast checks over code
170+
runs-on: ubuntu-latest
171+
172+
steps:
173+
- uses: actions/checkout@v2
174+
175+
- name: Install formatter and linter
176+
run: |
177+
pip install autopep8 pylint
178+
179+
- name: Lint code
180+
run: |
181+
pylint mdsapt
182+
183+
- name: Ensure formatting
184+
run: |
185+
autopep8 -r mdsapt --diff --exit-code

environment.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,23 @@ channels:
77
- defaults
88
dependencies:
99
- mdanalysis
10-
- nglview
1110
- numpy
1211
- openmm
1312
- pandas
1413
- pdbfixer
1514
- psi4
1615
- pytest
16+
- pydantic
1717
- pytest-cov
1818
- pyyaml
1919
- rdkit
20+
21+
# Optional deps
22+
- nglview
23+
24+
# Development deps
25+
- autopep8
26+
- pyright # type-checking
27+
- pylint
28+
- pytest
29+
- pytest-cov

mdsapt/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from . import log
55

66
# Import core classes
7-
from .reader import InputReader
7+
from .config import Config
88
from .optimizer import Optimizer
99
from .sapt import TrajectorySAPT
1010

mdsapt/config.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
r"""
2+
:mod:`mdsapt.reader` -- Reads input file and saves configuration
3+
================================================================
4+
5+
MDSAPT uses an yaml file to get user's configurations for SAPT calculations
6+
class`mdsapt.reader.InputReader` is responsible for reading the yaml file and
7+
returning the information from it. If a yaml file is needed it can be generated
8+
using the included *mdsapt_get_runinput* script.
9+
10+
.. autoexception:: ConfigurationError
11+
12+
.. autoclass:: InputReader
13+
:members:
14+
:inherited-members:
15+
"""
16+
17+
from enum import Enum
18+
from pathlib import Path
19+
from typing import List, Dict, Tuple, Literal, Optional, \
20+
Union, Any, Set
21+
22+
import yaml
23+
24+
import MDAnalysis as mda
25+
26+
import logging
27+
28+
from pydantic import BaseModel, conint, Field, root_validator, \
29+
FilePath, ValidationError, DirectoryPath
30+
31+
logger = logging.getLogger('mdsapt.config')
32+
33+
34+
class Psi4Config(BaseModel):
35+
"""Psi4 configuration details
36+
37+
The SAPT method to use.
38+
39+
NOTE: You can use any valid Psi4 method, but it might fail if you don't use a SAPT method.
40+
41+
The basis to use in Psi4.
42+
43+
NOTE: We do not verify if this is a valid basis set or not.
44+
"""
45+
46+
method: str
47+
basis: str
48+
save_output: bool # whether to save the raw output of Psi4. May be useful for debugging.
49+
settings: Dict[str, str] # Other Psi4 settings you would like to provide.
50+
51+
52+
class SysLimitsConfig(BaseModel):
53+
"""Resource limits for your system."""
54+
ncpus: conint(ge=1)
55+
memory: str
56+
57+
58+
class ChargeGuesser(Enum):
59+
Standard = 'standard'
60+
RDKit = 'rdkit'
61+
62+
63+
class SimulationConfig(BaseModel):
64+
ph: float
65+
charge_guesser: ChargeGuesser
66+
67+
68+
class DetailedTopologySelection(BaseModel):
69+
path: FilePath
70+
charge_overrides: Dict[int, int] = Field(default_factory=dict)
71+
72+
73+
TopologySelection = Union[FilePath, DetailedTopologySelection]
74+
75+
76+
def topology_selection_path(sel: TopologySelection) -> FilePath:
77+
if isinstance(sel, DetailedTopologySelection):
78+
return sel.path
79+
return sel
80+
81+
82+
class RangeFrameSelection(BaseModel):
83+
start: Optional[conint(ge=0)]
84+
stop: Optional[conint(ge=0)]
85+
step: Optional[conint(ge=1)]
86+
87+
@root_validator()
88+
def check_start_before_stop(cls, values: Dict[str, int]) -> Dict[str, int]:
89+
assert values['start'] <= values['stop'], "start must be before stop"
90+
return values
91+
92+
93+
class TrajectoryAnalysisConfig(BaseModel):
94+
"""
95+
A selection of the frames used in this analysis.
96+
97+
Serialization behavior
98+
----------------------
99+
If this value is a range, it will be serialized using start/stop/step.
100+
Otherwise, it will be serialized into a List[int].
101+
"""
102+
type: Literal['trajectory']
103+
topology: TopologySelection
104+
trajectories: List[FilePath]
105+
pairs: List[Tuple[conint(ge=0), conint(ge=0)]]
106+
frames: Union[List[int], RangeFrameSelection]
107+
output: str
108+
109+
@root_validator()
110+
def check_valid_md_system(cls, values: Dict[str, Any]) -> Dict[str, Any]:
111+
errors: List[str] = []
112+
113+
top_path: TopologySelection = values['topology']
114+
trj_path: List[FilePath] = values['trajectories']
115+
ag_pair: List[Tuple[conint(ge=0), conint(ge=0)]] = values['pairs']
116+
frames: Union[List[int], RangeFrameSelection] = values['frames']
117+
118+
try:
119+
unv = mda.Universe(str(topology_selection_path(top_path)),
120+
[str(p) for p in trj_path])
121+
except (mda.exceptions.NoDataError, OSError, ValueError):
122+
errors.append('Error while creating universe using provided topology and trajectories')
123+
raise ValidationError(errors) # If Universe doesn't load need to stop
124+
125+
# Ensure that
126+
items: Set[int] = {i for pair in ag_pair for i in pair}
127+
128+
for sel in items:
129+
ag: mda.AtomGroup = unv.select_atoms(f'resid {sel}')
130+
if len(ag) == 0:
131+
errors.append(f"Selection {sel} returns an empty AtomGroup.")
132+
133+
134+
135+
trajlen: int = len(unv.trajectory)
136+
if isinstance(frames, RangeFrameSelection):
137+
if trajlen <= frames.stop:
138+
errors.append(f'Stop {frames.stop} exceeds trajectory length {trajlen}.')
139+
else:
140+
frames: List[int]
141+
for frame in frames:
142+
if frame >= trajlen:
143+
errors.append(f'Frame {frame} exceeds trajectory length {trajlen}')
144+
145+
if len(errors) > 0:
146+
raise ValidationError(errors)
147+
return values
148+
149+
150+
class DockingAnalysisConfig(BaseModel):
151+
type: Literal['docking']
152+
topologies: Union[List[TopologySelection], DirectoryPath]
153+
pairs: List[Tuple[int, int]]
154+
155+
156+
class Config(BaseModel):
157+
psi4: Psi4Config
158+
simulation: SimulationConfig
159+
system_limits: SysLimitsConfig
160+
analysis: Union[TrajectoryAnalysisConfig, DockingAnalysisConfig] = Field(..., discriminator='type')
161+
162+
163+
def load_from_yaml_file(path: Union[str, Path]) -> Config:
164+
if isinstance(path, str):
165+
path = Path(path)
166+
167+
with path.open() as f:
168+
try:
169+
return Config(**yaml.safe_load(f))
170+
except ValidationError as err:
171+
logger.exception(f"Error while loading {path}")
172+
raise err

mdsapt/data/template_input.yaml

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,59 @@
1-
topology_path:
2-
trajectory_paths:
3-
-
4-
selection_resid_num:
5-
-
6-
int_pairs:
7-
# Place pair names defined above in list in a list
8-
- []
9-
trajectory_settings:
10-
start:
11-
stop:
12-
step:
13-
system_settings:
14-
ncpus:
15-
memory:
16-
time:
17-
opt_settings:
18-
pH: 7.0
19-
sapt_settings:
1+
psi4:
202
method: 'sapt'
213
basis: 'jun-cc-pvdz'
4+
save_output: true
225
settings:
236
reference: 'rhf'
24-
save_psi4_output: true
7+
simulation:
8+
ph: 7.0
9+
charge_guesser: 'standard'
10+
# charge_guesser: 'rdkit' # to use rdkit. Make sure it is installed first.
11+
system_limits:
12+
ncpus:
13+
memory:
14+
analysis:
15+
### This section is for running TrajectorySAPT. To run other types of analyses, see below.
16+
type: 'trajectory'
17+
18+
topology: 'your/file/path.pdb'
19+
# If you want to override the charges of specific, you may specify it this way.
20+
# topology:
21+
# path: 'your/file/path.pdb'
22+
# charge_overrides:
23+
# 132: -2
24+
25+
trajectories:
26+
- 'your/trajectory/path.dcd'
27+
pairs:
28+
- [132, 152]
29+
- [34, 152]
30+
frames:
31+
# To select a set of specific frames:
32+
- 1
33+
- 4
34+
- 8
35+
36+
# # Alternatively, to select a range of frames: (these are mutually exclusive)
37+
# start:
38+
# stop:
39+
# step:
40+
41+
output: 'output.csv'
42+
43+
# ### For running DockingSAPT
44+
# type: 'docking'
45+
#
46+
# topologies:
47+
# - 'one/topology.pdb'
48+
# - 'another/topology.pdb'
49+
# - 'a/glob/of/topologies/*.pdb'
50+
# - 'a/directory/full/of/topologies'
51+
# - path: 'one/specific/topology/with/weird/charges.pdb'
52+
# charge_overrides:
53+
# 145: -4
54+
#
55+
# pairs:
56+
# - [132, 152]
57+
# - [34, 152]
58+
#
59+
# output: 'output.csv'

0 commit comments

Comments
 (0)