Ejemplo n.º 1
0
 def __initialize_variables__(self, axis):
     # check atoms indexes
     assert not len(
         set.intersection(set(self.cylinderAtomsIndexes),
                          set(self.targetAtomsIndexes))
     ), Logger.error(
         "cylinderAtomsIndexes and targetAtomsIndexes can't have any index in common"
     )
     # set axis
     if axis is None:
         axis = {"principal": self.cylinderAtomsIndexes}
     self.axis = AxisDefinition(self._trajectory, axis)
Ejemplo n.º 2
0
 def __initialize_variables__(self, axis):
     self.weights = np.array(get_records_database_property_values(self.targetAtomsIndexes, self.structure, self.weighting))
     elements = self._trajectory.elements
     self.elements = [elements[idx] for idx in self.targetAtomsIndexes]
     elementsSet = set(self.elements)
     self.elementsWeights = dict(zip(elementsSet,[get_element_property(el, self.weighting) for el in elementsSet]))
     self.elementsNumber = dict(Counter(self.elements))
     # check atoms indexes
     assert not len(set.intersection(set(self.cylinderAtomsIndexes), set(self.targetAtomsIndexes))), Logger.error("cylinderAtomsIndexes and targetAtomsIndexes can't have any index in common")
     # set axis
     if axis is None:
         axis = {"principal":self.cylinderAtomsIndexes}
     self.axis = AxisDefinition(self._trajectory, axis)
Ejemplo n.º 3
0
 def __initialize_variables__(self, weighting, axis, radii, length):
     # get elements
     elements = self._trajectory.elements
     self.elements = [elements[idx] for idx in self.targetAtomsIndexes]
     self.elementsSet = list(set(self.elements))
     # set weighting
     assert is_element_property(weighting), Logger.error(
         "weighting '%s' don't exist in database" % weighting)
     self.weighting = weighting
     self.weights = np.array(
         [get_element_property(el, self.weighting) for el in self.elements])
     self.elementsWeights = dict(
         zip(self.elementsSet, [
             get_element_property(el, self.weighting)
             for el in self.elementsSet
         ]))
     self.elementsNumber = dict(Counter(self.elements))
     # set axis
     self.axis = AxisDefinition(self._trajectory, axis)
     # set radii
     assert isinstance(
         radii,
         (list, tuple, set,
          np.ndarray)), Logger.error("radii must be a list or numpy.array")
     try:
         radii = np.array(sorted(set(radii)), dtype=np.float32)
     except:
         raise Logger.error("radii element must be numbers")
     assert len(
         radii.shape) == 1, Logger.error("radii must be uni-dimensional")
     assert radii[0] > 0, Logger.error("all radii array must be positive")
     self.radii = radii
     # set length
     try:
         length = float(length)
     except:
         raise Logger.error("length must be numbers")
     assert length > 0, Logger.error("length must be positive")
     self.length = length
     # cylinder volume
     self.cylinderVolume = self.length * np.pi * self.radii[-1]**2
     self.cylindersVolumes = 2 * np.pi * self.radii[0:-1] * (
         self.radii[1:] - self.radii[0:-1]) * self.length
     self.cumCylindersVolumes = np.pi * self.length * self.radii[1:]**2
Ejemplo n.º 4
0
 def __initialize_variables__(self, weighting, axis, radius, lengths):
     # get elements
     elements = self._trajectory.elements
     self.elements = [elements[idx] for idx in self.targetAtomsIndexes]
     self.elementsSet = list(set(self.elements))
     # set weighting
     assert is_element_property(weighting), Logger.error(
         "weighting '%s' don't exist in database" % weighting)
     self.weighting = weighting
     self.weights = np.array(
         [get_element_property(el, self.weighting) for el in self.elements])
     self.elementsWeights = dict(
         zip(self.elementsSet, [
             get_element_property(el, self.weighting)
             for el in self.elementsSet
         ]))
     self.elementsNumber = dict(Counter(self.elements))
     # set axis
     self.axis = AxisDefinition(self._trajectory, axis)
     # set length
     assert isinstance(lengths, (
         list, tuple, set,
         np.ndarray)), Logger.error("lengths must be a list or numpy.array")
     try:
         lengths = np.array(sorted(set(lengths)), dtype=np.float32)
     except:
         raise Logger.error("lengths element must be numbers")
     assert len(lengths.shape) == 1, Logger.error(
         "lengths must be uni-dimensional")
     self.lengths = lengths
     # set radius
     try:
         radius = float(radius)
     except:
         raise Logger.error("radius must be numbers")
     assert radius > 0, Logger.error("radius must be positive")
     self.radius = radius
     # cylinder volume
     self.cylinderVolume = (self.lengths[-1] -
                            self.lengths[0]) * np.pi * self.radius**2
     self.diskVolumes = (self.lengths[1:] -
                         self.lengths[0:-1]) * np.pi * self.radius**2
     self.cumDisksVolumes = np.cumsum(self.diskVolumes)
