AtomSelectionConfigurator.py 7.53 KB
Newer Older
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
#MDANSE : Molecular Dynamics Analysis for Neutron Scattering Experiments
#------------------------------------------------------------------------------------------
#Copyright (C)
#2015- Eric C. Pellegrini Institut Laue-Langevin
#BP 156
#6, rue Jules Horowitz
#38042 Grenoble Cedex 9
#France
#pellegrini[at]ill.fr
#goret[at]ill.fr
#aoun[at]ill.fr
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

''' 
Created on Mar 30, 2015

30
:author: Eric C. Pellegrini
31
32
33
34
35
36
37
'''

import collections
import operator

import numpy

38
from MDANSE.Framework.UserDefinitionsStore import UD_STORE
39
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator, ConfiguratorError
eric pellegrini's avatar
test    
eric pellegrini committed
40
from MDANSE.Framework.AtomSelectionParser import AtomSelectionParser
41

42
# The granularities at which the selection will be performed
43
44
45
46
47
48
49
50
LEVELS = collections.OrderedDict()
LEVELS["atom"]     = {"atom" : 0, "atomcluster" : 0, "molecule" : 0, "nucleotidechain" : 0, "peptidechain" : 0, "protein" : 0}
LEVELS["group"]    = {"atom" : 0, "atomcluster" : 1, "molecule" : 1, "nucleotidechain" : 1, "peptidechain" : 1, "protein" : 1}
LEVELS["residue"]  = {"atom" : 0, "atomcluster" : 1, "molecule" : 1, "nucleotidechain" : 2, "peptidechain" : 2, "protein" : 2}
LEVELS["chain"]    = {"atom" : 0, "atomcluster" : 1, "molecule" : 1, "nucleotidechain" : 3, "peptidechain" : 3, "protein" : 3}
LEVELS["molecule"] = {"atom" : 0, "atomcluster" : 1, "molecule" : 1, "nucleotidechain" : 3, "peptidechain" : 3, "protein" : 4}

class AtomSelectionConfigurator(IConfigurator):    
51
52
53
54
    '''
    This configurator allows the selection of a specific set of atoms on which the analysis will be performed.

    Without any selection, all the atoms stored into the trajectory file will be selected.
55
        
56
    :note: this configurator depends on 'trajectory' and 'grouping_level' configurators to be configured
57
    '''
58
59
60
61
62
63

    type = 'atom_selection'
    
    _default = "all"
                    
    def configure(self, configuration, value):
64
        '''
65
66
67
        Configure an input value. 
        
        The value must be a string that can be either an atom selection string or a valid user 
68
69
70
71
72
73
74
        definition.
        
        :param configuration: the current configuration
        :type configuration: a MDANSE.Framework.Configurable.Configurable object
        :param value: the input value
        :type value: str
        '''
75
76
                          
        trajConfig = configuration[self._dependencies['trajectory']]
eric pellegrini's avatar
eric pellegrini committed
77
78
79
80
        
        if value is None:
            value = 'all'
        elif not isinstance(value,basestring):
81
82
83
84
            raise ConfiguratorError("invalid type for atom selection. Must be a string", self)
        
        self["value"] = value
        
85
86
87
88
        if UD_STORE.has_definition(trajConfig["basename"],"atom_selection",value):
            ud = UD_STORE.get_definition(trajConfig["basename"],"atom_selection",value)
            self.update(ud)
        else:        
89
            parser = AtomSelectionParser(trajConfig["instance"].universe)
90
91
            self["indexes"] = parser.parse(value)
            self["expression"] = value
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

        self["n_selected_atoms"] = len(self["indexes"])
        atoms = sorted(trajConfig["universe"].atomList(), key = operator.attrgetter('index'))
        selectedAtoms = [atoms[idx] for idx in self["indexes"]]
        self["elements"] = [[at.symbol] for at in selectedAtoms]

        if self._dependencies.has_key("grouping_level"):
            self.group(selectedAtoms, configuration[self._dependencies['grouping_level']]['value'])
        else:
            self.group(selectedAtoms)
                                 
        self.set_contents()
            
    @staticmethod                                                                                                                        
    def find_parent(atom, level):
107
108
109
110
111
112
113
114
115
116
117
        '''
        Retrieve recursively the parent of a given MMTK atom at a given level.
        For example, a level of 1 will return the direct parent of the atom. 
        
        :note: this is a static method
        
        :param atom: the atom for which the parent is searched for
        :type atom: MMTK.Atom object
        :param level: the level of the parent
        :type level: int
        '''
118
119
120
121
122
123
        
        for _ in range(level):
            atom = atom.parent
            
        return atom
    
124
125
126
127
128
129
130
131
132
133
134
    def group(self, atoms, level="atom"):
        '''
        Group the selected atoms according to a given granularity and update
        the configurator with the grouping results.
        
        :param atoms: the atoms for 
        :type atoms: list of MMTK.Atom
        :param level: the level of granularity at which the atoms should be grouped
        :type level: str
        '''
                                        
135
136
        groups = collections.OrderedDict()
        
137
        for at in atoms:
138
139
            lvl = LEVELS[level][at.topLevelChemicalObject().__class__.__name__.lower()]
            parent = self.find_parent(at,lvl)        
140
            groups.setdefault(parent,[]).append(at.index)
141
142
143
144
145
146
147
148
149
150
151
152
153
        
        self["groups"] = groups.values()
            
        self["n_groups"] = len(self["groups"])
        
        if level != "atom":
            self["elements"] = [["dummy"]]*self["n_groups"]
                                        
        self["level"] = level
                
        self.set_contents()
                        
    def set_contents(self):
154
155
156
157
        '''
        Sets the contents of the atom selection.
        '''
                    
158
159
160
161
162
163
164
165
        self["contents"] = collections.OrderedDict()
        self['index_to_symbol'] = collections.OrderedDict()
        for i, group in enumerate(self["elements"]):
            for j, el in enumerate(group):
                self["contents"].setdefault(el,[]).append(self["groups"][i][j])
                self['index_to_symbol'][self["groups"][i][j]] = el
                 
        for k,v in self["contents"].items():
166
            self["contents"][k] = numpy.array(v,dtype=numpy.int32)
167
168
169
            
        self["n_atoms_per_element"] = dict([(k,len(v)) for k,v in self["contents"].items()])              
        self['n_selected_elements'] = len(self["contents"])
eric pellegrini's avatar
test    
eric pellegrini committed
170
                        
171
    def get_information(self):
172
        '''
173
        Returns some informations the atom selection.
174
        
175
        :return: the information about the atom selection.
176
177
        :rtype: str
        '''
178
179
180

        if not self.has_key("n_selected_atoms"):
            return "No configured yet"
181
182
        
        info = []
183
184
185
186
        info.append("Number of selected atoms:%d" % self["n_selected_atoms"])
        info.append("Level of selection:%s" % self["level"])
        info.append("Number of selected groups:%d" % self["n_groups"])
        info.append("Selected elements:%s" % self["contents"].keys())
187
        
188
        return "\n".join(info)