def append_to_swap_list(self, subList): """ append a sub list to swap list :Parameters: #. subList (List): The sub-list of atom indexes to append to swapList. """ assert isinstance(subList, (list,tuple)), LOGGER.error("subList must be a list") subSL = [] for num in subList: assert is_integer(num), LOGGER.error("subList items must be integers") num = INT_TYPE(num) assert num>=0, LOGGER.error("subList items must be positive") subSL.append(num) assert len(set(subSL))==len(subSL), LOGGER.error("swapList items must not have any redundancy") assert len(subSL) == self.__swapLength, LOGGER.error("swapList item length must be equal to swapLength") # append self.__swapList = list(self.__swapList) subSL = np.array(subSL, dtype=INT_TYPE) self.__swapList.append(subSL) self.__swapList = tuple(self.__swapList) # set uncollected atoms swapList self._remainingAtomsSwapList = self.__swapList # reset collector state self._collectorState = None
def set_adjust_scale_factor(self, adjustScaleFactor): """ Sets adjust scale factor. :Parameters: #. adjustScaleFactor (list, tuple): Used to adjust fit or guess the best scale factor during EMC runtime. It must be a list of exactly three entries.\n 1. The frequency in number of accepted moves of finding the best scale factor. If 0 frequency is given, it means that the scale factor is fixed. 2. The minimum allowed scale factor value. 3. The maximum allowed scale factor value. """ assert isinstance(adjustScaleFactor, (list, tuple)), LOGGER.error('adjustScaleFactor must be a list.') assert len(adjustScaleFactor) == 3, LOGGER.error('adjustScaleFactor must be a list of exactly three items.') freq = adjustScaleFactor[0] minSF = adjustScaleFactor[1] maxSF = adjustScaleFactor[2] assert is_integer(freq), LOGGER.error("adjustScaleFactor first item (frequency) must be an integer.") freq = INT_TYPE(freq) assert freq>=0, LOGGER.error("adjustScaleFactor first (frequency) item must be bigger or equal to 0.") assert is_number(minSF), LOGGER.error("adjustScaleFactor second item (minimum) must be a number.") minSF = FLOAT_TYPE(minSF) assert is_number(maxSF), LOGGER.error("adjustScaleFactor third item (maximum) must be a number.") maxSF = FLOAT_TYPE(maxSF) assert minSF<=maxSF, LOGGER.error("adjustScaleFactor second item (minimum) must be smaller or equal to third second item (maximum).") # set values self.__adjustScaleFactorFrequency = freq self.__adjustScaleFactorMinimum = minSF self.__adjustScaleFactorMaximum = maxSF # dump to repository self._dump_to_repository({'_ExperimentalConstraint__adjustScaleFactorFrequency': self.__adjustScaleFactorFrequency, '_ExperimentalConstraint__adjustScaleFactorMinimum' : self.__adjustScaleFactorMinimum, '_ExperimentalConstraint__adjustScaleFactorMaximum' : self.__adjustScaleFactorMaximum}) # reset constraint self.reset_constraint()
def select_index(self): """ Select index. :Returns: #. index (integer): the selected group index in engine groups list """ return INT_TYPE(generate_random_integer(0, len(self.engine.groups) - 1))
def select_index(self): """ Select index. :Returns: #. index (integer): the selected group index in engine groups list """ return INT_TYPE( np.searchsorted(self._selectionScheme, generate_random_float()))
def compute_after_move(self, realIndexes, relativeIndexes, movedBoxCoordinates): """ Compute constraint after move is executed :Parameters: #. realIndexes (numpy.ndarray): Not used here. #. relativeIndexes (numpy.ndarray): Group atoms relative index the move will be applied to. #. movedBoxCoordinates (numpy.ndarray): The moved atoms new coordinates. """ bondsIndexes = self.activeAtomsDataBeforeMove["bondsIndexes"] # change coordinates temporarily boxData = np.array(self.engine.boxCoordinates[relativeIndexes], dtype=FLOAT_TYPE) self.engine.boxCoordinates[relativeIndexes] = movedBoxCoordinates # compute data after move if len(bondsIndexes): bonds, reduced = full_bonds_coords( idx1=self._atomsCollector.get_relative_indexes( self.__bondsList[0] [bondsIndexes]), #self.__bondsList[0][bondsIndexes], idx2=self._atomsCollector.get_relative_indexes( self.__bondsList[1] [bondsIndexes]), #self.__bondsList[1][bondsIndexes], lowerLimit=self.__bondsList[2][bondsIndexes], upperLimit=self.__bondsList[3][bondsIndexes], boxCoords=self.engine.boxCoordinates, basis=self.engine.basisVectors, isPBC=self.engine.isPBC, reduceDistanceToUpper=False, reduceDistanceToLower=False, ncores=INT_TYPE(1)) else: bonds = None reduced = None # set active data after move self.set_active_atoms_data_after_move({ "bondsLength": bonds, "reducedLengths": reduced }) # reset coordinates self.engine.boxCoordinates[relativeIndexes] = boxData # compute standardError after move if bonds is None: self.set_after_move_standard_error(self.standardError) else: # bondsIndexes is a fancy slicing, RL is a copy not a view. RL = self.data["reducedLengths"][bondsIndexes] self.data["reducedLengths"][ bondsIndexes] += reduced - self.activeAtomsDataBeforeMove[ "reducedLengths"] self.set_after_move_standard_error( self.compute_standard_error(data=self.data)) self.data["reducedLengths"][bondsIndexes] = RL
def compute_after_move(self, realIndexes, relativeIndexes, movedBoxCoordinates): """ Compute constraint after move is executed. :Parameters: #. realIndexes (numpy.ndarray): Not used here. #. relativeIndexes (numpy.ndarray): Group atoms relative index the move will be applied to. #. movedBoxCoordinates (numpy.ndarray): The moved atoms new coordinates. """ # get angles indexes anglesIndex = self.activeAtomsDataBeforeMove["anglesIndex"] # change coordinates temporarily boxData = np.array(self.engine.boxCoordinates[relativeIndexes], dtype=FLOAT_TYPE) self.engine.boxCoordinates[relativeIndexes] = movedBoxCoordinates # compute data before move if len(anglesIndex): angles, reduced = full_angles_coords( central=self._atomsCollector.get_relative_indexes( self.__anglesList[0][anglesIndex]), left=self._atomsCollector.get_relative_indexes( self.__anglesList[1][anglesIndex]), right=self._atomsCollector.get_relative_indexes( self.__anglesList[2][anglesIndex]), lowerLimit=self.__anglesList[3][anglesIndex], upperLimit=self.__anglesList[4][anglesIndex], boxCoords=self.engine.boxCoordinates, basis=self.engine.basisVectors, isPBC=self.engine.isPBC, reduceAngleToUpper=False, reduceAngleToLower=False, ncores=INT_TYPE(1)) else: angles = None reduced = None # set active data after move self.set_active_atoms_data_after_move({ "angles": angles, "reducedAngles": reduced }) # reset coordinates self.engine.boxCoordinates[relativeIndexes] = boxData # compute standardError after move if angles is None: self.set_after_move_standard_error(self.standardError) else: # anglesIndex is a fancy slicing, RL is a copy not a view. RL = self.data["reducedAngles"][anglesIndex] self.data["reducedAngles"][ anglesIndex] += reduced - self.activeAtomsDataBeforeMove[ "reducedAngles"] self.set_after_move_standard_error( self.compute_standard_error(data=self.data)) self.data["reducedAngles"][anglesIndex] = RL
def compute_data(self): """ Compute data and update engine constraintsData dictionary. """ if len(self._atomsCollector): anglesData = np.zeros(self.__anglesList[0].shape[0], dtype=FLOAT_TYPE) reducedData = np.zeros(self.__anglesList[0].shape[0], dtype=FLOAT_TYPE) anglesIndexes = set(set(range(self.__anglesList[0].shape[0]))) anglesIndexes = list(anglesIndexes - self._atomsCollector._randomData) central = self._atomsCollector.get_relative_indexes( self.__anglesList[0][anglesIndexes]) left = self._atomsCollector.get_relative_indexes( self.__anglesList[1][anglesIndexes]) right = self._atomsCollector.get_relative_indexes( self.__anglesList[2][anglesIndexes]) lowerLimit = self.__anglesList[3][anglesIndexes] upperLimit = self.__anglesList[4][anglesIndexes] else: central = self._atomsCollector.get_relative_indexes( self.__anglesList[0]) left = self._atomsCollector.get_relative_indexes( self.__anglesList[1]) right = self._atomsCollector.get_relative_indexes( self.__anglesList[2]) lowerLimit = self.__anglesList[3] upperLimit = self.__anglesList[4] # compute data angles, reduced = full_angles_coords( central=central, left=left, right=right, lowerLimit=lowerLimit, upperLimit=upperLimit, boxCoords=self.engine.boxCoordinates, basis=self.engine.basisVectors, isPBC=self.engine.isPBC, reduceAngleToUpper=False, reduceAngleToLower=False, ncores=INT_TYPE(1)) # create full length data if len(self._atomsCollector): anglesData[anglesIndexes] = angles reducedData[anglesIndexes] = reduced angles = anglesData reduced = reducedData # set data self.set_data({"angles": angles, "reducedAngles": reduced}) self.set_active_atoms_data_before_move(None) self.set_active_atoms_data_after_move(None) # set standardError self.set_standard_error(self.compute_standard_error(data=self.data)) # set original data if self.originalData is None: self._set_original_data(self.data)
def set_swap_list(self, swapList): """ Set the swap-list to exchange atoms position from. :Parameters: #. swapList (None, List): The list of atoms.\n If None is given, no swapping or exchanging will be performed.\n If List is given, it must contain lists of atom indexes where every sub-list length must be equal to swapLength. """ C = _Container() # add container if not C.is_container('swapList'): C.add_container('swapList') # check if swapList already defined loc = C.get_location_by_hint(swapList) if loc is not None: self.__swapList = C.get_value(loc) elif swapList is None: self.__swapList = () else: SL = [] assert isinstance( swapList, (list, tuple)), LOGGER.error("swapList must be a list") for sl in swapList: assert isinstance( sl, (list, tuple)), LOGGER.error("swapList items must be a list") subSL = [] for num in sl: assert is_integer(num), LOGGER.error( "swapList sub-list items must be integers") num = INT_TYPE(num) assert num >= 0, LOGGER.error( "swapList sub-list items must be positive") subSL.append(num) assert len(set(subSL)) == len(subSL), LOGGER.error( "swapList items must not have any redundancy") if self.swapLength is not None: assert len(subSL) == self.swapLength, LOGGER.error( "swapList item length must be equal to swapLength") SL.append(np.array(subSL, dtype=INT_TYPE)) self.__swapList = tuple(SL) # add swapList to container C.set_value(container='swapList', value=self.__swapList, hint=swapList) # set uncollected atoms swapList self._remainingAtomsSwapList = self.__swapList # reset collector state self._collectorState = None
def set_recur(self, recur): """ Sets the recur value. :Parameters: #. recur (integer): Set the recur value. It must be a positive integer. """ assert is_integer(recur), LOGGER.error("recur must be an integer") recur = INT_TYPE(recur) assert recur >= 0, LOGGER.error("recur must be positive") self.__recur = recur self.__recurAsSet = recur
def set_group_axis(self, groupAxis): """ Sets group axis value. :Parameters: #. groupAxis (dict): The group axis. Only one key is allowed. If key is fixed, value must be a list, tuple or a numpy.array of a vector such as [X,Y,Z]. If key is symmetry, in this case the group axis is computed as one of the three symmetry axis of the group atoms. the value must be even 0, 1 or 2 for respectively the first, second and tertiary symmetry axis. """ assert isinstance(groupAxis, dict), LOGGER.error("groupAxis must be a dictionary") assert len(groupAxis) == 1, LOGGER.error( "groupAxis must have a single key") key = groupAxis.keys()[0] val = groupAxis[key] if key == "fixed": self.__mustComputeGroupAxis = False assert isinstance( val, (list, set, tuple, np.ndarray)), LOGGER.error("groupAxis value must be a list") if isinstance(val, np.ndarray): assert len(val.shape) == 1, LOGGER.error( "groupAxis value must have a single dimension") val = list(val) assert len(val) == 3, LOGGER.error( "groupAxis fixed value must be a vector") for v in val: assert is_number(v), LOGGER.error( "groupAxis value item must be numbers") val = np.array([FLOAT_TYPE(v) for v in val], dtype=FLOAT_TYPE) norm = FLOAT_TYPE(np.sqrt(np.sum(val**2))) val /= norm elif key == "symmetry": self.__mustComputeGroupAxis = True assert is_integer(val), LOGGER.error( "groupAxis symmetry value must be an integer") val = INT_TYPE(val) assert val >= 0 and val < 3, LOGGER.error( "groupAxis symmetry value must be positive smaller than 3") else: self.__mustComputeGroupAxis = None raise Exception( LOGGER.error( "groupAxis key must be either 'fixed' or 'symmetry'")) # set groupAxis self.__groupAxis = {key: val}
def set_axis(self, axis): """ Set the symmetry axis index to translate along. :Parameters: #. axis (integer): Must be 0,1 or 2 for respectively the main, secondary or tertiary symmetry axis """ assert is_integer(axis), LOGGER.error("rotation symmetry axis must be an integer.") axis = INT_TYPE(axis) assert axis>=0, LOGGER.error("rotation symmetry axis must be positive.") assert axis<=2, LOGGER.error("rotation symmetry axis must be smaller or equal to 2.") # convert to radian and store amplitude self.__axis = axis
def compute_data(self): """ Compute constraint's data.""" if len(self._atomsCollector): bondsData = np.zeros(self.__bondsList[0].shape[0], dtype=FLOAT_TYPE) reducedData = np.zeros(self.__bondsList[0].shape[0], dtype=FLOAT_TYPE) bondsIndexes = set(set(range(self.__bondsList[0].shape[0]))) bondsIndexes = list(bondsIndexes - self._atomsCollector._randomData) idx1 = self._atomsCollector.get_relative_indexes( self.__bondsList[0][bondsIndexes]) idx2 = self._atomsCollector.get_relative_indexes( self.__bondsList[1][bondsIndexes]) lowerLimit = self.__bondsList[2][bondsIndexes] upperLimit = self.__bondsList[3][bondsIndexes] else: idx1 = self._atomsCollector.get_relative_indexes( self.__bondsList[0]) idx2 = self._atomsCollector.get_relative_indexes( self.__bondsList[1]) lowerLimit = self.__bondsList[2] upperLimit = self.__bondsList[3] # compute bonds, reduced = full_bonds_coords( idx1=idx1, idx2=idx2, lowerLimit=lowerLimit, upperLimit=upperLimit, boxCoords=self.engine.boxCoordinates, basis=self.engine.basisVectors, isPBC=self.engine.isPBC, reduceDistanceToUpper=False, reduceDistanceToLower=False, ncores=INT_TYPE(1)) # create full length data if len(self._atomsCollector): bondsData[bondsIndexes] = bonds reducedData[bondsIndexes] = reduced bonds = bondsData reduced = reducedData self.set_data({"bondsLength": bonds, "reducedLengths": reduced}) self.set_active_atoms_data_before_move(None) self.set_active_atoms_data_after_move(None) # set standardError self.set_standard_error(self.compute_standard_error(data=self.data)) # set original data if self.originalData is None: self._set_original_data(self.data)
def __check_single_weight(self, w): """Checks a single group weight tuple format""" assert isinstance(w, (list,set,tuple)),LOGGER.error("weights list items must be tuples") assert len(w)==2, LOGGER.error("weights list tuples must have exactly 2 items") idx = w[0] wgt = w[1] assert is_integer(idx), LOGGER.error("weights list tuples first item must be an integer") idx = INT_TYPE(idx) assert idx>=0, LOGGER.error("weights list tuples first item must be positive") assert idx<len(self.__collection), LOGGER.error("weights list tuples first item must be smaller than the number of generators in collection") assert is_number(wgt), LOGGER.error("weights list tuples second item must be an integer") wgt = FLOAT_TYPE(wgt) assert wgt>0, LOGGER.error("weights list tuples first item must be bigger than 0") # all True return idx and weight return idx, wgt
def set_maximum_collected(self, maximumCollected): """ Set maximum collected number of atoms allowed. :Parameters: #. maximumCollected (None, Integer): The maximum number allowed of atoms to be removed and collected from the engine. This property is general to the system and checks engine's collected atoms not the number of removed atoms via this generator. If None is given, the remover will not check for the number of already removed atoms before attempting a remove. """ if maximumCollected is not None: assert is_integer(maximumCollected), LOGGER.error("maximumCollected must be an integer") maximumCollected = INT_TYPE(maximumCollected) assert maximumCollected>0, LOGGER.error("maximumCollected must be bigger than 0") self.__maximumCollected = maximumCollected
def set_center(self, center): """ Sets center value. :Parameters: #. center (dict): The center value dictionary. Must have a single key and this can only be 'fixed' or 'indexes'. If key is fixed, value must be a list or a numpy.array of a point coordinates such as [X,Y,Z] If key is indexes, value must be a list or a numpy array of indexes. """ assert isinstance(center, dict), LOGGER.error("center must be a dictionary") assert len(center) == 1, LOGGER.error("center must have a single key") key = center.keys()[0] val = center[key] assert isinstance( val, (list, set, tuple, np.ndarray)), LOGGER.error("center value must be a list") if isinstance(val, np.ndarray): assert len(val.shape) == 1, LOGGER.error( "center value must have a single dimension") assert len(val) > 0, LOGGER.error( "center value must be a non-zero list.") for v in val: assert is_number(v), LOGGER.error( "center value item must be numbers") if key == "fixed": self.__mustCompute = False assert len(val) == 3, LOGGER.error( "fixed center must have exactly 3 elements corresponding to X,Y and Z coordinates of the center point." ) val = np.array([FLOAT_TYPE(v) for v in val], dtype=FLOAT_TYPE) elif key == "indexes": self.__mustCompute = True for v in val: assert is_integer(v), LOGGER.error( "indexes center items be integers") val = np.array([INT_TYPE(v) for v in val], dtype=INT_TYPE) for v in val: assert v >= 0, LOGGER.error( "indexes center items be positive integers") else: self.__mustCompute = None raise Exception( LOGGER.error("center key must be either 'fixed' or 'indexes'")) # set center self.__center = {key: val}
def set_swap_length(self, swapLength): """ Set swap length. it will reset swaplist automatically. :Parameters: #. swapLength (Integer): The swap length that defines the length of the group and the length of the every swap sub-list in swapList. """ assert is_integer(swapLength), LOGGER.error("swapLength must be an integer") swapLength = INT_TYPE(swapLength) assert swapLength>0, LOGGER.error("swapLength must be bigger than 0") self.__swapLength = swapLength self.__swapList = () # set uncollected atoms swapList self._remainingAtomsSwapList = self.__swapList # reset collector state self._collectorState = None
def compute_before_move(self, realIndexes, relativeIndexes): """ Compute constraint's data before move is executed. :Parameters: #. realIndexes (numpy.ndarray): Group atoms index the move will be applied to. #. relativeIndexes (numpy.ndarray): Not used here. """ # get bonds indexes bondsIndexes = [] #for idx in relativeIndexes: for idx in realIndexes: bondsIndexes.extend(self.__bonds[idx]['map']) #bondsIndexes = list(set(bondsIndexes)) # remove collected bonds bondsIndexes = list( set(bondsIndexes) - set(self._atomsCollector._randomData)) # compute data before move if len(bondsIndexes): bonds, reduced = full_bonds_coords( idx1=self._atomsCollector.get_relative_indexes( self.__bondsList[0][bondsIndexes]), idx2=self._atomsCollector.get_relative_indexes( self.__bondsList[1][bondsIndexes]), lowerLimit=self.__bondsList[2][bondsIndexes], upperLimit=self.__bondsList[3][bondsIndexes], boxCoords=self.engine.boxCoordinates, basis=self.engine.basisVectors, isPBC=self.engine.isPBC, reduceDistanceToUpper=False, reduceDistanceToLower=False, ncores=INT_TYPE(1)) else: bonds = None reduced = None # set data before move self.set_active_atoms_data_before_move({ "bondsIndexes": bondsIndexes, "bondsLength": bonds, "reducedLengths": reduced }) self.set_active_atoms_data_after_move(None)
def compute_before_move(self, realIndexes, relativeIndexes): """ Compute constraint before move is executed. :Parameters: #. realIndexes (numpy.ndarray): Group atoms index the move will be applied to. #. relativeIndexes (numpy.ndarray): Not used here. """ # get angles indexes anglesIndex = [] #for idx in relativeIndexes: for idx in realIndexes: anglesIndex.extend(self.__angles[idx]['centralMap']) anglesIndex.extend(self.__angles[idx]['otherMap']) anglesIndex = list(set(anglesIndex) - self._atomsCollector._randomData) # compute data before move if len(anglesIndex): angles, reduced = full_angles_coords( central=self._atomsCollector.get_relative_indexes( self.__anglesList[0][anglesIndex]), left=self._atomsCollector.get_relative_indexes( self.__anglesList[1][anglesIndex]), right=self._atomsCollector.get_relative_indexes( self.__anglesList[2][anglesIndex]), lowerLimit=self.__anglesList[3][anglesIndex], upperLimit=self.__anglesList[4][anglesIndex], boxCoords=self.engine.boxCoordinates, basis=self.engine.basisVectors, isPBC=self.engine.isPBC, reduceAngleToUpper=False, reduceAngleToLower=False, ncores=INT_TYPE(1)) else: angles = None reduced = None # set data before move self.set_active_atoms_data_before_move({ "anglesIndex": anglesIndex, "angles": angles, "reducedAngles": reduced }) self.set_active_atoms_data_after_move(None)
def set_atoms_list(self, atomsList): """ Set atoms index list from which atoms will be picked to attempt removal. This method must be overloaded and not be called from this class but from its children. Otherwise a usage error will be raised. :Parameters: #. atomsList (None, list,set,tuple,np.ndarray): The list of atoms index to chose and remove from. """ if atomsList is not None: C = _Container() # add container if not C.is_container('removeAtomsList'): C.add_container('removeAtomsList') # check if atomsList already defined loc = C.get_location_by_hint(atomsList) if loc is not None: atomsList = C.get_value(loc) else: assert isinstance( atomsList, (list, tuple, np.ndarray)), LOGGER.error( "atomsList must be either a list or a numpy.array") CL = [] for idx in atomsList: assert is_integer(idx), LOGGER.error( "atomsList items must be integers") assert idx >= 0, LOGGER.error( "atomsList item must equal or bigger than 0") CL.append(INT_TYPE(idx)) setCL = set(CL) assert len(setCL) == len(CL), LOGGER.error( "atomsList redundancy is not allowed") AL = np.array(CL, dtype=INT_TYPE) # add swapList to container C.set_value(container='removeAtomsList', value=AL, hint=atomsList) atomsList = AL # set atomsList attribute self.__atomsList = atomsList # reset collector state self._collectorState = None
def move(self, coordinates): """ Moves coordinates. :Parameters: #. coordinates (np.ndarray): The coordinates on which to apply the transformation :Returns: #. coordinates (np.ndarray): The new coordinates after applying the transformation """ if self.__randomize: index = INT_TYPE( np.searchsorted(self.__selectionScheme, generate_random_float()) ) moveGenerator = self.__collection[ index ] else: moveGenerator = self.__collection[self.__step] self.__step = (self.__step+1)%len(self.__collection) # perform the move return moveGenerator.move(coordinates)
def set_bonds(self, bondsList, tform=True): """ Sets bonds dictionary by parsing bondsList list. :Parameters: #. bondsList (None, list): Bonds definition list. If None is given no bonds are defined. Otherwise it can be of any of the following two forms: tuples format: every item must be a list or tuple of four items.\n #. First atom index forming the bond. #. Second atom index forming the bond. #. Lower limit or the minimum bond length allowed. #. Upper limit or the maximum bond length allowed. four vectors format: List of exaclty four lists or numpy.arrays of the same length.\n #. List contains the first atoms index forming the bond. #. List contains the second atoms index forming the bond. #. List containing the lower limit or the minimum bond length allowed. #. List containing the upper limit or the maximum bond length allowed. #. tform (boolean): set whether given bondsList follows tuples format, If not then it must follow the four vectors one. """ # check if bondsList is given if bondsList is None: bondsList = [[], [], [], []] tform = False elif len(bondsList) == 4 and len(bondsList[0]) == 0: tform = False if self.engine is None: self.__bondsList = bondsList self.__bonds = {} else: NUMBER_OF_ATOMS = self.engine.get_original_data("numberOfAtoms") # set bonds and bondsList definition oldBondsList = self.__bondsList oldBonds = self.__bonds self.__bondsList = [ np.array([], dtype=INT_TYPE), np.array([], dtype=INT_TYPE), np.array([], dtype=FLOAT_TYPE), np.array([], dtype=FLOAT_TYPE) ] self.__bonds = {} self.__dumpBonds = False # build bonds try: if tform: for bond in bondsList: self.add_bond(bond) else: for idx in xrange(len(bondsList[0])): bond = [ bondsList[0][idx], bondsList[1][idx], bondsList[2][idx], bondsList[3][idx] ] self.add_bond(bond) except Exception as e: self.__dumpBonds = True self.__bondsList = oldBondsList self.__bonds = oldBonds LOGGER.error(e) import traceback raise Exception(traceback.format_exc()) self.__dumpBonds = True # finalize bonds for idx in xrange(NUMBER_OF_ATOMS): self.__bonds[INT_TYPE(idx)] = self.__bonds.get( INT_TYPE(idx), { "indexes": [], "map": [] }) # dump to repository self._dump_to_repository({ '_BondConstraint__bondsList': self.__bondsList, '_BondConstraint__bonds': self.__bonds }) # reset constraint self.reset_constraint()
def set_orientation_axis(self, orientationAxis): """ Set orientation axis value. :Parameters: #. orientationAxis (dict): The axis to align the group axis with. If key is fixed, value must be a list, tuple or a numpy.array of a vector such as [X,Y,Z]. If Key is symmetry, in this case the value must be a list of two items, the first one is a list of atoms indexes to compute symmetry axis and the second item must be even 0, 1 or 2 for respectively the first, second and tertiary symmetry axis. """ assert isinstance( orientationAxis, dict), LOGGER.error("orientationAxis must be a dictionary") assert len(orientationAxis) == 1, LOGGER.error( "orientationAxis must have a single key") key = orientationAxis.keys()[0] val = orientationAxis[key] if key == "fixed": self.__mustComputeOrientationAxis = False assert isinstance(val, (list, set, tuple, np.ndarray)), LOGGER.error( "orientationAxis value must be a list") if isinstance(val, np.ndarray): assert len(val.shape) == 1, LOGGER.error( "orientationAxis value must have a single dimension") val = list(val) assert len(val) == 3, LOGGER.error( "orientationAxis fixed value must be a vector") for v in val: assert is_number(v), LOGGER.error( "orientationAxis value item must be numbers") val = np.array([FLOAT_TYPE(v) for v in val], dtype=FLOAT_TYPE) norm = FLOAT_TYPE(np.sqrt(np.sum(val**2))) val /= norm elif key == "symmetry": self.__mustComputeOrientationAxis = True assert isintance(val, (list, tuple)), LOGGER.error( "orientationAxis symmetry value must be a list") assert len(val) == 2, LOGGER.error( "orientationAxis symmetry value must be a list of two items") val0 = [] for v in val[0]: assert is_integer(v), LOGGER.error( "orientationAxis symmetry value list items must be integers" ) v0 = INT_TYPE(v) assert v0 >= 0, LOGGER.error( "orientationAxis symmetry value list items must be positive" ) val0.append(v0) assert len(set(val0)) == len(val[0]), LOGGER.error( "orientationAxis symmetry value list redundant items indexes found" ) val0 = np.array(val0, dtype=INT_TYPE) val1 = val[1] assert is_integer(val1), LOGGER.error( "orientationAxis symmetry value second item must be an integer" ) val1 = INT_TYPE(val1) assert val1 >= 0 and val1 < 3, LOGGER.error( "orientationAxis symmetry value second item must be positive smaller than 3" ) val = (val0, val1) else: self.__mustComputeOrientationAxis = None raise Exception( LOGGER.error( "orientationAxis key must be either 'fixed' or 'symmetry'") ) # set orientationAxis self.__orientationAxis = {key: val}
def set_angles(self, anglesList, tform=True): """ Sets the angles dictionary by parsing the anglesList. All angles are in degrees. :Parameters: #. anglesList (list): The angles list definition that can be given in two different formats. tuples format: every item must be a list of five items.\n #. Central atom index. #. Index of the left atom forming the angle (interchangeable with the right atom). #. Index of the right atom forming the angle (interchangeable with the left atom). #. Minimum lower limit or the minimum angle allowed in degrees which later will be converted to rad. #. Maximum upper limit or the maximum angle allowed in degrees which later will be converted to rad. five vectors format: List of exaclty five lists or numpy.arrays or vectors of the same length.\n #. List containing central atom indexes. #. List containing the index of the left atom forming the angle (interchangeable with the right atom). #. List containing the index of the right atom forming the angle (interchangeable with the left atom). #. List containing the minimum lower limit or the minimum angle allowed in degrees which later will be converted to rad. #. List containing the maximum upper limit or the maximum angle allowed in degrees which later will be converted to rad. #. tform (boolean): set whether given anglesList follows tuples format, If False, then it must follow the five vectors one. """ # check if anglesList is given if anglesList is None: bondsList = [[], [], [], [], []] tform = False elif len(anglesList) == 5 and len(anglesList[0]) == 0: tform = False if self.engine is None: self.__anglesList = anglesList self.__angles = {} else: NUMBER_OF_ATOMS = self.engine.get_original_data("numberOfAtoms") oldAnglesList = self.__anglesList oldAngles = self.__angles self.__anglesList = [ np.array([], dtype=INT_TYPE), np.array([], dtype=INT_TYPE), np.array([], dtype=INT_TYPE), np.array([], dtype=FLOAT_TYPE), np.array([], dtype=FLOAT_TYPE) ] self.__angles = {} self.__dumpAngles = False # build angles try: if tform: for angle in anglesList: self.add_angle(angle) else: for idx in xrange(len(anglesList[0])): angle = [ anglesList[0][idx], anglesList[1][idx], anglesList[2][idx], anglesList[3][idx], anglesList[4][idx] ] self.add_angle(angle) except Exception as e: self.__dumpAngles = True self.__anglesList = oldAnglesList self.__angles = oldAngles LOGGER.error(e) import traceback raise Exception(traceback.format_exc()) self.__dumpAngles = True # finalize angles for idx in xrange(NUMBER_OF_ATOMS): self.__angles[INT_TYPE(idx)] = self.__angles.get( INT_TYPE(idx), { "left": [], "right": [], "centralMap": [], "otherMap": [] }) # dump to repository self._dump_to_repository({ '_BondsAngleConstraint__anglesList': self.__anglesList, '_BondsAngleConstraint__angles': self.__angles }) # reset constraint self.reset_constraint()
def set_coordination_number_definition(self, coordNumDef): """ Set the coordination number definition. :Parameters: #. coordNumDef (None, list, tuple): Coordination number definition. It must be None, list or tuple where every element is a list or a tuple of exactly 6 items and an optional 7th item for weight. #. core atoms: Can be any of the following: * string: indicating atomic element. * dictionary: Key as atomic attribute among (element, name) and value is the attribute value. * list, tuple, set, numpy.ndarray: core atoms index. #. in shell atoms: Can be any of the following: * string: indicating atomic element. * dictionary: Key as atomic attribute among (element, name) and value is the attribute value. * list, tuple, set, numpy.ndarray: in shell atoms index #. Lower distance limit of the coordination shell. #. Upper distance limit of the coordination shell. #. :math:`N_{min}` : minimum number of neighbours in the shell. #. :math:`N_{max}` : maximum number of neighbours in the shell. #. :math:`W_{i}` : weight contribution to the standard error, this is optional, if not given it is set automatically to 1.0. :: e.g. [ ('Ti','Ti', 2.5, 3.5, 5, 7.1, 1), ('Ni','Ti', 2.2, 3.1, 7.2, 9.7, 100), ...] [ ({'element':'Ti'},'Ti', 2.5, 3.5, 5, 7.1, 0.1), ...] [ ({'name':'au'},'Au', 2.5, 3.5, 4.1, 6.3), ...] [ ({'name':'Ni'},{'element':'Ti'}, 2.2, 3.1, 7, 9), ...] [ ('Ti',range(100,500), 2.2, 3.1, 7, 9), ...] [ ([0,10,11,15,1000],{'name':'Ti'}, 2.2, 3.1, 7, 9, 5), ...] """ if self.engine is None: self.__coordNumDef = coordNumDef return elif coordNumDef is None: coordNumDef = [] ########## check definitions, create coordination number data ########## self.__initialize_constraint_data() ALL_NAMES = self.engine.get_original_data("allNames") NAMES = self.engine.get_original_data("names") ALL_ELEMENTS = self.engine.get_original_data("allElements") ELEMENTS = self.engine.get_original_data("elements") NUMBER_OF_ATOMS = self.engine.get_original_data("numberOfAtoms") for CNDef in coordNumDef: assert isinstance(CNDef, (list, tuple)), LOGGER.error( "coordNumDef item must be a list or a tuple") if len(CNDef) == 6: coreDef, shellDef, lowerShell, upperShell, minCN, maxCN = CNDef weight = 1.0 elif len(CNDef) == 7: coreDef, shellDef, lowerShell, upperShell, minCN, maxCN, weight = CNDef else: raise LOGGER.error("coordNumDef item must have 6 or 7 items") # core definition if isinstance(coreDef, basestring): coreDef = str(coreDef) assert coreDef in ELEMENTS, LOGGER.error( "core atom definition '%s' is not a valid element" % coreDef) coreIndexes = [ idx for idx, el in enumerate(ALL_ELEMENTS) if el == coreDef ] elif isinstance(coreDef, dict): assert len(coreDef) == 1, LOGGER.error( "core atom definition dictionary must be of length 1") key, value = coreDef.keys()[0], coreDef.values()[0] if key is "name": assert value in NAMES, LOGGER.error( "core atom definition '%s' is not a valid name" % coreDef) coreIndexes = [ idx for idx, el in enumerate(ALL_NAMES) if el == coreDef ] elif key is "element": assert value in ELEMENTS, LOGGER.error( "core atom definition '%s' is not a valid element" % coreDef) coreIndexes = [ idx for idx, el in enumerate(ALL_ELEMENTS) if el == coreDef ] else: raise LOGGER.error( "core atom definition dictionary key must be either 'name' or 'element'" ) elif isinstance(coreDef, (list, tuple, set, np.ndarray)): coreIndexes = [] if isinstance(coreDef, np.ndarray): assert len(coreDef.shape) == 1, LOGGER.error( "core atom definition numpy.ndarray must be 1D") for c in coreDef: assert is_integer(c), LOGGER.error( "core atom definition index must be integer") c = INT_TYPE(c) assert c >= 0, LOGGER.error( "core atom definition index must be >=0") assert c < NUMBER_OF_ATOMS, LOGGER.error( "core atom definition index must be smaler than number of atoms in system" ) coreIndexes.append(c) # shell definition if isinstance(shellDef, basestring): shellDef = str(shellDef) assert shellDef in ELEMENTS, LOGGER.error( "core atom definition '%s' is not a valid element" % shellDef) shellIndexes = [ idx for idx, el in enumerate(ALL_ELEMENTS) if el == shellDef ] elif isinstance(shellDef, dict): assert len(shellDef) == 1, LOGGER.error( "core atom definition dictionary must be of length 1") key, value = shellDef.keys()[0], shellDef.values()[0] if key is "name": assert value in NAMES, LOGGER.error( "core atom definition '%s' is not a valid name" % shellDef) shellIndexes = [ idx for idx, el in enumerate(ALL_NAMES) if el == shellDef ] elif key is "element": assert value in ELEMENTS, LOGGER.error( "core atom definition '%s' is not a valid element" % shellDef) shellIndexes = [ idx for idx, el in enumerate(ALL_ELEMENTS) if el == shellDef ] else: raise LOGGER.error( "core atom definition dictionary key must be either 'name' or 'element'" ) elif isinstance(shellDef, (list, tuple, set, np.ndarray)): shellIndexes = [] if isinstance(shellDef, np.ndarray): assert len(shellDef.shape) == 1, LOGGER.error( "core atom definition numpy.ndarray must be 1D") for c in shellDef: assert is_integer(c), LOGGER.error( "core atom definition index must be integer") c = INT_TYPE(c) assert c >= 0, LOGGER.error( "core atom definition index must be >=0") assert c < NUMBER_OF_ATOMS, LOGGER.error( "core atom definition index must be smaler than number of atoms in system" ) shellIndexes.append(c) # lower and upper shells definition assert is_number(lowerShell), LOGGER.error( "Coordination number lower shell '%s' must be a number." % lowerShell) lowerShell = FLOAT_TYPE(lowerShell) assert lowerShell >= 0, LOGGER.error( "Coordination number lower shell '%s' must be a positive." % lowerShell) assert is_number(upperShell), LOGGER.error( "Coordination number upper shell '%s' must be a number." % key) upperShell = FLOAT_TYPE(upperShell) assert upperShell > lowerShell, LOGGER.error( "Coordination number lower shell '%s' must be smaller than upper shell %s" % (lowerShell, upperShell)) # minimum and maximum number of atoms definitions assert is_number(minCN), LOGGER.error( "Coordination number minimum atoms '%s' must be a number." % minCN) minCN = FLOAT_TYPE(minCN) assert minCN >= 0, LOGGER.error( "Coordination number minimim atoms '%s' must be >=0." % minCN) assert is_number(maxCN), LOGGER.error( "Coordination number maximum atoms '%s' must be a number." % key) maxCN = FLOAT_TYPE(maxCN) assert maxCN >= minCN, LOGGER.error( "Coordination number minimum atoms '%s' must be smaller than maximum atoms %s" % (minCN, maxCN)) # check weight assert is_number(weight), LOGGER.error( "Coordination number weight '%s' must be a number." % weight) weight = FLOAT_TYPE(weight) assert weight > 0, LOGGER.error( "Coordination number weight '%s' must be >0." % weight) # append coordination number data self.__coresIndexes.append(sorted(set(coreIndexes))) self.__shellsIndexes.append(sorted(set(shellIndexes))) self.__lowerShells.append(lowerShell) self.__upperShells.append(upperShell) self.__minAtoms.append(minCN) self.__maxAtoms.append(maxCN) #self.__coordNumData.append( FLOAT_TYPE(0) ) self.__coordNumData.append(None) self.__weights.append(weight) ########## set asCoreDefIdxs and inShellDefIdxs points ########## for _ in xrange(NUMBER_OF_ATOMS): self.__asCoreDefIdxs.append([]) self.__inShellDefIdxs.append([]) for defIdx, indexes in enumerate(self.__coresIndexes): self.__coresIndexes[defIdx] = np.array(indexes, dtype=INT_TYPE) for atIdx in indexes: self.__asCoreDefIdxs[atIdx].append(defIdx) for defIdx, indexes in enumerate(self.__shellsIndexes): self.__shellsIndexes[defIdx] = np.array(indexes, dtype=INT_TYPE) for atIdx in indexes: self.__inShellDefIdxs[atIdx].append(defIdx) for atIdx in xrange(NUMBER_OF_ATOMS): self.__asCoreDefIdxs[atIdx] = np.array(self.__asCoreDefIdxs[atIdx], dtype=INT_TYPE) self.__inShellDefIdxs[atIdx] = np.array( self.__inShellDefIdxs[atIdx], dtype=INT_TYPE) # set all to arrays #self.__coordNumData = np.array( self.__coordNumData, dtype=FLOAT_TYPE ) self.__weights = np.array(self.__weights, dtype=FLOAT_TYPE) self.__numberOfCores = np.array( [len(idxs) for idxs in self.__coresIndexes], dtype=FLOAT_TYPE) # set definition self.__coordNumDef = coordNumDef # dump to repository self._dump_to_repository({ '_AtomicCoordinationNumberConstraint__coordNumDef': self.__coordNumDef, '_AtomicCoordinationNumberConstraint__coordNumData': self.__coordNumData, '_AtomicCoordinationNumberConstraint__weights': self.__weights, '_AtomicCoordinationNumberConstraint__numberOfCores': self.__numberOfCores, '_AtomicCoordinationNumberConstraint__coresIndexes': self.__coresIndexes, '_AtomicCoordinationNumberConstraint__shellsIndexes': self.__shellsIndexes, '_AtomicCoordinationNumberConstraint__lowerShells': self.__lowerShells, '_AtomicCoordinationNumberConstraint__upperShells': self.__upperShells, '_AtomicCoordinationNumberConstraint__minAtoms': self.__minAtoms, '_AtomicCoordinationNumberConstraint__maxAtoms': self.__maxAtoms }) # reset constraint self.reset_constraint() # ADDED 2017-JAN-08
def add_angle(self, angle): """ Add a single angle to the list of constraint angles. All angles are in degrees. :Parameters: #. angle (list): The angle list of five items.\n #. Central atom index. #. Index of the left atom forming the angle (interchangeable with the right atom). #. Index of the right atom forming the angle (interchangeable with the left atom). #. Minimum lower limit or the minimum angle allowed in degrees which later will be converted to rad. #. Maximum upper limit or the maximum angle allowed in degrees which later will be converted to rad. """ assert self.engine is not None, LOGGER.error( "setting an angle is not allowed unless engine is defined.") NUMBER_OF_ATOMS = self.engine.get_original_data("numberOfAtoms") assert isinstance( angle, (list, set, tuple)), LOGGER.error("angle items must be lists") assert len(angle) == 5, LOGGER.error( "angle items must be lists of 5 items each") centralIdx, leftIdx, rightIdx, lower, upper = angle assert centralIdx < NUMBER_OF_ATOMS, LOGGER.error( "angle atom index must be smaller than maximum number of atoms") assert leftIdx < NUMBER_OF_ATOMS, LOGGER.error( "angle atom index must be smaller than maximum number of atoms") assert rightIdx < NUMBER_OF_ATOMS, LOGGER.error( "angle atom index must be smaller than maximum number of atoms") centralIdx = INT_TYPE(centralIdx) leftIdx = INT_TYPE(leftIdx) rightIdx = INT_TYPE(rightIdx) assert is_number(lower) lower = FLOAT_TYPE(lower) assert is_number(upper) upper = FLOAT_TYPE(upper) assert lower >= 0, LOGGER.error( "angle items lists fourth item must be positive") assert upper > lower, LOGGER.error( "angle items lists fourth item must be smaller than the fifth item" ) assert upper <= 180, LOGGER.error( "angle items lists fifth item must be smaller or equal to 180") lower *= FLOAT_TYPE(PI / FLOAT_TYPE(180.)) upper *= FLOAT_TYPE(PI / FLOAT_TYPE(180.)) # create central angle if not self.__angles.has_key(centralIdx): anglesCentral = { "left": [], "right": [], "centralMap": [], "otherMap": [] } else: anglesCentral = { "left": self.__angles[centralIdx]["left"], "right": self.__angles[centralIdx]["right"], "centralMap": self.__angles[centralIdx]["centralMap"], "otherMap": self.__angles[centralIdx]["otherMap"] } # create left angle if not self.__angles.has_key(leftIdx): anglesLeft = { "left": [], "right": [], "centralMap": [], "otherMap": [] } else: anglesLeft = { "left": self.__angles[leftIdx]["left"], "right": self.__angles[leftIdx]["right"], "centralMap": self.__angles[leftIdx]["centralMap"], "otherMap": self.__angles[leftIdx]["otherMap"] } # create right angle if not self.__angles.has_key(rightIdx): anglesRight = { "left": [], "right": [], "centralMap": [], "otherMap": [] } else: anglesRight = { "left": self.__angles[rightIdx]["left"], "right": self.__angles[rightIdx]["right"], "centralMap": self.__angles[rightIdx]["centralMap"], "otherMap": self.__angles[rightIdx]["otherMap"] } # check for re-defining setPos = ileft = iright = None if leftIdx in anglesCentral["left"] and rightIdx in anglesCentral[ "right"]: ileft = anglesCentral["left"].index(leftIdx) iright = anglesCentral["right"].index(rightIdx) elif leftIdx in anglesCentral["right"] and rightIdx in anglesCentral[ "left"]: ileft = anglesCentral["right"].index(leftIdx) iright = anglesCentral["left"].index(rightIdx) if (ileft is not None) and (ileft == iright): LOGGER.warn( "Angle definition for central atom index '%i' and interchangeable left '%i' atom and right '%i' atom is already defined. New angle limits [%.3f,%.3f] are set." % (centralIdx, leftIdx, rightIdx, lower, upper)) setPos = anglesCentral["centralMap"][ileft] # set angle if setPos is None: anglesCentral['left'].append(leftIdx) anglesCentral['right'].append(rightIdx) anglesCentral['centralMap'].append(len(self.__anglesList[0])) anglesLeft['otherMap'].append(len(self.__anglesList[0])) anglesRight['otherMap'].append(len(self.__anglesList[0])) self.__anglesList[0] = np.append(self.__anglesList[0], centralIdx) self.__anglesList[1] = np.append(self.__anglesList[1], leftIdx) self.__anglesList[2] = np.append(self.__anglesList[2], rightIdx) self.__anglesList[3] = np.append(self.__anglesList[3], lower) self.__anglesList[4] = np.append(self.__anglesList[4], upper) else: assert self.__anglesList[0][setPos] == centralIdx, LOOGER.error( "mismatched angles central atom '%s' and '%s'" % (elf.__anglesList[0][setPos], centralIdx)) assert sorted([leftIdx, rightIdx]) == sorted([ self.__anglesList[1][setPos], self.__anglesList[2][setPos] ]), LOOGER.error( "mismatched angles left and right at central atom '%s' and '%s'" % (elf.__anglesList[0][setPos], centralIdx)) self.__anglesList[3][setPos] = lower self.__anglesList[4][setPos] = upper self.__angles[centralIdx] = anglesCentral self.__angles[leftIdx] = anglesLeft self.__angles[rightIdx] = anglesRight # dump to repository if self.__dumpAngles: self._dump_to_repository({ '_BondsAngleConstraint__anglesList': self.__anglesList, '_BondsAngleConstraint__angles': self.__angles }) # reset constraint self.reset_constraint()
def add_bond(self, bond): """ Add a single bond to constraint's list of bonds. :Parameters: #. bond (list): The bond list of four items.\n #. First atom index forming the bond. #. Second atom index forming the bond. #. Lower limit or the minimum bond length allowed. #. Upper limit or the maximum bond length allowed. """ assert self.engine is not None, LOGGER.error( "setting a bond is not allowed unless engine is defined.") NUMBER_OF_ATOMS = self.engine.get_original_data("numberOfAtoms") assert isinstance( bond, (list, set, tuple)), LOGGER.error("bond items must be lists") assert len(bond) == 4, LOGGER.error( "bond items must be lists of 4 items each") idx1, idx2, lower, upper = bond assert is_integer(idx1), LOGGER.error( "bondsList items lists first item must be an integer") idx1 = INT_TYPE(idx1) assert is_integer(idx2), LOGGER.error( "bondsList items lists second item must be an integer") idx2 = INT_TYPE(idx2) assert idx1 < NUMBER_OF_ATOMS, LOGGER.error( "bond atom index must be smaller than maximum number of atoms") assert idx2 < NUMBER_OF_ATOMS, LOGGER.error( "bond atom index must be smaller than maximum number of atoms") assert idx1 >= 0, LOGGER.error("bond first item must be positive") assert idx2 >= 0, LOGGER.error("bond second item must be positive") assert idx1 != idx2, LOGGER.error( "bond first and second items can't be the same") assert is_number(lower), LOGGER.error( "bond third item must be a number") lower = FLOAT_TYPE(lower) assert is_number(upper), LOGGER.error( "bond fourth item must be a number") upper = FLOAT_TYPE(upper) assert lower >= 0, LOGGER.error("bond third item must be positive") assert upper > lower, LOGGER.error( "bond third item must be smaller than the fourth item") # create bonds if not self.__bonds.has_key(idx1): bondsIdx1 = {"indexes": [], "map": []} else: bondsIdx1 = { "indexes": self.__bonds[idx1]["indexes"], "map": self.__bonds[idx1]["map"] } if not self.__bonds.has_key(INT_TYPE(idx2)): bondsIdx2 = {"indexes": [], "map": []} else: bondsIdx2 = { "indexes": self.__bonds[idx2]["indexes"], "map": self.__bonds[idx2]["map"] } # set bond if idx2 in bondsIdx1["indexes"]: assert idx1 in bondsIdx2["indexes"], LOOGER.error( "mismatched bonds between atom '%s' and '%s'" % (idx1, idx2)) at2InAt1 = bondsIdx1["indexes"].index(idx2) at1InAt2 = bondsIdx2["indexes"].index(idx1) assert bondsIdx1["map"][at2InAt1] == bondsIdx2["map"][ at1InAt2], LOOGER.error( "bonded atoms '%s' and '%s' point to different defintions" % (idx1, idx2)) setPos = bondsIdx1["map"][at2InAt1] LOGGER.warn( "Bond between atom index '%i' and '%i' is already defined. New bond limits [%.3f,%.3f] will replace old bond limits [%.3f,%.3f]. " % (idx2, idx1, lower, upper, self.__bondsList[2][setPos], self.__bondsList[3][setPos])) self.__bondsList[0][setPos] = idx1 self.__bondsList[1][setPos] = idx2 self.__bondsList[2][setPos] = lower self.__bondsList[3][setPos] = upper else: bondsIdx1["map"].append(len(self.__bondsList[0])) bondsIdx2["map"].append(len(self.__bondsList[0])) bondsIdx1["indexes"].append(idx2) bondsIdx2["indexes"].append(idx1) self.__bondsList[0] = np.append(self.__bondsList[0], idx1) self.__bondsList[1] = np.append(self.__bondsList[1], idx2) self.__bondsList[2] = np.append(self.__bondsList[2], lower) self.__bondsList[3] = np.append(self.__bondsList[3], upper) self.__bonds[idx1] = bondsIdx1 self.__bonds[idx2] = bondsIdx2 # dump to repository if self.__dumpBonds: self._dump_to_repository({ '_BondConstraint__bondsList': self.__bondsList, '_BondConstraint__bonds': self.__bonds }) # reset constraint self.reset_constraint()