Skip to content

Commit 8e8a1a4

Browse files
committed
fixed the docs
1 parent 5d05aed commit 8e8a1a4

12 files changed

Lines changed: 92 additions & 64 deletions

doc/source/explanation/atom-selection-language.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ lookups and element checks:
5252
| `hydrogen` | All atoms with `element == "H"` |
5353
| `noh` | All non-hydrogen atoms |
5454
| `all` | Every atom in the molecule |
55-
| `none` | No atoms |
5655

5756
## Per-atom field comparisons
5857

@@ -100,10 +99,13 @@ mol.atomselect("beta < 20")
10099
mol.atomselect("resid 40 to 60")
101100
mol.atomselect("index 0 to 99")
102101

103-
# Negation with !=
104-
mol.atomselect("chain != B")
102+
# Negation: use `not`
103+
mol.atomselect("not chain B")
105104
```
106105

106+
The `!=` operator only applies inside the modulo form (e.g.
107+
`resid % 2 != 0`); for plain field comparisons use `not`.
108+
107109
## Boolean composition
108110

109111
Combine selections with `and`, `or`, `not`, and parentheses:

doc/source/explanation/segments-chains-and-bonds.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,15 @@ from moleculekit.tools.autosegment import autoSegment
7777

7878
mol_seg = autoSegment(mol) # returns a modified copy
7979
import numpy as np
80-
print(np.unique(mol_seg.segid)) # e.g. ['P0', 'P1', 'W0']
80+
print(np.unique(mol_seg.segid)) # e.g. ['P0', 'P1', 'P2']
8181
```
8282

83+
Each contiguous polypeptide run (and each contiguous run of water residues)
84+
gets the next `P{i}` segid. Water residues additionally receive `chain = "W"`
85+
to keep them visually distinct from polymer chains, but their **segid** stays
86+
on the same `P{i}` numbering sequence as everything else; there is no `W0`
87+
segid.
88+
8389
The function accepts a `basename` argument to control naming: `basename="P"`
8490
produces `P0`, `P1`, `P2`, etc. The `fields` argument controls which field(s)
8591
are written: `("segid",)` (default), `("chain",)`, or `("segid", "chain")`.

doc/source/explanation/system-preparation-pipeline.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ up the ligand spec automatically.
193193
### mutateResidue before systemPrepare
194194

195195
When you want to swap canonical residues (e.g. a point mutation), call
196-
{py:meth}`~moleculekit.molecule.Molecule.mutateResidue` with `sel` and `new_resname` first. The mutator uses Dunbrack rotamers
196+
{py:meth}`~moleculekit.molecule.Molecule.mutateResidue` with `sel` and `newres` first. The mutator uses Dunbrack rotamers
197197
to place the new sidechain, and {py:func}`~moleculekit.tools.preparation.systemPrepare` then protonates and optimizes
198198
the mutated residue along with the rest of the protein.
199199

doc/source/explanation/trajectories-and-frames.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,33 +51,41 @@ The `read` method accepts `frames` and `skip` arguments to load a subset
5151
without reading the whole file into memory:
5252

5353
```python
54-
# Load every 10th frame
54+
# Load every 10th frame (uniform stride)
5555
mol.read("run1.xtc", skip=10)
5656

57-
# Load a specific list of frame indices
58-
mol.read("run1.xtc", frames=range(0, 1000, 10))
59-
60-
# Load only frame 500
57+
# Load only frame 500 of a single trajectory
6158
mol.read("run1.xtc", frames=[500])
59+
60+
# Load a specific list of frame indices from a single trajectory
61+
mol.read("run1.xtc", frames=[[0, 10, 20, 30, 40]])
6262
```
6363

64-
`skip=N` applies uniform subsampling; `frames=` takes an arbitrary list of
65-
indices.
64+
`skip=N` applies uniform subsampling. `frames=` is **per file**: it must be a
65+
list with one entry per trajectory in `filename`, and each entry is either an
66+
int (single frame) or a list of ints (multiple frames). When reading just one
67+
trajectory this means wrapping your indices in an outer list, as in the third
68+
example above. For most subsampling use cases `skip=` is simpler.
6669

6770
### Multi-trajectory loads
6871

