def dump(self, requested_ts=[], desc=""): if not self._is_loaded: nu.warn( "LAMMPS trajectory file not loaded. Use load() function first." ) return dumpatom = self._get_line(self.trj_file) if not desc: desc = "Dumping trajectories" for t in tqdm.tqdm(self.timesteps, ncols=120, desc=desc): tinfo = {} chunk = [next(dumpatom) for i in range(self._nchunk)] if requested_ts: if not t in requested_ts: del (chunk) continue coords = chunk[9:] for line in coords: atominfo = {} coord = line.split() id = int(coord[self._dump_keywords_r['id']]) type = int(coord[self._dump_keywords_r['type']]) atominfo['id'] = id atominfo['type'] = type for index, i in enumerate(coord[2:]): atominfo[self._dump_keywords[index + 2]] = float(i) tinfo[id] = atominfo self.coord[t] = tinfo if len(self.coord) == len(self.timesteps): self._is_dumped = True # True only if timesteps in the trajectory file are fully loaded.
def getMass(myBGF, aNo_list=[], ff_file="", silent=False): """ def getMass(myBGF, aNo_list, ff_file): Returns a sum of atom masses in aNo_list. """ if ff_file: parse = ff_file.split() update_mass(parse) total_mass = 0 if not aNo_list: for atom in myBGF.a: aNo_list.append(atom.aNo) for ano in aNo_list: atom = myBGF.getAtom(ano) fftype = atom.ffType.strip() try: total_mass += float(atom_mass[fftype]) except KeyError: nu.warn("No atom mass found for atomtype %s!" % fftype) total_mass += 0.0 return total_mass
def load(self, force=False): ''' Read timestep from trajectory file and stores it to self.timesteps ''' def timesteps_remove_repeat(): # prevent repeat self.timesteps = list(set(self.timesteps)) self.timesteps.sort() if self._is_loaded and not force: #nu.warn("Trajectory file already loaded!") timesteps_remove_repeat() return if not self.nheader or not self.natoms: nu.warn("Trajectory file %s seems to be empty." % self.trj_file) return 0 dumpatom = self._get_line(self.trj_file) while 1: try: chunk = [next(dumpatom) for i in range(self._nchunk)] except StopIteration: break t = int(chunk[1]) self.timesteps.append(t) #if not self.silent: sys.stdout.write( '\rGetting information from LAMMPS trajectory file %s .. Fetched timestep: %d' % (self.trj_file, t)) sys.stdout.flush() self.natoms[t] = int(chunk[3]) self.xlo[t] = float(chunk[5].split(' ')[0]) self.xhi[t] = float(chunk[5].split(' ')[1]) self.ylo[t] = float(chunk[6].split(' ')[0]) self.yhi[t] = float(chunk[6].split(' ')[1]) self.zlo[t] = float(chunk[7].split(' ')[0]) self.zhi[t] = float(chunk[7].split(' ')[1]) self.pbc[t] = [ self.xhi[t] - self.xlo[t], self.yhi[t] - self.ylo[t], self.zhi[t] - self.zlo[t] ] sys.stdout.write('\n') sys.stdout.flush() # save the information to pickle with open(self.data_file, 'wb') as f: timemark = time.ctime(os.path.getmtime(self.trj_file)) pickle.dump(timemark, f) pickle.dump(self, f) timesteps_remove_repeat() self._is_loaded = True
def check_connectivity(): # connectivity check-up print("Checking nanotube connectivity..") for atom in mybgf.a: if not "C" in atom.ffType: continue if len(atom.CONECT) != 3: nu.warn("Defect on nanotube connection found: " + str(atom.CONECTline()))
def setAmineGroupInfo(bgf_file, out_file, silent=True): """ setAmineGroupInfo(bgf_file, silent=True): get a BGF file or BgfFile class object returns a BgfFile or 1 with primary, secondary, and tertiary amines information corrected. Note that corrections WILL BE applied to the original BGF file. """ n_pri = 0 n_sec = 0 n_ter = 0 n_garbage = 0 # open bgf if isinstance(bgf_file, bgf.BgfFile): myBGF = bgf_file else: if not silent: print("Reading " + bgf_file + " ..") myBGF = bgf.BgfFile(bgf_file) # get nitrogen ano lists l_nitrogen_ano = [] for atom in myBGF.a: if "N_" in atom.ffType: l_nitrogen_ano.append(atom.aNo) # for every nitrogen for aNo in l_nitrogen_ano: n_carbon = 0 connected_ano = myBGF.getAtom(aNo).CONECT ## for every connection for aNo2 in connected_ano: if "C_" in myBGF.getAtom(aNo2).ffType: n_carbon += 1 ### no carbon: primary, 1 carbons: secondary, 2 carbons: tertiary if n_carbon == 1: myBGF.getAtom(aNo).rName = "PRI" elif n_carbon == 2: myBGF.getAtom(aNo).rName = "SEC" elif n_carbon == 3: myBGF.getAtom(aNo).rName = "TER" else: n_garbage += 1 # what are you?? if n_garbage != 0: nu.warn("Suspicious amine groups are found!") return 0 # save if isinstance(out_file, str): if not silent: print("Saving information to " + out_file + " ..") myBGF.saveBGF(out_file) return getAmineGroupInfo(myBGF) else: return getAmineGroupInfo(myBGF)
def update_pbc(self, pbc): if len(pbc) < 3: nu.warn( "update_pbc(): Wrong assignment for the dimension of the model. Must be 3." ) elif len(pbc) == 3: self.bgfmodel.CRYSTX = pbc + [90.0, 90.0, 90.0] self.pbc = pbc elif len(pbc) == 6: self.bgfmodel.CRYSTX = pbc self.pbc = pbc[:3]
def adjust_pbc(self, out_file=""): """ adjust pbc of BGF model for infinite connectivity. """ if not self.isRadiusCalc: nu.warn( "adjust_pbc: You are adjusting PBC without calculating height and radius of CNT." ) self.calc_height_radius() # set PBC _dim = self.pbc margin = 0.0 val = raw_input("Do you want to change the margin [N]? ") if "y" in val.lower(): val = raw_input( "Margin from the radius to the pbc [%3.1f]? " % margin) or 0.0 if self.orientation == "x": _dim = [ self.height + 1.0, self.radius + margin, self.radius + margin ] elif self.orientation == "y": _dim = [ self.radius + margin, self.height + 1.0, self.radius + margin ] elif self.orientation == "z": _dim = [ self.radius + margin, self.radius + margin, self.height + 1.0 ] else: if self.orientation == "x": _dim[0] = self.height + 1.0 if self.orientation == "y": _dim[1] = self.height + 1.0 if self.orientation == "z": _dim[2] = self.height + 1.0 self.update_pbc(_dim) # move CM to boxcenter boxcenter = np.array(self.pbc) / 2 cm = np.array(self.get_nt_center()) delta = boxcenter - cm coord = self.coord2nparray(self.bgfmodel.a) self.set_coord(coord + delta) self.isPBCadjusted = True
def find_NT_atoms(self): _NT_atoms = [] for atom in self.bgfmodel.a: if "NT" in atom.rName: _NT_atoms.append(atom) if _NT_atoms == []: nu.warn("find_NT_atoms: No atoms with residue name NT found.") print("find_NT_atoms(): found " + str(len(_NT_atoms)) + " atoms for the nanotube in BGF file.") return _NT_atoms
def check_connectivity(self): # connectivity check-up print("Checking nanotube connectivity..") try: for atom in self.NTatoms: if len(atom.CONECT) != 3: raise ValueError except ValueError: nu.warn("Defect on nanotube connection found: " + str(atom.CONECTline())) else: print(" No connectivity error found.")
def find_WAT_atoms(self): _WAT_atoms = [] for atom in self.bgfmodel.a: if "WAT" in atom.rName: _WAT_atoms.append(atom) if _WAT_atoms == []: nu.warn("find_WAT_atoms: No atoms with residue name WAT found.") print("find_WAT_atoms(): found " + str(len(_WAT_atoms)) + " atoms for water in BGF file. (" + str(len(_WAT_atoms) / 3) + " molecules)") return _WAT_atoms
def __init__(self, fname, *args, **kwargs): bgf_file = "" if fname: bgf_file = fname if isinstance(bgf_file, bgf.BgfFile): self.bgfmodel = bgf_file self.model_filename = "" # if the model is directly from bgf object, no filename will be assigned. else: self.bgfmodel = bgf.BgfFile(bgf_file) self.model_filename = bgf_file # # Determine nanotube type and atom types # self.NTatoms = self.find_NT_atoms() self.WATatoms = self.find_WAT_atoms() self.otheratoms = self.find_other_atoms() self.find_type() self.set_ff() # # Set number of atoms # self.n_allatoms = len(self.bgfmodel.a) self.n_nanotube = len(self.NTatoms) self.n_water = len(self.WATatoms) # # Properties of CNT: requires special treatment so not automatically calculated. # self.isAligned = False self.isRadiusCalc = False self.isPBCadjusted = False self.orientation = None self.radius = None self.height = None self.zhi = None self.zlo = None self.water_position_determined = False # model check-up: total charge chg = bgftools.charge(self.bgfmodel) if chg != 0 and abs(chg) > 1e-10: nu.warn("Charge is not neutral: " + str(chg)) if self.bgfmodel.CRYSTX != []: self.pbc = self.bgfmodel.CRYSTX[:3] else: nu.warn("Nanotube class: An empty object created.")
def saveBGF(self, *args): if self._bgf == None: nu.warn("No BGF model converted from " + self._filename + ": save failed.") return 0 if len(args) != 1: tempfile = os.path.abspath('.') + "/" + "POSCAR.bgf" nu.warn("Output filename not specified. " + self._filename + " will be stored at " + tempfile) self._bgf.saveBGF(tempfile) else: print(self._filename + " file saved to " + str(args[0])) self._bgf.saveBGF(args[0])
def check_branching_condition(self, monomer_id, branch_type): # number of branches check num_current_branch = len(self.neighbors(monomer_id)) if num_current_branch > self.max_num_branch: nu.warn("Cannot add a monomer: branch already full.") return False # type check for i in self[monomer_id]: if monomer_id < i and self[monomer_id][i]['branch'] == branch_type: nu.warn("Cannot add a monomer: branch already exists.") return False return True
def calc_height_radius(self): """ sets center, height, orientation, axis """ if not self.isAligned: nu.warn( "calc_height_radius: Nanotube is not aligned along axes. Values may be inaccurate." ) nt_coord = self.coord2nparray(self.NTatoms) min_x, min_y, min_z = nt_coord.min(axis=0) max_x, max_y, max_z = nt_coord.max(axis=0) range = np.array([[min_x, max_x], [min_y, max_y], [min_z, max_z]]) diff = [max_x - min_x, max_y - min_y, max_z - min_z] #self.center = np.mean(nt_coord, axis=0) # set nanotube center self.center = self.get_nt_center() # set nanotube center #std_c = np.std(nt_coord, axis=0) # height and orientation estimation: can be inaccurate self.height = np.max(diff) # set nanotube height if np.argmax(diff) == 0: self.orientation = "x" self.axis = np.array([0, 1, 1]) self.axismask = np.array([1, 0, 0]) if np.argmax(diff) == 1: self.orientation = "y" self.axis = np.array([1, 0, 1]) self.axismask = np.array([0, 1, 0]) if np.argmax(diff) == 2: self.orientation = "z" self.axis = np.array([1, 1, 0]) self.axismask = np.array([0, 0, 1]) self.zlo, self.zhi = np.sum(range.T * self.axismask, axis=1) self.radius = np.mean( np.sqrt(((nt_coord * self.axis - self.center * self.axis)**2).sum( axis=-1))) # set nanotube radius self.bgfmodel.REMARK.append("Nanotube center: %s" % self.center) self.bgfmodel.REMARK.append("Nanotube height: %8.3f" % self.height) self.bgfmodel.REMARK.append("Nanotube radius: %8.3f" % self.radius) print("\t* Nanotube height: " + str(self.height)) print("\t* Nanotube radius: " + str(self.radius)) self.isRadiusCalc = True
def add_monomer(self, monomer_id, branch_type): """ branch_type: 1: left, 2: right """ if not self.check_branching_condition(monomer_id, branch_type): nu.warn("Failed to add monomer.") return False self.num_monomer += 1 new_monomer_id = self.num_monomer self.add_node(self.num_monomer) # add a new monomer self.add_edge(monomer_id, new_monomer_id, branch=branch_type) return new_monomer_id
def getCom(myBGF, ff_file='', aNo_list=[], silent=False): """ getCom(myBGF): Returns a (x, y, z) of the center of mass of the molecule. """ # init mrx = 0 mry = 0 mrz = 0 m = 0 # if aNo_list not specified, CoM of whole molecule will be returned. if not aNo_list: for atom in myBGF.a: aNo_list.append(atom.aNo) # read atom mass from the dictionary for i in aNo_list: atom = myBGF.getAtom(i) fftype_key = atom.ffType.strip() if not fftype_key in atom_mass: parse = ff_file.split() update_mass(parse, silent=silent) if not fftype_key in atom_mass: nu.die( "No ffType %s found in the atom mass dictionary. You have to specify a ff_file to countinue." % fftype_key) mrx += (atom.x * float(atom_mass[fftype_key])) mry += (atom.y * float(atom_mass[fftype_key])) mrz += (atom.z * float(atom_mass[fftype_key])) m += float(atom_mass[fftype_key]) try: x = mrx / m y = mry / m z = mrz / m except: nu.warn( "Total mass is zero for the molecule while calculating center of mass!" ) x = 0 y = 0 z = 0 return (x, y, z)
def write(self, out_file, zip=False): if zip: nu.warn( "Atoms will be specified in range if possible. (i.e. xx - yy)") else: nu.warn("Atom numbers will be recorded in full specification.") with open(out_file, 'w') as f: n_group = len(self.grp.keys()) groups = self.grp.keys() groups.sort() f.write("Total Groups: %d\n" % n_group) for i in groups: atoms = sorted(self.grp[i]['atoms']) f.write("Group %d Atoms %d\n" % (i, len(atoms))) if zip: f.write(''.join( (('%i - %i ' % r) if len(r) == 2 else '%i' % r) for r in nu.range_extract(atoms)) + "\n") else: f.write(''.join("%d " % i for i in atoms) + "\n") f.write("Constraints" + "\n") f.write(''.join("%d " % self.grp[i]['constraints'] for i in groups) + "\n") f.write("RotationalSymmetryNumber" + "\n") f.write(''.join("%d " % self.grp[i]['rotsym'] for i in groups) + "\n") f.write("LinearMoleculeFlag" + "\n") f.write(''.join("%d " % self.grp[i]['linear'] for i in groups) + "\n") if self.grp[1]['volume']: f.write("GroupVolume" + "\n") f.write(''.join("%-16.5f " % self.grp[i]['volume'] for i in groups) + "\n") if self.grp[1]['energy']: f.write("GroupEnergy" + "\n") f.write(''.join("%-16.5f " % self.grp[i]['energy'] for i in groups) + "\n")
def bgf2qcin(bgf_file, qchem_in_file, rem_file): # bgf open print("Reading " + bgf_file + " ..") myBGF = bgf.BgfFile(bgf_file) # output output = [ "$comment\n", "from " + bgf_file + " at " + time.asctime(time.gmtime()) + " on " + os.environ["HOSTNAME"] + " by " + os.environ["USER"] + "\n", "$end\n" ] # charge and multiplicity output += ["\n$molecule\n"] chg = myBGF.charge() if abs(chg) < 0.000001: output += ["0" + "\t" + "1" + "\n"] else: nu.warn("Charge is not neutral. Charge will be written as " + "{0:<4.1f}".format(chg)) output += ["{0:<4.1f}".format(chg) + "\t" + "1" + "\n"] # coordinates for atom in myBGF.a: output += [ atom.ffType.split("_")[0] + "\t" + "{0:10.6f} {1:10.6f} {2:10.6f}".format(atom.x, atom.y, atom.z) + "\n" ] output += ["$end\n\n"] # rem remf = open(rem_file) remline = remf.readlines() output += remline # target file qcinf = open(qchem_in_file, 'w') qcinf.writelines(output) qcinf.close() print("Generated " + qchem_in_file + " . Done.")
def build_random(self): if self.num_target_node == 0: nu.warn("Number of target monomers not specified.") raise BuildError # while the polymer reach to the target nodes while self.num_monomer < self.num_target_node: # update_branch avail_branch = self.update_branch() if len(avail_branch) == 0: nu.warn("No more available branch sites.") break # pick one site pick = random.choice(avail_branch) # attach self.add_monomer(pick[0], pick[1]) # pick = [monomer_id, monomer_type]
def get_density(bgf_file, trj_file, ff_file="", draw=False, out_file="", n=0, silent=False): mass = get_mass(bgf_file, ff_file=ff_file) # float volume = get_volume(trj_file) # dict density = {d: mass / volume[d] / 6.022 * 10 for d in volume.keys()} df = pd.DataFrame(density, index=['density']) df = df.T if n < len(df): df = df[-n:] else: nu.warn( "Not enough sample shots to calculate density with requested number of last snapshots." ) density = float(df.mean()) if not silent: print(df) print("Average density: {0:<11.5f}".format(density)) if draw: import matplotlib.pyplot as plt plt.figure() df.plot() plt.legend(loc='best') fr = plt.gca() fr.axes.set_ylim(bottom=0) plt.show() if out_file: df.to_csv(out_file, sep=' ', index_label='t') nu.warn( "the character # should be added to the first of the line 1 if you want to plot the file on gnuplot." ) return density
def make_bulk_infinite(self, *args): """ calculate nanotube height, trim water molecules outside the box, and connect the two ends to make infinite nanotube. """ self.make_pbc() self.calc_height_radius() self.adjust_pbc() # trim temp_list = [] for atom in self.bgfmodel.a: if "O" in atom.ffType and "WAT" in atom.rName: if atom.z > self.pbc[2] or atom.z < 0.0: temp_list.append(atom.aNo) temp_list += atom.CONECT temp_list = list(set(temp_list)) print("\tFound " + str(len(temp_list)) + " atoms for water outside of the box: will be removed.") del_list = [] for i in temp_list: del_list.append(self.bgfmodel.a2i[i]) self.bgfmodel.delAtoms(del_list, False) self.bgfmodel.renumber() print('\tTrim successful!') self.update() # connect if len(args) == 0: self.make_infinite() elif len(args) == 1: self.make_infinite(args[0]) else: nu.warn("Wrong arguments passed.") return 0
def find_S_bonds(self): if self._bgf == None: nu.warn("Cannot make bonds--BGF model unassigned.") return 0 aNo_pair = [] for atom1 in self._bgf.a: if not "S" in atom1.ffType: continue x1 = np.array([atom1.x, atom1.y, atom1.z]) for atom2 in self._bgf.a: if atom1 == atom2: continue if not "S" in atom2.ffType: continue x2 = np.array([atom2.x, atom2.y, atom2.z]) dist = self.distance(x1, x2, self._latticeVectors, self._inv_latticeVectors) if 1.95 <= dist <= 2.15: aNo_pair.append([atom1.aNo, atom2.aNo]) for i in aNo_pair: self._bgf.connectAtoms(i[0], i[1]) print(this + ": " + str(len(aNo_pair)) + " bonds are created.") return 1
def make_periodic(*args): """ MakePeriodic() returns 0 MakePeriodic(bgf_file) returns BgfFile() object with CRYSTX {0 0 0 90 90 90} MakePeriodic(bgf_file, pbc) returns BgfFile() object with CRYSTX {x y z 90 90 90} MakePeriodic(bgf_file, CRYSTX) returns BgfFile() object with CRYSTX {x y z a b c} MakePeriodic(bgf_file, out_file, pbc) records out_file and returns 1 MakePeriodic(bgf_file, out_file, CRYSTX) records out_file and returns 1 """ if len(args) == 0: nu.warn("No BGF file or instance specified.") return 0 elif len(args) >= 1: mybgf = args[0] if isinstance(mybgf, str): mybgf = bgf.BgfFile(mybgf) mybgf.PERIOD = "111" mybgf.AXES = "ZYX" mybgf.SGNAME = "P 1 1 1" mybgf.CELLS = [-1, 1, -1, 1, -1, 1] if len(args) == 1: mybgf.CRYSTX = [0.0, 0.0, 0.0, 90.0, 90.0, 90.0] nu.warn("PBC set to [0.0, 0.0, 0.0, 90.0, 90.0, 90.0]") return mybgf elif len(args) >= 2: if len(args[-1]) == 3: mybgf.CRYSTX = args[1] + [90.0, 90.0, 90.0] elif len(args[-1]) == 6: mybgf.CRYSTX = args[1] else: nu.warn("Wrong pbc provided.") return 0 if len(args) == 2: return mybgf elif len(args) == 3: mybgf.saveBGF(args[1]) return 1 else: nu.warn("Too many parameters (>4) provided.") return 0
def adjust_interior_water(self, n_water): if not self._water_position_determined: nu.warn("You have to run determine_water_position() first.") return if not n_water: nu.warn( "You must specify the number of water molecules inside the Nanotube." ) return self.bgfmodel = bgftools.renumberMolecules(self.bgfmodel, 0, False) waters = set() for atom in self.bgfmodel.a: if atom.chain == 'I': waters.add(atom.rNo) waters = list(waters) print("\tFound %d interior water molecules." % len(waters)) if len(waters) < n_water: nu.warn("Too few solvent molecules to choose %d molecules." % n_water) return rNos = random.sample(waters, n_water) print("%d water molecules are randomly chosen. Others are removed." % n_water) delist = [] for atom in self.bgfmodel.a: if atom.chain == 'I' and not atom.rNo in rNos: delist.append(self.bgfmodel.a2i[atom.aNo]) delist.sort() self.bgfmodel.delAtoms(delist, False) self.bgfmodel.renumber() self.bgfmodel = bgftools.renumberMolecules(self.bgfmodel, 0, False) self.update()
def countWaterCNT(bgf_file, trj_file, step, doSave, silent=False): # init timestep = 0 l_timestep = [] line = [] n_header = 0 t1 = 0 t2 = 0 # clock myBGF = bgf.BgfFile(bgf_file) myTRJ = open(trj_file) myTRJ.seek(0) myDAT = open(bgf_file[:-4] + ".count.dat", "w") myDAT.write("Time\tmin_X\tmax_X\tHeight\tRadius\tNumber\n") # how many steps to go? wc_trj_file = popen("grep TIMESTEP " + trj_file + " | wc -l ").read() n_timestep = int(wc_trj_file.split()[0]) print("The trajectory contains " + str(n_timestep) + " timesteps.") # extract aNos of CNT in the BGF file aNo_CNT = [] aNo_MtOH_C = [] for atom in myBGF.a: # Carbons in CNT if "CNT" in atom.rName: aNo_CNT.append(atom.aNo) # Carbons in MeOH if "MET" in atom.rName and "C" in atom.aName: aNo_MtOH_C.append(atom.aNo) N_CNT = len(aNo_CNT) # the number of CNT atoms # Find header of the trajectory file while 1: templine = myTRJ.readline() line.append(templine.strip('\n').strip('ITEM: ')) n_header += 1 if "ITEM: ATOMS" in templine: break # INITIAL trajectory information timestep = int(line[1]) natoms = int(line[3]) boxsize = [ line[5].split(' ')[0], line[5].split(' ')[1], line[6].split(' ')[0], line[6].split(' ')[1], line[7].split(' ')[0], line[7].split(' ')[1] ] boxsize = [float(i) for i in boxsize] keywords = line[8].strip('ATOMS ') # for every shot in the trajectory file update BGF and manipulate dumpatom = get_line(trj_file) processed_step = 0 t1 = t2 = 0 elapsed_time = 0 while 1: try: chunk = [next(dumpatom) for i in range(natoms + n_header)] except StopIteration: break timestep = int(chunk[1]) natoms = int(chunk[3]) boxsize = [ chunk[5].split(' ')[0], chunk[5].split(' ')[1], chunk[6].split(' ')[0], chunk[6].split(' ')[1], chunk[7].split(' ')[0], chunk[7].split(' ')[1] ] boxsize = [float(i) for i in boxsize] boxsize = [(boxsize[1] - boxsize[0]), (boxsize[3] - boxsize[2]), (boxsize[5] - boxsize[4])] keywords = chunk[8].split('ATOMS ')[1].strip('\n').split(' ') ### Show progress t1 = time.time() remaining_time = elapsed_time * (n_timestep - processed_step) sys.stdout.write('\r' + "Reading timestep.. " + str(timestep) + " (Elapsed time for the previous step: " + "{0:4.1f}".format(elapsed_time) + " seconds, Remaining time: " + "{0:4.1f}".format(remaining_time) + " seconds = " + "{0:4.1f} minutes".format(remaining_time / 60) + ")") sys.stdout.flush() processed_step += 1 ### if step is specified, then save only for that step. if step != 0: if timestep == step: pass else: continue else: pass ### update myBGF with trajectory information ### natom_bgf = len(myBGF.a) # number of atoms in BGF file if not natom_bgf == natoms: nu.die( "Number of atoms in trajectory file does not match with BGF file." ) mode = "" if 'xs' in keywords or 'ys' in keywords or 'zs' in keywords: mode = 'scaled' elif 'x' in keywords or 'y' in keywords or 'z' in keywords: mode = 'normal' elif 'xu' in keywords or 'yu' in keywords or 'zu' in keywords: mode = 'unwrapped' # actual coordinate coordinfo = chunk[9:] # assume that coordinfo is similar to ['id', 'type', 'xs', 'ys', 'zs', 'ix', 'iy', 'iz'] for atomline in coordinfo: atomcoord = atomline.split(' ') atom = myBGF.getAtom(int(atomcoord[0])) if mode == 'scaled': atom.x = float(atomcoord[2]) * boxsize[0] atom.y = float(atomcoord[3]) * boxsize[1] atom.z = float(atomcoord[4]) * boxsize[2] elif mode == 'unwrapped': atom.x = float(atomcoord[2]) atom.y = float(atomcoord[3]) atom.z = float(atomcoord[4]) elif mode == 'normal': try: ix_index = keywords.index('ix') iy_index = keywords.index('iy') iz_index = keywords.index('iz') except ValueError: nu.warn( "No image information no the trajectory file. Will be treated as unwrapped." ) atom.x = float(atomcoord[2]) atom.y = float(atomcoord[3]) atom.z = float(atomcoord[4]) else: atom.x = (int(atomcoord[ix_index]) * boxsize[0]) + float( atomcoord[2]) atom.y = (int(atomcoord[iy_index]) * boxsize[1]) + float( atomcoord[3]) atom.z = (int(atomcoord[iz_index]) * boxsize[2]) + float( atomcoord[4]) try: for i in range(0, 3): myBGF.CRYSTX[i] = boxsize[i] except: pass #nu.warn("Crystal information error: is this file not periodic?") # apply periodic condition myBGF = bgftools.periodicMoleculeSort(myBGF, 0) #myBGF.saveBGF(bgf_file[:-4] + "." + str(timestep) + ".trjupdated.bgf") ### myBGF update complete! ### aNo_MtOH_C_in_CNT = copy.deepcopy(aNo_MtOH_C) # initialize for the moment of inertia and the center of mass calculation U = 0 Ut = 0 Uv = 0 Ixx = 0 Ixy = 0 Ixz = 0 Iyx = 0 Iyy = 0 Iyz = 0 Izx = 0 Izy = 0 Izz = 0 Mx = 0 My = 0 Mz = 0 # transpose for "all atoms in BGF": move COM of CNT as origin # com of CNT Mx = 0 My = 0 Mz = 0 for atom in myBGF.a: if "CNT" in atom.rName: Mx += atom.x / N_CNT My += atom.y / N_CNT Mz += atom.z / N_CNT # move for atom in myBGF.a: atom.x -= Mx atom.y -= My atom.z -= Mz # calculate the moment of inertia (MI) and the center of mass (COM) of CNT from "myBGF" for aNo in aNo_CNT: atom = myBGF.getAtom(aNo) Ixx += (atom.y**2 + atom.z**2) / N_CNT Iyy += (atom.x**2 + atom.z**2) / N_CNT Izz += (atom.x**2 + atom.y**2) / N_CNT Ixy -= (atom.x * atom.y) / N_CNT Ixz -= (atom.x * atom.z) / N_CNT Iyz -= (atom.y * atom.z) / N_CNT # the moment of inertia tensor I = numpy.array([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]]) # eigenvalue & eigenvector calculation eigval, eigvec = numpy.linalg.eig( I) # eigval[0] is the minimum among the values. # rearrange the U vector U = numpy.matrix(eigvec) Ut = U.T # "myBGF" rotation for atom in myBGF.a: v = numpy.matrix([atom.x, atom.y, atom.z]).T Uv = Ut * v atom.x = float(Uv[0]) atom.y = float(Uv[1]) atom.z = float(Uv[2]) """ # com of CNT Mx = 0; My = 0; Mz = 0; for atom in myBGF.a: if "CNT" in atom.rName: Mx += atom.x / N_CNT My += atom.y / N_CNT Mz += atom.z / N_CNT # transpose for "all atoms in BGF": move COM of CNT as origin for atom in myBGF.a: atom.x -= Mx atom.y -= My atom.z -= Mz """ # for CNT atoms, calculate some properties min_x_CNT = 0.0 max_x_CNT = 0.0 radius_CNT = 0.0 height_CNT = 0.0 aNo_MtOH_C_not_in_CNT = [] temp = [] min_y_CNT = 0.0 max_y_CNT = 0.0 min_z_CNT = 0.0 max_z_CNT = 0.0 CNT_orientation = "" # check the orientation of CNT for aNo in aNo_CNT: atom = myBGF.getAtom(aNo) # minimum and maximum x coord of CNT: this will be the height of CNT if atom.x < min_x_CNT: min_x_CNT = atom.x if atom.x > max_x_CNT: max_x_CNT = atom.x if atom.y < min_y_CNT: min_y_CNT = atom.y if atom.y > max_y_CNT: max_y_CNT = atom.y if atom.z < min_z_CNT: min_z_CNT = atom.z if atom.z > max_z_CNT: max_z_CNT = atom.z x_diff = max_x_CNT - min_x_CNT y_diff = max_y_CNT - min_y_CNT z_diff = max_z_CNT - min_z_CNT if x_diff > y_diff and x_diff > z_diff: # CNT is aligned along x axis height_CNT = x_diff CNT_orientation = "x" elif y_diff > x_diff and y_diff > z_diff: # CNT is aligned along y axis height_CNT = y_diff CNT_orientation = "y" elif z_diff > x_diff and z_diff > y_diff: # CNT is aligned along z axis height_CNT = z_diff CNT_orientation = "z" if CNT_orientation == "x": for aNo in aNo_CNT: atom = myBGF.getAtom(aNo) radius_CNT += math.sqrt(atom.y**2 + atom.z**2) # average radius of CNT elif CNT_orientation == "y": for aNo in aNo_CNT: atom = myBGF.getAtom(aNo) radius_CNT += math.sqrt(atom.x**2 + atom.z**2) # average radius of CNT elif CNT_orientation == "z": for aNo in aNo_CNT: atom = myBGF.getAtom(aNo) radius_CNT += math.sqrt(atom.x**2 + atom.y**2) # average radius of CNT radius_CNT = radius_CNT / N_CNT # determine whether C in MtOH is in the CNT Cylinder if doSave: myBGF2 = copy.deepcopy(myBGF) for aNo in aNo_MtOH_C: atom = myBGF.getAtom(aNo) if CNT_orientation == "x": dist = math.sqrt(atom.y**2 + atom.z**2) if atom.x > min_x_CNT and atom.x < max_x_CNT and dist < radius_CNT: # inside of the CNT pass else: # delete atoms delete_list = [] dummy = bgftools.getmolecule(myBGF, myBGF.getAtom(aNo), delete_list) for i in delete_list: aNo_MtOH_C_not_in_CNT.append(myBGF.a2i[i]) # outside of the CNT aNo_MtOH_C_in_CNT.remove(aNo) elif CNT_orientation == "y": dist = math.sqrt(atom.x**2 + atom.z**2) if atom.y > min_y_CNT and atom.y < max_y_CNT and dist < radius_CNT: pass else: # delete atoms delete_list = [] dummy = bgftools.getmolecule(myBGF, myBGF.getAtom(aNo), delete_list) for i in delete_list: aNo_MtOH_C_not_in_CNT.append(myBGF.a2i[i]) # outside of the CNT aNo_MtOH_C_in_CNT.remove(aNo) elif CNT_orientation == "z": dist = math.sqrt(atom.x**2 + atom.y**2) if atom.z > min_z_CNT and atom.z < max_z_CNT and dist < radius_CNT: pass else: # delete atoms delete_list = [] dummy = bgftools.getmolecule(myBGF, myBGF.getAtom(aNo), delete_list) for i in delete_list: aNo_MtOH_C_not_in_CNT.append(myBGF.a2i[i]) # outside of the CNT aNo_MtOH_C_in_CNT.remove(aNo) myBGF2.delAtoms(aNo_MtOH_C_not_in_CNT) myBGF2.renumber() myDAT.write(str(timestep) + "\t") myDAT.write(str("{0:<8.3f}".format(min_x_CNT))) myDAT.write(str("{0:<8.3f}".format(max_x_CNT))) myDAT.write(str("{0:<8.3f}".format(min_y_CNT))) myDAT.write(str("{0:<8.3f}".format(max_y_CNT))) myDAT.write(str("{0:<8.3f}".format(min_z_CNT))) myDAT.write(str("{0:<8.3f}".format(max_z_CNT))) myDAT.write(str("{0:<8.3f}".format(height_CNT))) myDAT.write(str("{0:<8.3f}".format(radius_CNT))) myDAT.write(str(len(aNo_MtOH_C_in_CNT)) + "\n") #myDAT.write(str(aNo_MtOH_C_in_CNT) + "\n") # write output if doSave: myBGF2.saveBGF(bgf_file[:-4] + "." + str(timestep) + ".bgf") t2 = time.time() # time mark elapsed_time = t2 - t1 print('') return 1
def add_ions_inside_nanotube(self, n_ion): """ Add Na+ and Cl- ions inside nanotube. """ if not self.isRadiusCalc == True: nu.warn("Nanotube radius not defined. Exiting.") return 0 ### find the last atom before resname WAT aNo_lastatom = 0 for i in self.bgfmodel.a: aNo = i.aNo if not "WAT" in i.rName: if aNo > aNo_lastatom: aNo_lastatom = aNo ### add n_add = 0 while n_add < n_ion: x = random.uniform(0, self.pbc[0]) y = random.uniform(0, self.pbc[1]) z = random.uniform(0, self.pbc[2]) xyz = np.array([x, y, z]) r = np.sqrt( ((xyz * self.axis - self.center * self.axis)**2).sum(axis=-1)) h = (xyz * self.axismask).sum() if r < self.radius and self.zlo < h < self.zhi: # Na+ ion atom_Na = bgf.BgfAtom() atom_Na.x = x atom_Na.y = y atom_Na.z = z atom_Na.aTag = 1 # HETATM atom_Na.ffType = "Na" atom_Na.rName = "ION" atom_Na.aName = "Na" atom_Na.charge = 1.00 atom_Na.rNo = 0 atom_Na.chain = "X" # Cl- ion atom_Cl = bgf.BgfAtom() atom_Cl.x = x + 2 atom_Cl.y = y + 2 atom_Cl.z = z + 2 atom_Cl.aTag = 1 # HETATM atom_Cl.ffType = "Cl" atom_Cl.rName = "ION" atom_Cl.aName = "Cl" atom_Cl.charge = -1.00 atom_Cl.rNo = 0 atom_Cl.chain = "X" self.bgfmodel.addAtom(atom_Na, self.bgfmodel.a2i[aNo_lastatom + 1]) self.bgfmodel.addAtom(atom_Cl, self.bgfmodel.a2i[aNo_lastatom + 2]) n_add += 1 self.bgfmodel.renumber() print("%d atoms added to the nanotube." % n_add)
def determine_water_position(self, *args): print("Marking water molecules whether internal or external..") if self.isRadiusCalc == False: nu.warn("Nanotube radius not calculated.") return 0 tempBGF = bgf.BgfFile() tempBGF = bgftools.make_periodic(tempBGF, self.pbc) self.make_pbc(self.pbc, self.bgfmodel.CRYSTX) # copy Nanotube atoms for atom in self.NTatoms + self.otheratoms: atom2 = copy.deepcopy(atom) tempBGF.addAtom(atom2) tempBGF.renumber() # mark rNames to Oxygen atoms n_oxygen_inside = 0 n_oxygen_outside = 0 for atom in self.WATatoms: if "H" in atom.ffType: continue xyz = np.array([atom.x, atom.y, atom.z]) r = np.sqrt( ((xyz * self.axis - self.center * self.axis)**2).sum(axis=-1)) h = (xyz * self.axismask).sum() if r < self.radius: # inside O atom.chain = "I" n_oxygen_inside += 1 else: # outside O atom.chain = "O" n_oxygen_outside += 1 print("Interior oxygens: " + str(n_oxygen_inside) + ", exterior oxygens: " + str(n_oxygen_outside)) self.n_inside_water = n_oxygen_inside self.n_outside_water = n_oxygen_outside # write water molecules chain = ["I", "O"] for cname in chain: print("Passing " + cname) for atom in self.WATatoms: if cname in atom.chain and "O" in atom.ffType: tempBGF.addAtom(atom) # l_Hatoms = atom.CONECT for index, ano in enumerate(l_Hatoms): atom2 = self.bgfmodel.getAtom(ano) atom2.chain = cname tempBGF.addAtom(atom2) # self.bgfmodel = tempBGF self.bgfmodel.renumber() self.bgfmodel = bgftools.renumberMolecules(self.bgfmodel, 0, False) self.update() print( "Water molecules are identified into interior(I) and exterior(O).") if len(args) == 0: nu.warn( "Residue name and coords for water molecules are modified.") elif len(args) == 1: print("Modified BGF file is saved to " + str(args[0])) self.bgfmodel.saveBGF(args[0]) self._water_position_determined = True
def make_infinite(self, *args): ''' args: filename to save ''' print("CNT.py: make_infinite()") if len(args) > 1: nu.warn("make_infinite(): Too many arguments.") if not self.isPBCadjusted: nu.warn( "make_infinite(): You are trying to make infinite NT without adjusting PBC." ) #return 0; print("\t* Found " + str(len(self.NTatoms)) + " atoms for the nanotube.") print("\t* Found " + str(len(self.WATatoms)) + " atoms for water.") if self.type == "BNT": self.detach_hydrogen() aNo_pair = [] for atom in self.NTatoms: if len(atom.CONECT) == 3: continue x = np.array([atom.x, atom.y, atom.z]) min_atom_aNo = 100000 # placeholder min_atom_d = 10000.0 for atom2 in self.NTatoms: if (not atom2.aNo in atom.CONECT) and len(atom2.CONECT) == 2: if (atom.rName == atom2.rName) and ( self.type == "CNT" or atom.ffType != atom2.ffType): y = np.array([atom2.x, atom2.y, atom2.z]) if -1.0 < ((x - y) * self.axismask).sum() < 1.0: continue # prevent adjacent atoms d = nu.pbc_dist(x, y, self.pbc) if d < min_atom_d: min_atom_aNo = atom2.aNo min_atom_d = d aNo_pair.append([atom.aNo, min_atom_aNo]) for i in aNo_pair: self.bgfmodel.connectAtoms(i[0], i[1]) self.check_connectivity() self.bgfmodel.renumber() if len(args) == 0: value = raw_input( "Do you want to save the infinite Nanotube structure to BGF file [N]? " ) if "y" in value.lower(): filename = self.model_filename[:-4] + ".infinite.bgf" value = raw_input("Filename to save [" + filename + "]? ") or filename self.bgfmodel.saveBGF(value) elif len(args) == 1: self.bgfmodel.saveBGF(args[0])
def find_type(self): #_NT_atoms = self.find_NT_atoms() if len(self.NTatoms) != 0: self.type = self.NTatoms[0].rName else: nu.warn("find_type(): Failed to find Nanotube types.")
def set_type(self, _type=""): if _type != "": nu.warn( "set_type(): You are manually assigning the nanotube type.") self.type = _type