Source code for pimms.lemonade.polymer

"""
Polymer - a lightweight view of one chain within one frame.

A Polymer holds only ``(store, frame_index, chain_index)``; every position array
and scalar it exposes is a view onto - or a cached lookup into - the trajectory
store's batched arrays, so creating one allocates nothing per bead.
"""

import numpy as np

from . import _analysis


[docs] class Polymer: __slots__ = ("_store", "_f", "_c", "_a0", "_a1") def __init__(self, store, frame_index, chain_index): self._store = store self._f = frame_index self._c = chain_index self._a0 = int(store.topology.offsets[chain_index]) self._a1 = int(store.topology.offsets[chain_index + 1]) # -- identity ---------------------------------------------------------- @property def chain_index(self): return self._c @property def frame_index(self): return self._f @property def chain_type(self): return int(self._store.topology.chain_types[self._c]) @property def sequence(self): return self._store.topology.sequences[self._c] def __len__(self): return self._a1 - self._a0 # -- positions --------------------------------------------------------- @property def positions(self): """Raw (wrapped) integer lattice positions, ``(L, 3)`` view.""" return self._store.positions[self._f, self._a0:self._a1] @property def whole_positions(self): """Positions made contiguous across periodic boundaries, ``(L, 3)``.""" return self._store.whole_positions()[self._f, self._a0:self._a1] # -- cached single-chain scalars (indexed from the batched arrays) ----- @property def center_of_mass(self): return self._store.centers_of_mass()[self._f, self._c] @property def radius_of_gyration(self): return float(self._store.radius_of_gyration()[self._f, self._c]) @property def asphericity(self): return float(self._store.asphericity()[self._f, self._c]) @property def end_to_end_distance(self): return float(self._store.end_to_end()[self._f, self._c]) @property def straddles_boundary(self): """True if any bond crosses a periodic boundary in the raw positions.""" nd = self._store.n_dim p = self.positions[:, :nd] if len(p) < 2: return False return bool(np.any(np.abs(np.diff(p, axis=0)) > 1)) # -- per-chain matrices ------------------------------------------------
[docs] def distance_map(self): """Full ``(L, L)`` inter-bead Euclidean distance matrix (whole coords).""" return _analysis.distance_map(self.whole_positions[:, :self._store.n_dim].astype(np.float64))
[docs] def internal_scaling(self): """``(separations, mean_distance)`` internal-scaling profile.""" return _analysis.internal_scaling(self.whole_positions[:, :self._store.n_dim].astype(np.float64))
def __repr__(self): return (f"<Polymer chain={self._c} frame={self._f} " f"seq={self.sequence!r} type={self.chain_type}>")