Ejemplo n.º 5
0
class MeanSquareDisplacementInCylinder(Analysis):
    """
    Computes the mean square displacement for a set of atoms splitting partials between inside and outside of a cylinder.

    :Parameters:
        #. trajectory (pdbTrajectory): pdbTrajectory instance.
        #. configurationsIndexes (list, set, tuple): List of selected indexes of configuration used to perform the analysis.
        #. cylinderAtomsIndexes (list, set, tuple): Selected atoms indexes supposedly forming a cylinder e.g. nanotube.
        #. targetAtomsIndexes (list, set, tuple): Selected target atoms indexes.
        #. weighting (database key): a database property to weight the mean square displacement partials.
        #. axis (None, vector): The cylinder main axis, If None main principal axis of cylinderAtomsIndexes is calculated automatically.
    """
    def __init__(self, trajectory, configurationsIndexes,
                       cylinderAtomsIndexes, targetAtomsIndexes,
                       axis=None, weighting="equal", histBin=1, *args, **kwargs):
        # set trajectory
        super(MeanSquareDisplacementInCylinder,self).__init__(trajectory, *args, **kwargs)
        # set configurations indexes
        self.configurationsIndexes = self.get_trajectory_indexes(configurationsIndexes)
        # set atoms indexes
        self.targetAtomsIndexes = self.get_atoms_indexes(targetAtomsIndexes)
        self.cylinderAtomsIndexes = self.get_atoms_indexes(cylinderAtomsIndexes)
        # set steps indexes
        self.numberOfSteps = len(self.targetAtomsIndexes)
        # set weighting
        assert is_element_property(weighting), Logger.error("weighting '%s' don't exist in database"%weighting)
        self.weighting = weighting
        # set residency time histogram bin
        try:
            self.histBin = float(histBin)
        except:
            raise Logger.error("histBin must be number convertible. %s is given."%histBin)
        assert self.histBin%1 == 0, logger.error("histBin must be integer. %s is given."%histBin)
        assert self.histBin>0, logger.error("histBin must be positive. %s is given."%histBin)
        assert self.histBin<len(self.configurationsIndexes), logger.error("histBin must smaller than numberOfConfigurations")
        # initialize variables
        self.__initialize_variables__(axis)
        # initialize results
        self.__initialize_results__()
        # get cylinder centers, matrices, radii, length
        Logger.info("%s --> initializing cylinder parameters along all configurations"%self.__class__.__name__)
        self.cylCenters, self.cylMatrices, self.cylRadii, self.cylLengths = self.__get_cylinder_properties__()

    def __initialize_variables__(self, axis):
        self.weights = np.array(get_records_database_property_values(self.targetAtomsIndexes, self.structure, self.weighting))
        elements = self._trajectory.elements
        self.elements = [elements[idx] for idx in self.targetAtomsIndexes]
        elementsSet = set(self.elements)
        self.elementsWeights = dict(zip(elementsSet,[get_element_property(el, self.weighting) for el in elementsSet]))
        self.elementsNumber = dict(Counter(self.elements))
        # check atoms indexes
        assert not len(set.intersection(set(self.cylinderAtomsIndexes), set(self.targetAtomsIndexes))), Logger.error("cylinderAtomsIndexes and targetAtomsIndexes can't have any index in common")
        # set axis
        if axis is None:
            axis = {"principal":self.cylinderAtomsIndexes}
        self.axis = AxisDefinition(self._trajectory, axis)

    def __initialize_results__(self):
        # time
        self.results['time'] = np.array([self.time[idx] for idx in self.configurationsIndexes], dtype=np.float)
        self.results['histogram_edges'] = self.histBin*np.array(range(int(len(self.configurationsIndexes)/self.histBin)+1))

        # mean square displacements
        for el in set(self.elements):
            self.results['residency_time_inside_%s' %el]  = [0.,0]
            self.results['residency_time_outside_%s' %el] = [0.,0]
            self.results['msd_inside_%s' %el]  = np.zeros((len(self.configurationsIndexes)), dtype=np.float)
            self.results['msd_inside_axial_%s' %el]  = np.zeros((len(self.configurationsIndexes)), dtype=np.float)
            self.results['msd_inside_transversal_%s' %el]  = np.zeros((len(self.configurationsIndexes)), dtype=np.float)
            self.results['msd_outside_%s' %el] = np.zeros((len(self.configurationsIndexes)), dtype=np.float)
            self.results['histogram_inside_%s' %el]  = np.zeros((int(len(self.configurationsIndexes)/self.histBin)+1), dtype=np.float)
            self.results['histogram_outside_%s' %el] = np.zeros((int(len(self.configurationsIndexes)/self.histBin)+1), dtype=np.float)

        # normalization
        self.insideNormalization = {}
        self.outsideNormalization = {}
        for el in set(self.elements):
            self.insideNormalization[el]  = np.zeros((len(self.configurationsIndexes)), dtype=np.float)
            self.outsideNormalization[el] = np.zeros((len(self.configurationsIndexes)), dtype=np.float)

    def __get_cylinder_properties__(self):
        cylCenters  = []
        cylMatrices = []
        cylRadii    = []
        cylLengths  = []
        # get the Frame index
        for confIdx in self.configurationsIndexes:
            # set working configuration index
            self._trajectory.set_configuration_index(confIdx)
            # get coordinates
            coordinates = self._trajectory.get_configuration_coordinates(confIdx)
            # cylinder atomTrajectory
            cylinderAtomsCoordinates = coordinates[self.cylinderAtomsIndexes]
            # get center and axis and rotation matrix
            center, rotationMatrix = self.axis.get_center_rotationMatrix(coordinates)
            # translate cylinder coordinates to center
            cylinderAtomsCoordinates -= center
            # change coordinates to cylinder axes system
            cylinderAtomsCoordinates = np.dot(cylinderAtomsCoordinates, rotationMatrix)
            # get radius and length
            cylRadiusSquared = np.sqrt( np.mean( cylinderAtomsCoordinates[:,1]**2+cylinderAtomsCoordinates[:,2]**2 ) )
            cylLength        = np.abs(np.max(cylinderAtomsCoordinates[:,0])-np.min(cylinderAtomsCoordinates[:,0]))
            # append center and rotation matrix, radius, length
            cylCenters.append(center)
            cylMatrices.append(rotationMatrix)
            cylRadii.append(cylRadiusSquared)
            cylLengths.append(cylLength)
        return np.array(cylCenters), np.array(cylMatrices), np.array(cylRadii), np.array(cylLengths)

    def __split_trajectory_to_inside_outside__(self, atomTrajectory):
        inside = []
        outside = []
        isIn = False
        isOut = False
        inCylBasisAtomTraj = np.empty(atomTrajectory.shape)
        for idx in range(atomTrajectory.shape[0]):
            #center = atomTrajectory[idx]-self.cylCenters[idx]
            center = self._trajectory.boundaryConditions.real_difference(self.cylCenters[idx], atomTrajectory[idx], index = idx)
            # transform to cnt basis
            inCylCoords = np.dot(center, self.cylMatrices[idx])
            # set inCylBasisAtomTraj
            inCylBasisAtomTraj[idx] = inCylCoords + self.cylCenters[idx]
            # check x if outside or inside nanotube
            if np.abs(inCylCoords[0]) > self.cylLengths[idx]/2.:
                if isOut:
                    outside[-1].append(idx)
                else:
                    outside.append([idx])
                isIn = False
                isOut = True
                continue
            # check radius if bigger or smaller than the nanotube's one
            radius = np.sqrt(inCylCoords[1]**2+inCylCoords[2]**2)
            if radius < self.cylRadii[idx]:
                if isIn:
                    inside[-1].append(idx)
                else:
                    inside.append([idx])
                isIn = True
                isOut = False
            else:
                if isOut:
                    outside[-1].append(idx)
                else:
                    outside.append([idx])
                isIn = False
                isOut = True
        return inside, outside, inCylBasisAtomTraj

    def __get_sub_trajectory_msd__(self, atomSubTrajectory):
        dsq = np.add.reduce(atomSubTrajectory*atomSubTrajectory,1)
        # sum_dsq1 is the cumulative sum of dsq
        sum_dsq1 = np.add.accumulate(dsq)
        # sum_dsq1 is the reversed cumulative sum of dsq
        sum_dsq2 = np.add.accumulate(dsq[::-1])
        # sumsq refers to SUMSQ in the published algorithm
        sumsq = 2.*sum_dsq1[-1]
        # this line refers to the instruction SUMSQ <-- SUMSQ - DSQ(m-1) - DSQ(N - m) of the published algorithm
        # In this case, msd is an array because the instruction is computed for each m ranging from 0 to len(traj) - 1
        # So, this single instruction is performing the loop in the published algorithm
        Saabb  = sumsq - np.concatenate(([0.], sum_dsq1[:-1])) - np.concatenate(([0.], sum_dsq2[:-1]))
        # Saabb refers to SAA+BB/(N-m) in the published algorithm
        # Sab refers to SAB(m)/(N-m) in the published algorithm
        Saabb = Saabb / (len(dsq) - np.arange(len(dsq)))
        Sab   = 2.*correlation(atomSubTrajectory)
        # The atomic MSD.
        return Saabb - Sab

    def step(self, index):
        """
        analysis step of calculation method.\n

        :Parameters:
            #. atomIndex (int): the atom step index

        :Returns:
            #. stepData (object): object used in combine method
        """
        # get atom index
        atomIndex = self.targetAtomsIndexes[index]
        # get atomTrajectory
        atomTrajectory = self._trajectory.get_atom_trajectory(atomIndex, self.configurationsIndexes)
        # get inside outside
        inside, outside, inCylBasisAtomTraj = self.__split_trajectory_to_inside_outside__(atomTrajectory)
        # get atom element
        element = self.elements[index]
        # residency time
        residencyInside  = [0,0]
        residencyOutside = [0,0]
        axialTraj = np.zeros((inCylBasisAtomTraj.shape[0], 1))
        transTraj = np.zeros((inCylBasisAtomTraj.shape[0], 2))
        # calculate msd inside
        for item in inside:
            self.results['histogram_inside_%s' %element][int(len(item)/self.histBin)] += 1
            if len(item) == 1:
                continue
            residencyInside[0] += self.results['time'][item[-1]]-self.results['time'][item[0]]
            residencyInside[1] += 1
            # get msd inside
            atomicMSD = self.__get_sub_trajectory_msd__(inCylBasisAtomTraj[item])
            self.results['msd_inside_%s' %element][0:atomicMSD.shape[0]] += atomicMSD
            # get msd axial
            axialTraj[item,0] = inCylBasisAtomTraj[item,0]
            axialAtomicMSD = self.__get_sub_trajectory_msd__(axialTraj[item])
            self.results['msd_inside_axial_%s' %element][0:axialAtomicMSD.shape[0]] += axialAtomicMSD
            # get msd transversal
            transTraj[item,0:] = inCylBasisAtomTraj[item,1:]
            transAtomicMSD = self.__get_sub_trajectory_msd__(transTraj[item])
            self.results['msd_inside_transversal_%s' %element][0:transAtomicMSD.shape[0]] += transAtomicMSD
            # update insideNormalization
            self.insideNormalization[element][0:atomicMSD.shape[0]] += 1
        # calculate msd outside
        for item in outside:
            self.results['histogram_outside_%s' %element][int(len(item)/self.histBin)] += 1
            if len(item) == 1:
                continue
            residencyOutside[0] += self.results['time'][item[-1]]-self.results['time'][item[0]]
            residencyOutside[1] += 1
            atomicMSD = self.__get_sub_trajectory_msd__(atomTrajectory[item])
            self.results['msd_outside_%s' %element][0:atomicMSD.shape[0]] += atomicMSD
            self.outsideNormalization[element][0:atomicMSD.shape[0]] += 1
        # return
        return index, (residencyInside, residencyOutside)

    def combine(self, index, stepData):
        """
        analysis combine method called after each step.\n

        :Parameters:
            #. atomIndex (int): the atomIndex of the last calculated atom
            #. stepData (object): the returned data from step method
        """
        element = self.elements[index]
        self.results['residency_time_inside_%s' %element][0] += stepData[0][0]
        self.results['residency_time_inside_%s' %element][1] += stepData[0][1]
        self.results['residency_time_outside_%s'%element][0] += stepData[1][0]
        self.results['residency_time_outside_%s'%element][1] += stepData[1][1]


    def finalize(self):
        """
        called once all the steps has been run.\n
        """
        # The MSDs per element are averaged.
        for el in set(self.elements):
            whereInside = np.where(self.insideNormalization[el]==0)[0]
            if len(whereInside) == 0:
                self.results['msd_inside_%s' %el][:] /= self.insideNormalization[el]
                self.results['msd_inside_axial_%s' %el][:] /= self.insideNormalization[el]
                self.results['msd_inside_transversal_%s' %el][:] /= self.insideNormalization[el]
            else:
                self.results['msd_inside_%s' %el][:whereInside[0]] /= self.insideNormalization[el][:whereInside[0]]
                self.results['msd_inside_axial_%s' %el][:whereInside[0]] /= self.insideNormalization[el][:whereInside[0]]
                self.results['msd_inside_transversal_%s' %el][:whereInside[0]] /= self.insideNormalization[el][:whereInside[0]]
            whereOutside = np.where(self.outsideNormalization[el]==0)[0]
            if len(whereOutside) == 0:
                self.results['msd_outside_%s' %el][:] /= self.outsideNormalization[el]
            else:
                self.results['msd_outside_%s' %el][:whereOutside[0]] /= self.outsideNormalization[el][:whereOutside[0]]
        # The MSDs per element are averaged.
        msdsInside   = {}
        msdsAxialInside   = {}
        msdsTransInside   = {}
        msdsOutside  = {}
        for el in set(self.elements):
            msdsInside[el]   = self.results['msd_inside_%s' %el]
            msdsAxialInside[el]   = self.results['msd_inside_axial_%s' %el]
            msdsTransInside[el]   = self.results['msd_inside_transversal_%s' %el]
            msdsOutside[el]  = self.results['msd_outside_%s' %el]
        # get msd total
        self.results['msd_inside_total']  = get_data_weighted_sum(msdsInside, numbers=self.elementsNumber, weights=self.elementsWeights)
        self.results['msd_inside_axial_total']  = get_data_weighted_sum(msdsAxialInside, numbers=self.elementsNumber, weights=self.elementsWeights)
        self.results['msd_inside_transversal_total']  = get_data_weighted_sum(msdsTransInside, numbers=self.elementsNumber, weights=self.elementsWeights)
        self.results['msd_outside_total'] = get_data_weighted_sum(msdsOutside, numbers=self.elementsNumber, weights=self.elementsWeights)
        # get residency time
        self.results['residency_time_inside_total'] = np.array([0.0])
        self.results['residency_time_outside_total'] = np.array([0.0])
        for el in set(self.elements):
            if self.results['residency_time_inside_%s' %el][1] == 0:
                self.results['residency_time_inside_%s' %el][1] = 1.
            if self.results['residency_time_outside_%s' %el][1] == 0:
                self.results['residency_time_outside_%s' %el][1] = 1.
            self.results['residency_time_inside_%s' %el]  = np.array([self.results['residency_time_inside_%s' %el][0]/self.results['residency_time_inside_%s' %el][1]])
            self.results['residency_time_outside_%s' %el] = np.array([self.results['residency_time_outside_%s' %el][0]/self.results['residency_time_outside_%s' %el][1]])
            self.results['residency_time_inside_total']  += self.results['residency_time_inside_%s' %el]
            self.results['residency_time_outside_total'] += self.results['residency_time_outside_%s' %el]
        self.results['residency_time_inside_total']  /= len(set(self.elements))
        self.results['residency_time_outside_total'] /= len(set(self.elements))
        # get histogram time
        self.results['histogram_inside_total']  = np.zeros((int(len(self.configurationsIndexes)/self.histBin)+1), dtype=np.float)
        self.results['histogram_outside_total'] = np.zeros((int(len(self.configurationsIndexes)/self.histBin)+1), dtype=np.float)
        for el in set(self.elements):
            self.results['histogram_inside_total']  += self.results['histogram_inside_%s' %el]
            self.results['histogram_outside_total'] += self.results['histogram_outside_%s' %el]
