File: multi_state_scheme.py

package info (click to toggle)
python-ihm 2.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,368 kB
  • sloc: python: 30,422; ansic: 5,990; sh: 24; makefile: 20
file content (288 lines) | stat: -rw-r--r-- 11,982 bytes parent folder | download
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.")