Fuzzy oil drop system of protein structure
The fuzzy oil drop of a protein, an ensemble replayed one member per frame or a chain folded by moves.
- Name:
fuzzy_oil_drop - Field:
biology/systems_biology - Formalism: lattice dynamics
- References: Konieczny et al. 2023; Levitt 1976
Select this system
from skcomplex.systems import create_simulation, create_system, describe
system = create_system('fuzzy_oil_drop')
entry = describe('fuzzy_oil_drop')
trajectory = create_simulation('fuzzy_oil_drop').prepare().run().trajectory_
Factory
@register_system(
field='biology/systems_biology',
tags=('protein', 'hydrophobicity', 'divergence entropy', 'structures', 'monte carlo'),
references=('Konieczny et al. 2023', 'Levitt 1976'),
defaults={'until': 37},
observables=_read_observables,
)
def fuzzy_oil_drop(
entry: str = ENTRY,
*,
dynamics: str = 'replay',
proposals: int = 1,
width: float = 60.0,
temperature: float = 0.0,
) -> System:
"""Return the fuzzy oil drop of a protein, an ensemble replayed one member per frame or a chain folded by moves.
The default replays the systems of an entry, which sets the effective atoms of the chain, the mean atom of each
residue, to one system per unit of time. With `dynamics='folding'`, the chain starts extended, with all `phi =
-120°` and `psi = 130°`, and each unit of time proposes moves of its dihedral angles, accepted by the row score `RD`
plus one for every pair of effective atoms closer than 3 Å and at least two residues apart. The observables `RD` and
`K` follow Konieczny et al. (2023 §1.3.1) through `FuzzyOilDropAnalysis`. The book gives no folding dynamics, so the
extended start, the clash rule, the rotation width of 60°, and the temperature are choices of this system.
Args:
entry:
The bundled entry, `'1L2Y'`, `'1UBQ'`, or `'5PTI'`.
dynamics:
`'replay'` or `'folding'`.
proposals:
The number of dihedral moves per unit of time of the folding.
width:
The largest rotation of a dihedral move, in degrees.
temperature:
The temperature of the folding, one over the intensity of its sampling, where 0 accepts only moves that do
not raise the score.
Returns:
system:
The system, with the lattice `chain` of the state `x` or `angle`.
Raises:
MissingExtraError:
The extra `structures` is not installed.
ValidationError:
The dynamics are unknown.
Examples:
>>> from skcomplex.systems.biology.systems_biology import fuzzy_oil_drop
>>> fuzzy_oil_drop().elements[0].space.shape
(20, 3)
"""
import_extra('Bio.PDB', 'fuzzy_oil_drop', 'structures')
if dynamics not in DYNAMICS:
error_msg = f'Unknown dynamics {dynamics!r}. Use one of {", ".join(DYNAMICS)}.'
raise ValidationError(error_msg)
structure = read_structure(entry)
if dynamics == 'replay':
ensemble = structure.effective_atoms
return System(
label=entry,
elements=[
LatticeElement(
'chain',
element=ChainCoordinateElement,
space=LatticeSpace((ensemble.shape[1], COORDINATES), boundary='fixed'),
values={'x': ensemble[0]},
visual=VisualSpec(BackboneVisual, {'position': 'x'}),
),
],
interactions=[EnsembleReplayInteraction('replay', target='chain', state='x', ensemble=ensemble)],
time=DiscreteTime(),
)
angles = np.full(BackboneGeometry(structure).prepare().angles_.size, EXTENDED_PHI)
angles[angles.size // 2 :] = EXTENDED_PSI
return System(
label=entry,
elements=[
LatticeElement(
'chain',
element=DihedralAngleElement,
space=LatticeSpace((1, angles.size), boundary='fixed'),
values={'angle': angles[None]},
visual=VisualSpec(BackboneVisual, {'position': 'angle', 'structure': structure}),
),
],
interactions=[
SamplingInteraction(
'folding',
target='chain',
state='angle',
objective=RowScoreSpec(partial(_score_angles, entry), proposals=proposals),
move=partial(_rotate_dihedral, width),
intensity=float('inf') if temperature == 0 else 1 / temperature,
),
],
time=DiscreteTime(),
)
Components
Elements:
ChainCoordinateElement, fromElement
Interactions:
EnsembleReplayInteraction, fromInteraction
class ChainCoordinateElement(Element):
"""Coordinate of an effective atom of a protein chain.
Args:
label:
The label of the element.
x:
The coordinate in ångström.
Examples:
>>> from skcomplex.systems.biology.systems_biology import ChainCoordinateElement
>>> ChainCoordinateElement('site').x
0.0
"""
def __init__(self, label: str, x: Annotated[float, StateSpec()] = 0.0) -> None:
super().__init__(label)
self.x = x
class EnsembleReplayInteraction(Interaction):
"""Drive that sets a lattice state to one member of an ensemble per unit of time, holding the last after the end.
Args:
label:
The label of the interaction.
target:
The path of the lattice group.
state:
The name of the state.
ensemble:
The members, members by the lattice shape.
Examples:
>>> import numpy as np
>>> from skcomplex.systems.biology.systems_biology import EnsembleReplayInteraction
>>> EnsembleReplayInteraction('replay', target='chain', state='x', ensemble=np.zeros((2, 3))).state
'x'
"""
def __init__(
self,
label: str,
*,
target: Annotated[str, RoleSpec()],
state: str,
ensemble: Array,
) -> None:
super().__init__(label)
self.target = target
self.state = state
self.ensemble = ensemble
def _read_member(self, values: Array, *, time: float) -> Array:
"""Return the member of the time of a step."""
return self.ensemble[min(int(time), len(self.ensemble) - 1)].reshape(values.shape)
def _get_laws(self, namespace: SimpleNamespace) -> Sequence[BaseLaw]:
"""Return the drive of the state."""
return [DriveLaw(getattr(self._get_member(namespace, 'target'), self.state), self._read_member)]