def sample_record() -> MoleculeData: md = MoleculeData.from_identifier('O') result = OptimizationResult.parse_file( _my_path.joinpath('records/xtb-neutral.json')) md.add_geometry(result, "xtb") md.subsets.append('pytest') return md
def parse_output(self, outfiles: Dict[str, str], input_model: OptimizationInput) -> OptimizationResult: # Get the stdout from the calculation (required) stdout = outfiles.pop("stdout") stderr = outfiles.pop("stderr") # Parse out the atomic results from the file atomic_results = harvest_as_atomic_result(input_model, stdout) # Isolate the converged result final_step = atomic_results[-1] return OptimizationResult( initial_molecule=input_model.initial_molecule, input_specification=input_model.input_specification, final_molecule=final_step.molecule, trajectory=atomic_results, energies=[ float(r.extras["qcvars"]["CURRENT ENERGY"]) for r in atomic_results ], stdout=stdout, stderr=stderr, success=True, provenance=Provenance(creator="NWChemRelax", version=self.get_version(), routine="nwchem_opt"), )
def compute(self, input_model: "OptimizationInput", config: "TaskConfig") -> "OptimizationResult": try: import geometric except ModuleNotFoundError: raise ModuleNotFoundError("Could not find geomeTRIC in the Python path.") input_data = input_model.dict() # Temporary patch for geomeTRIC input_data["initial_molecule"]["symbols"] = list(input_data["initial_molecule"]["symbols"]) # Set retries to two if zero while respecting local_config local_config = config.dict() local_config["retries"] = local_config.get("retries", 2) or 2 input_data["input_specification"]["extras"]["_qcengine_local_config"] = local_config # Run the program output_data = geometric.run_json.geometric_run_json(input_data) output_data["provenance"] = { "creator": "geomeTRIC", "routine": "geometric.run_json.geometric_run_json", "version": geometric.__version__, } output_data["schema_name"] = "qcschema_optimization_output" output_data["input_specification"]["extras"].pop("_qcengine_local_config", None) if output_data["success"]: output_data = OptimizationResult(**output_data) return output_data
def compute(self, input_data: "OptimizationInput", config: "TaskConfig") -> "OptimizationResult": try: import berny except ModuleNotFoundError: raise ModuleNotFoundError("Could not find Berny in the Python path.") # Get berny version from the installed package, use setuptools' # pkg_resources for python < 3.8 if sys.version_info >= (3, 8): from importlib.metadata import distribution else: from pkg_resources import get_distribution as distribution berny_version = distribution("pyberny").version # Berny uses the stdlib logging module and by default uses per-module # loggers. For QCEngine, we create one logger per BernyProcedure # instance, by using the instance's id(), and send all logging messages # to a string stream log_stream = StringIO() log = logging.getLogger(f"{__name__}.{id(self)}") log.addHandler(logging.StreamHandler(log_stream)) log.setLevel("INFO") input_data = input_data.dict() geom_qcng = input_data["initial_molecule"] comput = {**input_data["input_specification"], "molecule": geom_qcng} program = input_data["keywords"].pop("program") trajectory = [] output_data = input_data.copy() try: # Pyberny uses angstroms for the Cartesian geometry, but atomic # units for everything else, including the gradients (hartree/bohr). geom_berny = berny.Geometry(geom_qcng["symbols"], geom_qcng["geometry"] / berny.angstrom) opt = berny.Berny(geom_berny, logger=log, **input_data["keywords"]) for geom_berny in opt: geom_qcng["geometry"] = np.stack(geom_berny.coords * berny.angstrom) ret = qcengine.compute(comput, program) trajectory.append(ret.dict()) opt.send((ret.properties.return_energy, ret.return_result)) except Exception: output_data["success"] = False output_data["error"] = {"error_type": "unknown", "error_message": f"Berny error:\n{traceback.format_exc()}"} else: output_data["success"] = True output_data.update( { "schema_name": "qcschema_optimization_output", "final_molecule": trajectory[-1]["molecule"], "energies": [r["properties"]["return_energy"] for r in trajectory], "trajectory": trajectory, "provenance": {"creator": "Berny", "routine": "berny.Berny", "version": berny_version}, "stdout": log_stream.getvalue(), # collect logged messages } ) if output_data["success"]: output_data = OptimizationResult(**output_data) return output_data
def _compress_optimizationresult( result: OptimizationResult, compression: CompressionEnum = CompressionEnum.lzma, compression_level: Optional[int] = None, ): """ Compresses outputs inside an OptimizationResult, storing them in extras Outputs for the AtomicResults stored in the trajectory will be stored in the extras for that AtomicResult """ # Handle the trajectory trajectory = [ _compress_common(x, compression, compression_level) for x in result.trajectory ] result = result.copy(update={"trajectory": trajectory}) # Now handle the outputs of the optimization itself return _compress_common(result, compression, compression_level)
def compute(self, input_model: "OptimizationInput", config: "TaskConfig") -> "Optimization": if self.found(raise_error=True): import optking input_data = input_model.dict() # Set retries to two if zero while respecting local_config local_config = config.dict() local_config["retries"] = local_config.get("retries", 2) or 2 input_data["input_specification"]["extras"][ "_qcengine_local_config"] = local_config # Run the program output_data = optking.optwrapper.optimize_qcengine(input_data) output_data["schema_name"] = "qcschema_optimization_output" output_data["input_specification"]["extras"].pop( "_qcengine_local_config", None) if output_data["success"]: output_data = OptimizationResult(**output_data) return output_data
def test_add_data(): md = MoleculeData.from_identifier("O") # Load the xtb geometry xtb_geom = OptimizationResult.parse_file( _my_path.joinpath('records/xtb-neutral.json')) md.add_geometry(xtb_geom) assert "xtb" in md.data assert "neutral" in md.data["xtb"] assert isclose(md.data["xtb"][ OxidationState.NEUTRAL].atomization_energy["xtb-no_zpe"], -0.515, abs_tol=1e-2) assert ("xtb", "neutral") == md.match_geometry(xtb_geom.final_molecule) # Load in a relaxed oxidized geometry xtb_geom_ox = OptimizationResult.parse_file( _my_path.joinpath('records/xtb-oxidized.json')) md.add_geometry(xtb_geom_ox) assert "xtb" in md.data assert "oxidized" in md.data["xtb"] assert ("xtb", "neutral") == md.match_geometry(xtb_geom_ox.initial_molecule) assert ("xtb", "oxidized") == md.match_geometry(xtb_geom_ox.final_molecule) assert md.data['xtb'][OxidationState.OXIDIZED].total_energy[OxidationState.OXIDIZED]['xtb'] != \ md.data['xtb'][OxidationState.NEUTRAL].total_energy[OxidationState.OXIDIZED]['xtb'] # Load in a oxidized energy for the neutral structure xtb_energy = AtomicResult.parse_file( _my_path.joinpath('records/xtb-neutral_xtb-oxidized-energy.json')) md.add_single_point(xtb_energy) # Add in solvation energies xtb_energy = AtomicResult.parse_file( _my_path.joinpath('records/xtb-neutral_acn.json')) md.add_single_point(xtb_energy) assert "acetonitrile" in md.data['xtb']['neutral'].solvation_energy[ 'neutral'] xtb_energy = AtomicResult.parse_file( _my_path.joinpath('records/xtb-oxidized_acn.json')) md.add_single_point(xtb_energy) assert "acetonitrile" in md.data['xtb']['oxidized'].solvation_energy[ 'oxidized'] # Show that we can compute a redox potential recipe = RedoxEnergyRecipe(name="xtb-vertical", geometry_level="xtb", energy_level="xtb", adiabatic=False) result = recipe.compute_redox_potential(md, OxidationState.OXIDIZED) assert md.oxidation_potential['xtb-vertical'] == result recipe = RedoxEnergyRecipe(name="xtb", geometry_level="xtb", energy_level="xtb", adiabatic=True) result = recipe.compute_redox_potential(md, OxidationState.OXIDIZED) assert md.oxidation_potential['xtb'] == result assert md.oxidation_potential['xtb'] < md.oxidation_potential[ 'xtb-vertical'] recipe = RedoxEnergyRecipe(name="xtb-acn", geometry_level="xtb", energy_level="xtb", adiabatic=True, solvent='acetonitrile', solvation_level='xtb') result = recipe.compute_redox_potential(md, OxidationState.OXIDIZED) assert md.oxidation_potential['xtb-acn'] == result assert md.oxidation_potential['xtb-acn'] != md.oxidation_potential['xtb'] # Add a single point small_basis computation smb_hessian = AtomicResult.parse_file( _my_path.joinpath('records/xtb-neutral_smb-neutral-hessian.json')) md.add_single_point(smb_hessian) assert isclose(md.data["xtb"][OxidationState.NEUTRAL].zpe[ OxidationState.NEUTRAL]['small_basis'], 0.02155, abs_tol=1e-3) # Add an NWChem with solvent smb_solvent = AtomicResult.parse_file( _my_path.joinpath('records/xtb-neutral_smb-neutral_water.json')) md.add_single_point(smb_solvent) assert 'small_basis' in md.data['xtb']['neutral'].total_energy_in_solvent[ 'neutral']['water']