From 9e9af360532c80931b50653917047848c3e9656a Mon Sep 17 00:00:00 2001 From: Hui Zheng Date: Fri, 10 Feb 2023 15:37:14 -0800 Subject: [PATCH 1/3] run black --- src/mpmorph/analysis/diffusion.py | 2 +- src/mpmorph/analysis/melting_points.py | 47 +++++++++++--------- src/mpmorph/analysis/structural_analysis.py | 3 -- src/mpmorph/database.py | 3 +- src/mpmorph/firetasks/dbtasks.py | 1 - src/mpmorph/fireworks/powerups.py | 6 +-- src/mpmorph/flows/md_flow.py | 6 +++ src/mpmorph/flows/vt_flow.py | 33 +++++++------- src/mpmorph/io.py | 8 ++-- src/mpmorph/jobs/equilibrate_volume.py | 1 - src/mpmorph/jobs/lammps_volume.py | 42 +++++++++-------- src/mpmorph/jobs/pv_from_calc.py | 6 +-- src/mpmorph/jobs/tasks/m3gnet_input.py | 1 - src/mpmorph/jobs/volume_temperature_sweep.py | 4 +- src/mpmorph/schemas/lammps_calc.py | 3 +- src/mpmorph/schemas/m3gnet_md_calc.py | 1 - src/mpmorph/schemas/pv_data_doc.py | 1 - src/mpmorph/schemas/vt_sweep_doc.py | 9 ++-- src/mpmorph/workflows/quench.py | 2 +- 19 files changed, 92 insertions(+), 87 deletions(-) diff --git a/src/mpmorph/analysis/diffusion.py b/src/mpmorph/analysis/diffusion.py index 879572ad..bb06bd9a 100644 --- a/src/mpmorph/analysis/diffusion.py +++ b/src/mpmorph/analysis/diffusion.py @@ -290,7 +290,7 @@ def plot(self, title=None, annotate=True, el="", **kwargs): self.y, yerr=self.yerr.T, label="Q[{}]: ".format(el) + tx + " K", - **kwargs + **kwargs, ) plt.ylabel("ln(D cm$^2$/s)", fontsize=15) plt.xlabel("1000/T K$^{-1}$", fontsize=15) diff --git a/src/mpmorph/analysis/melting_points.py b/src/mpmorph/analysis/melting_points.py index 17bd8da5..a3ff8724 100644 --- a/src/mpmorph/analysis/melting_points.py +++ b/src/mpmorph/analysis/melting_points.py @@ -7,15 +7,15 @@ import matplotlib.pyplot as plt import math -class MeltingPointClusterAnalyzer(): +class MeltingPointClusterAnalyzer: def _get_clusters(self, points): clustering = AgglomerativeClustering(n_clusters=2).fit(points) cluster1 = points[np.argwhere(clustering.labels_ == 1).squeeze()].T cluster2 = points[np.argwhere(clustering.labels_ == 0).squeeze()].T return cluster1, cluster2 - def plot_vol_vs_temp(self, ts, vs, plot_title = None): + def plot_vol_vs_temp(self, ts, vs, plot_title=None): points = np.array(list(zip(ts, vs))) cluster1, cluster2 = self._get_clusters(points) plt.scatter(*cluster1) @@ -23,13 +23,13 @@ def plot_vol_vs_temp(self, ts, vs, plot_title = None): plt.xlabel("Temperature (K)") plt.ylabel("Volume (A^3)") Tm = self.estimate_melting_temp(ts, vs) - plt.plot([Tm, Tm], [min(vs), max(vs)], color='r') - + plt.plot([Tm, Tm], [min(vs), max(vs)], color="r") + if plot_title is None: plt.title("Volume vs Temperature by Clustering") else: plt.title(plot_title) - + def estimate_melting_temp(self, temps, vols): points = np.array(list(zip(temps, vols))) cluster1, cluster2 = self._get_clusters(points) @@ -42,8 +42,8 @@ def estimate_melting_temp(self, temps, vols): return np.mean([max(solid_range), min(liquid_range)]) -class MeltingPointSlopeAnalyzer(): +class MeltingPointSlopeAnalyzer: def split_dset(self, pts, split_idx): return pts[0:split_idx], pts[split_idx:] @@ -56,7 +56,7 @@ def assess_splits(self, xs, ys): for idx in pt_idxs: _, _, _, _, total_err = self.get_split_fit(xs, ys, idx) errs.append(total_err) - + return list(zip(pt_idxs, errs)) def get_linear_ys(self, m, b, xs): @@ -79,32 +79,31 @@ def plot_split(self, xs, ys, split_idx): plt.scatter(rightxs, rightys) plt.plot(rightxs, right_fit_ys) - + def get_best_split(self, xs, ys): split_errs = self.assess_splits(xs, ys) errs = [pt[1] for pt in split_errs] idxs = [pt[0] for pt in split_errs] best_split_idx = idxs[np.argmin(errs)] return best_split_idx - + def plot_vol_vs_temp(self, temps, vols): split_idx = self.get_best_split(temps, vols) self.plot_split(temps, vols, split_idx) Tm = self.estimate_melting_temp(temps, vols) print(Tm) - plt.plot([Tm, Tm], [min(vols), max(vols)], color='r') - + plt.plot([Tm, Tm], [min(vols), max(vols)], color="r") def estimate_melting_temp(self, temps, vols): best_split_idx = self.get_best_split(temps, vols) return np.mean([temps[best_split_idx], temps[best_split_idx - 1]]) -class MeltingPointSlopeRMSEAnalyzer(MeltingPointSlopeAnalyzer): +class MeltingPointSlopeRMSEAnalyzer(MeltingPointSlopeAnalyzer): def get_split_fit(self, xs, ys, split_idx): leftx, rightx = self.split_dset(xs, split_idx) lefty, righty = self.split_dset(ys, split_idx) - + lslope, lintercept, r_value, p_value, std_err = linregress(leftx, lefty) left_y_pred = lintercept + lslope * np.array(leftx) lefterr = mean_squared_error(y_true=lefty, y_pred=left_y_pred, squared=False) @@ -112,23 +111,29 @@ def get_split_fit(self, xs, ys, split_idx): rslope, rintercept, r_value, p_value, std_err = linregress(rightx, righty) right_y_pred = rintercept + rslope * np.array(rightx) righterr = mean_squared_error(y_true=righty, y_pred=right_y_pred, squared=False) - - combined_err = math.sqrt(lefterr ** 2 + righterr ** 2) + + combined_err = math.sqrt(lefterr**2 + righterr**2) combined_err = lefterr + righterr return lslope, lintercept, rslope, rintercept, combined_err -class MeltingPointSlopeStdErrAnalyzer(MeltingPointSlopeAnalyzer): +class MeltingPointSlopeStdErrAnalyzer(MeltingPointSlopeAnalyzer): def get_split_fit(self, xs, ys, split_idx): leftx, rightx = self.split_dset(xs, split_idx) lefty, righty = self.split_dset(ys, split_idx) - + leftfit = linregress(leftx, lefty) lefterr = leftfit.stderr - + rightfit = linregress(rightx, righty) righterr = rightfit.stderr - - combined_err = math.sqrt(lefterr ** 2 + righterr ** 2) + + combined_err = math.sqrt(lefterr**2 + righterr**2) combined_err = lefterr + righterr - return leftfit.slope, leftfit.intercept, rightfit.slope, rightfit.intercept, combined_err + return ( + leftfit.slope, + leftfit.intercept, + rightfit.slope, + rightfit.intercept, + combined_err, + ) diff --git a/src/mpmorph/analysis/structural_analysis.py b/src/mpmorph/analysis/structural_analysis.py index 2becc70f..ef2884cf 100644 --- a/src/mpmorph/analysis/structural_analysis.py +++ b/src/mpmorph/analysis/structural_analysis.py @@ -47,7 +47,6 @@ def polyhedra_connectivity(structures, pair, cutoff, step_freq=1): polyhedra_list.append(set(current_poly)) for polypair in itertools.combinations(polyhedra_list, 2): - polyhedra_pair_type = (len(polypair[0]), len(polypair[1])) shared_vertices = len(polypair[0].intersection(polypair[1])) @@ -143,7 +142,6 @@ class BondAngleDistribution(object): """ def __init__(self, structures, cutoffs, step_freq=1): - self.bond_angle_distribution = None self.structures = structures self.step_freq = step_freq @@ -251,7 +249,6 @@ def get_bond_angle_distribution(self): # get all pair combinations of neoghbor sites of i: for p in itertools.combinations(neighbors[i], 2): - # check if pairs are within the defined cutoffs if self._cutoff_type == "dict": if self._check_skip_triplet(s_index, i, p[0][2], p[1][2]): diff --git a/src/mpmorph/database.py b/src/mpmorph/database.py index 20684cba..272fff15 100644 --- a/src/mpmorph/database.py +++ b/src/mpmorph/database.py @@ -30,7 +30,7 @@ def __init__( collection="tasks", user=None, password=None, - **kwargs + **kwargs, ): super(VaspMDCalcDb, self).__init__( host, port, database, collection, user, password, **kwargs @@ -75,7 +75,6 @@ def insert_task( # insert structures at each ionic step into GridFS if parse_ionic_steps and "calcs_reversed" in task_doc: - # Convert from ionic steps dictionary to pymatgen.core.trajectory.Trajectory object ionic_steps_dict = task_doc["calcs_reversed"][0]["output"]["ionic_steps"] time_step = task_doc["input"]["incar"]["POTIM"] diff --git a/src/mpmorph/firetasks/dbtasks.py b/src/mpmorph/firetasks/dbtasks.py index 1e07f82d..0f660f99 100644 --- a/src/mpmorph/firetasks/dbtasks.py +++ b/src/mpmorph/firetasks/dbtasks.py @@ -222,7 +222,6 @@ def load_trajectories_from_gfs(runs, mmdb, gfs_keys=None): trajectory = None for i, (fs_id, fs) in enumerate(gfs_keys): - if fs == "trajectories_fs" or fs == "rebuild_trajectories_fs": # Load stored Trajectory print(fs_id, "is stored in trajectories_fs") diff --git a/src/mpmorph/fireworks/powerups.py b/src/mpmorph/fireworks/powerups.py index 22f16257..4d244c33 100644 --- a/src/mpmorph/fireworks/powerups.py +++ b/src/mpmorph/fireworks/powerups.py @@ -46,7 +46,7 @@ def aggregate_trajectory(fw, **kwargs): def add_cont_structure(fw): prev_struct_task = PreviousStructureTask() insert_i = 2 - for (i, task) in enumerate(fw.tasks): + for i, task in enumerate(fw.tasks): if task.fw_name == "{{atomate.vasp.firetasks.run_calc.RunVaspCustodian}}": insert_i = i break @@ -68,7 +68,7 @@ def add_pass_pv(fw, **kwargs): def add_pv_volume_rescale(fw): insert_i = 2 - for (i, task) in enumerate(fw.tasks): + for i, task in enumerate(fw.tasks): if task.fw_name == "{{atomate.vasp.firetasks.run_calc.RunVaspCustodian}}": insert_i = i break @@ -80,7 +80,7 @@ def add_pv_volume_rescale(fw): def add_rescale_volume(fw, **kwargs): rsv_task = RescaleVolumeTask(**kwargs) insert_i = 2 - for (i, task) in enumerate(fw.tasks): + for i, task in enumerate(fw.tasks): if task.fw_name == "{{atomate.vasp.firetasks.run_calc.RunVaspCustodian}}": insert_i = i break diff --git a/src/mpmorph/flows/md_flow.py b/src/mpmorph/flows/md_flow.py index a193cdde..b63aa6c6 100644 --- a/src/mpmorph/flows/md_flow.py +++ b/src/mpmorph/flows/md_flow.py @@ -14,6 +14,10 @@ M3GNET_MD_CONVERGED_VOL_FLOW = "M3GNET_MD_CONVERGED_VOL_FLOW" LAMMPS_VOL_FLOW = "LAMMPS_VOL_FLOW" + +def get_md_temperature_sweeping(structure, temp, steps, converge_first = True, initial_vol_scale = 1, **input_kwargs): + + def get_md_flow_m3gnet(structure, temp, steps, converge_first = True, initial_vol_scale = 1, **input_kwargs): inputs = M3GNetMDInputs( temperature=temp, @@ -91,3 +95,5 @@ def _get_converge_flow(structure: Structure, pv_md_maker: PVFromCalc, production flow = Flow([equil_vol_job, final_md_job], output=final_md_job.output, name=M3GNET_MD_CONVERGED_VOL_FLOW) return flow + + diff --git a/src/mpmorph/flows/vt_flow.py b/src/mpmorph/flows/vt_flow.py index 2771f747..7c1ee123 100644 --- a/src/mpmorph/flows/vt_flow.py +++ b/src/mpmorph/flows/vt_flow.py @@ -8,6 +8,7 @@ VOLUME_TEMPERATURE_SWEEP = "VOLUME_TEMPERATURE_SWEEP" + def get_vt_sweep_flow( structure, lower_bound=100, @@ -16,25 +17,25 @@ def get_vt_sweep_flow( output_name="vt.out", steps=2000, ): - vs = [] volume_jobs = [] temps = list(range(lower_bound, upper_bound, temp_step)) for temp in temps: - job = get_equil_vol_flow( - structure=structure, - temp=temp, - steps=steps - ) + job = get_equil_vol_flow(structure=structure, temp=temp, steps=steps) volume_jobs.append(job) vs.append(job.output.volume) collect_job = _collect_vt_results(vs, temps, structure, output_name) - new_flow = Flow([*volume_jobs, collect_job], output=collect_job.output, name=VOLUME_TEMPERATURE_SWEEP) + new_flow = Flow( + [*volume_jobs, collect_job], + output=collect_job.output, + name=VOLUME_TEMPERATURE_SWEEP, + ) return new_flow + def get_vt_sweep_flow_lammps( structure, lower_bound=100, @@ -42,9 +43,8 @@ def get_vt_sweep_flow_lammps( temp_step=100, output_name="vt.out", steps=2000, - mp_id=None + mp_id=None, ): - v_outputs = [] volume_jobs = [] temps = list(range(lower_bound, upper_bound, temp_step)) @@ -60,9 +60,10 @@ def get_vt_sweep_flow_lammps( collect_job = _collect_vt_results(v_outputs, temps, structure, output_name, mp_id) - - flow_name = f'{structure.composition.reduced_formula}-Melting Point' - new_flow = Flow([*volume_jobs, collect_job], output=collect_job.output, name=flow_name) + flow_name = f"{structure.composition.reduced_formula}-Melting Point" + new_flow = Flow( + [*volume_jobs, collect_job], output=collect_job.output, name=flow_name + ) return new_flow @@ -75,18 +76,18 @@ def _collect_vt_results(v_outputs, ts, structure, output_fn, mp_id): "mp_id": mp_id, "reduced_formula": structure.composition.reduced_formula, "formula": structure.composition.formula, - "uuid": str(uuid.uuid4()) + "uuid": str(uuid.uuid4()), } with open(output_fn, "+w") as f: f.write(json.dumps(result)) return result -def get_converged_vol(v_output): + +def get_converged_vol(v_output): df = pd.DataFrame.from_dict(v_output) total_steps = (len(df) - 1) * 10 avging_window = int(total_steps / 30) - vols = df.iloc[-avging_window::]['vol'] + vols = df.iloc[-avging_window::]["vol"] eq_vol = vols.values.mean() return float(eq_vol) - diff --git a/src/mpmorph/io.py b/src/mpmorph/io.py index d095ecbe..8a43529c 100644 --- a/src/mpmorph/io.py +++ b/src/mpmorph/io.py @@ -17,13 +17,13 @@ def get_string_from_struct( ): format_str = "{{:.{0}f}}".format(significant_figures) - for (si, structure) in enumerate(structures): + for si, structure in enumerate(structures): lines = [system, "1.0", str(structure.lattice)] lines.append(" ".join(self.get_site_symbols(structure))) lines.append(" ".join([str(x) for x in self.get_natoms(structure)])) lines.append("Direct configuration= " + str(si + 1)) - for (i, site) in enumerate(structure): + for i, site in enumerate(structure): coords = site.frac_coords line = " ".join([format_str.format(c) for c in coords]) line += " " + site.species_string @@ -72,9 +72,9 @@ def get_string(self, system="unknown system", significant_figures=6): # positions = np.add(self.trajectory[0].frac_coords, self.trajectory.displacements) atoms = [site.specie.symbol for site in self.trajectory[0]] - for (si, position_array) in enumerate(positions): + for si, position_array in enumerate(positions): lines.append("Direct configuration= " + str(si + 1)) - for (i, coords) in enumerate(position_array): + for i, coords in enumerate(position_array): line = " ".join([format_str.format(c) for c in coords]) line += " " + atoms[i] lines.append(line) diff --git a/src/mpmorph/jobs/equilibrate_volume.py b/src/mpmorph/jobs/equilibrate_volume.py index 646fe719..74533c35 100644 --- a/src/mpmorph/jobs/equilibrate_volume.py +++ b/src/mpmorph/jobs/equilibrate_volume.py @@ -29,7 +29,6 @@ class EquilibriumVolumeSearchMaker(Maker): def make( self, original_structure: Structure, md_pv_data_docs: List[MDPVDataDoc] = None ): - if md_pv_data_docs is not None and len(md_pv_data_docs) > MAX_MD_JOBS: raise RuntimeError( "Maximum number of jobs for equilibrium volume search exceeded" diff --git a/src/mpmorph/jobs/lammps_volume.py b/src/mpmorph/jobs/lammps_volume.py index 2598ef09..6efab85c 100644 --- a/src/mpmorph/jobs/lammps_volume.py +++ b/src/mpmorph/jobs/lammps_volume.py @@ -16,6 +16,7 @@ from pkg_resources import resource_filename + class LammpsCalcMaker(Maker): """ Run LAMMPS directly using m3gnet (no custodian). @@ -27,10 +28,7 @@ class LammpsCalcMaker(Maker): name = "LAMMPS_CALCULATION" @job(trajectory="trajectory", output_schema=LammpsCalc) - def make(self, temperature: int, - total_steps: int, - structure: Structure = None): - + def make(self, temperature: int, total_steps: int, structure: Structure = None): lammps_bin = os.environ.get("LAMMPS_CMD") m3gnet_path = os.environ.get("M3GNET_PATH") @@ -40,19 +38,22 @@ def make(self, temperature: int, "m3gnet_path": m3gnet_path, "species": chem_sys_str, "total_steps": total_steps, - "print_every_n_step": 10 + "print_every_n_step": 10, } - - template_path = resource_filename('mpmorph', 'jobs/lammps-templates/template.lammps') + template_path = resource_filename( + "mpmorph", "jobs/lammps-templates/template.lammps" + ) data_filename: str = "data.lammps" - data = LammpsData.from_structure(structure, atom_style='atomic') + data = LammpsData.from_structure(structure, atom_style="atomic") # Write the input files - linp = LammpsTemplateGen().get_input_set(script_template=template_path, - settings=script_options, - data=data, - data_filename=data_filename) + linp = LammpsTemplateGen().get_input_set( + script_template=template_path, + settings=script_options, + data=data, + data_filename=data_filename, + ) linp.write_input(directory=".") input_name = "in.lammps" @@ -76,12 +77,15 @@ def make(self, temperature: int, trajectory = Trajectory.from_structures(structs, constant_lattice=False) - df = pd.read_csv("step_temp_vol_density.txt", delimiter=" ", index_col="step", skiprows=1, names=["step", "temp", "vol", "density"]) + df = pd.read_csv( + "step_temp_vol_density.txt", + delimiter=" ", + index_col="step", + skiprows=1, + names=["step", "temp", "vol", "density"], + ) - metadata = { - "temperature": temperature, - "total_steps": total_steps - } + metadata = {"temperature": temperature, "total_steps": total_steps} output = LammpsCalc( dir_name=os.getcwd(), @@ -89,6 +93,6 @@ def make(self, temperature: int, composition=structure.composition, reduced_formula=structure.composition.reduced_formula, metadata=metadata, - dump_data=df.to_dict() + dump_data=df.to_dict(), ) - return output \ No newline at end of file + return output diff --git a/src/mpmorph/jobs/pv_from_calc.py b/src/mpmorph/jobs/pv_from_calc.py index 57f7bb92..e82ec028 100644 --- a/src/mpmorph/jobs/pv_from_calc.py +++ b/src/mpmorph/jobs/pv_from_calc.py @@ -30,7 +30,6 @@ def make(self, structure, scale_factor=None): @dataclass class PVFromM3GNet(PVFromCalc): - name: str = "PV_FROM_M3GNET" parameters: M3GNetMDInputs = None @@ -44,10 +43,10 @@ def build_doc(self, m3gnet_calc: M3GNetMDCalculation): p_data = m3gnet_calc_to_pressure(m3gnet_calc) return MDPVDataDoc(volume=v_data, pressure=p_data) + @dataclass class PVFromM3GNetLammps(PVFromCalc): - """Generates a MDPVDataDoc using Lammps run with M3gnet and a npt ensemble. - """ + """Generates a MDPVDataDoc using Lammps run with M3gnet and a npt ensemble.""" name: str = "PV_FROM_M3GNET_LAMMPS" parameters: M3GNetMDInputs = None @@ -76,7 +75,6 @@ def m3gnet_calc_to_pressure(m3gnet_calc: M3GNetMDCalculation): @dataclass class PVFromVasp(PVFromCalc): - name: str = "PV_FROM_VASP" md_maker: Maker = MDMaker() diff --git a/src/mpmorph/jobs/tasks/m3gnet_input.py b/src/mpmorph/jobs/tasks/m3gnet_input.py index cf47e6c9..8392eceb 100644 --- a/src/mpmorph/jobs/tasks/m3gnet_input.py +++ b/src/mpmorph/jobs/tasks/m3gnet_input.py @@ -11,7 +11,6 @@ def one_atmosphere(): @dataclass class M3GNetMDInputs: - ensemble: str = "nvt" temperature: float = 2000.0 pressure: float = 1.01325 * units.bar diff --git a/src/mpmorph/jobs/volume_temperature_sweep.py b/src/mpmorph/jobs/volume_temperature_sweep.py index dd8aa53c..83c66693 100644 --- a/src/mpmorph/jobs/volume_temperature_sweep.py +++ b/src/mpmorph/jobs/volume_temperature_sweep.py @@ -2,8 +2,8 @@ from mpmorph.jobs.tasks.m3gnet_input import M3GNetMDInputs import dataclasses -class VolumeTemperatureSweepMaker(Maker): +class VolumeTemperatureSweepMaker(Maker): name: str = "VOLUME_TEMPERATURE_SWEEP" md_parameters: M3GNetMDInputs = None @@ -35,5 +35,3 @@ def make( new_flow = Flow([*volume_jobs, collect_job], output=collect_job.output) return Response(replace=new_flow) - - diff --git a/src/mpmorph/schemas/lammps_calc.py b/src/mpmorph/schemas/lammps_calc.py index 918086e3..c76c17d1 100644 --- a/src/mpmorph/schemas/lammps_calc.py +++ b/src/mpmorph/schemas/lammps_calc.py @@ -22,8 +22,7 @@ class LammpsCalc(BaseModel): description="The reduced formula of the structure's composition." ) dump_data: dict = Field( - None, - description="Any additional data collected via LAMMPS dump files" + None, description="Any additional data collected via LAMMPS dump files" ) metadata: dict = Field( None, diff --git a/src/mpmorph/schemas/m3gnet_md_calc.py b/src/mpmorph/schemas/m3gnet_md_calc.py index 941639a0..586e23c6 100644 --- a/src/mpmorph/schemas/m3gnet_md_calc.py +++ b/src/mpmorph/schemas/m3gnet_md_calc.py @@ -47,7 +47,6 @@ def from_directory( ), **kwargs, ): - """ Create a M3GnetCalculation document from a directory containing output files of a M3GNet MD run. diff --git a/src/mpmorph/schemas/pv_data_doc.py b/src/mpmorph/schemas/pv_data_doc.py index bb40b89b..b836d8fd 100644 --- a/src/mpmorph/schemas/pv_data_doc.py +++ b/src/mpmorph/schemas/pv_data_doc.py @@ -2,7 +2,6 @@ class MDPVDataDoc(BaseModel): - task_label: str = Field(None, description="The name of the task.") volume: float = Field(None, description="The volume data from the MD run") pressure: float = Field(None, description="The pressure of the MD run") diff --git a/src/mpmorph/schemas/vt_sweep_doc.py b/src/mpmorph/schemas/vt_sweep_doc.py index 4681ba43..c3b7d61f 100644 --- a/src/mpmorph/schemas/vt_sweep_doc.py +++ b/src/mpmorph/schemas/vt_sweep_doc.py @@ -3,8 +3,11 @@ class VTSweepDoc(BaseModel): - task_label: str = Field(None, description="The name of the task.") volumes: float = Field(description="The volume at each temperature") - temps: float = Field(description="The temperatures at which the volume was equilibrated") - structure: Structure = Field(description="The original structure for which this sweep was performed") + temps: float = Field( + description="The temperatures at which the volume was equilibrated" + ) + structure: Structure = Field( + description="The original structure for which this sweep was performed" + ) diff --git a/src/mpmorph/workflows/quench.py b/src/mpmorph/workflows/quench.py index 21832e2c..6e8066af 100644 --- a/src/mpmorph/workflows/quench.py +++ b/src/mpmorph/workflows/quench.py @@ -33,7 +33,7 @@ def get_quench_wf( hold_args = kwargs.get("hold_args", {"md_params": {"nsteps": 500}}) quench_args = kwargs.get("quench_args", {}) - for (i, structure) in enumerate(structures): + for i, structure in enumerate(structures): _fw_list = [] if quench_type == "slow_quench": for temp in np.arange( From f68009e9340dba734cc8e4dcb4d6f98144a761ae Mon Sep 17 00:00:00 2001 From: Hui Zheng Date: Mon, 13 Feb 2023 19:44:07 -0800 Subject: [PATCH 2/3] remove the unfinished function --- src/mpmorph/flows/md_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mpmorph/flows/md_flow.py b/src/mpmorph/flows/md_flow.py index b63aa6c6..376fbf89 100644 --- a/src/mpmorph/flows/md_flow.py +++ b/src/mpmorph/flows/md_flow.py @@ -15,7 +15,7 @@ LAMMPS_VOL_FLOW = "LAMMPS_VOL_FLOW" -def get_md_temperature_sweeping(structure, temp, steps, converge_first = True, initial_vol_scale = 1, **input_kwargs): +# def get_md_temperature_sweeping(structure, temp, steps, converge_first = True, initial_vol_scale = 1, **input_kwargs): def get_md_flow_m3gnet(structure, temp, steps, converge_first = True, initial_vol_scale = 1, **input_kwargs): From 268601a2ce8e9b3ab2e4128ec5b10e95e2b5b659 Mon Sep 17 00:00:00 2001 From: Max Gallant Date: Wed, 22 Feb 2023 10:35:27 -0800 Subject: [PATCH 3/3] Add basic temp sweep code --- src/mpmorph/jobs/lammps/helpers.py | 37 +++++++++++ .../lammps_basic_const_temp.py} | 45 ++----------- .../jobs/lammps/lammps_basic_temp_sweep.py | 65 +++++++++++++++++++ .../templates/basic_constant_temp.lammps} | 0 .../templates/basic_temp_sweep.lammps} | 25 ++++++- 5 files changed, 131 insertions(+), 41 deletions(-) create mode 100644 src/mpmorph/jobs/lammps/helpers.py rename src/mpmorph/jobs/{lammps_volume.py => lammps/lammps_basic_const_temp.py} (50%) create mode 100644 src/mpmorph/jobs/lammps/lammps_basic_temp_sweep.py rename src/mpmorph/jobs/{lammps-templates/template.lammps => lammps/templates/basic_constant_temp.lammps} (100%) rename src/mpmorph/jobs/{lammps-templates/in.melt_template => lammps/templates/basic_temp_sweep.lammps} (77%) diff --git a/src/mpmorph/jobs/lammps/helpers.py b/src/mpmorph/jobs/lammps/helpers.py new file mode 100644 index 00000000..b5fa38ab --- /dev/null +++ b/src/mpmorph/jobs/lammps/helpers.py @@ -0,0 +1,37 @@ +from ase.io.lammpsrun import read_lammps_dump_text +from pymatgen.io.ase import AseAtomsAdaptor +from pymatgen.core.trajectory import Trajectory +from pymatgen.io.lammps.inputs import LammpsTemplateGen +from pymatgen.io.lammps.data import LammpsData +from subprocess import PIPE, Popen + +def trajectory_from_lammps_dump(dump_path): + with open(dump_path, "r+") as f: + atoms = read_lammps_dump_text(f, index=slice(0, None)) + + structs = [] + + for a in atoms: + structs.append(AseAtomsAdaptor().get_structure(a)) + + return Trajectory.from_structures(structs, constant_lattice=False) + +def run_lammps(structure, template_path, template_opts, lammps_bin): + data_filename: str = "data.lammps" + data = LammpsData.from_structure(structure, atom_style='atomic') + # Write the input files + linp = LammpsTemplateGen().get_input_set(script_template=template_path, + settings=template_opts, + data=data, + data_filename=data_filename) + + linp.write_input(directory=".") + input_name = "in.lammps" + # Run LAMMPS + + lammps_cmd = [lammps_bin, "-in", input_name] + print(f"Running: {' '.join(lammps_cmd)}") + with Popen(lammps_cmd, stdout=PIPE, stderr=PIPE) as p: + (stdout, stderr) = p.communicate() + + print(f"LAMMPS finished running: {stdout} \n {stderr}") \ No newline at end of file diff --git a/src/mpmorph/jobs/lammps_volume.py b/src/mpmorph/jobs/lammps/lammps_basic_const_temp.py similarity index 50% rename from src/mpmorph/jobs/lammps_volume.py rename to src/mpmorph/jobs/lammps/lammps_basic_const_temp.py index 2598ef09..f5bfdecc 100644 --- a/src/mpmorph/jobs/lammps_volume.py +++ b/src/mpmorph/jobs/lammps/lammps_basic_const_temp.py @@ -1,24 +1,17 @@ -from subprocess import PIPE, Popen - import os from jobflow import Maker, job import pandas as pd - -from pymatgen.io.lammps.inputs import LammpsTemplateGen -from pymatgen.io.lammps.data import LammpsData from pymatgen.core.structure import Structure -from ase.io.lammpsrun import read_lammps_dump_text -from pymatgen.io.ase import AseAtomsAdaptor -from pymatgen.core.trajectory import Trajectory +from .helpers import run_lammps, trajectory_from_lammps_dump from mpmorph.schemas.lammps_calc import LammpsCalc from pkg_resources import resource_filename -class LammpsCalcMaker(Maker): +class BasicLammpsConstantTempMaker(Maker): """ - Run LAMMPS directly using m3gnet (no custodian). + Run LAMMPS directly using m3gnet at a constant temperature. Required params: lammsps_cmd (str): lammps command to run sans the input file name. e.g. 'mpirun -n 4 lmp_mpi' @@ -44,37 +37,11 @@ def make(self, temperature: int, } - template_path = resource_filename('mpmorph', 'jobs/lammps-templates/template.lammps') - - data_filename: str = "data.lammps" - data = LammpsData.from_structure(structure, atom_style='atomic') - # Write the input files - linp = LammpsTemplateGen().get_input_set(script_template=template_path, - settings=script_options, - data=data, - data_filename=data_filename) - - linp.write_input(directory=".") - input_name = "in.lammps" - # Run LAMMPS - - lammps_cmd = [lammps_bin, "-in", input_name] - print(f"Running: {' '.join(lammps_cmd)}") - with Popen(lammps_cmd, stdout=PIPE, stderr=PIPE) as p: - (stdout, stderr) = p.communicate() - - print(f"LAMMPS finished running: {stdout} \n {stderr}") - - # Build trajectory from LAMMPS output .xyz file - with open("trajectory.lammpstrj", "r+") as f: - atoms = read_lammps_dump_text(f, index=slice(0, None)) - - structs = [] + template_path = resource_filename('mpmorph', 'jobs/lammps-templates/basic_constant_temp.lammps') - for a in atoms: - structs.append(AseAtomsAdaptor().get_structure(a)) + run_lammps(structure, template_path, script_options, lammps_bin) - trajectory = Trajectory.from_structures(structs, constant_lattice=False) + trajectory = trajectory_from_lammps_dump("trajectory.lammpstrj") df = pd.read_csv("step_temp_vol_density.txt", delimiter=" ", index_col="step", skiprows=1, names=["step", "temp", "vol", "density"]) diff --git a/src/mpmorph/jobs/lammps/lammps_basic_temp_sweep.py b/src/mpmorph/jobs/lammps/lammps_basic_temp_sweep.py new file mode 100644 index 00000000..8135d48f --- /dev/null +++ b/src/mpmorph/jobs/lammps/lammps_basic_temp_sweep.py @@ -0,0 +1,65 @@ +import os +from jobflow import Maker, job + +import pandas as pd +from pymatgen.core.structure import Structure + +from mpmorph.schemas.lammps_calc import LammpsCalc +from .helpers import run_lammps, trajectory_from_lammps_dump + +from pkg_resources import resource_filename + +class BasicLammpsTempSweepMaker(Maker): + """ + Run LAMMPS directly using m3gnet sweeping over a range of temperatures. + Required params: + lammsps_cmd (str): lammps command to run sans the input file name. + e.g. 'mpirun -n 4 lmp_mpi' + """ + + name = "LAMMPS_CALCULATION" + + @job(trajectory="trajectory", output_schema=LammpsCalc) + def make(self, temp_initial: int, + temp_final: int, + total_steps: int, + structure: Structure = None): + + lammps_bin = os.environ.get("LAMMPS_CMD") + m3gnet_path = os.environ.get("M3GNET_PATH") + + chem_sys_str = " ".join(el.symbol for el in structure.composition.elements) + + script_options = { + "tempstart": temp_initial, + "tempstop": temp_final, + "m3gnet_path": m3gnet_path, + "species": chem_sys_str, + "total_steps": total_steps, + "print_every_n_step": 10 + } + + + template_path = resource_filename('mpmorph', 'jobs/lammps-templates/basic_temp_sweep.lammps') + + run_lammps(structure, template_path, script_options, lammps_bin) + + trajectory = trajectory_from_lammps_dump("trajectory.lammpstrj") + + # df = pd.read_csv("step_temp_vol_density.txt", delimiter=" ", index_col="step", skiprows=1, names=["step", "temp", "vol", "density"]) + + metadata = { + "temp_initial": temp_initial, + "temp_final": temp_final, + "total_steps": total_steps + } + + output = LammpsCalc( + dir_name=os.getcwd(), + trajectory=trajectory, + composition=structure.composition, + reduced_formula=structure.composition.reduced_formula, + metadata=metadata, + dump_data={} + ) + return output \ No newline at end of file diff --git a/src/mpmorph/jobs/lammps-templates/template.lammps b/src/mpmorph/jobs/lammps/templates/basic_constant_temp.lammps similarity index 100% rename from src/mpmorph/jobs/lammps-templates/template.lammps rename to src/mpmorph/jobs/lammps/templates/basic_constant_temp.lammps diff --git a/src/mpmorph/jobs/lammps-templates/in.melt_template b/src/mpmorph/jobs/lammps/templates/basic_temp_sweep.lammps similarity index 77% rename from src/mpmorph/jobs/lammps-templates/in.melt_template rename to src/mpmorph/jobs/lammps/templates/basic_temp_sweep.lammps index 0381b7c3..8629693c 100644 --- a/src/mpmorph/jobs/lammps-templates/in.melt_template +++ b/src/mpmorph/jobs/lammps/templates/basic_temp_sweep.lammps @@ -1,5 +1,23 @@ # Script originally made by Oscar Guerrero # Reference: https://orca.cardiff.ac.uk/id/eprint/101322/1/MRSPaper2.pdf + +##################################################################### +# READ THIS BEFORE MAKING CHANGES +# +# NOTICE: ANY VARIABLES BEGINNING WITH _ ARE INTERNAL TO THE LAMMPS SCRIPT +# VARIABLES FOR USE REPLACEMENT BY TEMPLATE HAVE NO _ IN NAME +# +# Parameters for this template: +# tempstart - The starting temp for the simulation +# tempstop - The ending temp for the simulation +# species - A space-separated list of elements present in the system, e.g. "Y Mn O" +# m3gnet_path - The path to the m3gnet potential installation +# print_every_n_step - The frequency with which info should be printed to stdout +# total_steps - The total number of simulation steps +########################################################################## + + + units metal atom_style atomic boundary p p p @@ -27,7 +45,7 @@ read_data data.dump #Define Interatomic Potential -pair_style m3gnet /global/home/users/huizheng/repos/lammps/potentials/M3GNET +pair_style m3gnet $m3gnet_path pair_coeff * * MP-2021.2.8-EFS $species # Equilibration @@ -82,8 +100,11 @@ run 0 fix data_melting all print $print_every_n_step "${eq8} ${eq9}" file temp_vs_ref_vol.txt screen no dump 1 all cfg 100 HgF2.step*.cfg mass type xs ys zs id +# What does this do? dump_modify 1 element $species -dump 2 all custom 100 dump.* id type x y z + +dump 2 all custom 100 trajectory.lammpstrj id element x y z + # Compute msd command and dump every 10 steps compute msd all msd com yes fix msd all ave/time 1 1 10 c_msd[4] file msd.txt