Example #1
0
    def __init__(self, reactants: List[Union[Molecule, MoleculeGraph]], products: List[Molecule],
                 autots_variables: Dict, gen_variables: Dict,
                 spin_multiplicity: Optional[int] = None):

        self.reactants = list()
        for reactant in reactants:
            self.reactants.append(mol_to_mol_graph(reactant))

        self.products = list()
        for product in products:
            self.products.append(mol_to_mol_graph(product))

        self.autots_variables = autots_variables
        self.gen_variables = gen_variables

        rct_sum = int(sum([m.molecule.charge for m in self.reactants]))
        pro_sum = int(sum([m.molecule.charge for m in self.products]))

        if rct_sum != pro_sum:
            raise ValueError("Reactant and product charges do not match!")

        self.autots_variables["charge"] = rct_sum

        nelectrons = int(sum([m.molecule._nelectrons for m in self.reactants]))
        if spin_multiplicity is None:
            if len(self.reactants) == len(self.products) == 1:
                self.autots_variables["multiplicity"] = self.reactants[0].molecule.spin_multiplicity
            else:
                self.autots_variables["multiplicity"] = 1 if nelectrons % 2 == 0 else 2
        else:
            if (spin_multiplicity % 2) == (nelectrons % 2):
                raise ValueError("Invalid spin multiplicity: {} with {} electrons!".format(spin_multiplicity, nelectrons))
            else:
                self.autots_variables["multiplicity"] = spin_multiplicity
Example #2
0
    def test_mol_to_mol_graph(self):
        mol = Molecule.from_file(
            (test_dir / "molecules" / "li2co3_1.xyz").as_posix())
        mg = MoleculeGraph.with_local_env_strategy(mol, OpenBabelNN())
        mg = metal_edge_extender(mg)

        self.assertEqual(mg, mol_to_mol_graph(mol))
Example #3
0
    def __init__(self,
                 molecule: Union[Molecule, MoleculeGraph],
                 job_type: Union[str, JaguarJobType],
                 path: Union[str, Path],
                 schrodinger_dir: Optional[Union[str, Path]] = "SCHRODINGER",
                 job_name: Optional[str] = None,
                 num_cores: Optional[int] = 40,
                 host: Optional[str] = None,
                 save_scratch: Optional[bool] = False,
                 input_params: Optional[Dict] = None):
        """

        :param molecule:
        :param job_type:
        :param path:
        :param schrodinger_dir:
        :param job_name:
        :param num_cores:
        :param host:
        :param save_scratch:
        :param input_params:
        """

        self.molecule = mol_to_mol_graph(molecule)

        if isinstance(job_type, str):
            if job_type.lower() not in job_type_mapping:
                raise ValueError("Job type {} unknown!".format(job_type))

            self.job_type = job_type_mapping[job_type.lower()]
        else:
            self.job_type = job_type

        if isinstance(path, Path):
            self.path = path
        else:
            self.path = Path(path)

        if schrodinger_dir == "SCHRODINGER":
            self.schrodinger_dir = Path(os.environ["SCHRODINGER"])
        else:
            if isinstance(schrodinger_dir, str):
                self.schrodinger_dir = Path(schrodinger_dir)
            else:
                self.schrodinger_dir = schrodinger_dir

        self.job_name = job_name
        self.num_cores = num_cores
        self.host = host
        self.save_scratch = save_scratch

        self.input_params = input_params
Example #4
0
    def __init__(self,
                 reactants: List[Union[Molecule, MoleculeGraph]],
                 products: List[Union[Molecule, MoleculeGraph]],
                 path: Union[str, Path],
                 schrodinger_dir: Optional[Union[str, Path]] = "SCHRODINGER",
                 job_name: Optional[str] = None,
                 num_cores: Optional[int] = 40,
                 host: Optional[str] = None,
                 save_scratch: Optional[bool] = False,
                 input_params: Optional[Dict] = None):
        """
        Args:
            reactants (list of Molecule objects): the reactants of the reaction
                to be examined
            products (list of Molecule objects): the products of the reaction
                to be examined
            path (str): The directory where this calculation will take place.
            schrodinger_dir (str): A path to the Schrodinger Suite of software.
                This is used to call AutoTS and other utilities. By default,
                this is "$SCHRODINGER", which should be an environment variable
                set at the time of installation.
            job_name (str): If provided (default None), this will set the name
                of the job in Schrodinger's jobcontrol system.
            num_cores (int): How many cores should the program be parallelized
                over (default 40). When multiple subjobs need to be run
                simultaneously, AutoTS will distribute these cores automatically
                between subjobs
            host (str): Which host should the calculation be run on? By default,
                this is "localhost", which should generally mean that the
                calculation is run on the current node without using a queueing
                system
            save_scratch (bool): If True (default False), save a *.zip file
                containing the contents of the calculation scratch directory
            input_params (dict): Keywords and associated values to be provided
                to TSSet
        """

        self.reactants = list()
        self.products = list()

        for reactant in reactants:
            self.reactants.append(mol_to_mol_graph(reactant))

        for product in products:
            self.products.append(mol_to_mol_graph(product))

        if isinstance(path, Path):
            self.path = path
        else:
            self.path = Path(path)

        if schrodinger_dir == "SCHRODINGER":
            self.schrodinger_dir = Path(os.environ["SCHRODINGER"])
        else:
            if isinstance(schrodinger_dir, str):
                self.schrodinger_dir = Path(schrodinger_dir)
            else:
                self.schrodinger_dir = schrodinger_dir

        self.job_name = job_name
        self.num_cores = num_cores
        self.host = host
        self.save_scratch = save_scratch

        self.input_params = input_params
