def string_array_to_molecule(parser_fun, file_name, mol=None): """ Convert a Numpy string array like: [['C', '-1.487460', '-0.028670', '-0.000060'], ['O', '0.376340', '0.028670', '-0.000060'], ['H', '-1.818910', '-1.067060', '-0.000060'], ['H', '-1.866470', '0.473700', '0.889930'], ['H', '-1.866470', '0.473700', '-0.890040'], ['H', '0.756720', '-0.950010', '-0.000060']] To a plams ``Molecule``. """ string_array_to_float = np.vectorize(float) mols = parse_file(parser_fun, file_name).asList() last_mol = np.array(mols[-1]) elems = last_mol[:, 0] coords = string_array_to_float(last_mol[:, 1:]) if mol: if len(coords) == len(mol): plams_mol = mol for i in range(len(plams_mol)): plams_mol.atoms[i].coords = tuple( [float(c) for c in coords[i]]) else: raise RuntimeError('Output molecule does not match input molecule') else: plams_mol = Molecule() for e, c in zip(elems, coords): plams_mol.add_atom(Atom(symbol=e, coords=tuple(c))) return plams_mol
def string_array_to_molecule(parser_fun, file_name, mol=None): """ Convert a Numpy string array like: [['C', '-1.487460', '-0.028670', '-0.000060'], ['O', '0.376340', '0.028670', '-0.000060'], ['H', '-1.818910', '-1.067060', '-0.000060'], ['H', '-1.866470', '0.473700', '0.889930'], ['H', '-1.866470', '0.473700', '-0.890040'], ['H', '0.756720', '-0.950010', '-0.000060']] To a plams ``Molecule``. """ string_array_to_float = np.vectorize(float) mols = parse_file(parser_fun, file_name).asList() last_mol = np.array(mols[-1]) elems = last_mol[:, 0] coords = string_array_to_float(last_mol[:, 1:]) if mol: if len(coords) == len(mol): plams_mol = mol for i in range(len(plams_mol)): plams_mol.atoms[i].coords = tuple([float(c) for c in coords[i]]) else: raise RuntimeError('Output molecule does not match input molecule') else: plams_mol = Molecule() for e, c in zip(elems, coords): plams_mol.add_atom(Atom(symbol=e, coords=tuple(c))) return plams_mol
def tuplesXYZ_to_plams(xs): """ Transform a list of namedTuples to a Plams molecule """ plams_mol = Molecule() for at in xs: symb = at.symbol cs = at.xyz plams_mol.add_atom(Atom(symbol=symb, coords=tuple(cs))) return plams_mol
def geometry_to_molecule(geometry): """ Convert a list of XYZ coordinates to a Molecule object """ mol = Molecule() for i in range(0, len(geometry)): mol.add_atom( Atom(symbol=geometry[i][0], coords=(geometry[i][1], geometry[i][2], geometry[i][3]))) return mol
def parse_molecule_traj(file_traj: PathLike) -> Molecule: """Read Molecules from the job_name.traj file.""" mols = manyXYZ(file_traj) # Last geometry corresponds to the optimized structure opt_mol = mols[-1] plams_mol = Molecule() for at in opt_mol: symb = at.symbol cs = at.xyz plams_mol.add_atom(Atom(symbol=symb, coords=tuple(cs))) return plams_mol
def supstitution_symmetry(mol): """Returns atomic symbols of substituted atoms (or first conection of non diatomic ligand). Writes type of substitution symetry at the molecular properties Parameters ---------- mol : |plams.Molecule| A PLAMS molecule Returns ------- str Type of subsymmetry """ dataframe, type_of_symetry = [], [] ligand_identity = mol.properties.ligID # Defining C atoms conected to substituents and making Molecule object (cmol) out of them catoms = mol.properties.coords_other_arrays cmol = Molecule() for at in catoms: cmol.add_atom(mol.closest_atom(at)) # If substitution is linear, simple combinations without repetition can be applied if len(ligand_identity) <= 2: if len(ligand_identity) == 1: logger.warning( "One does not simply ask for subsymmetry of one atom!") return elif len(ligand_identity) == 0: logger.warning( "One does not simply ask for subsymmetry of no atom") return else: subsymmetry = 'linear' else: # Getting non zero row indices from data frame - defines symmetry type dataframe = get_symmetry(cmol, decimals=2) type_of_symetry = np.unique(dataframe.to_numpy().nonzero()[0]) # Assign type of symetry and atomic symbols if list(type_of_symetry) == [0, 1, 8, 9]: subsymmetry = 'D2h' else: logger.warning("Subsymmetry is not recognized") return return subsymmetry
def _test_distribute(mol: Molecule, symbol: str, **kwargs) -> Molecule: """Helper function for :func:`test_distribute`.""" if not isinstance(mol, Molecule): mol = Molecule(mol) _idx_in = [i for i, at in enumerate(mol) if at.symbol == symbol] idx_in = np.fromiter(_idx_in, count=len(_idx_in), dtype=int) idx_out = distribute_idx(mol, idx_in, **kwargs) a = symbol b = 'I' if a != 'I' else 'Br' mol2 = Molecule() for i, at in enumerate(mol): if at.symbol != symbol: continue symbol_new = a if i not in idx_out else b mol2.add_atom(Atom(symbol=symbol_new, coords=at.coords, mol=mol2)) return mol2
def _parse_ion(ion: Molecule | str | int) -> Tuple[Molecule, Atom]: """Interpret and parse the **ion** argument in :func:`.get_xyn`. Construct and return a new :math:`XY_{n=0}` molecule and the atom :math:`X` itself. If **ion** is a polyatomic ion then :math:`XY_{n=0}` is a copy of **ion** and :math:`X` is the first atom with a non-zero charge. Parameters ---------- ion : |str|_, |int|_ or |plams.Molecule|_ An ion (:math:`X`), be it mono- (*e.g.* atomic number or symbol) or poly-atomic. Returns ------- |plams.Molecule|_ and |plams.Atom|_ A :math:`XY_{n=0}` molecule and the the charged atom from :math:`X`. Raises ------ MoleculeError Raised if ion is an instance of :math:`Molecule` but does not contain any charged atoms. """ if isinstance(ion, Molecule): XYn = ion.copy() for i, at in enumerate(XYn, 1): if not at.properties.charge: continue # Found an atom with non-zero charge; return a copy ret = XYn.copy() return ret, ret[i] raise MoleculeError( "No atoms were found in 'ion' with a non-zero charge") else: # Ion is an atomic number or symbol X = Atom(atnum=to_atnum(ion)) XYn = Molecule() XYn.add_atom(X) return XYn, X
def from_rdmol(rdkit_mol, confid=-1): """ Translate an RDKit molecule into a PLAMS molecule type. :parameter rdkit_mol: RDKit molecule :parameter int confid: conformer identifier from which to take coordinates :type rdkit_mol: rdkit.Chem.Mol :return: a PLAMS molecule :rtype: plams.Molecule """ if isinstance(rdkit_mol, Molecule): return rdkit_mol # Create plams molecule plams_mol = Molecule() total_charge = 0 try: Chem.Kekulize(rdkit_mol) except: pass conf = rdkit_mol.GetConformer(id=confid) for rd_atom in rdkit_mol.GetAtoms(): pos = conf.GetAtomPosition(rd_atom.GetIdx()) ch = rd_atom.GetFormalCharge() pl_atom = Atom(rd_atom.GetAtomicNum(), coords=(pos.x, pos.y, pos.z), charge=ch) if rd_atom.GetPDBResidueInfo(): pl_atom.properties.pdb_info = get_PDBResidueInfo(rd_atom) plams_mol.add_atom(pl_atom) total_charge += ch for bond in rdkit_mol.GetBonds(): at1 = plams_mol.atoms[bond.GetBeginAtomIdx()] at2 = plams_mol.atoms[bond.GetEndAtomIdx()] plams_mol.add_bond(Bond(at1, at2, bond.GetBondTypeAsDouble())) plams_mol.charge = total_charge for propname in rdkit_mol.GetPropNames(): plams_mol.properties[propname] = rdkit_mol.GetProp(propname) return plams_mol
def create_molecule(name, r=2.25): mol = Molecule() mol.add_atom(Atom(symbol='Ba', coords=(0, 0, 0))) mol.add_atom(Atom(symbol='F', coords=(0, 0, r))) return mol
AtomNamesList = kf.read('Molecule', 'AtomicNumbers') AtomSymbolList = kf.read('Molecule', 'AtomSymbols') Coords = Units.convert(kf.read('Molecule', 'Coords'), 'Bohr', 'Angstrom') energy = Units.convert(kf.read('AMSResults', 'Energy'), 'au', 'kcal/mol') # 1 Hartree = 1 a.u npCoords = np.array(Coords) npCoords = npCoords.reshape(int(npCoords.size / 3), 3) mol = Molecule() AtomSymbolList = AtomSymbolList.strip() SymbolList = list(AtomSymbolList.split()) SymbolList = [s.strip() for s in SymbolList] for at, coord in zip(SymbolList, npCoords): mol.add_atom(Atom(symbol=at, coords=coord)) # Keep in mind, that coordinate indexing starts from 0; pes_at1 = Atom(symbol=SymbolList[pes_atom1_id - 1], coords=npCoords[pes_atom1_id - 1]) pes_at2 = Atom(symbol=SymbolList[pes_atom2_id - 1], coords=npCoords[pes_atom2_id - 1]) pes_bond = Bond(pes_at1, pes_at2) line = [(pes_atom1_id, pes_atom2_id), direction, PESnum, pes_bond.length(), energy] data.append(line) column_names = [ 'id', 'str/sq', 'PES', 'Bond length [A]', 'Energy [kcal / mol]' ] table_data = pd.DataFrame(data, columns=column_names)
def run_ff_anionic(mol: Molecule, anchor: Atom, s: Settings) -> None: r"""Assign neutral parameters to an anionic species (*e.g.* carboxylate). Consists of 4 distinct steps: * **mol** is capped with a proton: *e.g.* :math:`RCO_2^- \rightarrow RCO_2H`. * Parameters are guessed for both fragments (using MATCH_) and then recombined into **mol**. * The capping proton is removed again. * The atomic charge of **anchor** is adjusted such that the total moleculair charge becomes zero. Performs an inplace update of **mol**. .. _MATCH: http://brooks.chem.lsa.umich.edu/index.php?page=match&subdir=articles/resources/software Parameters ---------- mol : :class:`Molecule<scm.plams.mol.molecule.Molecule>` A cationic molecule. anchor : :class:`Atom<scm.plams.mol.atom.Atom>` The atom in **mol** with the formal negative charge. s : :class:`Settings<scm.plams.core.settings.Settings>` The job Settings to-be passed to :class:`MATCHJob<nanoCAT.ff.match_job.MATCHJob>`. See Also -------- :func:`run_match_job()<nanoCAT.ff.ff_assignment.run_match_job>` Assign atom types and charges to **mol** based on the results of MATCH_. :func:`run_ff_cationic()<nanoCAT.ff.ff_cationic.run_ff_cationic>` Assign neutral parameters to a cationic species (*e.g.* ammonium). """ # noqa if anchor not in mol: raise MoleculeError("Passed 'anchor' is not part of 'mol'") anchor.properties.charge = 0 # Cap the anion with a proton mol_with_h = add_Hs(mol) _cap_h = mol_with_h[-1] cap_h = Atom(atnum=_cap_h.atnum, coords=_cap_h.coords, mol=mol, settings=mol[1].properties.copy()) cap_h.properties.pdb_info.IsHeteroAtom = False cap_h.properties.pdb_info.Name = 'Hxx' mol.add_atom(cap_h) mol.add_bond(Bond(anchor, cap_h, mol=mol)) # Guess parameters and remove the capping proton run_match_job(mol, s) mol.delete_atom(cap_h) # Set the total charge of the system to 0 anchor.properties.charge_float -= sum(at.properties.charge_float for at in mol) return None