69-
Pass a list of trajectory files to the `Molecule` constructor, or call
70-
`read(..., append=True)` repeatedly. Frames are concatenated in order:
72+
Pass topology and trajectories as a single list to the `Molecule` constructor,
73+
or call `read(..., append=True)` repeatedly. Frames are concatenated in order:
7174

7275
```python
73-
mol = Molecule("system.prmtop", ["run1.xtc", "run2.xtc", "run3.xtc"])
76+
mol = Molecule(["system.prmtop", "run1.xtc", "run2.xtc", "run3.xtc"])
7477
# Equivalent to:
7578
mol = Molecule("system.prmtop")
7679
mol.read("run1.xtc")
7780
mol.read("run2.xtc", append=True)
7881
mol.read("run3.xtc", append=True)
7982
```
8083

84+
The second positional argument of {py:class}`~moleculekit.molecule.Molecule`
85+
is `name=`, **not** a trajectory file — passing trajectories there assigns
86+
them to `mol.name` and loads nothing. Always use a single list (as above) or
87+
the explicit `read(..., append=True)` form.
88+
8189
`mol.numFrames` is the total number of frames from all trajectories. Frame
8290
ordering mirrors the order the files were passed.
8391

@@ -123,9 +131,9 @@ precision, what they store, and typical file size:
123131

124132
| Format | Extension | Precision | Box | Velocities | Notes |
125133
|---|---|---|---|---|---|
126-
| XTC | `.xtc` | Reduced (lossy ~0.001 Å) | Yes | No | GROMACS native; very compact |
134+
| XTC | `.xtc` | Reduced (lossy XDR fixed-point, ~0.01 Å) | Yes | No | GROMACS native; very compact |
127135
| DCD | `.dcd` | Full `float32` | Varies | No | NAMD/CHARMM native |
128-
| TRR | `.trr` | Full `float64` | Yes | Yes | GROMACS native; larger files |
136+
| TRR | `.trr` | Full (file stores up to `float64`; moleculekit holds `coords` as `float32`) | Yes | Yes | GROMACS native; larger files |
129137
| NetCDF | `.nc`, `.ncdf` | Full `float32` | Yes | Optional | AMBER native |
130138

131139
XTC is the most common format encountered in practice. Its compressed storage

doc/source/howto/compute-dihedrals.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ print(f"psi(17) after : {np.degrees(mol.getDihedral(psi17)):.1f}°")
3434
| Method / argument | What it does |
3535
|---|---|
3636
| `mol.getDihedral(atom_quad)` | Returns the dihedral angle in **radians** for the four atom indices in `atom_quad`. Operates on the current `mol.frame`. |
37-
| `mol.setDihedral(atom_quad, radians, bonds=None, guessBonds=False)` | Rotates the downstream half of the molecule around the central bond of the dihedral so the angle becomes `radians`. For a chain of modifications pass `bonds=mol._getBonds()` once to keep the bond table from being re-guessed on every call. |
37+
| `mol.setDihedral(atom_quad, radians, bonds=None, guessBonds=False)` | Rotates the downstream half of the molecule around the central bond of the dihedral so the angle becomes `radians`. For a chain of modifications pass `bonds=mol.bonds` (or a pre-built bond array) once to keep the bond table from being re-built on every call. |
3838

3939
## Common variations
4040

