def __str__(self): ret_val = "CartesianRepresentation of molecule {}, in {}:\n".format( shortstr(self.molecule), self.units) for i, (x, y, z) in enumerate(grouper(3, self)): ret_val += indent( '{:>9} {:12.8f} {:12.8f} {:12.8f}\n'.format( '[{}-{}]'.format(3 * i, 3 * i + 2), x.value, y.value, z.value), 2) return ret_val
def __str__(self): ret_val = "CartesianRepresentation of molecule {}, in {}:\n".format(shortstr(self.molecule), self.units) for i, (x, y, z) in enumerate(grouper(3, self)): ret_val += indent( "{:>9} {:12.8f} {:12.8f} {:12.8f}\n".format( "[{}-{}]".format(3 * i, 3 * i + 2), x.value, y.value, z.value ), 2, ) return ret_val
def task_description(self): # TODO add some more helpful information to this, with an argument for verbosity level ret_val = "" basics = [ "Path", self.directory, "Input File Name", self.runner.input_file.split(os.sep)[-1], "Output File Name", self.runner.output_file.split(os.sep)[-1], ] extras = [ "Input Generator Class", self.input_generator.__class__.__name__, "Output Parser Class", self.output_parser.__class__.__name__, "Runner Class", self.runner.__class__.__name__, ] allstats = basics + extras lwidth = max(len(k) for k, v in grouper(2, allstats)) rwidth = max(len(v) for k, v in grouper(2, allstats)) props = [] for name, value in grouper(2, allstats): props.append(('{:.<'+str(lwidth)+'}{:.>'+str(rwidth)+'}').format( name, value )) return '\n'.join(props)
def coordinate_with_cartesian_displacements(self, base_coord, disps): disps = tuple(disps) if all(d == 0 for d in disps): return base_coord ret_val = self.displaced_coordinates.get((base_coord, disps), None) if ret_val is None: new_atoms = [] for atom, disp in zip(base_coord.atoms, grouper(3, disps)): new_atoms.append(atom.displaced(LightVector(disp)*self.delta)) ret_val = base_coord.copy_with_atoms(new_atoms) # Cache it for later to avoid gratuitous creation of displaced Molecule instances self.displaced_coordinates[(base_coord, disps)] = ret_val return ret_val
def value_for_displacements(self, pairs): # get the position vector xyzvect = LightVector([a.position for a in self.atoms]).ravel() # make pairs into a list in case it's a general iterator pairs = list(pairs) int_idxs = self.internal_indices_for_coordinates(*[pair[0] for pair in pairs]) for i, (__, ndeltas) in enumerate(pairs): xyzvect[int_idxs[i]] += self.b_tensor_finite_difference_delta * ndeltas return self.__class__.value_for_xyz( Matrix( list(grouper(3, xyzvect)) ) )
def iter_atom_coords(self, with_atom=False): for i, (a, b, c) in enumerate(grouper(3, self)): if with_atom: yield a, b, c, self.atoms[i] else: yield a, b, c
def transform_tensor(self, tensor, to_representation): """ """ self.freeze() to_representation.freeze() shape = (len(to_representation), ) * len(tensor.shape) ret_val = RepresentationDependentTensor( shape=shape, representation=to_representation) #--------------------------------------------------------------------------------# if isinstance(to_representation, CartesianRepresentation): if len(self) != len(to_representation): raise ValueError( "incompatible representation sizes ({} != {})".format( len(self), len(to_representation))) if tensor.representation is not self: raise ValueError( "representation {} can only transform a tensor whose representation attribute is the same " " as itself (tensor.representation was {} ".format( self, tensor.representation)) if self.molecule.is_linear(): raise NotImplementedError( "linear molecule cartesian-to-cartesian transformation is not" " yet implemented. Shouldn't be too hard...") if sanity_checking_enabled: #TODO use a recentered version of the 'from' representation if it is not centered pass # Make sure things are centered #if not self.molecule.is_centered(cartesian_representation=self): # raise ValueError("CartesianRepresentation objects transformed from and to" # " must be centered at the center of mass ({} was not)".format( # self # )) #elif not self.molecule.is_centered(cartesian_representation=to_representation): # raise ValueError("CartesianRepresentation objects transformed from and to" # " must be centered at the center of mass ({} was not)".format( # to_representation # )) #----------------------------------------# old = self.value new = to_representation.value unitconv = self.units.to(to_representation.units) oldmat = Matrix(old).reshape((len(self) / 3, 3)) newmat = Matrix(new).reshape((len(self) / 3, 3)) #----------------------------------------# # Check to see if the molecule is planar. If so, append the cross product of any # two atoms' positions not colinear with the origin if any(norm(oldmat[:dir].view(Vector)) < 1e-12 \ or norm(newmat[:dir].view(Vector)) < 1e-12 \ for dir in [X, Y, Z]): first_atom = None # TODO unit concious cutoff (e.g. this would fail if the units of the position matrix were meters) nonorigin_cutoff = 1e-2 for i, v in enumerate(grouper(3, old)): if norm(LightVector(v)) > nonorigin_cutoff: first_atom = i break second_atom = None for i in range(first_atom + 1, len(self) // 3): #TODO make sure this works with Molecule.linear_cutoff if norm(oldmat[i]) > nonorigin_cutoff and norm(newmat[i]) > nonorigin_cutoff \ and abs(angle_between_vectors(oldmat[first_atom], oldmat[i])) > 1e-5 \ and abs(angle_between_vectors(newmat[first_atom], newmat[i])) > 1e-5: second_atom = i break oldmat = Matrix( list(oldmat.rows) + [cross(oldmat[first_atom], oldmat[second_atom])]) newmat = Matrix( list(newmat.rows) + [cross(newmat[first_atom], newmat[second_atom])]) #----------------------------------------# rot_mat = newmat.T * np.linalg.pinv(oldmat.T) # Divide by unit conversion because the representation dependent tensor # is [some units] *per* representation units rot_mat /= unitconv trans_mat = Matrix(shape=(len(self), len(self))) for idx in xrange(len(self) // 3): off = idx * 3 trans_mat[off:off + 3, off:off + 3] = rot_mat order = len(tensor.shape) # This is basically impossible to read what I'm doing here, but work it out # looking at the numpy.einsum documentation and you'll see that this is correct. # Basically, we want to contract the row indices of the transformation matrix # with each axis of the tensor to be transformed. einsumargs = sum( ([trans_mat, [2 * i, 2 * i + 1]] for i in xrange(order)), []) einsumargs += [ tensor, [i for i in xrange(2 * order) if i % 2 != 0] ] einsumargs += [[i for i in xrange(2 * order) if i % 2 == 0]] np.einsum(*einsumargs, out=ret_val) return ret_val #--------------------------------------------------------------------------------# elif isinstance(to_representation, InternalRepresentation): if len(tensor.shape) == 1: A = to_representation.a_matrix ret_val[...] = A.T * tensor.view(Vector) else: raise NotImplementedError("use transform_forcefield instead") return ret_val #--------------------------------------------------------------------------------# elif isinstance(to_representation, NormalRepresentation): B = to_representation.b_matrix ret_val[...] = tensor.linearly_transformed(B) return ret_val else: raise NotImplementedError( "Transformation of arbitrary tensors from representation of type '{}' to " " representations of type '{}' is not implemented.".format( self.__class__.__name__, to_representation.__class__.__name__))
def transform_tensor(self, tensor, to_representation): """ """ self.freeze() to_representation.freeze() shape = (len(to_representation),) * len(tensor.shape) ret_val = RepresentationDependentTensor(shape=shape, representation=to_representation) # --------------------------------------------------------------------------------# if isinstance(to_representation, CartesianRepresentation): if len(self) != len(to_representation): raise ValueError( "incompatible representation sizes ({} != {})".format(len(self), len(to_representation)) ) if tensor.representation is not self: raise ValueError( "representation {} can only transform a tensor whose representation attribute is the same " " as itself (tensor.representation was {} ".format(self, tensor.representation) ) if self.molecule.is_linear(): raise NotImplementedError( "linear molecule cartesian-to-cartesian transformation is not" " yet implemented. Shouldn't be too hard..." ) if sanity_checking_enabled: # TODO use a recentered version of the 'from' representation if it is not centered pass # Make sure things are centered # if not self.molecule.is_centered(cartesian_representation=self): # raise ValueError("CartesianRepresentation objects transformed from and to" # " must be centered at the center of mass ({} was not)".format( # self # )) # elif not self.molecule.is_centered(cartesian_representation=to_representation): # raise ValueError("CartesianRepresentation objects transformed from and to" # " must be centered at the center of mass ({} was not)".format( # to_representation # )) # ----------------------------------------# old = self.value new = to_representation.value unitconv = self.units.to(to_representation.units) oldmat = Matrix(old).reshape((len(self) / 3, 3)) newmat = Matrix(new).reshape((len(self) / 3, 3)) # ----------------------------------------# # Check to see if the molecule is planar. If so, append the cross product of any # two atoms' positions not colinear with the origin if any( norm(oldmat[:dir].view(Vector)) < 1e-12 or norm(newmat[:dir].view(Vector)) < 1e-12 for dir in [X, Y, Z] ): first_atom = None # TODO unit concious cutoff (e.g. this would fail if the units of the position matrix were meters) nonorigin_cutoff = 1e-2 for i, v in enumerate(grouper(3, old)): if norm(LightVector(v)) > nonorigin_cutoff: first_atom = i break second_atom = None for i in range(first_atom + 1, len(self) // 3): # TODO make sure this works with Molecule.linear_cutoff if ( norm(oldmat[i]) > nonorigin_cutoff and norm(newmat[i]) > nonorigin_cutoff and abs(angle_between_vectors(oldmat[first_atom], oldmat[i])) > 1e-5 and abs(angle_between_vectors(newmat[first_atom], newmat[i])) > 1e-5 ): second_atom = i break oldmat = Matrix(list(oldmat.rows) + [cross(oldmat[first_atom], oldmat[second_atom])]) newmat = Matrix(list(newmat.rows) + [cross(newmat[first_atom], newmat[second_atom])]) # ----------------------------------------# rot_mat = newmat.T * np.linalg.pinv(oldmat.T) # Divide by unit conversion because the representation dependent tensor # is [some units] *per* representation units rot_mat /= unitconv trans_mat = Matrix(shape=(len(self), len(self))) for idx in xrange(len(self) // 3): off = idx * 3 trans_mat[off : off + 3, off : off + 3] = rot_mat order = len(tensor.shape) # This is basically impossible to read what I'm doing here, but work it out # looking at the numpy.einsum documentation and you'll see that this is correct. # Basically, we want to contract the row indices of the transformation matrix # with each axis of the tensor to be transformed. einsumargs = sum(([trans_mat, [2 * i, 2 * i + 1]] for i in xrange(order)), []) einsumargs += [tensor, [i for i in xrange(2 * order) if i % 2 != 0]] einsumargs += [[i for i in xrange(2 * order) if i % 2 == 0]] np.einsum(*einsumargs, out=ret_val) return ret_val # --------------------------------------------------------------------------------# elif isinstance(to_representation, InternalRepresentation): if len(tensor.shape) == 1: A = to_representation.a_matrix ret_val[...] = A.T * tensor.view(Vector) else: raise NotImplementedError("use transform_forcefield instead") return ret_val # --------------------------------------------------------------------------------# elif isinstance(to_representation, NormalRepresentation): B = to_representation.b_matrix ret_val[...] = tensor.linearly_transformed(B) return ret_val else: raise NotImplementedError( "Transformation of arbitrary tensors from representation of type '{}' to " " representations of type '{}' is not implemented.".format( self.__class__.__name__, to_representation.__class__.__name__ ) )