def benzene_from_parts(self): ch = mb.load(get_fn('ch.mol2')) ch.name = 'CH' mb.translate(ch, -ch[0].pos) ch.add(mb.Port(anchor=ch[0], separation=0.07), 'a') mb.rotate_around_z(ch['a'], 120.0 * (np.pi/180.0)) ch.add(mb.Port(anchor=ch[0], separation=0.07), 'b') mb.rotate_around_z(ch['b'], -120.0 * (np.pi/180.0)) ch_copy = mb.clone(ch) benzene = mb.Compound(name='Benzene') benzene.add(ch) current = ch for _ in range(5): ch_new = mb.clone(ch_copy) mb.force_overlap(move_this=ch_new, from_positions=ch_new['a'], to_positions=current['b']) current = ch_new benzene.add(ch_new) carbons = [p for p in benzene.particles_by_name('C')] benzene.add_bond((carbons[0],carbons[-1])) return benzene
def apply(self, compound, orientation='', compound_port=''): """Arrange copies of a Compound as specified by the Pattern. Parameters ---------- compound orientation Returns ------- """ compounds = list() if self.orientations.get(orientation): for port in self.orientations[orientation]: new_compound = clone(compound) new_port = new_compound.labels[compound_port] equivalence_transform(new_compound, new_port['up'], port['up']) compounds.append(new_compound) else: for point in self.points: new_compound = clone(compound) translate(new_compound, point) compounds.append(new_compound) return compounds
def apply_to_compound(self, guest, guest_port_name='down', host=None, backfill=None, backfill_port_name='up'): """Attach copies of a guest Compound to Ports on a host Compound. Parameters ---------- guest guest_port_name host backfill backfill_port_name Returns ------- """ n_ports = len(host.available_ports()) assert n_ports >= self.points.shape[0], "Not enough ports for pattern." assert_port_exists(guest_port_name, guest) box = host.boundingbox pattern = self.points * box.lengths + box.mins port_positions = np.empty(shape=(n_ports, 3)) port_list = list() for port_idx, port in enumerate(host.available_ports()): port_positions[port_idx, :] = port['up']['middle'].pos port_list.append(port) used_ports = set() # Keep track of used ports for backfilling. guests = [] for point in pattern: closest_point_idx = np.argmin(host.min_periodic_distance(point, port_positions)) closest_port = port_list[closest_point_idx] used_ports.add(closest_port) # Attach the guest to the closest port. new_guest = clone(guest) equivalence_transform(new_guest, new_guest.labels[guest_port_name], closest_port) guests.append(new_guest) # Move the port as far away as possible (simpler than removing it). # There may well be a more elegant/efficient way of doing this. port_positions[closest_point_idx, :] = np.array([np.inf, np.inf, np.inf]) backfills = [] if backfill: assert_port_exists(backfill_port_name, backfill) # Attach the backfilling Compound to unused ports. for port in port_list: if port not in used_ports: new_backfill = clone(backfill) # Might make sense to have a backfill_port_name option... equivalence_transform( new_backfill, new_backfill.labels[backfill_port_name], port) backfills.append(new_backfill) return guests, backfills
def test_rigid_with_subcompounds5(self, rigid_benzene): rigid_benzene2 = mb.clone(rigid_benzene) double = mb.Compound(subcompounds=[rigid_benzene, rigid_benzene2]) double2 = mb.clone(double) compound = mb.Compound(subcompounds=[double, double2]) assert compound.max_rigid_id is 3 assert len(list(compound.rigid_particles())) == 48 for rigid_id in range(4): assert len(list(compound.rigid_particles(rigid_id=rigid_id))) == 12
def __init__(self, proto, port_labels=("up", "down"), n=2): if n < 1: raise Exception('n must be 1 or more') super(Polymer, self).__init__() for label in port_labels: assert_port_exists(label, proto) last_part = None for _ in range(n): this_part = clone(proto) self.add(this_part, 'monomer[$]') if last_part is None: first_part = this_part else: # Transform this part, such that it's bottom port is rotated # and translated to the last part's top port. equivalence_transform(this_part, this_part.labels[port_labels[1]], last_part.labels[port_labels[0]]) last_part = this_part # Hoist the last part's top port to be the top port of the polymer. self.add(last_part.labels[port_labels[0]], port_labels[0], containment=False) # Hoist the first part's bottom port to be the bottom port of the polymer. self.add(first_part.labels[port_labels[1]], port_labels[1], containment=False)
def test_bond_graph(self, ch3): compound = mb.Compound() compound.add(ch3) assert compound.n_bonds == 3 assert all(compound.bond_graph.has_node(particle) for particle in ch3.particles()) ch3_nobonds = mb.clone(ch3) for bond in ch3_nobonds.bonds(): ch3_nobonds.remove_bond(bond) compound.add(ch3_nobonds) assert compound.n_bonds == 3 assert not any(compound.bond_graph.has_node(particle) for particle in ch3_nobonds.particles()) carbons = list(compound.particles_by_name('C')) compound.add_bond((carbons[0], carbons[1])) assert compound.n_bonds == 4 assert all(compound.bond_graph.has_node(particle) for particle in carbons) assert any(compound.bond_graph.has_node(particle) for particle in ch3_nobonds.particles()) compound.remove_bond((carbons[0], carbons[1])) assert not any(compound.bond_graph.has_node(particle) for particle in ch3_nobonds.particles())
def test_different_translate_tos_not_origin(self, methane): shifted = mb.clone(methane) np.random.seed(0) point = np.random.rand(3) shifted.translate_to(point) x = mb.coordinate_transform._translate_to(methane.xyz, point) assert np.array_equal(shifted.xyz, x)
def test_create_semi_rigid_bodies_hierarchy(self, benzene_from_parts): n_benzenes = 10 filled = mb.fill_box(benzene_from_parts, n_compounds=n_benzenes, box=[0, 0, 0, 4, 4, 4]) filled.name = 'Benzene box' filled2 = mb.clone(filled) compound = mb.Compound(subcompounds=[filled, filled2]) compound.label_rigid_bodies(discrete_bodies='Benzene box') assert compound.max_rigid_id == 1 assert filled.max_rigid_id == 0 assert filled2.max_rigid_id == 1 assert len(list(compound.rigid_particles())) == n_benzenes * 2 * 12 compound.unlabel_rigid_bodies() compound.label_rigid_bodies(discrete_bodies='Benzene', rigid_particles='C') assert compound.max_rigid_id == (n_benzenes*2) - 1 assert filled.max_rigid_id == n_benzenes - 1 assert filled2.max_rigid_id == (n_benzenes*2) - 1 assert len(list(compound.rigid_particles())) == n_benzenes * 2 * 6 assert len(list(filled.rigid_particles())) == n_benzenes * 6 assert len(list(filled2.rigid_particles())) == n_benzenes * 6 compound.unlabel_rigid_bodies() compound.label_rigid_bodies(discrete_bodies='CH') assert compound.max_rigid_id == (n_benzenes*2*6) - 1 assert filled.max_rigid_id == (n_benzenes*6) - 1 assert filled2.max_rigid_id == (n_benzenes*2*6) - 1 assert len(list(compound.rigid_particles())) == n_benzenes * 2 * 12 assert len(list(filled.rigid_particles())) == n_benzenes * 12 assert len(list(filled2.rigid_particles())) == n_benzenes * 12
def test_resnames_mdtraj(self, h2o, ethane): system = mb.Compound([h2o, mb.clone(h2o), ethane]) traj = system.to_trajectory(residues=['Ethane', 'H2O']) residues = list(traj.top.residues) assert traj.n_residues == 3 assert residues[0].name == 'H2O' assert residues[1].name == 'H2O' assert residues[2].name == 'Ethane' traj = system.to_trajectory(residues='Ethane') residues = list(traj.top.residues) assert traj.n_residues == 2 assert residues[0].name == 'RES' assert residues[1].name == 'Ethane' traj = system.to_trajectory(residues=['Ethane']) residues = list(traj.top.residues) assert traj.n_residues == 2 assert residues[0].name == 'RES' assert residues[1].name == 'Ethane' traj = system.to_trajectory() residues = list(traj.top.residues) assert traj.n_residues == 1 assert residues[0].name == 'RES'
def test_resnames_parmed(self, h2o, ethane): system = mb.Compound([h2o, mb.clone(h2o), ethane]) struct = system.to_parmed(residues=['Ethane', 'H2O']) assert len(struct.residues) == 3 assert struct.residues[0].name == 'H2O' assert struct.residues[1].name == 'H2O' assert struct.residues[2].name == 'Ethane' assert sum(len(res.atoms) for res in struct.residues) == len(struct.atoms) struct = system.to_parmed(residues=['Ethane', 'H2O']) assert len(struct.residues) == 3 assert struct.residues[0].name == 'H2O' assert struct.residues[1].name == 'H2O' assert struct.residues[2].name == 'Ethane' assert sum(len(res.atoms) for res in struct.residues) == len(struct.atoms) struct = system.to_parmed(residues='Ethane') assert len(struct.residues) == 2 assert struct.residues[0].name == 'RES' assert struct.residues[1].name == 'Ethane' assert sum(len(res.atoms) for res in struct.residues) == len(struct.atoms) struct = system.to_parmed() assert len(struct.residues) == 1 assert struct.residues[0].name == 'RES' assert sum(len(res.atoms) for res in struct.residues) == len(struct.atoms)
def _connect_and_reconnect(chf, bond_vector): first = mb.clone(chf) second = mb.clone(chf) first.add(mb.Port(anchor=first[0], orientation=bond_vector, separation=0.075), label='up') second.add(mb.Port(anchor=second[0], orientation=-bond_vector, separation=0.075), label='down') c2h2f2 = mb.Compound(subcompounds=(first, second)) mb.force_overlap(first, first['up'], second['down']) fccf_dihedral_init = calc_dihedral(first[2].pos, first[0].pos, second[0].pos, second[2].pos) c2h2f2.remove_bond((first[0], second[0])) mb.force_overlap(first, first['port[0]'], second['port[0]']) fccf_dihedral_final = calc_dihedral(first[2].pos, first[0].pos, second[0].pos, second[2].pos) return fccf_dihedral_init, fccf_dihedral_final
def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345): """Solvate a compound in a box of solvent using packmol. Parameters ---------- solute : mb.Compound solvent : mb.Compound n_solvent : int box : mb.Box overlap : float Returns ------- solvated : mb.Compound """ if not PACKMOL: raise IOError("Packmol not found") box = _validate_box(box) if not isinstance(solvent, (list, set)): solvent = [solvent] if not isinstance(n_solvent, (list, set)): n_solvent = [n_solvent] # In angstroms for packmol. box_mins = box.mins * 10 box_maxs = box.maxs * 10 overlap *= 10 center_solute = (box_maxs + box_mins) / 2 # Build the input file for each compound and call packmol. solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1] solute_pdb = tempfile.mkstemp(suffix='.pdb')[1] solute.save(solute_pdb, overwrite=True) input_text = (PACKMOL_HEADER.format(overlap, solvated_pdb, seed) + PACKMOL_SOLUTE.format(solute_pdb, *center_solute)) for solv, m_solvent in zip(solvent, n_solvent): m_solvent = int(m_solvent) solvent_pdb = tempfile.mkstemp(suffix='.pdb')[1] solv.save(solvent_pdb, overwrite=True) input_text += PACKMOL_BOX.format(solvent_pdb, m_solvent, box_mins[0], box_mins[1], box_mins[2], box_maxs[0], box_maxs[1], box_maxs[2]) proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True) out, err = proc.communicate(input=input_text) if err: _packmol_error(out, err) # Create the topology and update the coordinates. solvated = Compound() solvated.add(solute) for solv, m_solvent in zip(solvent, n_solvent): for _ in range(m_solvent): solvated.add(clone(solv)) solvated.update_coordinates(solvated_pdb) return solvated
def test_remove_bond_add_ports(self, hydrogen): h_clone = mb.clone(hydrogen) h2 = mb.Compound(subcompounds=(hydrogen, h_clone)) mb.force_overlap(h_clone, h_clone['up'], hydrogen['up']) h2.remove_bond((h2[0], h2[1])) assert len(h2.all_ports()) == 2 assert len(hydrogen.all_ports()) == 1 assert len(h_clone.all_ports()) == 1
def fill_box(compound, n_compounds, box, overlap=0.2, seed=12345): """Fill a box with a compound using packmol. Parameters ---------- compound : mb.Compound or list of mb.Compound n_compounds : int or list of int box : mb.Box overlap : float Returns ------- filled : mb.Compound """ if not PACKMOL: msg = "Packmol not found." if sys.platform.startswith("win"): msg = (msg + " If packmol is already installed, make sure that the " "packmol.exe is on the path.") raise IOError(msg) box = _validate_box(box) if not isinstance(compound, (list, set)): compound = [compound] if not isinstance(n_compounds, (list, set)): n_compounds = [n_compounds] # In angstroms for packmol. box_mins = box.mins * 10 box_maxs = box.maxs * 10 overlap *= 10 # Build the input file for each compound and call packmol. filled_pdb = tempfile.mkstemp(suffix='.pdb')[1] input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed) for comp, m_compounds in zip(compound, n_compounds): m_compounds = int(m_compounds) compound_pdb = tempfile.mkstemp(suffix='.pdb')[1] comp.save(compound_pdb, overwrite=True) input_text += PACKMOL_BOX.format(compound_pdb, m_compounds, box_mins[0], box_mins[1], box_mins[2], box_maxs[0], box_maxs[1], box_maxs[2]) proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True) out, err = proc.communicate(input=input_text) if err: _packmol_error(out, err) # Create the topology and update the coordinates. filled = Compound() for comp, m_compounds in zip(compound, n_compounds): for _ in range(m_compounds): filled.add(clone(comp)) filled.update_coordinates(filled_pdb) return filled
def __init__(self): super(Hexane, self).__init__() self.add(propyl, 'propyl1') self.add(mb.clone(propyl), 'propyl2') mb.force_overlap(self['propyl1'], self['propyl1']['down'], self['propyl2']['down'])
def test_rigid_with_subcompounds2(self, rigid_benzene): rigid_benzene2 = mb.clone(rigid_benzene) compound = mb.Compound(subcompounds=[rigid_benzene, rigid_benzene2]) assert compound.max_rigid_id is 1 assert rigid_benzene.max_rigid_id is 0 assert rigid_benzene2.max_rigid_id is 1 assert len(list(compound.rigid_particles())) == 24 assert len(list(compound.rigid_particles(rigid_id=0))) == 12 assert len(list(compound.rigid_particles(rigid_id=1))) == 12
def __init__(self): super(MonoLJ, self).__init__() lj_proto = mb.Particle(name='LJ', pos=[0, 0, 0]) pattern = mb.Grid3DPattern(5, 5, 5) pattern.scale(5) for pos in pattern: lj_particle = clone(lj_proto) lj_particle.translate(pos) self.add(lj_particle)
def test_build_from_single_particle(self): compound = mb.Compound() compound.rigid_id = 0 atom = mb.Compound(name='atom') atom.rigid_id = 0 atom2 = mb.clone(atom) compound.add([atom, atom2], reset_rigid_ids=False) assert compound.contains_rigid == True assert compound.rigid_id is None assert compound.max_rigid_id is 0 assert len(list(compound.rigid_particles())) == 2
def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345): """Solvate a compound in a box of solvent using packmol. Parameters ---------- solute : mb.Compound solvent : mb.Compound n_solvent : int box : mb.Box overlap : float Returns ------- solvated : mb.Compound """ if not PACKMOL: raise IOError("Packmol not found") if isinstance(box, (list, tuple)): box = Box(lengths=box) n_solvent = int(n_solvent) solute_pdb = tempfile.mkstemp(suffix='.pdb')[1] solute.save(solute_pdb, overwrite=True) solvent_pdb = tempfile.mkstemp(suffix='.pdb')[1] solvent.save(solvent_pdb, overwrite=True) solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1] # In angstroms for packmol. box_lengths = box.lengths * 10 overlap *= 10 # center_solute = (-solute.center) * 10 center_solute = box_lengths/2 # Build the input file and call packmol. input_text = (PACKMOL_HEADER.format(overlap, solvated_pdb, seed) + PACKMOL_SOLUTE.format(solute_pdb, *center_solute) + PACKMOL_BOX.format(solvent_pdb, n_solvent, *box_lengths)) proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True) out, err = proc.communicate(input=input_text) if err: _packmol_error(out, err) # Create the topology and update the coordinates. solvated = Compound() solvated.add(solute) for _ in range(n_solvent): solvated.add(clone(solvent)) solvated.update_coordinates(solvated_pdb) return solvated
def test_chainnames_mdtraj(self, h2o, ethane): system = mb.Compound([h2o, mb.clone(h2o), ethane]) traj = system.to_trajectory(chains=['Ethane', 'H2O']) assert traj.n_chains == 3 traj = system.to_trajectory(chains='Ethane') assert traj.n_chains == 2 traj = system.to_trajectory(chains=['Ethane']) assert traj.n_chains == 2 traj = system.to_trajectory() assert traj.n_chains == 1
def test_create_semi_rigid_bodies_filled_no_increment(self, benzene_from_parts): n_benzenes = 10 filled = mb.fill_box(benzene_from_parts, n_compounds=n_benzenes, box=[0, 0, 0, 4, 4, 4]) filled.label_rigid_bodies(discrete_bodies='Benzene', rigid_particles='C') filled2 = mb.clone(filled) filled.add(filled2, reset_rigid_ids=False) assert filled.max_rigid_id == n_benzenes - 1 assert len(list(filled.rigid_particles())) == n_benzenes * 2 * 6 for rigid_id in range(n_benzenes): assert len(list(filled.rigid_particles(rigid_id=rigid_id))) == 12
def test_rigid_from_parts2(self, rigid_ch): rigid_ch_copy = mb.clone(rigid_ch) benzene = mb.Compound() benzene.add(rigid_ch_copy, reset_rigid_ids=False) current = rigid_ch_copy for _ in range(5): ch_new = mb.clone(rigid_ch) mb.force_overlap(move_this=ch_new, from_positions=ch_new['a'], to_positions=current['b']) current = ch_new benzene.add(ch_new, reset_rigid_ids=False) carbons = [p for p in benzene.particles_by_name('C')] benzene.add_bond((carbons[0],carbons[-1])) assert benzene.contains_rigid is True assert benzene.rigid_id is None assert benzene.max_rigid_id is 0 assert benzene.children[0].contains_rigid == True assert benzene.children[0].rigid_id is None assert len(list(benzene.rigid_particles(rigid_id=0))) == 12
def fill_box(compound, n_compounds, box, overlap=0.2, seed=12345): """Fill a box with a compound using packmol. Parameters ---------- compound : mb.Compound n_compounds : int box : mb.Box overlap : float Returns ------- filled : mb.Compound """ if not PACKMOL: msg = "Packmol not found." if sys.platform.startswith("win"): msg = (msg + " If packmol is already installed, make sure that the " "packmol.exe is on the path.") raise IOError(msg) if isinstance(box, (list, tuple)): box = Box(lengths=box) n_compounds = int(n_compounds) compound_pdb = tempfile.mkstemp(suffix='.pdb')[1] compound.save(compound_pdb, overwrite=True) filled_pdb = tempfile.mkstemp(suffix='.pdb')[1] # In angstroms for packmol. box_lengths = box.lengths * 10 overlap *= 10 # Build the input file and call packmol. input_text = (PACKMOL_HEADER.format(overlap, filled_pdb, seed) + PACKMOL_BOX.format(compound_pdb, n_compounds, *box_lengths)) proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True) out, err = proc.communicate(input=input_text) if err: _packmol_error(out, err) # Create the topology and update the coordinates. filled = Compound() for _ in range(n_compounds): filled.add(clone(compound)) filled.update_coordinates(filled_pdb) return filled
def __init__(self, anchor=None): super(Port, self).__init__(name='Port', port_particle=True) self.anchor = anchor up = Compound(name='subport', port_particle=True) up.add(Particle(name='G', pos=[0, 0, 0], port_particle=True), 'middle') up.add(Particle(name='G', pos=[0, 0.02, 0], port_particle=True), 'top') up.add(Particle(name='G', pos=[-0.02, -0.01, 0], port_particle=True), 'left') up.add(Particle(name='G', pos=[0.0, -0.02, 0.01], port_particle=True), 'right') down = clone(up) rotate_around_z(down, np.pi) self.add(up, 'up') self.add(down, 'down')
def test_rigid_with_subcompounds4(self, benzene): benzene.label_rigid_bodies(rigid_particles='C') benzene2 = mb.clone(benzene) compound = mb.Compound(subcompounds=[benzene, benzene2]) assert compound.contains_rigid is True assert compound.rigid_id is None assert compound.max_rigid_id is 1 assert benzene.contains_rigid is True assert benzene.rigid_id is None assert benzene.max_rigid_id is 0 assert benzene2.contains_rigid is True assert benzene2.rigid_id is None assert benzene2.max_rigid_id is 1 assert len(list(compound.rigid_particles())) == 12 assert len(list(compound.rigid_particles(rigid_id=0))) == 6 assert len(list(compound.rigid_particles(rigid_id=1))) == 6
def test_increment_rigid_id(self, rigid_benzene): compound = mb.Compound() rigid_benzene2 = mb.clone(rigid_benzene) compound.add(rigid_benzene) compound.add(rigid_benzene2) assert rigid_benzene.contains_rigid is True assert rigid_benzene.rigid_id is None assert rigid_benzene.max_rigid_id is 0 assert rigid_benzene2.contains_rigid is True assert rigid_benzene2.rigid_id is None assert rigid_benzene2.max_rigid_id is 1 assert compound.contains_rigid is True assert compound.rigid_id is None assert compound.max_rigid_id is 1 assert len(list(compound.rigid_particles(rigid_id=0))) == 12 assert len(list(compound.rigid_particles(rigid_id=1))) == 12
def __init__(self): super(TnpBox, self).__init__() tnp_proto = Tnp(ball_radius=5, n_chains=5, chain_length=8) pattern = mb.Grid3DPattern(3, 3, 3) pattern.scale(100) rnd = random.Random() rnd.seed(1928) for pos in pattern: tnp = clone(tnp_proto) mb.rotate_around_x(tnp, rnd.uniform(0, 2 * pi)) mb.rotate_around_y(tnp, rnd.uniform(0, 2 * pi)) mb.rotate_around_z(tnp, rnd.uniform(0, 2 * pi)) mb.translate(tnp, pos) self.add(tnp)
def test_update_coords_update_ports(self, ch2): distances = np.round([ch2.min_periodic_distance(port.pos, ch2[0].pos) for port in ch2.referenced_ports()], 5) orientations = np.round([port.pos - port.anchor.pos for port in ch2.referenced_ports()], 5) ch2_clone = mb.clone(ch2) ch2_clone[0].pos += [1, 1, 1] ch2_clone.save('ch2-shift.pdb') ch2.update_coordinates('ch2-shift.pdb') updated_distances = np.round([ch2.min_periodic_distance(port.pos, ch2[0].pos) for port in ch2.referenced_ports()], 5) updated_orientations = np.round([port.pos - port.anchor.pos for port in ch2.referenced_ports()], 5) assert np.array_equal(distances, updated_distances) assert np.array_equal(orientations, updated_orientations)
def test_intermol_conversion2(self, ethane, h2o): # 2 distinct Ethane objects. compound = mb.Compound([ethane, mb.clone(ethane), h2o]) molecule_types = [type(ethane), type(h2o)] intermol_system = compound.to_intermol(molecule_types=molecule_types) assert len(intermol_system.molecule_types) == 2 assert 'Ethane' in intermol_system.molecule_types assert 'H2O' in intermol_system.molecule_types assert len(intermol_system.molecule_types['Ethane'].bonds) == 7 assert len(intermol_system.molecule_types['H2O'].bonds) == 2 assert len(intermol_system.molecule_types['Ethane'].molecules) == 2 ethanes = list(intermol_system.molecule_types['Ethane'].molecules) assert len(ethanes[0].atoms) == len(ethanes[1].atoms) == 8 assert len(intermol_system.molecule_types['H2O'].molecules) == 1 h2os = list(intermol_system.molecule_types['H2O'].molecules) assert len(h2os[0].atoms) == 3
def test_increment_rigid_id_partial(self, benzene): compound = mb.Compound() benzene.label_rigid_bodies(rigid_particles='C') benzene2 = mb.clone(benzene) compound.add(benzene) compound.add(benzene2) assert benzene.contains_rigid is True assert benzene.rigid_id is None assert benzene.max_rigid_id is 0 assert benzene2.contains_rigid is True assert benzene2.rigid_id is None assert benzene2.max_rigid_id is 1 assert compound.contains_rigid is True assert compound.rigid_id is None assert compound.max_rigid_id is 1 assert len(list(compound.rigid_particles())) == 12 assert len(list(compound.rigid_particles(rigid_id=0))) == 6 assert len(list(compound.rigid_particles(rigid_id=1))) == 6
def run_md_main(molecule, functional, project_name, dire, temperature, box_length, number_of_molecules, simulation_time, CUTOFF, SCF_tolerence, basis_set, ensemble, timestep, thermostat): current_molecule = mb.clone(molecule) current_molecule = current_molecule.to_parmed() atom_list, mass_list, total_mass = info_molecule(current_molecule) unique_atom_list = remove_duplicate(atom_list) num_atoms = len(atom_list) num_unique_atoms = len(unique_atom_list) total_atoms = num_atoms * number_of_molecules string = "tail -{} {}-pos-1.xyz > {}.xyz".format(total_atoms, project_name + "pre", project_name) subprocess.call(string, shell=True) if basis_set[0] == None: basis_set = basis_set * num_unique_atoms if thermostat == None: thermostat = 'NOSE' if CUTOFF == None: CUTOFF = 900 if SCF_tolerence == None: SCF_tolerence = 1e-6 if ensemble == None: ensemble = 'NVT' #we need to decide time_step if timestep == None: lightest = min(mass_list) if lightest < 1.5: time_step = 0.5 elif (lightest >= 1.5) and (lightest < 40): time_step = 1 if lightest >= 40: time_step = 1.5 steps = round(simulation_time * 1000 / time_step) mySim = sim.SIM() mySim.GLOBAL.RUN_TYPE = "MD" mySim.GLOBAL.PROJECT = project_name mySim.GLOBAL.PRINT_LEVEL = "LOW" #FORCE EVAL SECTION mySim.FORCE_EVAL.METHOD = 'QUICKSTEP' mySim.FORCE_EVAL.STRESS_TENSOR = 'ANALYTICAL' mySim.FORCE_EVAL.DFT.BASIS_SET_FILE_NAME = dire + 'BASIS_MOLOPT' mySim.FORCE_EVAL.DFT.POTENTIAL_FILE_NAME = dire + 'GTH_POTENTIALS' mySim.FORCE_EVAL.DFT.CHARGE = 0 mySim.FORCE_EVAL.DFT.MULTIPLICITY = 1 mySim.FORCE_EVAL.DFT.MGRID.CUTOFF = CUTOFF mySim.FORCE_EVAL.DFT.MGRID.REL_CUTOFF = 50 mySim.FORCE_EVAL.DFT.MGRID.NGRIDS = 4 mySim.FORCE_EVAL.DFT.QS.METHOD = 'GPW' mySim.FORCE_EVAL.DFT.QS.EPS_DEFAULT = 1E-8 mySim.FORCE_EVAL.DFT.QS.EXTRAPOLATION = 'ASPC' mySim.FORCE_EVAL.DFT.POISSON.PERIODIC = "XYZ" mySim.FORCE_EVAL.DFT.PRINT.E_DENSITY_CUBE.SECTION_PARAMETERS = "OFF" mySim.FORCE_EVAL.DFT.SCF.SCF_GUESS = 'ATOMIC' mySim.FORCE_EVAL.DFT.SCF.MAX_SCF = 2 mySim.FORCE_EVAL.DFT.SCF.EPS_SCF = SCF_tolerence mySim.FORCE_EVAL.DFT.SCF.OT.SECTION_PARAMETERS = ".TRUE." mySim.FORCE_EVAL.DFT.SCF.OT.PRECONDITIONER = "FULL_SINGLE_INVERSE" mySim.FORCE_EVAL.DFT.SCF.OT.MINIMIZER = "DIIS" mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.SECTION_PARAMETERS = '.TRUE.' mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.MAX_SCF = 2 mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.EPS_SCF = 1e-6 mySim.FORCE_EVAL.DFT.SCF.PRINT.RESTART.SECTION_PARAMETERS = 'OFF' mySim.FORCE_EVAL.DFT.SCF.PRINT.DM_RESTART_WRITE = '.TRUE.' mySim.FORCE_EVAL.DFT.XC.XC_FUNCTIONAL.SECTION_PARAMETERS = functional mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.POTENTIAL_TYPE = 'PAIR_POTENTIAL' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.TYPE = 'DFTD3' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.PARAMETER_FILE_NAME = 'dftd3.dat' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.REFERENCE_FUNCTIONAL = functional mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.R_CUTOFF = 11 mySim.FORCE_EVAL.SUBSYS.COORD.DEFAULT_KEYWORD = project_name + ".xyz" mySim.FORCE_EVAL.SUBSYS.init_atoms(num_atoms) for i in range(num_unique_atoms): mySim.FORCE_EVAL.SUBSYS.KIND[ i + 1].SECTION_PARAMETERS = unique_atom_list[i] if basis_set[i] == None: mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set_setter( unique_atom_list[i]) else: mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set[i] mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].POTENTIAL = potential( unique_atom_list[i], functional) mySim.FORCE_EVAL.SUBSYS.CELL.ABC = '{L} {L} {L}'.format(L=10 * box_length) #MOTION SECTION mySim.MOTION.GEO_OPT.OPTIMIZER = 'BFGS' mySim.MOTION.GEO_OPT.MAX_ITER = 100 mySim.MOTION.GEO_OPT.MAX_DR = 0.003 mySim.MOTION.MD.ENSEMBLE = ensemble mySim.MOTION.MD.STEPS = steps mySim.MOTION.MD.TIMESTEP = time_step mySim.MOTION.MD.TEMPERATURE = temperature mySim.MOTION.MD.THERMOSTAT.TYPE = thermostat mySim.MOTION.MD.THERMOSTAT.REGION = "MASSIVE" mySim.MOTION.MD.THERMOSTAT.NOSE.LENGTH = 3 mySim.MOTION.MD.THERMOSTAT.NOSE.YOSHIDA = 3 mySim.MOTION.MD.THERMOSTAT.NOSE.TIMECON = 1000.0 mySim.MOTION.MD.THERMOSTAT.NOSE.MTS = 2 #mySim.MOTION.MD.PRINT.ENERGY.EACH.MD = 20 #mySim.MOTION.MD.PRINT.PROGRAM_RUN_INFO.EACH.MD = 20 #mySim.MOTION.MD.AVERAGES.SECTION_PARAMETERS= ".falbmbjse." mySim.MOTION.PRINT.STRESS.SECTION_PARAMETERS = 'OFF' mySim.MOTION.PRINT.TRAJECTORY.EACH.MD = 1 mySim.MOTION.PRINT.VELOCITIES.SECTION_PARAMETERS = 'OFF' mySim.MOTION.PRINT.FORCES.SECTION_PARAMETERS = "OFF" mySim.MOTION.PRINT.RESTART_HISTORY.SECTION_PARAMETERS = "ON" mySim.MOTION.PRINT.RESTART_HISTORY.EACH.MD = 500 mySim.MOTION.PRINT.RESTART.SECTION_PARAMETERS = "ON" mySim.MOTION.PRINT.RESTART.BACKUP_COPIES = 3 mySim.MOTION.PRINT.RESTART.EACH.MD = 1 mySim.write_changeLog(fn="md-main-changeLog.out") mySim.write_errorLog() mySim.write_inputFile(fn='md-main.inp')
def md_files(instance): molecules = instance.molecules functional = instance.functional box = instance.box cutoff = instance.cutoff scf_tolerance = instance.scf_tolerance basis_set = instance.basis_set basis_set_filename = instance.basis_set_filename potential_filename = instance.potential_filename fixed_list = instance.fixed_list periodicity = instance.periodicity simulation_time = instance.simulation_time time_step = instance.time_step ensemble = instance.ensemble project_name = instance.project_name temperature = instance.temperature pressure = instance.pressure n_molecules = instance.n_molecules thermostat = instance.thermostat traj_type = instance.traj_type traj_freq = instance.traj_freq seed = instance.seed input_filename = instance.input_filename output_filename = instance.output_filename filled_box = mb.packing.fill_box(compound=molecules, n_compounds=n_molecules, box=box) initial_coord_file = project_name + ".xyz" filled_box.save(initial_coord_file, overwrite='True') with open(initial_coord_file, 'r') as fin: data = fin.read().splitlines(True) with open(initial_coord_file, 'w') as fout: fout.writelines(data[2:]) #deleting first two lines print('MD initial structure saved as {}'.format(initial_coord_file)) atom_list = [] mass_list = [] for i in range(len(molecules)): current_molecule = mb.clone(molecules[i]) current_molecule_pmd = current_molecule.to_parmed() x, y = info_molecule(current_molecule_pmd) atom_list.extend(x) mass_list.extend(y) unique_atom_list = remove_duplicate(atom_list) num_atoms = len(atom_list) num_unique_atoms = len(unique_atom_list) unique_atom_list.sort() mySim = sim.SIM() #setting defaults n_steps = int(simulation_time / time_step) mySim.GLOBAL.RUN_TYPE = "MD" mySim.GLOBAL.PROJECT_NAME = project_name mySim.GLOBAL.PRINT_LEVEL = "LOW" mySim.GLOBAL.SEED = seed #FORCE EVAL SECTION mySim.FORCE_EVAL.METHOD = 'QUICKSTEP' mySim.FORCE_EVAL.STRESS_TENSOR = 'ANALYTICAL' mySim.FORCE_EVAL.DFT.BASIS_SET_FILE_NAME = basis_set_filename mySim.FORCE_EVAL.DFT.POTENTIAL_FILE_NAME = potential_filename mySim.FORCE_EVAL.DFT.CHARGE = 0 mySim.FORCE_EVAL.DFT.MULTIPLICITY = 1 mySim.FORCE_EVAL.DFT.MGRID.CUTOFF = cutoff mySim.FORCE_EVAL.DFT.MGRID.REL_CUTOFF = 50 mySim.FORCE_EVAL.DFT.MGRID.NGRIDS = 4 mySim.FORCE_EVAL.DFT.QS.METHOD = 'GPW' mySim.FORCE_EVAL.DFT.QS.EPS_DEFAULT = 1E-7 mySim.FORCE_EVAL.DFT.QS.EXTRAPOLATION = 'ASPC' mySim.FORCE_EVAL.DFT.POISSON.PERIODIC = periodicity mySim.FORCE_EVAL.DFT.PRINT.E_DENSITY_CUBE.SECTION_PARAMETERS = "OFF" mySim.FORCE_EVAL.DFT.SCF.SCF_GUESS = 'ATOMIC' mySim.FORCE_EVAL.DFT.SCF.MAX_SCF = 30 mySim.FORCE_EVAL.DFT.SCF.EPS_SCF = scf_tolerance mySim.FORCE_EVAL.DFT.SCF.OT.SECTION_PARAMETERS = ".TRUE." mySim.FORCE_EVAL.DFT.SCF.OT.PRECONDITIONER = "FULL_SINGLE_INVERSE" mySim.FORCE_EVAL.DFT.SCF.OT.MINIMIZER = "DIIS" mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.SECTION_PARAMETERS = '.TRUE.' mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.MAX_SCF = 10 mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.EPS_SCF = 1e-6 mySim.FORCE_EVAL.DFT.SCF.PRINT.RESTART.SECTION_PARAMETERS = 'OFF' mySim.FORCE_EVAL.DFT.SCF.PRINT.DM_RESTART_WRITE = '.TRUE.' mySim.FORCE_EVAL.DFT.XC.XC_FUNCTIONAL.SECTION_PARAMETERS = functional mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.POTENTIAL_TYPE = 'PAIR_POTENTIAL' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.TYPE = 'DFTD3' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.PARAMETER_FILE_NAME = 'dftd3.dat' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.REFERENCE_FUNCTIONAL = functional mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.R_CUTOFF = 8 mySim.FORCE_EVAL.SUBSYS.COORD.DEFAULT_KEYWORD = project_name + ".xyz" mySim.FORCE_EVAL.SUBSYS.init_atoms(num_atoms) for i in range(num_unique_atoms): mySim.FORCE_EVAL.SUBSYS.KIND[ i + 1].SECTION_PARAMETERS = unique_atom_list[i] if basis_set == [None]: mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set_setter( unique_atom_list[i]) else: mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set[ unique_atom_list[i]] mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].POTENTIAL = potential( unique_atom_list[i], functional) mySim.FORCE_EVAL.SUBSYS.CELL.ABC = '{a} {b} {c}'.format( a=10 * box.lengths[0], b=10 * box.lengths[1], c=10 * box.lengths[2]) mySim.FORCE_EVAL.SUBSYS.CELL.ALPHA_BETA_GAMMA = '{a} {b} {c}'.format( a=box.angles[0], b=box.angles[1], c=box.angles[2]) #MOTION SECTION mySim.MOTION.GEO_OPT.OPTIMIZER = 'BFGS' mySim.MOTION.GEO_OPT.MAX_ITER = 100 mySim.MOTION.GEO_OPT.MAX_DR = 0.003 mySim.MOTION.MD.ENSEMBLE = ensemble mySim.MOTION.MD.STEPS = n_steps mySim.MOTION.MD.TIMESTEP = time_step if temperature_ensemble(ensemble): mySim.MOTION.MD.TEMPERATURE = temperature mySim.MOTION.MD.THERMOSTAT.TYPE = thermostat mySim.MOTION.MD.THERMOSTAT.REGION = "MASSIVE" mySim.MOTION.MD.THERMOSTAT.NOSE.LENGTH = 5 mySim.MOTION.MD.THERMOSTAT.NOSE.YOSHIDA = 3 mySim.MOTION.MD.THERMOSTAT.NOSE.TIMECON = 1000.0 mySim.MOTION.MD.THERMOSTAT.NOSE.MTS = 2 if pressure_ensemble(ensemble): mySim.MOTION.MD.BAROSTAT.PRESSURE = pressure if pressure == None: print('you need to define pressure') if fixed_list is not None: mySim.MOTION.CONSTRAINT.FIXED_ATOMS.LIST = fixed_list mySim.MOTION.MD.THERMOSTAT.REGION = "GLOBAL" if periodicity == 'NONE': mySim.FORCE_EVAL.DFT.POISSON.PERIODIC = 'NONE' mySim.FORCE_EVAL.DFT.POISSON.POISSON_SOLVER = 'WAVELET' if not is_cubic(box) and periodicity == 'NONE': print( 'The box should be cubic for non-periodic calculations and the box must be around 15 times the size of the molecule' ) mySim.MOTION.PRINT.STRESS.SECTION_PARAMETERS = 'OFF' mySim.MOTION.PRINT.TRAJECTORY.EACH.MD = traj_freq mySim.MOTION.PRINT.TRAJECTORY.FORMAT = traj_type mySim.MOTION.PRINT.VELOCITIES.SECTION_PARAMETERS = 'OFF' mySim.MOTION.PRINT.FORCES.SECTION_PARAMETERS = "OFF" mySim.MOTION.PRINT.RESTART_HISTORY.SECTION_PARAMETERS = "ON" mySim.MOTION.PRINT.RESTART_HISTORY.EACH.MD = 500 mySim.MOTION.PRINT.RESTART.SECTION_PARAMETERS = "ON" mySim.MOTION.PRINT.RESTART.BACKUP_COPIES = 3 mySim.MOTION.PRINT.RESTART.EACH.MD = 1 mySim.write_changeLog(fn="md-changeLog.out") mySim.write_errorLog() mySim.write_inputFile(fn=input_filename) print('MD input file saved as {}'.format(input_filename))
def __init__(self, lipids, ref_atoms, n_lipids_x=10, n_lipids_y=10, area_per_lipid=1.0, lipid_box=None, spacing_z=None, solvent=None, solvent_per_lipid=None, n_solvent=None, solvent_density=None, solvent_mass=None, tilt=0, random_seed=12345, mirror=True): super(Bilayer, self).__init__() # Santitize inputs i = 0 while i < len(lipids): if lipids[i][1] == 0: lipids.remove(i) else: i += 1 if sum([lipid[1] for lipid in lipids]) != 1.0: raise ValueError('Lipid fractions do not add up to 1.') assert len(ref_atoms) == len(lipids) self.lipids = lipids self.ref_atoms = ref_atoms self._lipid_box = lipid_box # 2D Lipid locations self.n_lipids_x = n_lipids_x self.n_lipids_y = n_lipids_y self.apl = area_per_lipid self.n_lipids_per_layer = self.n_lipids_x * self.n_lipids_y self.pattern = mb.Grid2DPattern(n_lipids_x, n_lipids_y) self.pattern.scale(np.sqrt(self.apl * self.n_lipids_per_layer)) # Z-orientation parameters self.tilt = tilt if spacing_z: self.spacing = spacing_z else: self.spacing = max([lipid[0].boundingbox.lengths[2] for lipid in self.lipids]) * np.cos(self.tilt) # Solvent parameters if solvent: self.solvent = solvent if solvent_per_lipid != None: self.n_solvent = (self.n_lipids_x * self.n_lipids_y * 2 * solvent_per_lipid) elif n_solvent != None: self.n_solvent = n_solvent else: raise ValueError("Requires either n_solvent_per_lipid ", "or n_solvent arguments") # Other parameters self.random_seed = random_seed self.mirror = mirror # Initialize variables and containers for lipids and solvent self.lipid_components = mb.Compound() self.solvent_components = mb.Compound() self._number_of_each_lipid_per_layer = [] # Assemble the lipid layers seed(self.random_seed) self.tilt_about = [random(), random(), 0] top_layer, top_lipid_labels = self.create_layer() self.lipid_components.add(top_layer) if self.mirror == True: bottom_layer, bottom_lipid_labels = self.create_layer( lipid_indices=top_lipid_labels, flip_orientation=True) else: bottom_layer, bottom_lipid_labels = self.create_layer( flip_orientation=True) bottom_layer.translate([0, 0, 0]) self.lipid_components.add(bottom_layer) self.lipid_components.translate_to([0, 0, 0]) # Assemble solvent components solvent_top = self.solvate(solvent_mass, solvent_density) solvent_bot = mb.clone(solvent_top) solvent_top.translate_to( [0, 0, self.spacing + 0.5 * solvent_top.boundingbox.lengths[2]] ) solvent_bot.translate_to( [0, 0, -self.spacing - 0.5 * solvent_top.boundingbox.lengths[2]] ) self.solvent_components.add(solvent_top) self.solvent_components.add(solvent_bot) # Add all compounds self.add(self.lipid_components) self.add(self.solvent_components)
def populate(self, compound_dict=None, x=1, y=1, z=1): """Expand lattice and create compound from lattice. Expands lattice based on user input. The user must also pass in a dictionary that contains the keys that exist in the basis_dict. The corresponding Compound will be the full lattice returned to the user. If no dictionary is passed to the user, Dummy Compounds will be used. Parameters ---------- x : int, optional, default=1 How many iterations in the x direction. y : int, optional, default=1 How many iterations in the y direction. z : int, optional, default=1 How many iterations in the z direction. compound_dict : dictionary, optional, default=None Link between basis_dict and Compounds. Exceptions Raised ----------------- ValueError : incorrect x,y, or z values. TypeError : incorrect type for basis vector Call Restrictions ----------------- Called after constructor by user. """ error_dict = {0: 'X', 1: 'Y', 2: 'Z'} # Make sure neither x, y, z input is None if None in [x, y, z]: raise ValueError('Attempt to replicate None times. ' 'None is not an acceptable replication ' 'amount, 1 is the default.') # Try to convert x, y, and z to int, raise a ValueError if fail try: x = int(x) y = int(y) z = int(z) except (ValueError, TypeError): raise ValueError('Cannot convert replication amounts into ' 'integers. x= {}, y= {}, z= {} needs to ' 'be an int.'.format(x, y, z)) for replication_amount, index in zip([x, y, z], range(3)): if replication_amount < 1: raise ValueError( 'Incorrect populate value: {} : {} is < 1. '.format( error_dict[index], replication_amount)) if ((isinstance(compound_dict, dict)) or (compound_dict is None)): pass else: raise TypeError('Compound dictionary is not of type dict. ' '{} was passed.'.format(type(compound_dict))) cell = defaultdict(list) [a, b, c] = self.lattice_spacing transform_mat = self.lattice_vectors # Unit vectors transform_mat = np.asarray(transform_mat, dtype=np.float64) transform_mat = np.reshape(transform_mat, newshape=(3, 3)) norms = np.linalg.norm(transform_mat, axis=1) # Normalized vectors for change of basis unit_vecs = np.divide(transform_mat.transpose(), norms) # Generate new coordinates for key, locations in self.lattice_points.items(): for coords in locations: for replication in it.product(range(x), range(y), range(z)): new_coords = np.asarray(coords, dtype=np.float64) new_coords = np.reshape(new_coords, (1, 3), order='C') new_coords[0][0] = new_coords[0][0] + replication[0] new_coords[0][1] = new_coords[0][1] + replication[1] new_coords[0][2] = new_coords[0][2] + replication[2] # Change of basis to cartesian new_coords = np.dot(unit_vecs, new_coords.transpose()) new_coords[0] = new_coords[0] * a new_coords[1] = new_coords[1] * b new_coords[2] = new_coords[2] * c new_coords = np.reshape(new_coords, (1, 3), order='C') tuple_of_coords = tuple(new_coords.flatten()) cell[key].append(tuple_of_coords) ret_lattice = mb.Compound() # Create (clone) a mb.Compound for the newly generate positions if compound_dict is None: for key_id, all_pos in cell.items(): particle = mb.Compound(name=key_id, pos=[0, 0, 0]) for pos in all_pos: particle_to_add = mb.clone(particle) particle_to_add.translate_to(list(pos)) ret_lattice.add(particle_to_add) else: for key_id, all_pos in cell.items(): if isinstance(compound_dict[key_id], mb.Compound): compound_to_move = compound_dict[key_id] for pos in all_pos: tmp_comp = mb.clone(compound_to_move) tmp_comp.translate_to(list(pos)) ret_lattice.add(tmp_comp) else: err_type = type(compound_dict.get(key_id)) raise TypeError('Invalid type in provided Compound ' 'dictionary. For key {}, type: {} was ' 'provided, not mbuild.Compound.'.format( key_id, err_type)) # set periodicity ret_lattice.periodicity = np.asarray([a * x, b * y, c * z], dtype=np.float64) warn('Periodicity of non-rectangular lattices are not valid with ' 'default boxes. Only rectangular lattices are valid ' 'at this time.') # if coordinates are below a certain threshold, set to 0 tolerance = 1e-12 ret_lattice.xyz_with_ports[ ret_lattice.xyz_with_ports <= tolerance] = 0. return ret_lattice
from __future__ import division import numpy as np import mbuild as mb from atools.patterns.random_hemisphere_pattern import RandomHemispherePattern class TipPattern(RandomHemispherePattern): def __init__(self, chain_density, tip_radius, seed=12345): area = 2 * np.pi * tip_radius**2 n = int(round(area * chain_density)) super(TipPattern, self).__init__(n=n, seed=seed, scale=tip_radius) if __name__ == "__main__": pattern = TipPattern(chain_density=2.0, tip_radius=2.0) lj_proto = mb.Compound(name='LJ') lj_box = mb.Compound() for pos in pattern: lj_particle = mb.clone(lj_proto) lj_particle.translate(pos) lj_box.add(lj_particle) lj_box.save('tip-pattern.xyz', overwrite=True)
def apply_to_compound(self, guest, guest_port_name='down', host=None, backfill=None, backfill_port_name='up', scale=True): """Attach copies of a guest Compound to Ports on a host Compound. Parameters ---------- guest : mb.Compound The Compound prototype to be applied to the host Compound guest_port_name : str, optional, default='down' The name of the port located on `guest` to attach to the host host : mb.Compound, optional, default=None A Compound with available ports to add copies of `guest` to backfill : mb.Compound, optional, default=None A Compound to add to the remaining available ports on `host` after clones of `guest` have been added for each point in the pattern backfill_port_name : str, optional, default='up' The name of the port located on `backfill` to attach to the host scale : bool, optional, default=True Scale the points in the pattern to the lengths of the `host`'s `boundingbox` and shift them by the `boundingbox`'s mins Returns ------- guests : list of mb.Compound List of inserted guest compounds on host compound backfills : list of mb.Compound List of inserted backfill compounds on host compound """ n_ports = len(host.available_ports()) assert n_ports >= self.points.shape[0], "Not enough ports for pattern." assert_port_exists(guest_port_name, guest) box = host.boundingbox if scale: self.scale(box.lengths) self.points += box.mins pattern = self.points port_positions = np.empty(shape=(n_ports, 3)) port_list = list() for port_idx, port in enumerate(host.available_ports()): port_positions[port_idx, :] = port['up']['middle'].pos port_list.append(port) used_ports = set() # Keep track of used ports for backfilling. guests = [] for point in pattern: closest_point_idx = np.argmin( host.min_periodic_distance(point, port_positions)) closest_port = port_list[closest_point_idx] used_ports.add(closest_port) # Attach the guest to the closest port. new_guest = clone(guest) force_overlap(new_guest, new_guest.labels[guest_port_name], closest_port) guests.append(new_guest) # Move the port as far away as possible (simpler than removing it). # There may well be a more elegant/efficient way of doing this. port_positions[closest_point_idx, :] = np.array( [np.inf, np.inf, np.inf]) backfills = [] if backfill: assert_port_exists(backfill_port_name, backfill) # Attach the backfilling Compound to unused ports. for port in port_list: if port not in used_ports: new_backfill = clone(backfill) # Might make sense to have a backfill_port_name option... force_overlap(new_backfill, new_backfill.labels[backfill_port_name], port) backfills.append(new_backfill) return guests, backfills
def test_spin_x_eq(self, sixpoints): compound2 = mb.clone(sixpoints) sixpoints.spin(np.pi * 1.23456789, np.asarray([1.0, 0.0, 0.0])) compound2.spin(np.pi * 1.23456789, around=np.asarray([1, 0, 0])) assert (np.allclose(compound2.xyz, sixpoints.xyz, atol=1e-16))
def solvate( solute, solvent, n_solvent, box, overlap=0.2, seed=12345, sidemax=100.0, edge=0.2, fix_orientation=False, temp_file=None, update_port_locations=False, ): """Solvate a compound in a box of solvent using PACKMOL. Parameters ---------- solute : mb.Compound Compound to be placed in a box and solvated. solvent : mb.Compound Compound to solvate the box. n_solvent : int Number of solvents to be put in box. box : mb.Box Box to be filled by compounds. overlap : float, units nm, default=0.2 Minimum separation between atoms of different molecules. seed : int, default=12345 Random seed to be passed to PACKMOL. sidemax : float, optional, default=100.0 Needed to build an initial approximation of the molecule distribution in PACKMOL. All system coordinates must fit with in +/- sidemax, so increase sidemax accordingly to your final box size edge : float, units nm, default=0.2 Buffer at the edge of the box to not place molecules. This is necessary in some systems because PACKMOL does not account for periodic boundary conditions in its optimization. fix_orientation : bool Specify if solvent should not be rotated when filling box, default=False. temp_file : str, default=None File name to write PACKMOL raw output to. update_port_locations : bool, default=False After packing, port locations can be updated, but since compounds can be rotated, port orientation may be incorrect. Returns ------- solvated : mb.Compound """ # check that the user has the PACKMOL binary on their PATH _check_packmol(PACKMOL) (box, min_tmp, max_tmp) = _validate_box(box) if not isinstance(solvent, (list, set)): solvent = [solvent] if not isinstance(n_solvent, (list, set)): n_solvent = [n_solvent] if not isinstance(fix_orientation, (list, set)): fix_orientation = [fix_orientation] * len(solvent) if len(solvent) != len(n_solvent): raise ValueError( "`n_solvent` and `n_solvent` must be of equal length.") # In angstroms for packmol. box_mins = np.asarray(min_tmp) * 10 box_maxs = np.asarray(max_tmp) * 10 overlap *= 10 center_solute = (box_maxs + box_mins) / 2 # Apply edge buffer box_maxs = np.subtract(box_maxs, edge * 10) box_mins = np.add(box_mins, edge * 10) # Build the input file for each compound and call packmol. solvated_xyz = _new_xyz_file() solute_xyz = _new_xyz_file() # generate list of temp files for the solvents solvent_xyz_list = list() try: solute.save(solute_xyz.name, overwrite=True) input_text = PACKMOL_HEADER.format( overlap, solvated_xyz.name, seed, sidemax * 10) + PACKMOL_SOLUTE.format(solute_xyz.name, *center_solute.tolist()) for (solv, m_solvent, rotate) in zip(solvent, n_solvent, fix_orientation): m_solvent = int(m_solvent) solvent_xyz = _new_xyz_file() solvent_xyz_list.append(solvent_xyz) solv.save(solvent_xyz.name, overwrite=True) input_text += PACKMOL_BOX.format( solvent_xyz.name, m_solvent, box_mins[0], box_mins[1], box_mins[2], box_maxs[0], box_maxs[1], box_maxs[2], PACKMOL_CONSTRAIN if rotate else "", ) _run_packmol(input_text, solvated_xyz, temp_file) # Create the topology and update the coordinates. solvated = Compound() solvated.add(clone(solute)) solvated = _create_topology(solvated, solvent, n_solvent) solvated.update_coordinates( solvated_xyz.name, update_port_locations=update_port_locations) finally: for file_handle in solvent_xyz_list: file_handle.close() os.unlink(file_handle.name) solvated_xyz.close() solute_xyz.close() os.unlink(solvated_xyz.name) os.unlink(solute_xyz.name) return solvated
def __init__(self, radius=2, angle=90.0, fluid=None, density=None, lattice=None, lattice_compound=None, x=None, y=None): super(Droplet, self).__init__() if fluid is None: raise ValueError('Fluid droplet compounds must be specified') if density is None: raise ValueError('Fluid density must be specified (units kg/m^3)') if x: if x < radius * 4: raise ValueError( 'Dimension x of sheet must be at least radius * 4') elif x > 100: raise ValueError( 'Dimension x of sheet must be less than 100 nm') else: x = radius * 4 if y: if y < radius * 4: raise ValueError( 'Dimension y of sheet must be at least radius * 4') elif y > 100: raise ValueError( 'Dimension y of sheet must be less than 100 nm') else: y = radius * 4 # Default to graphene lattice if lattice is None: if lattice_compound is not None: raise ValueError( 'If Lattice is None, defaults to a Graphene surface. ' + 'In this case, do not specify lattice_compound.') lattice_compound = mbuild.Compound(name='C') lattice_spacing = [0.2456, 0.2456, 0.335] angles = [90.0, 90.0, 120.0] carbon_locations = [[0, 0, 0], [2 / 3, 1 / 3, 0]] basis = {lattice_compound.name: carbon_locations} lattice = mbuild.Lattice(lattice_spacing=lattice_spacing, angles=angles, lattice_points=basis) compound_dict = {lattice_compound.name: lattice_compound} factor = np.cos(np.pi / 6) # fixes non-cubic lattice # Estimate the number of lattice repeat units replicate = [int(x / 0.2456), int(y / 0.2456) * (1 / factor)] lat = lattice.populate(compound_dict=compound_dict, x=replicate[0], y=replicate[1], z=3) for particle in lat.particles(): if particle.xyz[0][0] < 0: particle.xyz[0][0] += lat.periodicity[0] lat.periodicity[1] *= factor else: if lattice_compound is None: raise ValueError('Lattice compounds must be specified') if not np.all(lattice.angles == 90.0): raise ValueError( 'Currently, only cubic lattices are supported. ' + 'If using Graphene, do not pass in a Lattice.') compound_dict = {lattice_compound.name: lattice_compound} lat = lattice.populate(compound_dict=compound_dict, x=int(x / lattice.lattice_spacing[0]), y=int(y / lattice.lattice_spacing[1]), z=int(1.5 / lattice.lattice_spacing[2])) sheet = mbuild.clone(lat) self.surface_height = np.max(sheet.xyz, axis=0)[2] coords = list(sheet.periodicity) height = get_height(radius, angle) sphere_coords = [coords[0] / 2, coords[1] / 2, radius, radius] sphere = mbuild.fill_sphere(compound=fluid, sphere=sphere_coords, density=density) to_remove = [] for child in sphere.children: for atom_coords in child.xyz: if height > radius: if atom_coords[2] < height - radius: to_remove += child break else: if atom_coords[2] < height: to_remove += child break sphere.remove(to_remove) sheet.name = 'LAT' sphere.name = 'FLD' sphere.xyz -= [0, 0, np.min(sphere.xyz, axis=0)[2]] sphere.xyz += [0, 0, self.surface_height + 0.3] self.add(sheet) self.add(sphere) self.periodicity[0] = sheet.periodicity[0] self.periodicity[1] = sheet.periodicity[1] self.periodicity[2] = radius * 5
def _add_backfill(self, backfill, backfill_port_name): for port in self['surface'].available_ports(): backfill_clone = mb.clone(backfill) mb.force_overlap(backfill_clone, backfill_clone[backfill_port_name], port) self.add(backfill_clone)
def __init__(self, chain_length, internal_group, locations, terminal_group): super(Alkylsilane, self).__init__() module = __import__('atools.lib.moieties.one_port.' + terminal_group) class_ = getattr(module.lib.moieties.one_port, terminal_group.title()) tgroup = class_() hmodule = __import__('atools.lib.moieties.multiple_ports.' + internal_group) hclass_ = getattr(hmodule.lib.moieties.multiple_ports, internal_group.title()) hgroup = hclass_() # Determine alkane segments if isinstance(locations, int): locations = [locations] locations.sort() silane = Silane() self.add(silane, 'silane') self.add(silane['down'], 'down', containment=False) if 0 in locations: ''' self.add(hgroup, 'hgroup') mb.force_overlap(self['silane'], self['silane']['up'], self['hgroup']['down']) ''' current_segment = silane else: first_length = locations[0] first_segment = Alkane(first_length, cap_front=False, cap_end=False) self.add(first_segment, 'bottom_chain') mb.force_overlap(self['silane'], self['silane']['up'], self['bottom_chain']['down']) current_segment = first_segment c_remove = 0 if internal_group in ['amide', 'hemiacetal']: c_remove += 1 for i, loc in enumerate(locations[1:]): hgroup_clone = mb.clone(hgroup) self.add(hgroup_clone, 'hgroup{}'.format(i + 1)) mb.force_overlap(self['hgroup{}'.format(i + 1)], self['hgroup{}'.format(i + 1)]['down'], current_segment['up']) current_segment = hgroup_clone length = loc - locations[i] - 1 - c_remove if length > 0: segment = Alkane(length, cap_front=False, cap_end=False) self.add(segment, 'internal_chain{}'.format(i + 1)) current_segment = segment mb.force_overlap( self['internal_chain{}'.format(i + 1)], self['internal_chain{}'.format(i + 1)]['down'], self['hgroup{}'.format(i + 1)]['up']) self.add(hgroup, 'hgroup') mb.force_overlap(self['hgroup'], self['hgroup']['down'], current_segment['up']) last_length = chain_length - locations[-1] - 1 - c_remove if last_length: last_segment = Alkane(last_length, cap_front=False, cap_end=False) self.add(last_segment, 'top_chain') mb.force_overlap(self['top_chain'], self['top_chain']['down'], self['hgroup']['up']) else: hydrogen = H() self.add(hydrogen, 'H-cap') mb.force_overlap(self['H-cap'], self['H-cap']['up'], self['hgroup']['up']) self.add(tgroup, 'terminal_group') mb.force_overlap(self['terminal_group'], self['terminal_group']['down'], self['top_chain']['up'])
def fill_region(compound, n_compounds, region, overlap=0.2, seed=12345, edge=0.2, fix_orientation=False, temp_file=None): """Fill a region of a box with a compound using packmol. Parameters ---------- compound : mb.Compound or list of mb.Compound Compound or list of compounds to be put in region. n_compounds : int or list of int Number of compounds to be put in region. region : mb.Box or list of mb.Box Region to be filled by compounds. overlap : float, units nm, default=0.2 Minimum separation between atoms of different molecules. seed : int, default=12345 Random seed to be passed to PACKMOL. edge : float, units nm, default=0.2 Buffer at the edge of the region to not place molecules. This is necessary in some systems because PACKMOL does not account for periodic boundary conditions in its optimization. fix_orientation : bool or list of bools Specify that compounds should not be rotated when filling the box, default=False. temp_file : str, default=None File name to write PACKMOL's raw output to. Returns ------- filled : mb.Compound If using mulitple regions and compounds, the nth value in each list are used in order. For example, if the third compound will be put in the third region using the third value in n_compounds. """ _check_packmol(PACKMOL) if not isinstance(compound, (list, set)): compound = [compound] if not isinstance(n_compounds, (list, set)): n_compounds = [n_compounds] if not isinstance(fix_orientation, (list, set)): fix_orientation = [fix_orientation]*len(compound) if compound is not None and n_compounds is not None: if len(compound) != len(n_compounds): msg = ("`compound` and `n_compounds` must be of equal length.") raise ValueError(msg) if compound is not None: if len(compound) != len(fix_orientation): msg = ("`compound`, `n_compounds`, and `fix_orientation` must be of equal length.") raise ValueError(msg) # See if region is a single region or list if isinstance(region, Box): # Cannot iterate over boxes region = [region] elif not any(isinstance(reg, (list, set, Box)) for reg in region): region = [region] region = [_validate_box(reg) for reg in region] # In angstroms for packmol. overlap *= 10 # Build the input file and call packmol. filled_pdb = tempfile.mkstemp(suffix='.pdb')[1] input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed) for comp, m_compounds, reg, rotate in zip(compound, n_compounds, region, fix_orientation): m_compounds = int(m_compounds) compound_pdb = tempfile.mkstemp(suffix='.pdb')[1] comp.save(compound_pdb, overwrite=True) reg_mins = reg.mins * 10 reg_maxs = reg.maxs * 10 reg_maxs -= edge * 10 # Apply edge buffer input_text += PACKMOL_BOX.format(compound_pdb, m_compounds, reg_mins[0], reg_mins[1], reg_mins[2], reg_maxs[0], reg_maxs[1], reg_maxs[2], PACKMOL_CONSTRAIN if rotate else "") _run_packmol(input_text, filled_pdb, temp_file) # Create the topology and update the coordinates. filled = Compound() for comp, m_compounds in zip(compound, n_compounds): for _ in range(m_compounds): filled.add(clone(comp)) filled.update_coordinates(filled_pdb) return filled
def test_init_with_subcompounds3(self, ethane, h2o): compound = mb.Compound([ethane, [h2o, mb.clone(h2o)]]) assert compound.n_particles == 8 + 2 * 3 assert compound.n_bonds == 7 + 2 * 2
def fill_box(compound, n_compounds=None, box=None, density=None, overlap=0.2, seed=12345, edge=0.2, compound_ratio=None, aspect_ratio=None, fix_orientation=False, temp_file=None): """Fill a box with a compound using packmol. Two arguments of `n_compounds, box, and density` must be specified. If `n_compounds` and `box` are not None, the specified number of n_compounds will be inserted into a box of the specified size. If `n_compounds` and `density` are not None, the corresponding box size will be calculated internally. In this case, `n_compounds` must be an int and not a list of int. If `box` and `density` are not None, the corresponding number of compounds will be calculated internally. For the cases in which `box` is not specified but generated internally, the default behavior is to calculate a cubic box. Optionally, `aspect_ratio` can be passed to generate a non-cubic box. Parameters ---------- compound : mb.Compound or list of mb.Compound Compound or list of compounds to be put in box. n_compounds : int or list of int Number of compounds to be put in box. box : mb.Box Box to be filled by compounds. density : float, units kg/m^3, default=None Target density for the system in macroscale units. If not None, one of `n_compounds` or `box`, but not both, must be specified. overlap : float, units nm, default=0.2 Minimum separation between atoms of different molecules. seed : int, default=12345 Random seed to be passed to PACKMOL. edge : float, units nm, default=0.2 Buffer at the edge of the box to not place molecules. This is necessary in some systems because PACKMOL does not account for periodic boundary conditions in its optimization. compound_ratio : list, default=None Ratio of number of each compound to be put in box. Only used in the case of `density` and `box` having been specified, `n_compounds` not specified, and more than one `compound`. aspect_ratio : list of float If a non-cubic box is desired, the ratio of box lengths in the x, y, and z directions. fix_orientation : bool or list of bools Specify that compounds should not be rotated when filling the box, default=False. temp_file : str, default=None File name to write PACKMOL's raw output to. Returns ------- filled : mb.Compound """ _check_packmol(PACKMOL) arg_count = 3 - [n_compounds, box, density].count(None) if arg_count != 2: msg = ("Exactly 2 of `n_compounds`, `box`, and `density` " "must be specified. {} were given.".format(arg_count)) raise ValueError(msg) if box is not None: box = _validate_box(box) if not isinstance(compound, (list, set)): compound = [compound] if n_compounds is not None and not isinstance(n_compounds, (list, set)): n_compounds = [n_compounds] if not isinstance(fix_orientation, (list, set)): fix_orientation = [fix_orientation]*len(compound) if compound is not None and n_compounds is not None: if len(compound) != len(n_compounds): msg = ("`compound` and `n_compounds` must be of equal length.") raise ValueError(msg) if compound is not None: if len(compound) != len(fix_orientation): msg = ("`compound`, `n_compounds`, and `fix_orientation` must be of equal length.") raise ValueError(msg) if density is not None: if box is None and n_compounds is not None: total_mass = np.sum([n*np.sum([a.mass for a in c.to_parmed().atoms]) for c,n in zip(compound, n_compounds)]) # Conversion from (amu/(kg/m^3))**(1/3) to nm L = (total_mass/density)**(1/3)*1.1841763 if aspect_ratio is None: box = _validate_box(Box(3*[L])) else: L *= np.prod(aspect_ratio) ** (-1/3) box = _validate_box(Box([val*L for val in aspect_ratio])) if n_compounds is None and box is not None: if len(compound) == 1: compound_mass = np.sum([a.mass for a in compound[0].to_parmed().atoms]) # Conversion from kg/m^3 / amu * nm^3 to dimensionless units n_compounds = [int(density/compound_mass*np.prod(box.lengths)*.60224)] else: if compound_ratio is None: msg = ("Determing `n_compounds` from `density` and `box` " "for systems with more than one compound type requires" "`compound_ratio`") raise ValueError(msg) if len(compound) != len(compound_ratio): msg = ("Length of `compound_ratio` must equal length of " "`compound`") raise ValueError(msg) prototype_mass = 0 for c, r in zip(compound, compound_ratio): prototype_mass += r * np.sum([a.mass for a in c.to_parmed().atoms]) # Conversion from kg/m^3 / amu * nm^3 to dimensionless units n_prototypes = int(density/prototype_mass*np.prod(box.lengths)*.60224) n_compounds = list() for c in compound_ratio: n_compounds.append(int(n_prototypes * c)) # In angstroms for packmol. box_mins = box.mins * 10 box_maxs = box.maxs * 10 overlap *= 10 # Apply edge buffer box_maxs -= edge * 10 # Build the input file for each compound and call packmol. filled_pdb = tempfile.mkstemp(suffix='.pdb')[1] input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed) for comp, m_compounds, rotate in zip(compound, n_compounds, fix_orientation): m_compounds = int(m_compounds) compound_pdb = tempfile.mkstemp(suffix='.pdb')[1] comp.save(compound_pdb, overwrite=True) input_text += PACKMOL_BOX.format(compound_pdb, m_compounds, box_mins[0], box_mins[1], box_mins[2], box_maxs[0], box_maxs[1], box_maxs[2], PACKMOL_CONSTRAIN if rotate else "") _run_packmol(input_text, filled_pdb, temp_file) # Create the topology and update the coordinates. filled = Compound() for comp, m_compounds in zip(compound, n_compounds): for _ in range(m_compounds): filled.add(clone(comp)) filled.update_coordinates(filled_pdb) filled.periodicity = np.asarray(box.lengths, dtype=np.float32) return filled
solvent=[water, na, cl], n_solvent=[5200, 400, 400]) box = mb.Box(system.periodicity) # #### Separate molecules into different compounds # Because we are going to apply multiple force fields, we need to separate the waters and graphene into separate mBuild compounds. Calling `apply` will apply the forcefield to the compounds and convert them to parametrized ParmEd `Structures` # In[ ]: water = mb.Compound() ions = mb.Compound() graphene = mb.Compound() for child in system.children: if child.name == 'SOL': water.add(mb.clone(child)) elif child.name in ['Na', 'Cl']: ions.add(mb.clone(child)) else: graphene.add(mb.clone(child)) water_pmd = spce.apply(water, residues='SOL') ions_pmd = jc.apply(ions, residues=['Na', 'Cl']) pore_pmd = c_ff.apply(graphene) # #### Now we will combine the two paramterezed ParmEd structures and save them as `gro` and `top` files # In[ ]: system = water_pmd + pore_pmd + ions_pmd system.box[:3] = box.maxs * 10.0
def test_add_label_exists(self, ethane, h2o): ethane.add(h2o, label='water') with pytest.raises(MBuildError): ethane.add(mb.clone(h2o), label='water')
def _visualize_py3dmol(self, show_ports=False, color_scheme={}, show_atomistic=False, scale=1.0): """ Visualize the Compound using py3Dmol. Allows for visualization of a Compound within a Jupyter Notebook. Modified to show atomistic elements (translucent) with larger CG beads. Parameters ---------- show_ports : bool, optional, default=False Visualize Ports in addition to Particles color_scheme : dict, optional Specify coloring for non-elemental particles keys are strings of the particle names values are strings of the colors i.e. {'_CGBEAD': 'blue'} show_atomistic : show the atomistic structure stored in CG_Compound.atomistic Returns ------ view : py3Dmol.view """ py3Dmol = import_("py3Dmol") atom_names = [] if self.atomistic is not None and show_atomistic: atomistic = mb.clone(self.atomistic) for particle in atomistic.particles(): if not particle.name: particle.name = "UNK" else: if (particle.name != 'Compound') and (particle.name != 'CG_Compound'): atom_names.append(particle.name) coarse = mb.clone(self) modified_color_scheme = {} for name, color in color_scheme.items(): # Py3dmol does some element string conversions, # first character is as-is, rest of the characters are lowercase new_name = name[0] + name[1:].lower() modified_color_scheme[new_name] = color modified_color_scheme[name] = color cg_names = [] for particle in coarse.particles(): if not particle.name: particle.name = "UNK" else: if (particle.name != 'Compound') and (particle.name != 'CG_Compound'): cg_names.append(particle.name) tmp_dir = tempfile.mkdtemp() view = py3Dmol.view() if atom_names: atomistic.save( os.path.join(tmp_dir, "atomistic_tmp.mol2"), show_ports=show_ports, overwrite=True, ) # atomistic with open(os.path.join(tmp_dir, "atomistic_tmp.mol2"), "r") as f: view.addModel(f.read(), "mol2", keepH=True) if cg_names: opacity = 0.6 else: opacity = 1.0 view.setStyle({ "stick": { "radius": 0.2 * scale, "opacity": opacity, "color": "grey" }, "sphere": { "scale": 0.3 * scale, "opacity": opacity, "colorscheme": modified_color_scheme, }, }) # coarse if cg_names: coarse.save( os.path.join(tmp_dir, "coarse_tmp.mol2"), show_ports=show_ports, overwrite=True, ) with open(os.path.join(tmp_dir, "coarse_tmp.mol2"), "r") as f: view.addModel(f.read(), "mol2", keepH=True) if self.atomistic is None: scale = 0.3 * scale else: scale = 0.7 * scale view.setStyle( {"atom": cg_names}, { "stick": { "radius": 0.2 * scale, "opacity": 1, "color": "grey" }, "sphere": { "scale": scale, "opacity": 1, "colorscheme": modified_color_scheme, }, }, ) view.zoomTo() return view
top_lipids.append([lipid_molecule, lipid_offset]) bot_lipids.append([lipid_molecule, lipid_offset]) index += 1 if len(bot_lipids) != n_lipid: sys.exit('Error setting up system components') # Generate bottom layer randomly bot_layer = mb.Compound() for i in range(n_x): for j in range(n_y): # Randomly select a lipid that has not yet been selected random_lipid = np.random.randint(0, len(bot_lipids)) # Create the mbuild Molecule for this lipid molecule_i = bot_lipids.pop(random_lipid) molecule_to_add = mb.clone(molecule_i[0]) # Apply tilt angle mb.spin_y(molecule_to_add, tilt_angle) # Apply z_offset z_offset = molecule_i[1] #z_offset = 0 # Apply APL and z_offset to identify the position for the molecule in the grid position = [i * spacing, j * spacing, z_offset] mb.translate(molecule_to_add, position) # Add the new molecule to the layer bot_layer.add(molecule_to_add) # Generate the top layer of lipids randomly top_layer = mb.Compound() for i in range(n_x): for j in range(n_y):
def _clone(self, clone_of=None, root_container=None): newone = super(Port, self)._clone(clone_of, root_container) newone.anchor = clone(self.anchor, clone_of, root_container) newone.used = self.used return newone
def populate(self, compound_dict=None, x=1, y=1, z=1): """Expand lattice and create compound from lattice. Expands lattice based on user input. The user must also pass in a dictionary that contains the keys that exist in the basis_dict. The corresponding Compound will be the full lattice returned to the user. If no dictionary is passed to the user, Dummy Compounds will be used. Parameters ---------- x : int, optional, default=1 How many iterations in the x direction. y : int, optional, default=1 How many iterations in the y direction. z : int, optional, default=1 How many iterations in the z direction. compound_dict : dictionary, optional, default=None Link between basis_dict and Compounds. Exceptions Raised ----------------- ValueError : incorrect x,y, or z values. TypeError : incorrect type for basis vector Call Restrictions ----------------- Called after constructor by user. """ x, y, z = self._sanitize_populate_args(x=x, y=y, z=z) if ((isinstance(compound_dict, dict)) or (compound_dict is None)): pass else: raise TypeError('Compound dictionary is not of type dict. ' '{} was passed.'.format(type(compound_dict))) cell = defaultdict(list) [a, b, c] = self.lattice_spacing transform_mat = self.lattice_vectors # Unit vectors transform_mat = np.asarray(transform_mat, dtype=np.float64) transform_mat = np.reshape(transform_mat, newshape=(3,3)) norms = np.linalg.norm(transform_mat, axis=1) # Normalized vectors for change of basis unit_vecs = np.divide(transform_mat.transpose(), norms) # Generate new coordinates for key, locations in self.lattice_points.items(): for coords in locations: for replication in it.product(range(x), range(y), range(z)): new_coords = np.asarray(coords, dtype=np.float64) new_coords = np.reshape(new_coords, (1, 3), order='C') new_coords[0][0] = new_coords[0][0] + replication[0] new_coords[0][1] = new_coords[0][1] + replication[1] new_coords[0][2] = new_coords[0][2] + replication[2] # Change of basis to cartesian new_coords = np.dot(unit_vecs, new_coords.transpose()) new_coords[0] = new_coords[0] * a new_coords[1] = new_coords[1] * b new_coords[2] = new_coords[2] * c new_coords = np.reshape(new_coords, (1, 3), order='C') tuple_of_coords = tuple(new_coords.flatten()) cell[key].append(tuple_of_coords) ret_lattice = mb.Compound() # Create (clone) a mb.Compound for the newly generate positions if compound_dict is None: for key_id, all_pos in cell.items(): particle = mb.Compound(name=key_id, pos=[0, 0, 0]) for pos in all_pos: particle_to_add = mb.clone(particle) particle_to_add.translate_to(list(pos)) ret_lattice.add(particle_to_add) else: for key_id, all_pos in cell.items(): if isinstance(compound_dict[key_id], mb.Compound): compound_to_move = compound_dict[key_id] for pos in all_pos: tmp_comp = mb.clone(compound_to_move) tmp_comp.translate_to(list(pos)) ret_lattice.add(tmp_comp) else: err_type = type(compound_dict.get(key_id)) raise TypeError('Invalid type in provided Compound ' 'dictionary. For key {}, type: {} was ' 'provided, not mbuild.Compound.' .format(key_id, err_type)) # set periodicity, currently assuming rectangular system if not np.all(np.allclose(self.angles, [90.0, 90.0, 90.0])): warn('Periodicity of non-rectangular lattices are not valid with ' 'default boxes. Only rectangular lattices are valid ' 'at this time.') ret_lattice.periodicity = np.asarray([a * x, b * y, c * z], dtype=np.float64) # if coordinates are below a certain threshold, set to 0 tolerance = 1e-12 ret_lattice.xyz_with_ports[ret_lattice.xyz_with_ports <= tolerance] = 0. return ret_lattice
def run_md_pre(molecule, functional, project_name, dire, temperature, box_length, number_of_molecules, simulation_time, CUTOFF, SCF_tolerence, basis_set, ensemble, timestep, thermostat, table): current_molecule = mb.clone(molecule) molecule_pmd = mb.clone(current_molecule) molecule_pmd = molecule_pmd.to_parmed() atom_list, mass_list, total_mass = info_molecule(molecule_pmd) unique_atom_list = remove_duplicate(atom_list) num_atoms = len(atom_list) num_unique_atoms = len(unique_atom_list) unique_atom_list.sort() box = mb.Box(lengths=[box_length, box_length, box_length]) current_molecule.xyz = table print(number_of_molecules) box_of_molecule = mb.fill_box(compound=current_molecule, n_compounds=number_of_molecules, box=box) filename = project_name + ".xyz" box_of_molecule.save(filename, overwrite=True) with open(project_name + ".xyz", 'r') as fin: data = fin.read().splitlines(True) with open(project_name + ".xyz", 'w') as fout: fout.writelines(data[2:]) if basis_set[0] == None: basis_set = basis_set * num_unique_atoms if thermostat == None: thermostat = 'NOSE' if CUTOFF == None: CUTOFF = 900 if SCF_tolerence == None: SCF_tolerence = 1e-6 if ensemble == None: ensemble = 'NVT' mySim = sim.SIM() mySim.GLOBAL.RUN_TYPE = "MD" mySim.GLOBAL.PROJECT = project_name + "pre" mySim.GLOBAL.PRINT_LEVEL = "LOW" #FORCE EVAL SECTION mySim.FORCE_EVAL.METHOD = 'QUICKSTEP' mySim.FORCE_EVAL.STRESS_TENSOR = 'ANALYTICAL' mySim.FORCE_EVAL.DFT.BASIS_SET_FILE_NAME = dire + 'BASIS_MOLOPT' mySim.FORCE_EVAL.DFT.POTENTIAL_FILE_NAME = dire + 'GTH_POTENTIALS' mySim.FORCE_EVAL.DFT.CHARGE = 0 mySim.FORCE_EVAL.DFT.MULTIPLICITY = 1 mySim.FORCE_EVAL.DFT.MGRID.CUTOFF = CUTOFF mySim.FORCE_EVAL.DFT.MGRID.REL_CUTOFF = 50 mySim.FORCE_EVAL.DFT.MGRID.NGRIDS = 4 mySim.FORCE_EVAL.DFT.QS.METHOD = 'GPW' mySim.FORCE_EVAL.DFT.QS.EPS_DEFAULT = 1E-8 mySim.FORCE_EVAL.DFT.QS.EXTRAPOLATION = 'ASPC' mySim.FORCE_EVAL.DFT.POISSON.PERIODIC = "XYZ" mySim.FORCE_EVAL.DFT.PRINT.E_DENSITY_CUBE.SECTION_PARAMETERS = "OFF" mySim.FORCE_EVAL.DFT.SCF.SCF_GUESS = 'ATOMIC' mySim.FORCE_EVAL.DFT.SCF.MAX_SCF = 2 mySim.FORCE_EVAL.DFT.SCF.EPS_SCF = SCF_tolerence mySim.FORCE_EVAL.DFT.SCF.OT.SECTION_PARAMETERS = ".TRUE." mySim.FORCE_EVAL.DFT.SCF.OT.PRECONDITIONER = "FULL_SINGLE_INVERSE" mySim.FORCE_EVAL.DFT.SCF.OT.MINIMIZER = "DIIS" mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.SECTION_PARAMETERS = '.TRUE.' mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.MAX_SCF = 10 mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.EPS_SCF = 1e-6 mySim.FORCE_EVAL.DFT.SCF.PRINT.RESTART.SECTION_PARAMETERS = 'OFF' mySim.FORCE_EVAL.DFT.SCF.PRINT.DM_RESTART_WRITE = '.TRUE.' mySim.FORCE_EVAL.DFT.XC.XC_FUNCTIONAL.SECTION_PARAMETERS = functional mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.POTENTIAL_TYPE = 'PAIR_POTENTIAL' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.TYPE = 'DFTD3' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.PARAMETER_FILE_NAME = 'dftd3.dat' mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.REFERENCE_FUNCTIONAL = functional mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.R_CUTOFF = 11 mySim.FORCE_EVAL.SUBSYS.COORD.DEFAULT_KEYWORD = project_name + ".xyz" mySim.FORCE_EVAL.SUBSYS.init_atoms(num_atoms) for i in range(num_unique_atoms): mySim.FORCE_EVAL.SUBSYS.KIND[ i + 1].SECTION_PARAMETERS = unique_atom_list[i] if basis_set[i] == None: mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set_setter( unique_atom_list[i]) else: mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set[i] mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].POTENTIAL = potential( unique_atom_list[i], functional) mySim.FORCE_EVAL.SUBSYS.CELL.ABC = '{L} {L} {L}'.format(L=10 * box_length) #MOTION SECTION mySim.MOTION.GEO_OPT.OPTIMIZER = 'BFGS' mySim.MOTION.GEO_OPT.MAX_ITER = 100 mySim.MOTION.GEO_OPT.MAX_DR = 0.003 mySim.MOTION.MD.ENSEMBLE = ensemble mySim.MOTION.MD.STEPS = 1 mySim.MOTION.MD.TIMESTEP = 0.2 mySim.MOTION.MD.TEMPERATURE = temperature mySim.MOTION.MD.THERMOSTAT.TYPE = thermostat mySim.MOTION.MD.THERMOSTAT.REGION = "MASSIVE" mySim.MOTION.MD.THERMOSTAT.NOSE.LENGTH = 3 mySim.MOTION.MD.THERMOSTAT.NOSE.YOSHIDA = 3 mySim.MOTION.MD.THERMOSTAT.NOSE.TIMECON = 1000.0 mySim.MOTION.MD.THERMOSTAT.NOSE.MTS = 2 #mySim.MOTION.MD.PRINT.ENERGY.EACH.MD = 20 #mySim.MOTION.MD.PRINT.PROGRAM_RUN_INFO.EACH.MD = 20 #mySim.MOTION.MD.AVERAGES.SECTION_PARAMETERS= ".falbmbjse." mySim.MOTION.PRINT.STRESS.SECTION_PARAMETERS = 'OFF' mySim.MOTION.PRINT.TRAJECTORY.EACH.MD = 1 mySim.MOTION.PRINT.VELOCITIES.SECTION_PARAMETERS = 'OFF' mySim.MOTION.PRINT.FORCES.SECTION_PARAMETERS = "OFF" mySim.MOTION.PRINT.RESTART_HISTORY.SECTION_PARAMETERS = "ON" mySim.MOTION.PRINT.RESTART_HISTORY.EACH.MD = 500 mySim.MOTION.PRINT.RESTART.SECTION_PARAMETERS = "ON" mySim.MOTION.PRINT.RESTART.BACKUP_COPIES = 3 mySim.MOTION.PRINT.RESTART.EACH.MD = 1 mySim.write_changeLog(fn="md-pre-changeLog.out") mySim.write_errorLog() mySim.write_inputFile(fn='md-pre.inp')
def _add_chemisorbed_chains(self, chain, spacing, n_chemisorbed, n_chains, chain_port_name, verbose): """Attach chains to the surface... """ available_sites = np.array(self['surface'].available_ports()) if n_chemisorbed and n_chains and n_chains < n_chemisorbed: raise Exception( 'Cannot specify `n_chains` less than `n_chemisorbed`!') added_chains = 0 while len(available_sites) > 0: if ((n_chemisorbed and added_chains == n_chemisorbed) or (n_chains and added_chains == n_chains)): available_sites = [] else: binding_site = random.choice(available_sites) new_chain = mb.clone(chain) self.crosslink_graph.add_node( new_chain.available_ports()[0].anchor, pos=(binding_site.pos[0], binding_site.pos[1]), surface_bound=True) si = list(new_chain.particles_by_name('Si'))[0] self.add(new_chain, 'chemisorbed_chain[$]') mb.force_overlap(new_chain, new_chain[chain_port_name], binding_site) available_sites = [ site for site in available_sites if (site != binding_site and self.min_periodic_distance( np.array([site.pos[0], site.pos[1], 0]), np.array([binding_site.pos[0], binding_site.pos[1], 0 ])) > spacing) ] added_chains += 1 if verbose: print('Added chemisorbed chain {}' ''.format(len(self['chemisorbed_chain']))) print('{} available sites remaining'.format( len(available_sites))) if n_chemisorbed and added_chains < n_chemisorbed: print( 'Maximum number of chemisorbed chains without overlaps has been ' 'reached! However, additional chains will be added to satisfy ' 'n_chemisorbed requirement.') while added_chains < n_chemisorbed: available_sites = np.array( [site for site in self['surface'].available_ports()]) chain_locations = np.array( [chain.pos for chain in self['chemisorbed_chain']]) chain_locations[:, 2] = 0 min_dists = [] for site in available_sites: min_dist = np.min([ self.min_periodic_distance( [site.pos[0], site.pos[1], 0.0], loc) for loc in chain_locations ]) min_dists.append(min_dist) binding_site = available_sites[np.argmax(min_dists)] new_chain = mb.clone(chain) self.crosslink_graph.add_node( new_chain.available_ports()[0].anchor, pos=(binding_site.pos[0], binding_site.pos[1]), surface_bound=True) si = list(new_chain.particles_by_name('Si'))[0] self.add(new_chain, 'chemisorbed_chain[$]') mb.force_overlap(new_chain, new_chain[chain_port_name], binding_site) added_chains += 1 if verbose: print('Added chemisorbed chain {}' ''.format(len(self['chemisorbed_chain'])))
def test_spin_z_eq(self, sixpoints): compound2 = mb.clone(sixpoints) sixpoints.spin(np.pi * 1.23456789, np.asarray([0.0, 0.0, 1.0])) spin_z(compound2, np.pi * 1.23456789) assert (np.allclose(compound2.xyz, sixpoints.xyz, atol=1e-16))
def _add_crosslinked_chains(self, chain, spacing, n_chains, chain_port_name, verbose): """Add crosslinked chains... """ # Add hydroxyl to prototype for crosslinked chain chain.spin(np.pi / 2, [1, 0, 0]) hydroxyl = Hydroxyl() mb.force_overlap(hydroxyl, hydroxyl['down'], chain[chain_port_name]) chain.add(hydroxyl) surface_level = max(self['surface'].xyz[:, 2]) chains_in_monolayer = len(self['chemisorbed_chain']) complete = False while not complete: vertices = self._get_voronoi_vertices() # Determine locations of chains already in the monolayer chemisorbed_chain_locations = np.array( [chain.pos for chain in self['chemisorbed_chain']]) if 'crosslinked_chain' in self.labels: crosslinked_chain_locations = np.array( [chain.pos for chain in self['crosslinked_chain']]) try: chain_locations = np.vstack( (chemisorbed_chain_locations, crosslinked_chain_locations)) except UnboundLocalError: chain_locations = chemisorbed_chain_locations chain_locations[:, 2] = 0 dists = [] for vertex in vertices: min_dist = np.min([ self.min_periodic_distance([vertex[0], vertex[1], 0.0], loc) for loc in chain_locations ]) dists.append(min_dist) ''' Add crosslinked chain to monolayer if: 1. n_chains is specified and chains_in_monolayer < n_chains 2. n_chains is not specified and np.max(dists) > spacing ''' if ((n_chains and chains_in_monolayer < n_chains) or (not n_chains and np.max(dists) > spacing)): site_2d = vertices[np.argmax(dists)] site = np.array([site_2d[0], site_2d[1], 0.0]) failed_attempts = 0 new_chain = mb.clone(chain) new_chain.translate_to(site) new_chain.translate([ 0.0, 0.0, surface_level + (new_chain.pos[2] - min(new_chain.xyz[:, 2])) + 0.15 ]) self.add(new_chain, 'crosslinked_chain[$]') new_chain_si = list(new_chain.particles_by_name('Si'))[0] self.crosslink_graph.add_node(new_chain_si, pos=(site[0], site[1]), surface_bound=False) chains_in_monolayer = len(self['crosslinked_chain']) + \ len(self['chemisorbed_chain']) if np.max(dists) <= spacing: print( 'Added chain would be closer to a neighboring chain than ' 'the specified `spacing` ({}). Chain will be added to ' 'meet `n_chains` requirement.'.format(spacing)) if verbose: print('Added crosslinked chain {} ({} total chains)' ''.format(len(self['crosslinked_chain']), chains_in_monolayer)) else: complete = True
def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345, edge=0.2, fix_orientation=False, temp_file=None): """Solvate a compound in a box of solvent using packmol. Parameters ---------- solute : mb.Compound Compound to be placed in a box and solvated. solvent : mb.Compound Compound to solvate the box. n_solvent : int Number of solvents to be put in box. box : mb.Box Box to be filled by compounds. overlap : float, units nm, default=0.2 Minimum separation between atoms of different molecules. seed : int, default=12345 Random seed to be passed to PACKMOL. edge : float, units nm, default=0.2 Buffer at the edge of the box to not place molecules. This is necessary in some systems because PACKMOL does not account for periodic boundary conditions in its optimization. fix_orientation : bool Specify if solvent should not be rotated when filling box, default=False. temp_file : str, default=None File name to write PACKMOL's raw output to. Returns ------- solvated : mb.Compound """ _check_packmol(PACKMOL) box = _validate_box(box) if not isinstance(solvent, (list, set)): solvent = [solvent] if not isinstance(n_solvent, (list, set)): n_solvent = [n_solvent] if not isinstance(fix_orientation, (list, set)): fix_orientation = [fix_orientation] * len(solvent) if len(solvent) != len(n_solvent): msg = ("`n_solvent` and `n_solvent` must be of equal length.") raise ValueError(msg) # In angstroms for packmol. box_mins = box.mins * 10 box_maxs = box.maxs * 10 overlap *= 10 center_solute = (box_maxs + box_mins) / 2 # Apply edge buffer box_maxs -= edge * 10 # Build the input file for each compound and call packmol. solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1] solute_pdb = tempfile.mkstemp(suffix='.pdb')[1] solute.save(solute_pdb, overwrite=True) input_text = (PACKMOL_HEADER.format(overlap, solvated_pdb, seed) + PACKMOL_SOLUTE.format(solute_pdb, *center_solute)) for solv, m_solvent, rotate in zip(solvent, n_solvent, fix_orientation): m_solvent = int(m_solvent) solvent_pdb = tempfile.mkstemp(suffix='.pdb')[1] solv.save(solvent_pdb, overwrite=True) input_text += PACKMOL_BOX.format(solvent_pdb, m_solvent, box_mins[0], box_mins[1], box_mins[2], box_maxs[0], box_maxs[1], box_maxs[2], PACKMOL_CONSTRAIN if rotate else "") _run_packmol(input_text, solvated_pdb, temp_file) # Create the topology and update the coordinates. solvated = Compound() solvated.add(solute) for solv, m_solvent in zip(solvent, n_solvent): for _ in range(m_solvent): solvated.add(clone(solv)) solvated.update_coordinates(solvated_pdb) return solvated
def test_equivalence_transform_deprectation_warning(self, ch2): ch22 = mb.clone(ch2) with pytest.warns(DeprecationWarning): mb.equivalence_transform(ch22, from_positions=ch22['up'], to_positions=ch2['down'])
def test_add_existing_parent(self, ethane, h2o): water_in_water = mb.clone(h2o) h2o.add(water_in_water) with pytest.raises(MBuildError): ethane.add(water_in_water)
def test_different_translates(self, methane): shifted = mb.clone(methane) shifted.translate([5, 4, 3]) shifted_methane_coords = mb.coordinate_transform._translate( methane.xyz, [5, 4, 3]) assert np.array_equal(shifted_methane_coords, shifted.xyz)
def test_clone_outside_containment(self, ch2, ch3): compound = mb.Compound() compound.add(ch2) mb.force_overlap(ch3, ch3['up'], ch2['up']) with pytest.raises(MBuildError): ch3_clone = mb.clone(ch3)
def test_different_translate_tos_origin(self, methane): shifted = mb.clone(methane) shifted.translate_to([0, 0, 0]) x = mb.coordinate_transform._translate_to(methane.xyz, [0, 0, 0]) assert np.array_equal(shifted.xyz, x)