Ejemplo n.º 6
0
class CylindricalDiskDensity(Analysis):
    """
    Computes the radial density profile in comparison to an axis, normalized to the total number of selected atoms.
    """
    def __init__(self, trajectory, configurationsIndexes, targetAtomsIndexes,
                 weighting, axis, radius, lengths, *args, **kwargs):
        # set trajectory
        super(CylindricalDiskDensity, self).__init__(trajectory, *args,
                                                     **kwargs)
        # set steps indexes
        self.configurationsIndexes = self.get_trajectory_indexes(
            configurationsIndexes)
        self.numberOfSteps = len(self.configurationsIndexes)
        # set targetAtomsIndexes
        self.targetAtomsIndexes = self.get_atoms_indexes(targetAtomsIndexes)
        # initialize variables
        self.__initialize_variables__(weighting, axis, radius, lengths)
        # initialize results
        self.__initialize_results__()

    def __initialize_variables__(self, weighting, axis, radius, lengths):
        # get elements
        elements = self._trajectory.elements
        self.elements = [elements[idx] for idx in self.targetAtomsIndexes]
        self.elementsSet = list(set(self.elements))
        # set weighting
        assert is_element_property(weighting), Logger.error(
            "weighting '%s' don't exist in database" % weighting)
        self.weighting = weighting
        self.weights = np.array(
            [get_element_property(el, self.weighting) for el in self.elements])
        self.elementsWeights = dict(
            zip(self.elementsSet, [
                get_element_property(el, self.weighting)
                for el in self.elementsSet
            ]))
        self.elementsNumber = dict(Counter(self.elements))
        # set axis
        self.axis = AxisDefinition(self._trajectory, axis)
        # set length
        assert isinstance(lengths, (
            list, tuple, set,
            np.ndarray)), Logger.error("lengths must be a list or numpy.array")
        try:
            lengths = np.array(sorted(set(lengths)), dtype=np.float32)
        except:
            raise Logger.error("lengths element must be numbers")
        assert len(lengths.shape) == 1, Logger.error(
            "lengths must be uni-dimensional")
        self.lengths = lengths
        # set radius
        try:
            radius = float(radius)
        except:
            raise Logger.error("radius must be numbers")
        assert radius > 0, Logger.error("radius must be positive")
        self.radius = radius
        # cylinder volume
        self.cylinderVolume = (self.lengths[-1] -
                               self.lengths[0]) * np.pi * self.radius**2
        self.diskVolumes = (self.lengths[1:] -
                            self.lengths[0:-1]) * np.pi * self.radius**2
        self.cumDisksVolumes = np.cumsum(self.diskVolumes)

    def __initialize_results__(self):
        # time
        self.results['time'] = np.array(
            [self.time[idx] for idx in self.configurationsIndexes],
            dtype=np.float)
        # radii
        self.results['lengths'] = (self.lengths[1:] + self.lengths[0:-1]) / 2.
        #volumes
        self.results['disksVolume'] = self.diskVolumes
        self.results['cumulutiveDisksVolume'] = self.cumDisksVolumes
        # Will store the radial_density_profile.
        self.cylindricalDistribution = {}
        self.cylindricalDensity = {}
        self.insideAtomsNumber = {}
        self.numberDensity = {}
        # initialize results per element
        for el in set(self.elements):
            self.results['CumulCylDiskDenDist_%s' % el] = np.zeros(
                len(self.lengths) - 1, dtype=np.float)
            self.results['CumulCylDiskDen_%s' % el] = np.zeros(
                len(self.lengths) - 1, dtype=np.float)
            self.results['CylDiskDenDist_%s' % el] = np.zeros(
                len(self.lengths) - 1, dtype=np.float)
            self.results['CylDiskDen_%s' % el] = np.zeros(len(self.lengths) -
                                                          1,
                                                          dtype=np.float)
            self.results['insideAtomsNumber_%s' % el] = np.zeros(
                self.numberOfSteps, dtype=np.float)
            self.results['numberDensity_%s' % el] = np.zeros(
                self.numberOfSteps, dtype=np.float)
            self.results['density_%s' % el] = np.zeros(self.numberOfSteps,
                                                       dtype=np.float)
        # initialize totals
        self.results['CumulCylDiskDenDist_total'] = np.zeros(
            len(self.lengths) - 1, dtype=np.float)
        self.results['CumulCylDiskDen_total'] = np.zeros(len(self.lengths) - 1,
                                                         dtype=np.float)
        self.results['CylDiskDenDist_total'] = np.zeros(len(self.lengths) - 1,
                                                        dtype=np.float)
        self.results['CylDiskDen_total'] = np.zeros(len(self.lengths) - 1,
                                                    dtype=np.float)
        self.results['insideAtomsNumber_total'] = np.zeros(self.numberOfSteps,
                                                           dtype=np.float)
        self.results['numberDensity_total'] = np.zeros(self.numberOfSteps,
                                                       dtype=np.float)
        self.results['density_total'] = np.zeros(self.numberOfSteps,
                                                 dtype=np.float)

    def step(self, index):
        """"
        analysis step of calculation method.\n

        :Parameters:
            #. index (int): the step index

        :Returns:
            #. stepData (object): object used in combine method
        """
        # get configuration index
        confIdx = self.configurationsIndexes[index]
        # set working configuration index
        self._trajectory.set_configuration_index(confIdx)
        # get coordinates
        coordinates = self._trajectory.get_configuration_coordinates(confIdx)
        targetAtomsCoordinates = coordinates[self.targetAtomsIndexes]
        # get center and axis and rotation matrix
        center, rotationMatrix = self.axis.get_center_rotationMatrix(
            coordinates)
        # translate to center
        targetAtomsCoordinates -= center
        # change coordinates to cylinder axes system
        targetAtomsCoordinates = np.dot(targetAtomsCoordinates, rotationMatrix)
        # calculate distance from axis vector
        radii = np.sqrt(targetAtomsCoordinates[:, 1]**2 +
                        targetAtomsCoordinates[:, 2]**2)
        # find inside cylinder indexes
        indexes = set(range(len(self.targetAtomsIndexes)))
        outOfLengthIdx = list(
            np.nonzero(targetAtomsCoordinates[:, 0] > self.lengths[-1])[0])
        outOfLengthIdx += list(
            np.nonzero(targetAtomsCoordinates[:, 0] < self.lengths[0])[0])
        outOfLengthIdx = set(outOfLengthIdx)
        outOfRadiusIdx = set(np.nonzero(radii > self.radius)[0])
        # substract outside nanotube
        indexes -= outOfLengthIdx
        indexes -= outOfRadiusIdx
        indexes = list(indexes)
        # get this frame arrays
        lengths = targetAtomsCoordinates[indexes, 0]
        # inside nantoube atoms elements
        selectedElements = np.array(self.elements)[indexes]
        # return
        return index, (lengths, selectedElements, indexes)

    def combine(self, index, stepData):
        """
        analysis combine method called after each step.\n

        :Parameters:
            #. index (int): the index of the last calculated step
            #. stepData (object): the returned data from step method
        """
        lengths = stepData[0]
        insideCylinderElements = stepData[1]
        insideCylinderIndexes = stepData[2]
        insideDensity = np.sum(
            self.weights[insideCylinderIndexes]) / self.cylinderVolume
        for el in self.elementsSet:
            # get element indexes
            indexes = np.nonzero(insideCylinderElements == el)[0]
            # calculate histogram
            hist, _ = np.histogram(lengths[indexes],
                                   bins=self.lengths,
                                   weights=self.weights[indexes])
            # calculate element density
            elementDensity = 1.0 * self.elementsWeights[el] * len(
                indexes) / self.cylinderVolume
            # update value
            self.results['CylDiskDenDist_%s' % el] += hist / insideDensity
            self.results['CylDiskDen_%s' % el] += hist
            self.results['insideAtomsNumber_%s' % el][index] = len(indexes)
            self.results['numberDensity_%s' %
                         el][index] = len(indexes) / self.cylinderVolume
            self.results['density_%s' % el][index] = elementDensity

    def finalize(self):
        """
        called once all the steps has been run.\n
        """
        for el in list(set(self.elements)):
            # update value
            self.results['CumulCylDiskDenDist_%s' % el] = 1.0 * np.cumsum(
                self.results['CylDiskDenDist_%s' %
                             el]) / self.cumDisksVolumes / self.numberOfSteps
            self.results['CumulCylDiskDen_%s' % el] = 1.0 * np.cumsum(
                self.results['CylDiskDen_%s' %
                             el]) / self.cumDisksVolumes / self.numberOfSteps
            self.results['CylDiskDenDist_%s' %
                         el] /= self.diskVolumes * self.numberOfSteps
            self.results['CylDiskDen_%s' %
                         el] /= self.diskVolumes * self.numberOfSteps
            self.results['CumulCylDiskDenDist_total'] += self.results[
                'CumulCylDiskDenDist_%s' % el]
            self.results['CumulCylDiskDen_total'] += self.results[
                'CumulCylDiskDen_%s' % el]
            self.results['CylDiskDenDist_total'] += self.results[
                'CylDiskDenDist_%s' % el]
            self.results['CylDiskDen_total'] += self.results['CylDiskDen_%s' %
                                                             el]
            self.results['insideAtomsNumber_total'] += self.results[
                'insideAtomsNumber_%s' % el]
            self.results['numberDensity_total'] += self.results[
                'numberDensity_%s' % el]
            self.results['density_total'] += self.results['density_%s' % el]
