def _validate_initialization(self): """ Make sure we have all of the things we need. Subclasses may override this, but all subclass implementations should call `super()._validate_initialization()` at the end of this method (if they do not, a SuperCallMissingError is raised). .. note:: This method should not make any modifications to the `Coordinate` itself, since it is only called when `sanity_checking_enabled` is `True`. Use `_finilize_initialization()` for this purpose. """ #--------------------------------------------------------------------------------# # Type 1 coordinate: non-orphaned if not self.is_orphaned(): # Enforce the requirement that all atoms of a type 1 coordinate be non-orphaned if any(a.is_orphaned() for a in self.atoms): raise ValueError( "non-orphaned Coordinates must be constructed entirely of non-orphaned" " atoms, but atom '{}' on coordinate '{}' is orphaned.".format( first(a for a in self.atoms if a.is_orphaned()), self )) # Enforce the requirement that all atoms of a type 1 coordinate should have # the exact same parent molecule as the parent representation's molecule if any(a.parent is not self.molecule for a in self.atoms): raise ValueError( "all atoms in a non-orphaned coordinate must have the same parent" " molecule (i.e. the exact same instance) as the coordinate, but" " atom '{}' does not have the same parent molecule as Coordinate" " '{}' ({} != {}).".format( first(a for a in self.atoms if a.parent is not self.molecule), self, id(first(a for a in self.atoms if a.parent is not self.molecule).parent), id(self.molecule) )) #--------------------------------------------------------------------------------# # Type 2 coordinate: orphaned with non-orphaned atoms elif any(not a.is_orphaned() for a in self.atoms): # Enforce the requirement that if one atom is non-orphaned, all of them must be. if any(a.is_orphaned() for a in self.atoms): raise ValueError( "orphaned coordinates composed of non-orphaned atoms must have all" " atoms non-orphaned, but atom '{}' on coordinate '{}' is orphaned.".format( first(a for a in self.atoms if a.is_orphaned()), self )) # Enforce the requirement that all atoms be on the same molecule if any(a.parent is not self.molecule for a in self.atoms): raise ValueError( "all atoms in a orphaned coordinate composed of non-orphaned atoms" "must have the same parent molecule (i.e. the exact same instance)," " but atom '{}' does not have the same parent molecule as '{}' for" " coordinate '{}'.".format( first(a for a in self.atoms if a.parent is not self.molecule), self.atoms[0], self )) #--------------------------------------------------------------------------------# # Type 3 coordinate: orphaned coordinate with all orphaned atoms, base_analog not None elif self.base_analog is not None: # Enforce the requirement that the base_analog's atoms exactly match the # coordinate's atoms' respective base_atom attributes if len(self.base_analog.atoms) != len(self.atoms): raise ValueError("orphaned coordinates with a base_analog must be composed" " of the same number of Atoms as their base_analog (got {}" " atoms for derived coordinate, {} atoms for base" " coordinate)".format( len(self.atoms), len(self.base_analog.atoms))) for my_atom, base_atom in zip(self.atoms, self.base_analog.atoms): if my_atom.base_atom is not base_atom: raise ValueError("base_atom of displaced Atom must correspond to the" " analogous atom in the displaced coordinate's" " base_analog's atoms, but '{}' is not the same as" " '{}' for coordinate '{}'".format( my_atom.base_atom, base_atom, short_str(self) )) #--------------------------------------------------------------------------------# # Type 4 coordinate: orphaned coordinate with all orphaned atoms, base_analog is None else: # Enforce the requirement that all atom's eventual parent molecules should be # the same. mol_found = None for atom in self.atoms: iter_atom = atom visited = [] while iter_atom is not None and iter_atom.parent is None: if iter_atom in visited: raise ValueError("circular base_atom dependency for '{}'".format(atom)) iter_atom = iter_atom.base_atom if iter_atom is None: raise ValueError("all orphaned atoms on an orphaned coordinate must have" " a `base_atom` that is not orphaned somewhere down the" " line. '{}' in coordinate '{}' is not like that.".format( atom, short_str(self) )) if mol_found is None: mol_found = iter_atom.parent elif mol_found is not iter_atom.parent: raise ValueError("all orphaned atoms on an orphaned coordinate must have" " the same parent molecule of the first non-orphaned coordinate" " in the `base_atom` hierarchy. The molecule obtained in this" " manner from '{}' in coordinate '{}' is not the same" " as the molecule obtained in this manner from '{}'.".format( atom, self.atoms[0], short_str(self) )) #--------------------------------------------------------------------------------# self._Coordinate_validate_called = True
def b_tensor_element_reindexed(current_coord, *coords): coords = tuple(coords) if not isinstance(coords[0], tuple) else coords[0] internal = coords[0] carts = coords[1:] #========================================# if internal.is_orphaned(): #----------------------------------------# # avoid the gratuitous creation of coordinates key = (type(internal), tuple(internal.atoms)) internal = SimpleInternalCoordinate.created_coords.get(key, None) if internal is None: SimpleInternalCoordinate.created_coords[key] = internal = coords[0] # TODO UNITS FOR CREATED COORDINATES!!! #----------------------------------------# # Check to make sure their all integers... if sanity_checking_enabled: if any(isinstance(cart, CartesianCoordinate) for cart in carts): cart = first(carts, lambda c: isinstance(c, CartesianCoordinate)) raise ValueError("ambiguous B tensor element retrieval for orphaned internal" " coordinate '{}' using cartesian coordinate '{}'".format( short_str(internal), short_str(cart) )) if not all(isinstance(cart, int) for cart in carts): raise ValueError( "b_tensor elements may not be retrieved for " " orphaned coordinates by anything but integer indices" ) #----------------------------------------# # get the offset indices # what we have now is integers in current_coord's internal indexing scheme new_idxs = current_coord.molecule_indices_for(carts) # This method uses the molecular indexing scheme. This is why we needed to # do the above index translation. return internal.b_tensor_element(*new_idxs) #========================================# else: # internal is not orphaned #----------------------------------------# # Tidy things up and convert internal indices # to molecular indices if sanity_checking_enabled: if any(isinstance(cart, CartesianCoordinate) for cart in carts) \ and not all(isinstance(cart, CartesianCoordinate) for cart in carts): raise ValueError("mixing of CartesianCoordinates and integers in b tensor" " element retrieval is confusing and thus no longer allowed." " coordinates were ('{}')".format( "', '".join(str(c) for c in carts) )) if all(isinstance(c, int) for c in carts): # These are integers in current_coord's internal scheme. Translate them to # the molecular indexing scheme. carts = current_coord.molecule_indices_for(carts) elif type_checking_enabled: if not all(isinstance(cart, CartesianCoordinate) for cart in carts): raise TypeError("arguments beyond the first two to b_tensor_element_reindexed" " must be either all ints or all CartesianCoordinates." " coordinates were ('{}')".format( "', '".join(str(c) for c in carts) )) #----------------------------------------# # This method uses the molecular indexing scheme. This is why we needed to # do the above index translation. return internal.b_tensor_element(*carts)
def _validate_initialization(self): """ Make sure we have all of the things we need. Subclasses may override this, but all subclass implementations should call `super()._validate_initialization()` at the end of this method (if they do not, a SuperCallMissingError is raised). .. note:: This method should not make any modifications to the `Coordinate` itself, since it is only called when `sanity_checking_enabled` is `True`. Use `_finilize_initialization()` for this purpose. """ #--------------------------------------------------------------------------------# # Type 1 coordinate: non-orphaned if not self.is_orphaned(): # Enforce the requirement that all atoms of a type 1 coordinate be non-orphaned if any(a.is_orphaned() for a in self.atoms): raise ValueError( "non-orphaned Coordinates must be constructed entirely of non-orphaned" " atoms, but atom '{}' on coordinate '{}' is orphaned.". format(first(a for a in self.atoms if a.is_orphaned()), self)) # Enforce the requirement that all atoms of a type 1 coordinate should have # the exact same parent molecule as the parent representation's molecule if any(a.parent is not self.molecule for a in self.atoms): raise ValueError( "all atoms in a non-orphaned coordinate must have the same parent" " molecule (i.e. the exact same instance) as the coordinate, but" " atom '{}' does not have the same parent molecule as Coordinate" " '{}' ({} != {}).".format( first(a for a in self.atoms if a.parent is not self.molecule), self, id( first(a for a in self.atoms if a.parent is not self.molecule).parent), id(self.molecule))) #--------------------------------------------------------------------------------# # Type 2 coordinate: orphaned with non-orphaned atoms elif any(not a.is_orphaned() for a in self.atoms): # Enforce the requirement that if one atom is non-orphaned, all of them must be. if any(a.is_orphaned() for a in self.atoms): raise ValueError( "orphaned coordinates composed of non-orphaned atoms must have all" " atoms non-orphaned, but atom '{}' on coordinate '{}' is orphaned." .format(first(a for a in self.atoms if a.is_orphaned()), self)) # Enforce the requirement that all atoms be on the same molecule if any(a.parent is not self.molecule for a in self.atoms): raise ValueError( "all atoms in a orphaned coordinate composed of non-orphaned atoms" "must have the same parent molecule (i.e. the exact same instance)," " but atom '{}' does not have the same parent molecule as '{}' for" " coordinate '{}'.".format( first(a for a in self.atoms if a.parent is not self.molecule), self.atoms[0], self)) #--------------------------------------------------------------------------------# # Type 3 coordinate: orphaned coordinate with all orphaned atoms, base_analog not None elif self.base_analog is not None: # Enforce the requirement that the base_analog's atoms exactly match the # coordinate's atoms' respective base_atom attributes if len(self.base_analog.atoms) != len(self.atoms): raise ValueError( "orphaned coordinates with a base_analog must be composed" " of the same number of Atoms as their base_analog (got {}" " atoms for derived coordinate, {} atoms for base" " coordinate)".format(len(self.atoms), len(self.base_analog.atoms))) for my_atom, base_atom in zip(self.atoms, self.base_analog.atoms): if my_atom.base_atom is not base_atom: raise ValueError( "base_atom of displaced Atom must correspond to the" " analogous atom in the displaced coordinate's" " base_analog's atoms, but '{}' is not the same as" " '{}' for coordinate '{}'".format( my_atom.base_atom, base_atom, short_str(self))) #--------------------------------------------------------------------------------# # Type 4 coordinate: orphaned coordinate with all orphaned atoms, base_analog is None else: # Enforce the requirement that all atom's eventual parent molecules should be # the same. mol_found = None for atom in self.atoms: iter_atom = atom visited = [] while iter_atom is not None and iter_atom.parent is None: if iter_atom in visited: raise ValueError( "circular base_atom dependency for '{}'".format( atom)) iter_atom = iter_atom.base_atom if iter_atom is None: raise ValueError( "all orphaned atoms on an orphaned coordinate must have" " a `base_atom` that is not orphaned somewhere down the" " line. '{}' in coordinate '{}' is not like that.". format(atom, short_str(self))) if mol_found is None: mol_found = iter_atom.parent elif mol_found is not iter_atom.parent: raise ValueError( "all orphaned atoms on an orphaned coordinate must have" " the same parent molecule of the first non-orphaned coordinate" " in the `base_atom` hierarchy. The molecule obtained in this" " manner from '{}' in coordinate '{}' is not the same" " as the molecule obtained in this manner from '{}'.". format(atom, self.atoms[0], short_str(self))) #--------------------------------------------------------------------------------# self._Coordinate_validate_called = True
def b_tensor_element_reindexed(current_coord, *coords): coords = tuple(coords) if not isinstance(coords[0], tuple) else coords[0] internal = coords[0] carts = coords[1:] #========================================# if internal.is_orphaned(): #----------------------------------------# # avoid the gratuitous creation of coordinates key = (type(internal), tuple(internal.atoms)) internal = SimpleInternalCoordinate.created_coords.get(key, None) if internal is None: SimpleInternalCoordinate.created_coords[ key] = internal = coords[0] # TODO UNITS FOR CREATED COORDINATES!!! #----------------------------------------# # Check to make sure their all integers... if sanity_checking_enabled: if any( isinstance(cart, CartesianCoordinate) for cart in carts): cart = first(carts, lambda c: isinstance(c, CartesianCoordinate)) raise ValueError( "ambiguous B tensor element retrieval for orphaned internal" " coordinate '{}' using cartesian coordinate '{}'". format(short_str(internal), short_str(cart))) if not all(isinstance(cart, int) for cart in carts): raise ValueError( "b_tensor elements may not be retrieved for " " orphaned coordinates by anything but integer indices" ) #----------------------------------------# # get the offset indices # what we have now is integers in current_coord's internal indexing scheme new_idxs = current_coord.molecule_indices_for(carts) # This method uses the molecular indexing scheme. This is why we needed to # do the above index translation. return internal.b_tensor_element(*new_idxs) #========================================# else: # internal is not orphaned #----------------------------------------# # Tidy things up and convert internal indices # to molecular indices if sanity_checking_enabled: if any(isinstance(cart, CartesianCoordinate) for cart in carts) \ and not all(isinstance(cart, CartesianCoordinate) for cart in carts): raise ValueError( "mixing of CartesianCoordinates and integers in b tensor" " element retrieval is confusing and thus no longer allowed." " coordinates were ('{}')".format("', '".join( str(c) for c in carts))) if all(isinstance(c, int) for c in carts): # These are integers in current_coord's internal scheme. Translate them to # the molecular indexing scheme. carts = current_coord.molecule_indices_for(carts) elif type_checking_enabled: if not all( isinstance(cart, CartesianCoordinate) for cart in carts): raise TypeError( "arguments beyond the first two to b_tensor_element_reindexed" " must be either all ints or all CartesianCoordinates." " coordinates were ('{}')".format("', '".join( str(c) for c in carts))) #----------------------------------------# # This method uses the molecular indexing scheme. This is why we needed to # do the above index translation. return internal.b_tensor_element(*carts)