Example #5
0
    def insert_autots_calculation(
            self,
            reactants: Union[List[Molecule], List[MoleculeGraph]],
            products: Union[List[Molecule], List[MoleculeGraph]],
            spin_multiplicity: Optional[int] = None,
            name: Optional[str] = None,
            input_params: Optional[Dict] = None,
            tags: Optional[Dict] = None,
            priority: Optional[int] = None,
            include_reaction_graph: Optional[bool] = False,
            additional_data: Optional[Dict] = None):
        """
        Add a reaction to the "queue" (self.autots_queue_collection collection).

        Args:
            reactants (list of Molecule objects): The reactants of the reaction.
                Can be separated molecules or a reaction complex
            products (list of Molecule objects): The products of the reaction.
                Can be separated molecules or a reaction complex
            name (str, or None): Name of the reaction. No
            input_params (Dict, or None): Dictionary with all input parameters
                for this calculation. These keywords and the associated values
                will be provided to TSSet (or, eventually, JaguarSet).
            tags (Dict, or None): Dictionary with some calculation metadata
                Ex: {"class": "production", "time": 3}
            priority (int, or None): To identify jobs that should or should
                not be run, calculations can be prioritized. The higher the
                priority, the more important the calculation. If the priority is
                None (default), then the job will not be selected unless
                chosen specifically by ID or other query. If the number is
                negative (< 0), the calculation will never be selected.
            include_reaction_graph (bool): Should a reaction graph be generated
                from the reactant and product MoleculeGraphs? This might be
                skipped because it can be costly to perform subgraph isomorphisms
                and identify the appropriate reaction graph.
            additional_data (dict): Any additional data that should be stored
                with a calculation.

        Returns:
            None
        """

        entry = {"state": "READY"}

        if len(reactants) == 0 or len(products) == 0:
            raise ValueError("reactants and products must be non-empty lists!")

        entry["reactants"] = list()
        entry["products"] = list()

        for reactant in reactants:
            entry["reactants"].append(mol_to_mol_graph(reactant))

        for product in products:
            entry["products"].append(mol_to_mol_graph(product))

        rct_charge = sum([m.molecule.charge for m in entry["reactants"]])
        pro_charge = sum([m.molecule.charge for m in entry["products"]])

        if rct_charge != pro_charge:
            raise ValueError(
                "Reactants and products do not have the same charge!")

        entry["charge"] = rct_charge

        rct_nelectrons = sum(
            [m.molecule._nelectrons for m in entry["reactants"]])
        pro_nelectrons = sum(
            [m.molecule._nelectrons for m in entry["products"]])

        if rct_nelectrons != pro_nelectrons:
            raise ValueError(
                "Reactants and products do not have the same number of electrons!"
            )

        entry["nelectrons"] = int(rct_nelectrons)
        if spin_multiplicity is None:
            if len(entry["reactants"]) == len(entry["products"]) == 1:
                entry["spin_multiplicity"] = entry["reactants"][
                    0].molecule.spin_multiplicity
            else:
                entry["spin_multiplicity"] = 1 if entry[
                    "nelectrons"] % 2 == 0 else 2
        else:
            if (spin_multiplicity % 2) == (entry["nelectrons"] % 2):
                raise ValueError(
                    "Invalid spin multiplicity: {} with {} electrons!".format(
                        spin_multiplicity, entry["nelectrons"]))
            else:
                entry["spin_multiplicity"] = spin_multiplicity

        if name is None:
            rct_names = [
                m.molecule.composition.alphabetical_formula + "_" +
                str(m.molecule.charge) for m in entry["reactants"]
            ]
            pro_names = [
                m.molecule.composition.alphabetical_formula + "_" +
                str(m.molecule.charge) for m in entry["products"]
            ]
            entry["name"] = " + ".join(rct_names) + " -> " + " + ".join(
                pro_names)
        else:
            entry["name"] = name

        entry["input"] = input_params
        entry["tags"] = tags
        entry["priority"] = priority

        union_rct = union_molgraph(entry["reactants"])
        union_pro = union_molgraph(entry["products"])

        entry["molgraph_rct"] = union_rct.as_dict()
        entry["molgraph_pro"] = union_pro.as_dict()

        if include_reaction_graph:
            reaction_graph = get_reaction_graphs(union_rct,
                                                 union_pro,
                                                 allowed_form=2,
                                                 allowed_break=2,
                                                 stop_at_one=True)
            if len(reaction_graph) == 0:
                raise RuntimeError("No valid reaction could be found between "
                                   "reactants and products!")

            entry["reaction_graph"] = reaction_graph[0].as_dict()
        else:
            entry["reaction_graph"] = None

        entry["reactants"] = [r.as_dict() for r in entry["reactants"]]
        entry["products"] = [p.as_dict() for p in entry["products"]]

        entry["rxnid"] = self.database["counter"].find_one_and_update(
            {"_id": "rxnid"}, {"$inc": {
                "c": 1
            }},
            return_document=ReturnDocument.AFTER)["c"]
        entry["created_on"] = datetime.datetime.now(datetime.timezone.utc)
        entry["updated_on"] = datetime.datetime.now(datetime.timezone.utc)

        entry["additional_data"] = additional_data

        doc = jsanitize(entry, allow_bson=True)
        self.database[self.autots_queue_collection].update_one(
            {"rxnid": doc["rxnid"]}, {"$set": doc}, upsert=True)
