def make_reactions(reactions, mol_energies): def energy_sum(keys): return { field: sum([getattr(mol_energies[k], field) for k in keys]) for field in GFIELDS } rx_energies = dict() all_rx_energies = list() for rx_name, reagents in reactions.items(): print(f"Calculating energies for reaction '{rx_name}'") pprint(reagents) educts = to_list(reagents["educts"]) ts = [ reagents["ts"], ] assert len(ts) == 1 products = to_list(reagents["products"]) add = reagents.get("add", []) add = [ add, ] if isinstance(add, str) else list(add) educt_ens = energy_sum(educts + add) ts_ens = energy_sum(ts + add) product_ens = energy_sum(products + add) energies = {} for field in GFIELDS: ens = np.array( [dct[field] for dct in (educt_ens, ts_ens, product_ens)]) energies[field] = ens rx_energies[rx_name] = energies return rx_energies
def get_reactants(rx): reactants = list() for key in ("educts", "ts", "products"): reactants.append(to_list(rx[key])) # 'add' may not be present so we have to handle it separately reactants.append(to_list(rx.get("add", []))) return reactants
def __init__(self, name, educts, ts, products, energies=None, k=None, add=None, label=None): self.name = name self.educts = to_list(educts) self.ts = to_list(ts) self.products = to_list(products) self.energies = energies self.k = k
def make_dash_data(reactions, thermos, thermochem_df): unique_Ts = thermochem_df["T"].unique() def sum_(keys, field): return sum([getattr(thermos[k], field) for k in keys]) df_cols = "name reactant single_point single_point_alt dG_solv T dG".split( ) rows = list() for rx_name, reagents in reactions.items(): add = reagents.get("add", []) add = [ add, ] if isinstance(add, str) else list(add) for reactant in ("educts", "ts", "products"): reactants = to_list(reagents[reactant]) if isinstance(reactants, str): reactants = [ reactants, ] reactants = reactants + add sp = sum_(reactants, "single_point") reactant_thermo = thermochem_df[thermochem_df["name"].isin( reactants)] dGs = reactant_thermo.groupby("T").dG.sum().to_list() sp_alt = sum_(reactants, "single_point_alt") dG_solv = sum_(reactants, "dG_solv") for T, dG in zip(unique_Ts, dGs): row = [rx_name, reactant, sp, sp_alt, dG_solv, T, dG] rows.append(row) df = pd.DataFrame(rows, columns=df_cols) return df
def run(): args = parse_args(sys.argv[1:]) no_alt = args.no_alt show_rxs = not args.norxs show_paths = not args.nopaths temperature = args.T if args.quick: show_paths = False educts, ts, products = [ reactants.split(",") for reactants in args.quick.split(";") ] assert len(ts) == 1 ed_keys = [f"ed{i}" for i in range(len(educts))] prod_keys = [f"prod{i}" for i in range(len(products))] inp_dict = { "molecules": dict(), "program": "orca", } def add_mols(keys, fns): for key, fn in zip(keys, fns): inp_dict["molecules"][key] = { "freq": fn, } add_mols(ed_keys, educts) add_mols([ "ts", ], ts) add_mols(prod_keys, products) # Dummy reaction inp_dict["reactions"] = { "quick": { "educts": ed_keys, "ts": "ts", "products": prod_keys, } } elif args.yaml: with open(args.yaml) as handle: inp_dict = yaml.load(handle) else: print( "Specify either a YAML file or use --quick! See also 'plotprofile --help'" ) sys.exit() reactions = inp_dict["reactions"] if args.interactive: rx_keys = list(reactions.keys()) for i, rx in enumerate(rx_keys): print(f"{i:02d}: {rx}") ind = int(input("Show reaction: ", )) rx_key = rx_keys[ind] reaction = reactions[rx_key] reactions = { rx_key: reaction, } # Modify molecules so only the ones actually needed are loaded rx_molecules = list( it.chain(*[to_list(mols) for mols in reaction.values()])) inp_dict["molecules"] = { key: val for key, val in inp_dict["molecules"].items() if key in rx_molecules } molecules = inp_dict["molecules"] thermos = load_thermos(molecules, inp_dict["program"]) mol_energies = load_molecule_energies(thermos, no_alt=no_alt) rx_strs = {rx_name: rx_to_string(rx) for rx_name, rx in reactions.items()} if args.parsedash: freq_logs = {k: v["freq"] for k, v in inp_dict["molecules"].items()} qc_datas = {k: QCData(v) for k, v in freq_logs.items()} temps = np.arange(298.00, 373.00, 5) rows = list() for mol_name, temp in it.product(qc_datas, temps): qc = qc_datas[mol_name] tc = thermochemistry(qc, temp) row = [ mol_name, ] + list(tc) rows.append(row) cols = [ "name", ] + list(tc._fields) df = pd.DataFrame(rows, columns=cols) df.to_pickle("thermochem_data") thermochem_df = pd.read_pickle("thermochem_data") df = make_dash_data(inp_dict["reactions"], thermos, thermochem_df) df.to_pickle("dash_data") print() print("Using these G-values:") for key, val in mol_energies.items(): print(key) pprint(val._asdict()) print() rx_energies = make_reactions(reactions, mol_energies) paths = inp_dict.get("paths", None) if paths is None: print("Found no defined paths in .yaml file. Using all defined " "reactions instead.") paths = {"autogenerated": list(reactions.keys())} if args.interactive: paths = dict() print_path_rx_energies(paths, rx_energies, rx_strs, temperature) dump_energies(rx_energies) mol_labels = { # Use molecule key as fallback if no label is defined mol: values.get("label", mol) for mol, values in molecules.items() } rx_labels = {} rx_titles = {} # Create label strings for plotting for rx_name in reactions: labels = [ v for k, v in reactions[rx_name].items() if k in ("educts", "ts", "products") ] pretty_labels = list() for lbl in labels: if isinstance(lbl, str): lbl = [ lbl, ] # Replace with pretty labels lbl = [mol_labels[mol] for mol in lbl] lbl = ",\n".join(lbl) pretty_labels.append(lbl) rx_labels[rx_name] = pretty_labels rx_title = reactions[rx_name].get("label", rx_name) rx_titles[rx_name] = rx_title # Try to use the 'best' energies for plotting. That is with alternative # single point and solvation. best_rx_energies = { rx_name: rx_energies[rx_name]["G_solv_alt"] for rx_name in rx_labels } if show_rxs: plot_reactions(best_rx_energies, rx_labels, rx_titles, temperature) else: print("Skipped plotting of reactions!") if show_paths and not args.interactive and (len(best_rx_energies) > 1): plot_paths(best_rx_energies, paths, rx_labels, rx_titles) else: print("Skipped plotting of reaction paths!") path_reactions = set(list(it.chain(*paths.values()))) all_reactions = set(list(reactions.keys())) remainder = all_reactions - path_reactions if len(remainder) > 0: print() print("Warning!".upper()) print("Not all defined reactions appeared in (defined) paths!") for i, rx in enumerate(remainder): print(f"\t{i:02d}: {rx}") print("Warning!".upper()) print() remainder_path = { "remainder": remainder, } print_path_rx_energies(remainder_path, rx_energies, rx_strs, temperature) to_compare = inp_dict.get("compare", dict()) compare_molecules(to_compare, mol_energies) # compare_molecules(to_compare, mol_energies, attr="G_gas_alt") # Kinetics if "kinetics" in inp_dict: kin_dict = inp_dict["kinetics"] kin_reactions = kin_dict.get("only", reactions.keys()) reaction_objs = list() for rx_name in kin_reactions: reactants = reactions[rx_name] educts = reactants["educts"] ts = reactants["ts"] products = reactants["products"] energies = rx_energies[rx_name] frx = Reaction(rx_name, educts, ts, products, energies) brx = frx.get_back_reaction() reaction_objs.extend((frx, brx)) c0s = kin_dict["c0s"] t_span = kin_dict["t_span"] mols, res = kinetics(reaction_objs, c0s, t_span=t_span) plot_kinetics(mols, res)