Ejemplo n.º 7
0
class InsideCylinderDistances(Analysis):
    """
    Computes the mean minimum distance between two atoms subset.
    """
    def __init__(self,
                 trajectory,
                 configurationsIndexes,
                 cylinderAtomsIndexes,
                 targetAtomsIndexes,
                 axis=None,
                 *args,
                 **kwargs):
        # set trajectory
        super(InsideCylinderDistances, self).__init__(trajectory, *args,
                                                      **kwargs)
        # set steps indexes
        self.configurationsIndexes = self.get_trajectory_indexes(
            configurationsIndexes)
        self.numberOfSteps = len(self.configurationsIndexes)
        # set atoms indexes
        self.targetAtomsIndexes = self.get_atoms_indexes(targetAtomsIndexes)
        self.cylinderAtomsIndexes = self.get_atoms_indexes(
            cylinderAtomsIndexes)
        # initialize variables
        self.__initialize_variables__(axis)
        # initialize results
        self.__initialize_results__()

    def __initialize_variables__(self, axis):
        # check atoms indexes
        assert not len(
            set.intersection(set(self.cylinderAtomsIndexes),
                             set(self.targetAtomsIndexes))
        ), Logger.error(
            "cylinderAtomsIndexes and targetAtomsIndexes can't have any index in common"
        )
        # set axis
        if axis is None:
            axis = {"principal": self.cylinderAtomsIndexes}
        self.axis = AxisDefinition(self._trajectory, axis)

    def __initialize_results__(self):
        # time
        self.results['time'] = np.array(
            [self.time[idx] for idx in self.configurationsIndexes],
            dtype=np.float)
        # mean minimum distances
        self.results['mean_minimum_distance'] = np.zeros(self.numberOfSteps,
                                                         dtype=np.float)
        self.results['minimum_distance'] = np.zeros(self.numberOfSteps,
                                                    dtype=np.float)
        self.results['mean_shell_thickness'] = np.zeros(self.numberOfSteps,
                                                        dtype=np.float)
        self.results['cylinder_radius'] = np.zeros(self.numberOfSteps,
                                                   dtype=np.float)

    def step(self, index):
        """"
        analysis step of calculation method.\n

        :Parameters:
            #. index (int): the step index

        :Returns:
            #. stepData (object): object used in combine method
        """
        # get configuration index
        confIdx = self.configurationsIndexes[index]
        # set working configuration index
        self._trajectory.set_configuration_index(confIdx)
        # get coordinates
        coordinates = self._trajectory.get_configuration_coordinates(confIdx)
        targetAtomsCoordinates = coordinates[self.targetAtomsIndexes]
        cylinderAtomsCoordinates = coordinates[self.cylinderAtomsIndexes]
        # get center and axis and rotation matrix
        center, rotationMatrix = self.axis.get_center_rotationMatrix(
            coordinates)
        # translate to center
        targetAtomsCoordinates -= center
        cylinderAtomsCoordinates -= center
        # change coordinates to cylinder axes system
        targetAtomsCoordinates = np.dot(targetAtomsCoordinates, rotationMatrix)
        cylinderAtomsCoordinates = np.dot(cylinderAtomsCoordinates,
                                          rotationMatrix)
        # calculate carbon cylinder dimensions
        cylRadiusSquared = np.mean(cylinderAtomsCoordinates[:, 1]**2 +
                                   cylinderAtomsCoordinates[:, 2]**2)
        cylLength = np.abs(
            np.max(cylinderAtomsCoordinates[:, 0]) -
            np.min(cylinderAtomsCoordinates[:, 0]))
        # calculate target atoms radius
        radiiSquared = targetAtomsCoordinates[:,
                                              1]**2 + targetAtomsCoordinates[:,
                                                                             2]**2
        # find inside cylinder indexes
        outOfLengthIdx = list(
            np.nonzero(targetAtomsCoordinates[:, 0] < -cylLength / 2.)[0])
        outOfLengthIdx += list(
            np.nonzero(targetAtomsCoordinates[:, 0] > cylLength / 2.)[0])
        outOfLengthIdx = set(outOfLengthIdx)
        outOfRadiusIdx = set(np.nonzero(radiiSquared > cylRadiusSquared)[0])
        # substract atomes outside cylinder indexes
        indexes = set(range(len(self.targetAtomsIndexes)))
        indexes -= outOfLengthIdx
        indexes -= outOfRadiusIdx
        indexes = list(indexes)
        # get this frame arrays
        targetAtomsCoordinates = targetAtomsCoordinates[indexes, :]
        # return
        return index, (cylinderAtomsCoordinates, targetAtomsCoordinates,
                       np.sqrt(cylRadiusSquared), cylLength)

    def combine(self, index, stepData):
        """
        analysis combine method called after each step.\n

        :Parameters:
            #. index (int): the index of the last calculated step
            #. stepData (object): the returned data from step method
        """
        cylinderAtomsCoordinates = stepData[0]
        targetAtomsCoordinates = stepData[1]
        cylRadius = stepData[2]
        cylLength = stepData[3]
        # update cylinder radius
        self.results['cylinder_radius'][index] = cylRadius
        # if nothing inside
        if not len(targetAtomsCoordinates):
            self.results['mean_minimum_distance'][index] = np.nan
            self.results['minimum_distance'][index] = np.nan
        else:
            # initialize distances
            distances = np.zeros(cylinderAtomsCoordinates.shape[0])
            # calculate minimum distances
            for ntIndex in range(cylinderAtomsCoordinates.shape[0]):
                # calculate the distance between subset atoms and target atoms
                # no need to calculate boundary conditions real distances because all atoms are translated to cylinder center
                difference = targetAtomsCoordinates - cylinderAtomsCoordinates[
                    ntIndex, :]
                distances[ntIndex] = np.sqrt(
                    np.min(np.add.reduce(difference**2, 1)))
            # calculate minimum distances at step index
            self.results['mean_minimum_distance'][index] = np.mean(distances)
            self.results['minimum_distance'][index] = np.min(distances)
        # calculate mean shell distance to cylinder wall
        targetAtomsDistances = np.sqrt(
            np.add.reduce(targetAtomsCoordinates[:, 1:3]**2, 1))
        self.results['mean_shell_thickness'][index] = np.mean(
            targetAtomsDistances)

    def finalize(self):
        """
        called once all the steps has been run.\n
        """
        pass