@@ -59,7 +59,7 @@ angles = np.array([
5959

6060
- Both `getDihedral` and `dihedralAngle` return angles in **radians**; convert with `np.degrees(angle)` if you want degrees.
6161
- `mol.getDihedral` operates on the current `mol.frame` — set `mol.frame = i` first if you want a specific frame.
62-
- `mol.setDihedral` rotates the downstream side of the dihedral in place; the upstream side is held fixed. If the topology is ambiguous (the rotation would split the molecule wrong), the call may fail — guard against this by passing an explicit `bonds=` array.
62+
- `mol.setDihedral` rotates the downstream side of the dihedral in place; the upstream side is held fixed. If the topology is ambiguous (the rotation would split the molecule wrong), the call may fail — guard against this by passing an explicit `bonds=mol.bonds` array.
6363
- For computing many dihedrals at once across a trajectory, prefer {py:class}`~moleculekit.projections.metricdihedral.MetricDihedral` from `moleculekit.projections.metricdihedral` — it batches the work efficiently.
6464

6565
## See also

doc/source/howto/compute-protein-ligand-interactions.md

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,48 @@ Detect hydrogen bonds, π–π stacking, cation–π, and σ-hole interactions b
88

99
```python
1010
import numpy as np
11-
from rdkit.Chem import ChemicalFeatures
12-
from rdkit import RDConfig
13-
import os
1411
from moleculekit.molecule import Molecule
15-
from moleculekit.interactions.interactions import hbonds_calculate
12+
from moleculekit.smallmol.smallmol import SmallMol
13+
from moleculekit.interactions.interactions import (
14+
hbonds_calculate,
15+
get_ligand_donors_acceptors,
16+
)
1617

1718
mol = Molecule("3PTB")
1819
mol.templateResidueFromSmiles(
1920
mol.resname == "BEN",
20-
smiles="NC(=N)c1ccccc1",
21+
smiles="[NH2+]=C(N)c1ccccc1",
2122
addHs=True,
2223
)
2324

24-
# Convert the ligand to RDKit, then ask RDKit which atoms are H-bond
25-
# donors and acceptors.
26-
lig = mol.copy(sel="resname BEN")
27-
rdlig = lig.toRDKitMol(sanitize=True)
28-
29-
fdef = os.path.join(RDConfig.RDDataDir, "BaseFeatures.fdef")
30-
factory = ChemicalFeatures.BuildFeatureFactory(fdef)
31-
features = factory.GetFeaturesForMol(rdlig)
32-
25+
# Build a SmallMol view of the ligand, then ask the library helper for
26+
# donor/acceptor indices in the parent Molecule's atom numbering.
27+
lig = SmallMol(mol.copy(sel="resname BEN"))
3328
start_idx = int(mol.atomselect("resname BEN", indexes=True)[0])
34-
acceptors = [idx + start_idx for f in features if f.GetFamily() == "Acceptor"
35-
for idx in f.GetAtomIds()]
36-
donor_pairs = [(idx + start_idx, idx + start_idx)
37-
for f in features if f.GetFamily() == "Donor"
38-
for idx in f.GetAtomIds()]
29+
donors, acceptors = get_ligand_donors_acceptors(lig, start_idx=start_idx)
3930

4031
# For non-periodic structures hbonds_calculate needs a zero box.
4132
mol.box = np.zeros((3, mol.numFrames), dtype=np.float32)
42-
hbonds = hbonds_calculate(mol, np.array(donor_pairs), np.array(acceptors), sel2="protein")
33+
hbonds = hbonds_calculate(mol, donors, acceptors, sel2="protein")
4334
print(f"H-bonds in frame 0: {len(hbonds[0])}")
4435
```
4536

4637
## Parameters that matter
4738

4839
| Function/parameter | What it does |
4940
|---|---|
50-
| `toRDKitMol(sanitize=True)` | Convert the ligand selection to an RDKit Mol for feature detection. |
51-
| `RDConfig.RDDataDir / "BaseFeatures.fdef"` | RDKit's standard donor/acceptor feature definition file. |
52-
| `start_idx` | Offset of the first ligand atom in the parent `Molecule`; required when the ligand does not start at atom 0. |
53-
| `hbonds_calculate(mol, donors, acceptors, sel2=...)` | Returns a list (one entry per frame) of H-bond arrays. |
41+
| {py:class}`~moleculekit.smallmol.smallmol.SmallMol` | Lightweight RDKit-backed view of the ligand used by the helper to walk the bond graph. |
42+
| `start_idx` | Offset of the first ligand atom in the parent `Molecule`; the helper adds it so the returned indices reference the parent. |
43+
| `get_ligand_donors_acceptors(smol, start_idx=...)` | Returns `(donors, acceptors)`. `donors` is a `(N, 2)` array of `[heavy_atom_idx, hydrogen_idx]` pairs; `acceptors` is a 1D array of heavy-atom indices. |
44+
| `hbonds_calculate(mol, donors, acceptors, sel2=...)` | Returns a list (one entry per frame) of H-bonds; each entry is a list of `(donor_heavy, donor_h, acceptor)` triples. |
5445

5546
## Common variations
5647

5748
```python
58-
# Visualise H-bonds in VMD (requires VMD on PATH)
59-
from moleculekit.interactions.interactions import view_hbonds
60-
61-
view_hbonds(mol, hbonds)
49+
# Convert the per-frame list of triples into a single numpy array of donors,
50+
# Hs, and acceptors for frame 0.
51+
frame0 = np.array(hbonds[0]) # shape (n_hbonds, 3)
52+
print("donor heavy atoms, Hs, acceptors:\n", frame0)
6253
```
6354

6455
```python
@@ -75,9 +66,9 @@ rings = get_protein_rings(mol)
7566

7667
## Gotchas
7768

78-
- Reasonable hydrogens must be present — run {py:func}`~moleculekit.tools.preparation.systemPrepare` before this step if the input lacks explicit H atoms.
79-
- Feature detection uses RDKit through {py:meth}`~moleculekit.molecule.Molecule.toRDKitMol`; make sure RDKit is installed.
80-
- `start_idx` is critical when the ligand atoms do not start at index 0 in the parent molecule.
69+
- Reasonable hydrogens must be present — run {py:func}`~moleculekit.tools.preparation.systemPrepare` (or pass `addHs=True` to {py:meth}`~moleculekit.molecule.Molecule.templateResidueFromSmiles`) before this step.
70+
- {py:func}`~moleculekit.interactions.interactions.get_ligand_donors_acceptors` returns donor *pairs* `[heavy, H]`, **not** lone heavy atoms. Building your own pairs as `(heavy, heavy)` silently produces wrong H-bond results because `hbonds_calculate` reads column 1 as the hydrogen index.
71+
- `start_idx` is critical when the ligand atoms do not start at index 0 in the parent molecule — the helper offsets every returned index by `start_idx`.
8172
- For non-periodic structures set `mol.box` to a zero array before calling `hbonds_calculate`.
8273

8374
## See also

doc/source/howto/compute-rmsd-rmsf.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ print(rmsd)
3333
## Common variations
3434

3535
```python
36+
import numpy as np
3637
# RMSF per Cα atom over a trajectory
3738
from moleculekit.projections.metricfluctuation import MetricFluctuation
3839

39-
rmsf = MetricFluctuation("protein and name CA").project(mol)
40-
# rmsf shape: (n_frames, n_CA); for a single per-atom value over the trajectory use rmsf.mean(axis=0)
40+
# .project returns squared per-atom displacement (shape (n_frames, n_CA),
41+
# units Ų). RMSF = sqrt(mean over frames of the squared displacement).
42+
sq_disp = MetricFluctuation("protein and name CA").project(mol)
43+
rmsf = np.sqrt(sq_disp.mean(axis=0)) # shape (n_CA,), units Å
4144
```
4245

4346
```python
@@ -51,7 +54,7 @@ rmsd_aligned = molRMSD(mol, ref, ca_idx, ca_idx)
5154
- {py:func}`~moleculekit.util.molRMSD` computes raw (non-mass-weighted) RMSD; align the structures first if you want to remove rigid-body motion.
5255
- RMSF is per-atom and depends on the choice of reference (mean structure); the result is sensitive to conformational heterogeneity.
5356
- Both `mol` and `refmol` must have the same number of selected atoms.
54-
- {py:class}`~moleculekit.projections.metricfluctuation.MetricFluctuation` returns an `(n_frames, n_atoms)` array — use `rmsf.mean(axis=0)` to get a per-atom fluctuation averaged over all frames.
57+
- {py:class}`~moleculekit.projections.metricfluctuation.MetricFluctuation` returns **squared** per-atom displacement (`(n_frames, n_atoms)`, units Ų). For RMSF you must take `np.sqrt(sq_disp.mean(axis=0))``sq_disp.mean(axis=0)` alone is mean-squared-fluctuation, not RMSF.
5558

5659
## See also
5760

doc/source/howto/fetch-from-rcsb-and-opm.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,24 @@ mol, thickness = get_opm_pdb("1BL8")
4747
from moleculekit.opm import align_to_opm
4848

4949
mol = Molecule("my_structure.pdb")
50-
mol_opm = align_to_opm(mol, maxalignments=3)
50+
results = align_to_opm(mol, maxalignments=3)
51+
52+
# results is a list, one entry per OPM hit. Each entry has the OPM PDB ID,
53+
# the membrane thickness, and a list of high-scoring sequence pairs (HSPs)
54+
# whose aligned_mol is `mol` re-imaged into the OPM frame.
55+
for hit in results:
56+
print(hit["pdbid"], "thickness:", hit["thickness"])
57+
for hsp in hit["hsps"]:
58+
aligned_mol = hsp["aligned_mol"] # Molecule, oriented in the OPM membrane frame
59+
print(f" TM={hsp['TM-Score']:.2f} RMSD={hsp['Common RMSD']:.2f} Å")
5160
```
5261

5362
## Gotchas
5463

5564
- RCSB downloads respect the server rate limits; avoid hammering the API in tight loops.
5665
- Set the `LOCAL_PDB_REPO` environment variable to a local PDB mirror directory to avoid repeated network downloads.
57-
- OPM membership requires a known PDB ID or a successful BLAST sequence alignment; structures absent from OPM will raise or return `None`.
66+
- OPM membership requires a known PDB ID or a successful BLAST sequence alignment; when nothing matches, {py:func}`~moleculekit.opm.align_to_opm` returns an **empty list** (not `None`).
67+
- {py:func}`~moleculekit.opm.align_to_opm` returns a `list[dict]` — the aligned `Molecule` objects live under `hit["hsps"][j]["aligned_mol"]`, not at the top level.
5868
- {py:func}`~moleculekit.opm.get_opm_pdb` with `keep=False` (default) strips the dummy membrane atoms that OPM adds; pass `keep=True` if you need them for visualization.
5969

6070
## See also

doc/source/howto/read-a-trajectory.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@ print(mol.numFrames)
2626
## Common variations
2727

2828
```python
29-
# Load topology first, then read a specific frame range
29+
# Read every 10th frame (uniform stride)
3030
mol = Molecule("topology.psf")
31-
mol.read("trajectory.xtc", frames=list(range(0, 500)))
31+
mol.read("trajectory.xtc", skip=10)
32+
```
33+
34+
```python
35+
# Read a single specific frame
36+
mol = Molecule("topology.psf")
37+
mol.read("trajectory.xtc", frames=[500])
3238
```
3339

3440
```python
@@ -41,9 +47,10 @@ print(mol.numFrames)
4147

4248
## Gotchas
4349

44-
- All frames are loaded into memory at once; for very long trajectories use `frames=` to read a slice.
50+
- All frames are loaded into memory at once; for very long trajectories use `skip` or `frames=` to read a subset.
4551
- The atom count of the trajectory must exactly match the topology — a mismatch raises an error.
46-
- `skip` and `frames` can be combined: `frames` selects the subset to consider, then `skip` further subsamples within that subset.
52+
- `frames=` is **per file**: when reading a single trajectory it must be a length-1 list whose single element is either an int or a list of ints (e.g. `frames=[[0, 10, 20]]`). For uniform subsampling prefer `skip=`.
53+
- `skip` strides the final merged coordinate array; it applies independently of `frames` and is the simplest way to subsample a long trajectory.
4754
- When `append=False` (default) a second `mol.read(traj)` call replaces the existing coordinates rather than extending them.
4855

4956
## See also

doc/source/howto/view-a-molecule.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ mol.view(viewer="pymol")
4646
When `viewer=` is not given, moleculekit picks a backend in this order:
4747

4848
1. `MOLECULEKIT_VIEWER` environment variable (e.g. `export MOLECULEKIT_VIEWER=molstar`).
49-
2. `moleculekit.config["viewer"]` runtime setting.
49+
2. The runtime setting from {py:func}`moleculekit.config.config` (e.g. `from moleculekit.config import config; config(viewer="molstar")`).
5050
3. VMD found on `$PATH`.
5151
4. PyMOL found on `$PATH`.
5252
5. Molstar fallback (always available).

0 commit comments

Comments
 (0)