def silanol(name="sl", short="SL"): """This function generates a silanol molecule. Parameters ---------- name : string, optional Molecule name short : string, optional Molecule short name Returns ------- mol : Molecule Molecule object """ # Initialize molecule mol = Molecule(name, short) # Define bonds lengths b = {"sio": 0.164, "oh": 0.098} # Build molecule mol.add("Si",[0, 0, 0]) mol.add("O", 0, r=b["sio"]) mol.add("H", 1, r=b["oh"]) # Move to zero mol.zero() # Return molecule return mol
def _block(self, dim, block): """Recursively duplicate and translate a given molecule block in all given dimensions. The duplication stops if the next added block would create the pore longer than specified in the constructor. Parameters ---------- dim : integer Repeat dimensions block : Molecule Molecule unit to be duplicated """ if dim < self._dim: p = [] for i in range(self._num[dim]): temp = copy.deepcopy(block) vec = [(i + 1) * self._repeat[dim] if j == dim else 0 for j in range(self._dim)] temp.translate(vec) p.append(temp) self._block(dim + 1, Molecule(inp=p)) else: self._structure = block
def alcohol(length, name="alcohol", short="ALC", is_h=True): """This function generates linear alcohol molecules. Parameters ---------- length : integer Number of carbon atoms name : string, optional Molecule name short : string, optional Molecule short name is_h : bool, optional True if hydrogens are needed Returns ------- mol : Molecule Molecule object """ # Initialize Molecule mol = Molecule(name, short) # Define bond lengths and angles b = {"cc": 0.153, "ch": 0.109, "co": 0.143, "oh": 0.098} a = {"ccc": 30.00, "cch": 109.47, "occ": 30.00, "coh": 109.47} # Add carbons mol.add("C", [0, 0, 0]) angle = a["ccc"] for i in range(length-1): angle *= -1 mol.add("C", mol.get_num()-1, r=b["cc"], theta=angle) # Add hydroxy mol.add("O", mol.get_num()-1, r=b["co"], theta=-angle) mol.add("H", mol.get_num()-1, r=b["oh"], theta=angle) # Add hydrogens if is_h: if length > 1: angle = -90 for i in range(length): # Boundary if i==0: for j in range(3): mol.add("H", i, r=b["ch"], theta=angle-30, phi=120*j) # Inner else: mol.add("H", i, r=b["ch"], theta=angle, phi=a["cch"]) mol.add("H", i, r=b["ch"], theta=angle, phi=-a["cch"]) # Switch orientation angle *= -1 # Methanol else: for i in range(3): mol.add("H", 0, r=b["ch"], theta=a["cch"], phi=i*120) # Move to zero mol.zero() # Return molecule return mol
def tms(name="tms", short="TMS", separation=30, is_si=True, is_hydro=True): """This function generates a trimethylsilyl (TMS) molecule. Parameters ---------- name : string, optional Molecule name short : string, optional Molecule short name separation : float, optional Sparation of carbon and hydrogen atoms is_si : bool, optional True if the terminus should be a lone silicon, False for a CH3 group is_hydro : bool, optional True if the hydrogen atoms should be added Returns ------- mol : Molecule Molecule object """ # Initialize molecule mol = Molecule(name, short) # Check silicon si = "Si" if is_si else "Ci" sio = "sio" if is_si else "co" sic = "sic" if is_si else "cc" # Define bond lengths and angles b = {"sio": 0.155, "sic": 0.186, "ch": 0.109, "co": 0.143, "cc": 0.153} # Build silyl chain mol.add(si, [0, 0, 0]) mol.add("O", 0, r=b[sio]) mol.add(si, 1, r=b[sio]) # Add methyl for i in range(3): mol.add("C", 2, r=b[sic], theta=separation+10, phi=120*i) if is_hydro: # Add hydrogens for i in range(3, 5+1): for j in range(3): mol.add("H", i, r=b["ch"], theta=separation, phi=120*j) # If not silicon ending for i in range(3): if not is_si: mol.add("H", 0, r=b["ch"], theta=180-separation, phi=120*i) # Move to zero mol.zero() # Return molecule return mol
def ketone(length, pos, name="ketone", short="KET", is_h=True): """This function generates linear ketone molecules. Parameters ---------- length : integer Number of carbon atoms pos : integer Position of the oxygen atom name : string, optional Molecule name short : string, optional Molecule short name is_h : bool True if hydrogens are needed Returns ------- mol : Molecule Molecule object """ # Check input if length < 3: print("Specified length is too small for ketones ...") return # Initialize Molecule mol = Molecule(name, short) # Define bond lengths and angles b = {"cc": 0.153, "ch": 0.109, "co": 0.123} a = {"ccc": 30.00, "cch": 109.47} # Add carbons mol.add("C", [0, 0, 0]) angle = a["ccc"] for i in range(length-1): angle *= -1 mol.add("C", mol.get_num()-1, r=b["cc"], theta=angle) # Add oxygen angle = -90 if pos % 2 == 0 else 90 mol.add("O", pos-1, r=b["co"], theta=angle) # Add hydrogens if is_h: angle = -90 for i in range(length): # Boundary if i==0 or i==length-1: for j in range(3): mol.add("H", i, r=b["ch"], theta=angle-30, phi=120*j) # Inner elif not i == pos-1: mol.add("H", i, r=b["ch"], theta=angle, phi=a["cch"]) mol.add("H", i, r=b["ch"], theta=angle, phi=-a["cch"]) # Switch orientation angle *= -1 # Move to zero mol.zero() # Return molecule return mol
def objectify(self, atoms): """Create molecule objects of specified list of atoms. Parameters ---------- atoms : list List of atom ids Returns ------- mol_list : list List of molecule objects """ # Initialize mol_list = [] # Run through all remaining atoms with a bond or more for atom_id in atoms: # Get atom object atom = self._block.get_atom_list()[atom_id] # Create molecule object if atom.get_atom_type() == "O": mol = Molecule("om", "OM") mol.add("O", atom.get_pos(), name="OM1") elif atom.get_atom_type() == "Si": mol = Molecule("si", "SI") mol.add("Si", atom.get_pos(), name="SI1") # Add to molecule list and global dictionary mol_list.append(mol) if not mol.get_short() in self._mol_dict["block"]: self._mol_dict["block"][mol.get_short()] = [] self._mol_dict["block"][mol.get_short()].append(mol) # Output return mol_list
def siloxane(self, sites, amount, normal, slx_dist=0.507, trials=1000, site_type="in"): """Attach siloxane bridges on the surface similar to Krishna et al. (2009). Here silicon atoms of silanol groups wich are at least 0.31 nm near each other can be converted to siloxan bridges, by removing one oxygen atom of the silanol groups and moving the other at the center of the two. Parameters ---------- sites : list List of silicon ids of which binding sites should be picked amount : int Number of molecules to attach normal : function Function that returns the normal vector of the surface for a given position slx_dist : float Silicon atom distance to search for parters in proximity trials : integer, optional Number of trials picking a random site site_type : string, optional Site type - interior **in**, exterior **ex** """ # Check site type input if not site_type in ["in", "ex"]: print("Pore - Wrong attachement site-type...") return # Create siloxane molecule mol = Molecule("siloxane", "SLX") mol.add("O", [0, 0, 0], name="OM1") mol.add("O", 0, r=0.09, name="OM1") mount = 0 axis = [0, 1] # Rotate molecule towards z-axis mol_axis = mol.bond(*axis) mol.rotate(geometry.cross_product(mol_axis, [0, 0, 1]), geometry.angle(mol_axis, [0, 0, 1])) mol.zero() # Search for silicon atoms near each other si_atoms = [self._block.get_atom_list()[atom] for atom in sites] si_dice = Dice(Molecule(inp=si_atoms), slx_dist+0.1, True) si_proxi = si_dice.find_parallel(None, ["Si", "Si"], slx_dist, 1e-2) si_matrix = {x[0]: x[1] for x in si_proxi} # Run through number of siloxan bridges to add mol_list = [] for i in range(amount): # Randomly pick an available site pair si = [] for j in range(trials): si_rand = random.choice(sites) if sites.index(si_rand) in si_matrix and si_matrix[sites.index(si_rand)]: si_rand_proxi = sites[si_matrix[sites.index(si_rand)][0]] if sites.index(si_rand_proxi) in si_matrix: if self._sites[si_rand]["state"] and (self._sites[si_rand_proxi]["state"]): si = [si_rand, si_rand_proxi] break # Place molecule on surface if si and self._sites[si[0]]["state"] and self._sites[si[1]]["state"]: # Create a copy of the molecule mol_temp = copy.deepcopy(mol) # Calculate center position pos_vec_halve = [x/2 for x in geometry.vector(self._block.pos(si[0]), self._block.pos(si[1]))] center_pos = [pos_vec_halve[x]+self._block.pos(si[0])[x] for x in range(self._dim)] # Rotate molecule towards surface normal vector surf_axis = normal(center_pos) mol_temp.rotate(geometry.cross_product([0, 0, 1], surf_axis), -geometry.angle([0, 0, 1], surf_axis)) # Move molecule to mounting position and remove temporary atom mol_temp.move(mount, center_pos) mol_temp.delete(0) # Add molecule to molecule list and global dictionary mol_list.append(mol_temp) if not mol_temp.get_short() in self._mol_dict[site_type]: self._mol_dict[site_type][mol_temp.get_short()] = [] self._mol_dict[site_type][mol_temp.get_short()].append(mol_temp) # Remove oxygen atom and if not geminal delete site for si_id in si: self._matrix.strip(self._sites[si_id]["o"][0]) if len(self._sites[si_id]["o"])==2: self._sites[si_id]["o"].pop(0) else: del self._sites[si_id] del si_matrix[sites.index(si_id)] return mol_list
def attach(self, mol, mount, axis, sites, amount, normal, scale=1, trials=1000, pos_list=[], site_type="in", is_proxi=True, is_random=True, is_rotate=False): """Attach molecules on the surface. Parameters ---------- mol : Molecule Molecule object to attach mount : integer Atom id of the molecule that is placed on the surface silicon atom axis : list List of two atom ids of the molecule that define the molecule axis sites : list List of silicon ids of which binding sites should be picked amount : int Number of molecules to attach normal : function Function that returns the normal vector of the surface for a given position scale : float, optional Circumference scaling around the molecule position trials : integer, optional Number of trials picking a random site pos_list : list, optional List of positions to find nearest available binding site for site_type : string, optional Site type - interior **in**, exterior **ex** is_proxi : bool, optional True to fill binding sites in proximity of filled binding site is_random : bool, optional True to randomly pick a binding site from given list is_rotate : bool, optional True to randomly rotate molecule around own axis Returns ------- mol_list : list List of molecule objects that are attached on the surface """ # Check site type input if not site_type in ["in", "ex"]: print("Pore - Wrong attachement site-type...") return # Rotate molecule towards z-axis mol_axis = mol.bond(*axis) mol.rotate(geometry.cross_product(mol_axis, [0, 0, 1]), geometry.angle(mol_axis, [0, 0, 1])) mol.zero() # Search for overlapping placements - Calculate diameter and add carbon VdW-raidus (Wiki) if is_proxi: mol_diam = (max(mol.get_box()[:2])+0.17)*scale si_atoms = [self._block.get_atom_list()[atom] for atom in sites] si_dice = Dice(Molecule(inp=si_atoms), mol_diam, True) si_proxi = si_dice.find_parallel(None, ["Si", "Si"], 0, mol_diam) si_matrix = {x[0]: x[1] for x in si_proxi} # Run through number of binding sites to add mol_list = [] for i in range(amount): si = None # Find nearest free site if a position list is given if pos_list: pos = pos_list[i] min_dist = 100000000 for site in sites: if self._sites[site]["state"]: length = geometry.length(geometry.vector(self._block.pos(site), pos)) if length < min_dist: si = site min_dist = length # Randomly pick an available site elif is_random: for j in range(trials): si_rand = random.choice(sites) if self._sites[si_rand]["state"]: si = si_rand break # Or use next binding site in given list else: si = sites[i] if i<len(sites) else None # Place molecule on surface if si is not None and self._sites[si]["state"]: # Disable binding site self._sites[si]["state"] = False # Create a copy of the molecule mol_temp = copy.deepcopy(mol) # Check if geminal if len(self._sites[si]["o"])==2: mol_temp.add("O", mount, r=0.164, theta=45) mol_temp.add("H", mol_temp.get_num()-1, r=0.098) mol_temp.set_name(mol.get_name()+"g") mol_temp.set_short(mol.get_short()+"G") # Rotate molecule towards surface normal vector surf_axis = normal(self._block.pos(si)) mol_temp.rotate(geometry.cross_product([0, 0, 1], surf_axis), -geometry.angle([0, 0, 1], surf_axis)) # Move molecule to mounting position mol_temp.move(mount, self._block.pos(si)) # Add molecule to molecule list and global dictionary mol_list.append(mol_temp) if not mol_temp.get_short() in self._mol_dict[site_type]: self._mol_dict[site_type][mol_temp.get_short()] = [] self._mol_dict[site_type][mol_temp.get_short()].append(mol_temp) # Remove bonds of occupied binding site self._matrix.strip([si]+self._sites[si]["o"]) # Recursively fill sites in proximity with silanol and geminal silanol if is_proxi: proxi_list = [sites[x] for x in si_matrix[sites.index(si)]] if len(proxi_list) > 0: mol_list.extend(self.attach(generic.silanol(), 0, [0, 1], proxi_list, len(proxi_list), normal, site_type=site_type, is_proxi=False, is_random=False)) return mol_list
def pattern(self): """Construct minimal block structure. Returns ------- block : Molecule Minimal block structure """ # Initialize mols = [self._hexagonal() for x in range(3)] # Combine three hexagonal molecules mols[0].add("O", 2, r=self._b) mols[0].add("O", 6, r=self._b) mols[0].add("O", 10, r=self._b) mols[1].move(0, mols[0].pos(12)) mols[1].translate([0, 0, self._b]) mols[1].delete([1, 2, 3, 4, 5, 6, 7]) mols[2].move(0, mols[0].pos(14)) mols[2].translate([0, 0, self._b]) mols[2].delete([2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) # Build block block = [] for i in range(11): block.append(Molecule(inp=copy.deepcopy(mols))) block[1].rotate("x", 180) block[1].move(18, block[0].pos(18)) block[1].translate([0, 0, self._b * 2]) block[1].add("O", block[0].pos(18), r=self._b) block[2].rotate("x", 180) block[2].move(0, block[0].pos(18)) block[3].move(4, block[0].pos(15)) block[4].rotate("x", 180) block[4].move(16, block[0].pos(18)) block[4].delete([21, 19, 15, 20, 14, 12, 10, 2, 11, 1, 0]) block[5].move(6, block[1].pos(16)) block[5].delete([21, 19, 15, 20, 14, 12, 10, 2, 11, 1, 0]) # Delete overlapping molecules block = Molecule(inp=block) overlap = block.overlap() block.delete(sum([overlap[x] for x in overlap], [])) # Check overlapping atoms due to repetition for dim in range(3): for pm in range(2): # Define translate vector translate = [0, 0, 0] translate[dim] = self._repeat[dim] translate[dim] *= -1 if pm == 1 else 1 # Remove atoms block = Molecule(inp=[block]) mol_repeat = copy.deepcopy(block) mol_repeat.translate(translate) block.delete([ x for x in Molecule(inp=[block, mol_repeat]).overlap() if x < block.get_num() ]) # Move to zero block.zero() return block
def _hexagonal(self): """Define hexagonal molecule. Returns ------- hex : Molecule Hexagonal molecule object """ hex = Molecule() hex.add("Si", [0, 0, 0]) hex.add("O", 0, r=self._b, theta=self._a, phi=60) hex.add("Si", 1, r=self._b, bond=[0, 1]) hex.add("O", 2, r=self._b, theta=180, phi=0) hex.add("Si", 3, r=self._b, bond=[2, 3]) hex.add("O", 4, r=self._b, theta=self._a, phi=300) hex.add("Si", 5, r=self._b, bond=[4, 5]) hex.add("O", 6, r=-self._b, theta=self._a, phi=60) hex.add("Si", 7, r=self._b, bond=[6, 7]) hex.add("O", 8, r=-self._b, theta=180, phi=0) hex.add("Si", 9, r=self._b, bond=[8, 9]) hex.add("O", 10, r=-self._b, theta=self._a, phi=300) hex.rotate("y", 90) hex.rotate("z", 90) hex.rotate("y", 180) hex.rotate("x", geometry.angle(hex.bond(8, 6), [0, 1, 0])) hex.rotate("x", -90) return hex