warp-md-cuda 0.1.0__cp311-cp311-manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
warp_md/__init__.py ADDED
@@ -0,0 +1,95 @@
1
+ try:
2
+ from .traj_py import (
3
+ PySystem as System,
4
+ PySelection as Selection,
5
+ PyTrajectory as Trajectory,
6
+ PyRgPlan as RgPlan,
7
+ PyRmsdPlan as RmsdPlan,
8
+ PyMsdPlan as MsdPlan,
9
+ PyRotAcfPlan as RotAcfPlan,
10
+ PyConductivityPlan as ConductivityPlan,
11
+ PyDielectricPlan as DielectricPlan,
12
+ PyDipoleAlignmentPlan as DipoleAlignmentPlan,
13
+ PyIonPairCorrelationPlan as IonPairCorrelationPlan,
14
+ PyStructureFactorPlan as StructureFactorPlan,
15
+ PyWaterCountPlan as WaterCountPlan,
16
+ PyEquipartitionPlan as EquipartitionPlan,
17
+ PyHbondPlan as HbondPlan,
18
+ PyRdfPlan as RdfPlan,
19
+ PyEndToEndPlan as EndToEndPlan,
20
+ PyContourLengthPlan as ContourLengthPlan,
21
+ PyChainRgPlan as ChainRgPlan,
22
+ PyBondLengthDistributionPlan as BondLengthDistributionPlan,
23
+ PyBondAngleDistributionPlan as BondAngleDistributionPlan,
24
+ PyPersistenceLengthPlan as PersistenceLengthPlan,
25
+ )
26
+ _IMPORT_ERROR = None
27
+ except Exception as exc: # pragma: no cover - allow CLI help without bindings
28
+ _IMPORT_ERROR = exc
29
+
30
+ def _missing(*_args, **_kwargs):
31
+ raise RuntimeError(
32
+ "warp-md Python bindings are unavailable. Run `maturin develop` or install warp-md."
33
+ ) from _IMPORT_ERROR
34
+
35
+ class _Missing:
36
+ def __init__(self, *args, **kwargs):
37
+ _missing(*args, **kwargs)
38
+
39
+ System = _Missing
40
+ Selection = _Missing
41
+ Trajectory = _Missing
42
+ RgPlan = _Missing
43
+ RmsdPlan = _Missing
44
+ MsdPlan = _Missing
45
+ RotAcfPlan = _Missing
46
+ ConductivityPlan = _Missing
47
+ DielectricPlan = _Missing
48
+ DipoleAlignmentPlan = _Missing
49
+ IonPairCorrelationPlan = _Missing
50
+ StructureFactorPlan = _Missing
51
+ WaterCountPlan = _Missing
52
+ EquipartitionPlan = _Missing
53
+ HbondPlan = _Missing
54
+ RdfPlan = _Missing
55
+ EndToEndPlan = _Missing
56
+ ContourLengthPlan = _Missing
57
+ ChainRgPlan = _Missing
58
+ BondLengthDistributionPlan = _Missing
59
+ BondAngleDistributionPlan = _Missing
60
+ PersistenceLengthPlan = _Missing
61
+ from .builder import (
62
+ charges_from_selections,
63
+ charges_from_table,
64
+ group_indices,
65
+ group_types_from_selections,
66
+ )
67
+
68
+ __all__ = [
69
+ "System",
70
+ "Selection",
71
+ "Trajectory",
72
+ "RgPlan",
73
+ "RmsdPlan",
74
+ "MsdPlan",
75
+ "RotAcfPlan",
76
+ "ConductivityPlan",
77
+ "DielectricPlan",
78
+ "DipoleAlignmentPlan",
79
+ "IonPairCorrelationPlan",
80
+ "StructureFactorPlan",
81
+ "WaterCountPlan",
82
+ "EquipartitionPlan",
83
+ "HbondPlan",
84
+ "RdfPlan",
85
+ "EndToEndPlan",
86
+ "ContourLengthPlan",
87
+ "ChainRgPlan",
88
+ "BondLengthDistributionPlan",
89
+ "BondAngleDistributionPlan",
90
+ "PersistenceLengthPlan",
91
+ "group_indices",
92
+ "group_types_from_selections",
93
+ "charges_from_selections",
94
+ "charges_from_table",
95
+ ]
warp_md/builder.py ADDED
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ import csv
4
+ from typing import Iterable, List, Mapping, Sequence
5
+
6
+
7
+ def group_indices(system, selection, group_by: str) -> List[List[int]]:
8
+ atoms = system.atom_table()
9
+ indices = selection.indices
10
+ resid = atoms["resid"]
11
+ chain_id = atoms["chain_id"]
12
+
13
+ if group_by == "resid":
14
+ keys = {idx: resid[idx] for idx in indices}
15
+ grouped = {}
16
+ for idx in indices:
17
+ grouped.setdefault(keys[idx], []).append(idx)
18
+ elif group_by == "chain":
19
+ keys = {idx: chain_id[idx] for idx in indices}
20
+ grouped = {}
21
+ for idx in indices:
22
+ grouped.setdefault(keys[idx], []).append(idx)
23
+ elif group_by in ("resid_chain", "chain_resid"):
24
+ keys = {idx: (chain_id[idx], resid[idx]) for idx in indices}
25
+ grouped = {}
26
+ for idx in indices:
27
+ grouped.setdefault(keys[idx], []).append(idx)
28
+ else:
29
+ raise ValueError("group_by must be resid, chain, or resid_chain")
30
+
31
+ return [grouped[key] for key in sorted(grouped.keys())]
32
+
33
+
34
+ def group_types_from_selections(
35
+ system,
36
+ selection,
37
+ group_by: str,
38
+ type_selections: Sequence[str],
39
+ ) -> List[int]:
40
+ groups = group_indices(system, selection, group_by)
41
+ type_sets = [set(system.select(expr).indices) for expr in type_selections]
42
+ group_types: List[int] = []
43
+ for group in groups:
44
+ assigned = None
45
+ for t, sel_set in enumerate(type_sets):
46
+ if any(idx in sel_set for idx in group):
47
+ assigned = t
48
+ break
49
+ if assigned is None:
50
+ raise ValueError("group has no matching type selection")
51
+ group_types.append(assigned)
52
+ return group_types
53
+
54
+
55
+ def charges_from_selections(
56
+ system,
57
+ selections: Iterable[Mapping[str, float]],
58
+ default: float = 0.0,
59
+ ) -> List[float]:
60
+ charges = [default] * system.n_atoms()
61
+ for entry in selections:
62
+ expr = entry.get("selection")
63
+ charge = entry.get("charge")
64
+ if expr is None or charge is None:
65
+ raise ValueError("each entry must have 'selection' and 'charge'")
66
+ sel = system.select(expr)
67
+ for idx in sel.indices:
68
+ charges[idx] = float(charge)
69
+ return charges
70
+
71
+
72
+ def charges_from_table(system, path: str, delimiter: str | None = None, default: float = 0.0) -> List[float]:
73
+ with open(path, "r", newline="") as handle:
74
+ sample = handle.read(1024)
75
+ handle.seek(0)
76
+ if delimiter is None:
77
+ delimiter = "\t" if "\t" in sample and "," not in sample else ","
78
+ reader = csv.DictReader(handle, delimiter=delimiter)
79
+ headers = {h.lower(): h for h in reader.fieldnames or []}
80
+ resname_key = headers.get("resname") or headers.get("residue") or headers.get("res")
81
+ name_key = headers.get("name") or headers.get("atom") or headers.get("atom_name") or headers.get("atomname")
82
+ charge_key = headers.get("charge") or headers.get("q")
83
+ if resname_key is None or name_key is None or charge_key is None:
84
+ raise ValueError("table must include resname, name/atom, and charge columns")
85
+ rows = list(reader)
86
+
87
+ entries = []
88
+ for row in rows:
89
+ resname = row[resname_key].strip()
90
+ name = row[name_key].strip()
91
+ charge = float(row[charge_key])
92
+ expr = f"resname {resname} and name {name}"
93
+ entries.append({"selection": expr, "charge": charge})
94
+ return charges_from_selections(system, entries, default=default)