1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
|
# coding=utf-8
import ihm
from ihm.model import _text_choice_property
"""Classes for handling connected/ordered schemes formed by multiple state
together with information on kinetic schemes"""
class MultiStateScheme:
"""MultiStateScheme collects information about a collection of
multiple states, that can form a connected/ordered scheme.
A special case is a kinetic scheme, for which kinetic rates and
relaxation times are available.
:param str name: The name of the multi-state scheme.
:param str details: Details on the scheme.
:param connectivities: A list of connectivities that belong to
the scheme.
:type connectivities: List of :class:`Connectivity`
:param relaxation_times: A list of relaxation times not assigned
to specific connectivities, but to the scheme
:type relaxation_times: List of :class:`RelaxationTime`
"""
def __init__(self, name, details=None, connectivities=None,
relaxation_times=None):
self.name = name
self.details = details
self._connectivity_list = []
self._relaxation_time_list = []
# states is filled automatically based on connectivity_list
self._states = []
if connectivities is not None:
for c in connectivities:
if c not in self._connectivity_list:
self.add_connectivity(c)
if relaxation_times is not None:
for r in relaxation_times:
if r not in self._relaxation_time_list:
self.add_relaxation_time(r)
def add_connectivity(self, connectivity):
"""Add a connectivity to the scheme.
:param connectivity: The connectivity to add to the scheme
:type connectivity: :class:`Connectivity`
"""
if connectivity is None:
return
if connectivity not in self._connectivity_list:
# Make sure that the connectivity has not been assigned to
# another scheme
if not connectivity._assigned_to_scheme:
connectivity.set_assigned_to_scheme()
self._connectivity_list.append(connectivity)
# If the connectivity has been assigned to another scheme,
# create a copy of the connectivity and use that
else:
old_connectivity = connectivity
connectivity = \
ihm.multi_state_scheme.Connectivity(
begin_state=old_connectivity.begin_state,
end_state=old_connectivity.end_state,
details=old_connectivity.details,
dataset_group=old_connectivity.dataset_group,
kinetic_rate=old_connectivity.kinetic_rate,
relaxation_time=old_connectivity.relaxation_time
)
connectivity.set_assigned_to_scheme()
self._connectivity_list.append(connectivity)
# Add the states that belong to the connectivity
self._add_state(connectivity.begin_state)
self._add_state(connectivity.end_state)
def _add_state(self, state):
"""Add a state to the self._states list if it is not present yet.
This function checks whether the state has optional properties,
such as a name. If this is the case, the name is compared to the names
already in the list. If the state does not have a name, it might only
be a list of elements. Then only the contents of the list are checked
This is important for empty states, i.e. those that do not have
models associated.
:param state: The state to add.
:type state: :class:`ihm.model.State`
"""
if state is None:
return
for tmp_state in self._states:
# Check whether both states have the name attributes
if hasattr(state, 'name') and hasattr(tmp_state, 'name'):
# compare the properties of the two states and the elements of
# the lists
if state.__dict__ == tmp_state.__dict__ \
and state == tmp_state:
# state found
return
# If neither of the two states has the name attribute, only compare
# the elements of the lists
if not hasattr(state, 'name') and not hasattr(tmp_state, 'name'):
# If the two states have the same elements
if state == tmp_state:
# state found
return
# If the state was not found in the list yet, add it
self._states.append(state)
def add_relaxation_time(self, relaxation_time):
"""Add a relaxation time to the scheme. This relaxation time is not
assigned to a connectivity.
:param relaxation_time: The relaxation time to add to the scheme.
:type relaxation_time: :class:`RelaxationTime`
"""
if relaxation_time is not None:
self._relaxation_time_list.append(relaxation_time)
def get_connectivities(self):
"""Return the connectivities assigned to a scheme"""
return self._connectivity_list
def get_relaxation_times(self):
"""Return the relaxation times assigned to a scheme"""
return self._relaxation_time_list
def get_states(self):
"""Return the states involved in a scheme"""
return self._states
def __eq__(self, other):
return ((self.__dict__ == other.__dict__)
and (self._connectivity_list ==
other._connectivity_list)
and (self._relaxation_time_list ==
other._relaxation_time_list))
class Connectivity:
"""A connectivity between states. Used to describe the directed
edge of graph.
If no end_state is given, the state is not connected to another state.
This could be the case for states where no connection to other states
could be resolved.
:param begin_state: The start state of the connectivity.
:type begin_state: :class:`ihm.model.State`
:param end_state: The end state of the connectivity. Can be None in case
of states that are not connected to others.
:type end_state: :class:`ihm.model.State`
:param details: Details to the connectivity.
:param dataset_group: The DatasetGroup that was used to obtain information
on the connectivity.
:type dataset_group: :class:`ihm.dataset.DatasetGroup`
:param kinetic_rate: A kinetic rate assigned to the connectivity.
:type kinetic_rate: :class:`KineticRate`
:param relaxation_time: A relaxation time assigned to the connectivity.
:type relaxation_time: :class:`RelaxationTime`
"""
def __init__(self, begin_state, end_state=None, details=None,
dataset_group=None, kinetic_rate=None, relaxation_time=None):
self.begin_state = begin_state
self.end_state = end_state
self.details = details
self.dataset_group = dataset_group
self.kinetic_rate = kinetic_rate
self.relaxation_time = relaxation_time
# The _assigned_to_scheme variable tracks whether the connectivity
# has been assigned to a scheme. This is to ensure that each
# connectivity is only assigned to a single scheme.
self._assigned_to_scheme = False
def set_assigned_to_scheme(self):
self._assigned_to_scheme = True
def __eq__(self, other):
return self.__dict__ == other.__dict__
class KineticRate:
"""A base class for a kinetic rate that can be assigned to a connectivity.
The kinetic rate could be a transition_rate_constant or
an equilibrium_constant. Alternatively, both could be provided.
:param float transition_rate_constant: A transition rate constant
describing the exchange between two states. Unit: per second.
:param equilibrium_constant: An equilibrium constant describing the
exchange between two states
:type equilibrium_constant: :class:`EquilibriumConstant` or
:class:`PopulationEquilibriumConstant` or
:class:`KineticRateEquilibriumConstant`
:param str details: Details on the kinetic rate.
:param dataset_group: The DatasetGroup used to determine the kinetic rate.
:type dataset_group: :class:`ihm.dataset.DatasetGroup`
:param file: External file containing measurement data for the kinetic
rate.
:type file: :class:`ihm.location.OutputFileLocation`
"""
def __init__(self,
transition_rate_constant=None,
equilibrium_constant=None,
details=None,
dataset_group=None,
file=None):
self.transition_rate_constant = transition_rate_constant
self.equilibrium_constant = equilibrium_constant
self.details = details
self.dataset_group = dataset_group
self.external_file = file
def __eq__(self, other):
return self.__dict__ == other.__dict__
class EquilibriumConstant:
"""Base class for an equilibrium constant.
This class handles the case that none of the derived classes is applicable.
:param float value: The value of the equilibrium constant
:param str unit: Unit of the equilibrium constant. Depending on what
the process describes, a unit might be applicable or not"""
def __init__(self, value, unit=None):
self.method = 'equilibrium constant is determined from another ' \
'method not listed'
self.value = value
self.unit = unit
def __eq__(self, other):
if other is None:
return False
return self.__dict__ == other.__dict__
class PopulationEquilibriumConstant(EquilibriumConstant):
"""An equilibrium constant determined from population"""
def __init__(self, value, unit=None):
super().__init__(value, unit)
self.method = 'equilibrium constant is determined from population'
class KineticRateEquilibriumConstant(EquilibriumConstant):
"""An equilibrium constant determined from kinetic rates as kAB/kBA"""
def __init__(self, value, unit=None):
super().__init__(value, unit)
self.method = 'equilibrium constant is determined from kinetic ' \
'rates, kAB/kBA'
class RelaxationTime:
"""A relaxation time determined for a scheme.
The relaxation time can either be connected to a specific connectivity
in the scheme or to the scheme in general if no assignment is possible.
:param float value: The relaxation time.
:param str unit: The unit of the relaxation time. Options are
['seconds','milliseconds', microseconds']
:param float amplitude: The amplitude of the relaxation time if determined.
:param str details: Details on the relaxation time.
:param dataset_group: DatasetGroup used to determine the relaxation time.
:type dataset_group: :class:`ihm.dataset.DatasetGroup`
:param file: An external file containing measurement data for
the relaxation time.
:type file: :class:`ihm.location.OutputFileLocation`
"""
def __init__(self, value, unit, amplitude=None,
details=None, dataset_group=None, file=None):
self.value = value
self.unit = unit
self.amplitude = amplitude
self.details = details
self.dataset_group = dataset_group
self.external_file = file
def __eq__(self, other):
return self.__dict__ == other.__dict__
# Check whether the given unit is within the allowed options
allowed_relaxation_time_units = ['seconds',
'milliseconds',
'microseconds']
unit = _text_choice_property(
"unit",
allowed_relaxation_time_units,
doc="The unit of the relaxation time.")
|