def build_column(self, pore, z, theta, correlation=True, var=0, correlation_length=0, pd=0, random_shift=True): """ Place a column at angle theta on xy plane with respect to a pore center :param pore: pore number (0 : npores - 1) :param z: mean z-positions of monomers in column :param theta: angle, with respect to pore center where column should be placed (degrees) :param correlation: adjust z positions so there is a correlation length :param var: variance in multivariate normal distribution used to make correlated points :param correlation_length: length for which correlation between stacked monomers to persist :param pd: Angle of wedge created between vertically adjacent monomers. Defined by angle between vectors extending from pore center to monomer head groups. :param random_shift: if True, randomly shift columns in z-direction by choosing a displacement from a uniform \ distribution bounded by (0, d), where d is the vertical distance between stacked monomers :param seed: random seed if you want to reproduce randomly displaced structures :type pore: int :type z: np.array :type theta: float :type correlation: bool :type var: float :type correlation_length: float :type pd: float :type random_shift: bool """ if correlation: z = z_correlation(z, correlation_length, v=var) if random_shift: dbwl = z[1] - z[0] # distance between stacked monomers z += np.random.uniform(0, dbwl) elif random_shift: dbwl = z[1] - z[0] # distance between stacked monomers z += np.random.uniform(0, dbwl) displaced_theta = pd natoms = self.LC_positions.shape[0] # number of atoms including ions pos = np.copy(self.LC_positions) pos[:, 0] += self.pore_radius displaced_pos = np.copy(pos) pos = transform.rotate_coords_z(pos, theta) displaced_pos = transform.rotate_coords_z(displaced_pos, theta + displaced_theta) column = np.zeros([z.size * natoms, 3]) before = np.array([0, 0, 0]) for l in range(z.size): if l % 2 == 0: column[l * natoms:(l + 1) * natoms, :] = transform.translate(pos, before, np.array([0, 0, z[l]])) else: column[l * natoms:(l + 1) * natoms, :] = transform.translate(displaced_pos, before, np.array([0, 0, z[l]])) self.names += self.LC_names self.all_residues += self.LC_residues column = transform.translate(column, before, [self.pore_centers[pore, 0], self.pore_centers[pore, 1], 0]) self.xyz = np.concatenate((self.xyz, column))
def place_solute(self, solute, placement_point, random=False, freeze=False, rem=.5): """ Place solute at desired point and energy minimze the system :param solute: name of solute object (str) :param placement_point: point to place solute (np.array([3]) :param random: place solute at random point in box (bool) :param freeze: freeze all atoms outside rem during energy minimization (bool) :param rem: radius from placement_point within which atoms will NOT be frozen (float, nm) :return: """ # p = placement_point - (solute.com - solute.xyz[0, 0, :]) # want to move com to placement point #solute_positions = random_orientation(solute.xyz[0, ...], solute.xyz[0, 0, :] - solute.xyz[0, 1, :], placement_point) # to be fixed in THE FUTURE solute_positions = transform.translate( solute.xyz[0, :, :], solute.com, placement_point) # translate solute to placement point self.positions = np.concatenate( (self.positions, solute_positions)) # add to array of positions self.residues += solute.residues # add solute residues to list of all residues self.names += solute.names # add solute atom names to list of all names self.top.add_residue(solute, write=True) # add 1 solute to topology # write new .gro file file_rw.write_gro_pos(self.positions, self.intermediate_fname, box=self.box_gromacs, ids=self.names, res=self.residues) if freeze: self.freeze_ndx(solute_placement_point=placement_point, res=solute.resname) nrg = self.energy_minimize(self.em_steps, freeze=freeze) if nrg >= 0: self.revert(solute) if random: self.place_solute_random(solute) else: #self.remove_water(placement_point, 3) self.place_solute(solute, placement_point, freeze=False)
def translate_to_origin(self): """ Translate molecule to the origin using the ref_atom_index attribute of LC """ self.LC_positions = transform.translate( self.LC_positions, self.LC_positions[self.ref_atom_index, :], [0, 0, 0])
def build_column(self, pore, z, theta, correlation=True, var=0, correlation_length=0, pd=0, random_shift=True): """ Place a column at angle theta on xy plane with respect to a pore center :param pore: pore number (0 : npores - 1) :param z: mean z-positions of monomers in column :param theta: angle, with respect to pore center where column should be placed :param correlation: adjust z positions so there is a correlation length :param var: variance in multivariate normal distribution used to make correlated points :param correlation_length: length for which correlation between stacked monomers to persist :param pd: Specify a nonzero value to make a parallel displaced configuration. The distance here (in nm) will\ be the distance between the center of masses of two vertically stacked monomers. :param random_shift: if True, randomly shift columns in z-direction by choosing a displacement from a uniform \ distribution bounded by (0, d), where d is the vertical distance between stacked monomers :type pore: int :type z: np.array :type theta: float :type correlation: bool :type var: float :type correlation_length: float :type pd: float :type random_shift: bool """ if correlation: z = z_correlation(z, correlation_length, v=var) if random_shift: dbwl = z[1] - z[0] # distance between stacked monomers z += np.random.uniform(0, dbwl) elif random_shift: dbwl = z[1] - z[0] # distance between stacked monomers z += np.random.uniform(0, dbwl) displaced_theta = (180 / np.pi) * (2 * np.arcsin(pd / (2 * self.pore_radius))) natoms = self.LC_positions.shape[0] # number of atoms including ions pos = np.copy(self.LC_positions) pos[:, 0] += self.pore_radius displaced_pos = np.copy(pos) pos = transform.rotate_coords_z(pos, theta) displaced_pos = transform.rotate_coords_z(displaced_pos, theta + displaced_theta) column = np.zeros([z.size * natoms, 3]) before = np.array([0, 0, 0]) for l in range(z.size): if l % 2 == 0: column[l * natoms:(l + 1) * natoms, :] = transform.translate( pos, before, np.array([0, 0, z[l]])) else: column[l * natoms:(l + 1) * natoms, :] = transform.translate( displaced_pos, before, np.array([0, 0, z[l]])) self.names += self.LC_names self.all_residues += self.LC_residues column = transform.translate( column, before, [self.pore_centers[pore, 0], self.pore_centers[pore, 1], 0]) self.xyz = np.concatenate((self.xyz, column))
def placement(z, pts, box): """ :param z: z location where solute should be placed :param pts: points which run through the pore :return: location to place solute """ # check if point is already in the spline if z in pts[:, 2]: ndx = np.where(pts[:, 2] == z)[0][0] return pts[ndx, :] # otherwise interpolate between closest spline points else: v = np.zeros([4, 2]) # vertices of unitcell box v[0, :] = [0, 0] v[1, :] = [box[0, 0], 0] v[3, :] = [box[1, 0], box[1, 1]] v[2, :] = v[3, :] + [box[0, 0], 0] center = [np.mean(v[:, 0]), np.mean(v[:, 1]), 0] # geometric center of box bounds = path.Path(v) # create a path tracing the vertices, v angle = np.arccos(box[1, 1] / box[0, 0]) # angle of monoclinic box if box[1, 0] < 0: # the case of an obtuse angle angle += np.pi / 2 m = (v[3, 1] - v[0, 1]) / ( v[3, 0] - v[0, 0] ) # slope from points connecting first and fourth vertices # shift = transform.translate(z, before, center) # # put_in_box(pt, box[0, 0], box[1, 1], m, angle) # find z positions, in between which solute will be placed lower = 0 while pts[lower, 2] < z: lower += 1 upper = pts.shape[0] - 1 while pts[upper, 2] > z: upper -= 1 limits = np.zeros([2, 3]) limits[0, :] = pts[lower, :] limits[1, :] = pts[upper, :] shift = transform.translate( limits, limits[0, :], center) # shift limits to geometric center of unit cell shift[:, 2] = [limits[0, 2], limits[1, 2]] # keep z positions the same for i in range( shift.shape[0] ): # check if the points are within the bounds of the unitcell if not bounds.contains_point(shift[i, :2]): shift[i, :] = put_in_box(shift[i, :], box[0, 0], box[1, 1], m, angle) # Use parametric representation of line between upper and lower points to find the xy value where z is satsified v = shift[1, :] - shift[0, :] # direction vector t = (z - shift[0, 2]) / v[2] # solve for t since we know z x = shift[0, 0] + t * v[0] y = shift[0, 1] + t * v[1] place = np.zeros([1, 3]) place[0, :] = [x, y, 0] place = transform.translate(place, center, limits[0, :]) # put xy coordinate back place[0, 2] = z if not bounds.contains_point( place[0, :]): # make sure everything is in the box again place[0, :] = put_in_box(place[0, :], box[0, 0], box[1, 1], m, angle) return place[0, :]
def trace_pores(pos, box, layers): """ Find the line which traces through the center of the pores :param pos: positions of atoms used to define pore location (args.ref) [natoms, 3] :param box: xy box vectors, [2, 2], mdtraj format :param layers: number of layers :return: points which trace the pore center """ atoms_p_pore = int(pos.shape[0] / 4) # atoms in each pore atoms_p_layer = int(atoms_p_pore / layers) # atom per layer v = np.zeros([4, 2]) # vertices of unitcell box v[0, :] = [0, 0] v[1, :] = [box[0, 0], 0] v[3, :] = [box[1, 0], box[1, 1]] v[2, :] = v[3, :] + [box[0, 0], 0] center = [np.mean(v[:, 0]), np.mean(v[:, 1]), 0] # geometric center of box bounds = path.Path(v) # create a path tracing the vertices, v angle = np.arccos(box[1, 1] / box[0, 0]) # angle of monoclinic box if box[1, 0] < 0: # the case of an obtuse angle angle += np.pi / 2 m = (v[3, 1] - v[0, 1]) / ( v[3, 0] - v[0, 0] ) # slope from points connecting first and third vertices centers = np.zeros([4 * layers, 3]) for p in range(4): pore = pos[ p * atoms_p_pore:(p + 1) * atoms_p_pore, :] # coordinates for atoms belonging to a single pore for l in range(layers): before = pore[ l * atoms_p_layer, :] # choose the first atom as a reference shift = transform.translate( pore[l * atoms_p_layer:(l + 1) * atoms_p_layer, :], before, center) # shift everything to towards the center for i in range( shift.shape[0] ): # check if the points are within the bounds of the unitcell if not bounds.contains_point(shift[i, :2]): shift[i, :] = put_in_box( shift[i, :], box[0, 0], box[1, 1], m, angle) # if its not in the unitcell, shift it so it is c = np.zeros([1, 3]) c[0, :] = [ np.mean(shift[:, 0]), np.mean(shift[:, 1]), np.mean(shift[:, 2]) ] # geometric center of reference atoms in this layer centers[p * layers + l, :] = transform.translate( c, center, before) # move everything back to where it was if not bounds.contains_point(centers[ p * layers, :]): # make sure everything is in the box again centers[p * layers + l, :] = put_in_box( centers[p * layers + l, :], box[0, 0], box[1, 1], m, angle) return centers
def trace_pores(pos, box, npoints, npores=4, progress=True): """ Find the line which traces through the center of the pores :param pos: positions of atoms used to define pore location (args.ref) [natoms, 3] :param box: xy box vectors, [2, 2], mdtraj format :param npoints: number of points for spline in each pore :param npores: number of pores in unit cell (assumed that atoms are number sequentially by pore. i.e. pore 1 atom numbers all precede those in pore 2) :param progress: set to True if you want a progress bar to be shown :return: points which trace the pore center """ single_frame = False if np.shape(pos.shape)[0] == 2: pos = pos[np.newaxis, ...] # add a new axis if we are looking at a single frame box = box[np.newaxis, ...] single_frame = True nframes = pos.shape[0] atoms_p_pore = int(pos.shape[1] / npores) # atoms in each pore v = np.zeros([nframes, 4, 2]) # vertices of unitcell box bounds = [] v[:, 0, :] = [0, 0] v[:, 1, 0] = box[:, 0, 0] v[:, 3, :] = np.vstack((box[:, 1, 0], box[:, 1, 1])).T v[:, 2, :] = v[:, 3, :] + np.vstack((box[:, 0, 0], np.zeros([nframes]))).T center = np.vstack((np.mean(v[..., 0], axis=1), np.mean(v[..., 1], axis=1), np.zeros(nframes))).T for t in range(nframes): bounds.append(mplPath.Path( v[t, ...])) # create a path tracing the vertices, v angle = np.arcsin( box[:, 1, 1] / box[:, 0, 0] ) # specific to case where magnitude of x and y box lengths are equal angle = np.where(box[:, 1, 0] < 0, angle + np.pi / 2, angle) # haven't tested this well yet m = (v[:, 3, 1] - v[:, 0, 1]) / ( v[:, 3, 0] - v[:, 0, 0] ) # slope from points connecting first and third vertices centers = np.zeros([nframes, npores, npoints, 3]) bin_centers = np.zeros([nframes, npores, npoints]) for t in tqdm.tqdm(range(nframes), disable=(not progress)): for p in range(npores): pore = pos[ t, p * atoms_p_pore:(p + 1) * atoms_p_pore, :] # coordinates for atoms belonging to a single pore while np.min(pore[:, 2]) < 0 or np.max(pore[:, 2]) > box[ t, 2, 2]: # because cross-linked configurations can extend very far up and down pore[:, 2] = np.where(pore[:, 2] < 0, pore[:, 2] + box[t, 2, 2], pore[:, 2]) pore[:, 2] = np.where(pore[:, 2] > box[t, 2, 2], pore[:, 2] - box[t, 2, 2], pore[:, 2]) _, bins = np.histogram(pore[:, 2], bins=npoints) # bin z-positions section_indices = np.digitize( pore[:, 2], bins) # list that tells which bin each atom belongs to bin_centers[t, p, :] = [(bins[i] + bins[i + 1]) / 2 for i in range(npoints)] for l in range(1, npoints + 1): atom_indices = np.where(section_indices == l)[0] before = pore[ atom_indices[0], :] # choose the first atom as a reference shift = transform.translate( pore[atom_indices, :], before, center[t, :]) # shift everything to towards the center for i in range( shift.shape[0] ): # check if the points are within the bounds of the unitcell if not bounds[t].contains_point(shift[i, :2]): shift[i, :] = put_in_box( shift[i, :], box[t, 0, 0], box[t, 1, 1], m[t], angle[t] ) # if its not in the unitcell, shift it so it is c = [np.mean(shift, axis=0)] centers[t, p, l - 1, :] = transform.translate( c, center[t, :], before) # move everything back to where it was if not bounds[t].contains_point(centers[ t, p, l - 1, :]): # make sure everything is in the box again centers[t, p, l - 1, :] = put_in_box(centers[t, p, l - 1, :], box[t, 0, 0], box[t, 1, 1], m[t], angle[t]) if single_frame: return centers[0, ...] # doesn't return bin center yet else: return centers, bin_centers