Example #6
0
    def insert_jaguar_calculation(self,
                                  molecule: Union[Molecule, MoleculeGraph],
                                  job_type: Union[str, JaguarJobType],
                                  name: Optional[str] = None,
                                  input_params: Optional[Dict] = None,
                                  tags: Optional[Dict] = None,
                                  priority: Optional[int] = None,
                                  additional_data: Optional[Dict] = None):
        """
        Add a calculation to the "queue" (self.jaguar_queue_collection collection).

        Args:
            molecule (Molecule or MoleculeGraph object): the molecule to be
                subjected to this calculation.
            name (str, or None): Name of the reaction. No
            input_params (Dict, or None): Dictionary with all input parameters
                for this calculation. These keywords and the associated values
                will be provided to TSSet (or, eventually, JaguarSet).
            tags (Dict, or None): Dictionary with some calculation metadata
                Ex: {"class": "production", "time": 3}
            priority (int, or None): To identify jobs that should or should
                not be run, calculations can be prioritized. The higher the
                priority, the more important the calculation. If the priority is
                None (default), then the job will not be selected unless
                chosen specifically by ID or other query. If the number is
                negative (< 0), the calculation will never be selected.
            include_reaction_graph (bool): Should a reaction graph be generated
                from the reactant and product MoleculeGraphs? This might be
                skipped because it can be costly to perform subgraph isomorphisms
                and identify the appropriate reaction graph.
            additional_data (dict): Any additional data that should be stored
                with a calculation.

        Returns:
            None
        """

        entry = {"state": "READY"}

        mg = mol_to_mol_graph(molecule)
        entry["molecule"] = mg.as_dict()
        entry["charge"] = mg.molecule.charge
        entry["nelectrons"] = int(mg.molecule._nelectrons)
        entry["spin_multiplicity"] = mg.molecule.spin_multiplicity

        if name is None:
            entry["name"] = entry["molecule"].composition.alphabetical_formula
            entry["name"] += "_" + str(entry["charge"])
            entry["name"] += "({})".format(entry["spin_multiplicity"])
        else:
            entry["name"] = name

        entry["input"] = input_params
        entry["tags"] = tags
        entry["priority"] = priority
        if isinstance(job_type, str):
            entry["job_type"] = job_type.lower()
        else:
            entry["job_type"] = job_type.name.lower()

        entry["calcid"] = self.database["counter"].find_one_and_update(
            {"_id": "calcid"}, {"$inc": {
                "c": 1
            }},
            return_document=ReturnDocument.AFTER)["c"]
        entry["created_on"] = datetime.datetime.now(datetime.timezone.utc)
        entry["updated_on"] = datetime.datetime.now(datetime.timezone.utc)

        entry["additional_data"] = additional_data

        doc = jsanitize(entry, allow_bson=True)
        self.database[self.jaguar_queue_collection].update_one(
            {"calcid": doc["calcid"]}, {"$set": doc}, upsert=True)