From 3fe8b85e17d9f3ef998a0725f5f90443b7b54627 Mon Sep 17 00:00:00 2001 From: Noam Bernstein Date: Wed, 18 Jun 2025 14:19:49 -0400 Subject: [PATCH 1/3] Fix scaling of cell shear by multiplying default by length scale, and when used by nat^(1/3) --- README_input_parameters.md | 8 ++++---- README_usage.md | 15 +++++++++++++-- pymatnext/ns_configs/ase_atoms/__init__.py | 8 +++++--- .../ns_configs/ase_atoms/ase_atoms_params.py | 8 ++++---- .../ns_configs/ase_atoms/walks_ase_calc.py | 11 +++++++---- pymatnext/ns_configs/ase_atoms/walks_lammps.py | 13 +++++++------ tests/test_sample.py | 18 +++++++----------- 7 files changed, 47 insertions(+), 34 deletions(-) diff --git a/README_input_parameters.md b/README_input_parameters.md index 2de0b85..e44063b 100644 --- a/README_input_parameters.md +++ b/README_input_parameters.md @@ -50,14 +50,14 @@ - cell_proportion = 0.0 - type_proportion = 0.0 - [max_step_size] - - pos_gmc_each_atom = -1.0 - - cell_volume_per_atom = -1.0 - - cell_shear = 0.2 + - pos_gmc_each_atom = -0.1 + - cell_volume_per_atom = -0.05 + - cell_shear_per_rt3_atom = -0.2 - cell_stretch = 0.2 - [step_size] - pos_gmc_each_atom = -1.0 - cell_volume_per_atom = -1.0 - - cell_shear = -1.0 + - cell_shear_per_rt3_atom = -1.0 - cell_stretch = -1.0 - [cell] - min_aspect_ratio = 0.8 diff --git a/README_usage.md b/README_usage.md index d35dc78..1146ab2 100644 --- a/README_usage.md +++ b/README_usage.md @@ -111,10 +111,21 @@ Hopefully section and key names are self explanatory. ### Additional notes on run parameters - - all intervals are in NS iterations, except `stdout_report_interval_s` which is in seconds + - All intervals are in NS iterations, except `stdout_report_interval_s` which is in seconds - `configs.calculator.type` can be `"ASE"` or `"LAMMPS"` - if `"LAMMPS"`, `args` consists of `cmds`, with LAMMPS commands, and `types` dict (one key for each species) - if `"ASE"`, `args` consists of `module` key with module that defines a `calc` symbol containing an `ase.calculators.Calculator` object - `configs.walk.*_traj_len` controls the number of steps in a walk block of that type - `configs.walk.*_proportion` controls the fraction of steps overall that are used for that type of move - - if `config.walk.type.sGC = true`, a dict of `mu` values, one per species, is required. + - If `config.walk.type.sGC = true`, a dict of `mu` values, one per species, is required. + - There are position, cell, and atom-type walks, with associated step size parameters that are auto-tuned. Default values depends on + an overall volume scale given by `initial_rand_vol_per_atom` and corresponding length scale given by its cube root. + - Maxima are in `[configs.walk.max_step_size]` section. Defaults for first three are negative. + - `pos_gmc_each_atom`: distance (typically A) that each atom should typically make in GMC step. If negative, used as multiplier for + length scale. + - `cell_volume_per_atom`: change in volume (typically A^3), will also be scaled by number of atoms. If negative, used as multiplier + for volume scale. + - `cell_shear_per_rt3_atom`: cell shear magnitude (typically A) which multiplies _normalized_ cell vectors, will also be scaled by + cube root of number of atoms. If negative used as multiplier for length scale. + - `cell_stretch`: cell stretch, fractional (i.e. strain) + - Initial values with same key names are in `[configs.walk.step_size]` section. Any negative values are replaced with half the corresponding maximum. diff --git a/pymatnext/ns_configs/ase_atoms/__init__.py b/pymatnext/ns_configs/ase_atoms/__init__.py index a8b6c71..1f526bb 100644 --- a/pymatnext/ns_configs/ase_atoms/__init__.py +++ b/pymatnext/ns_configs/ase_atoms/__init__.py @@ -62,7 +62,7 @@ class NSConfig_ASE_Atoms(): filename_suffix = ".extxyz" n_quantities = -1 - _step_size_params = ["pos_gmc_each_atom", "cell_volume_per_atom", "cell_shear", "cell_stretch"] + _step_size_params = ["pos_gmc_each_atom", "cell_volume_per_atom", "cell_shear_per_rt3_atom", "cell_stretch"] _max_E_hist = collections.deque(maxlen=1000) _walk_moves = ["gmc", "cell", "type"] _Zs = [] @@ -432,9 +432,11 @@ def _prep_walk(self, params, vol_per_atom=None): assert set(list(self.max_step_size.keys())) == set(self._step_size_params) # max step size for position GMC and cell volume defaults are scaled to volume per atom if self.max_step_size["pos_gmc_each_atom"] < 0.0: - self.max_step_size["pos_gmc_each_atom"] = (vol_per_atom ** (1.0/3.0)) / 10.0 + self.max_step_size["pos_gmc_each_atom"] = (vol_per_atom ** (1.0/3.0)) * np.abs(self.max_step_size["pos_gmc_each_atom"]) if self.max_step_size["cell_volume_per_atom"] < 0.0: - self.max_step_size["cell_volume_per_atom"] = vol_per_atom / 20.0 + self.max_step_size["cell_volume_per_atom"] = vol_per_atom * np.abs(self.max_step_size["cell_volume_per_atom"]) + if self.max_step_size["cell_shear_per_rt3_atom"] < 0.0: + self.max_step_size["cell_shear_per_rt3_atom"] = (vol_per_atom ** (1.0/3.0)) * np.abs(self.max_step_size["cell_shear_per_rt3_atom"]) # actual step sizes self.step_size = params["step_size"].copy() diff --git a/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py b/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py index b2183e8..26a82fe 100644 --- a/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py +++ b/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py @@ -27,16 +27,16 @@ "type_proportion": 0.0, "max_step_size": { - "pos_gmc_each_atom": -1.0, - "cell_volume_per_atom": -1.0, - "cell_shear": 0.2, + "pos_gmc_each_atom": -0.1, + "cell_volume_per_atom": -0.05, + "cell_shear_per_rt3_atom": -0.2, "cell_stretch": 0.2 }, "step_size": { "pos_gmc_each_atom": -1.0, "cell_volume_per_atom": -1.0, - "cell_shear": -1.0, + "cell_shear_per_rt3_atom": -1.0, "cell_stretch": -1.0 }, diff --git a/pymatnext/ns_configs/ase_atoms/walks_ase_calc.py b/pymatnext/ns_configs/ase_atoms/walks_ase_calc.py index 8fd54e8..e9eeef9 100644 --- a/pymatnext/ns_configs/ase_atoms/walks_ase_calc.py +++ b/pymatnext/ns_configs/ase_atoms/walks_ase_calc.py @@ -126,12 +126,15 @@ def walk_cell(ns_atoms, Emax, rng): Returns ------- - [("cell_volume_per_atom", int n_attempt, int n_success), ("cell_shear", ...), ("cell_stretch", ...)] info on move params and attempts/successes + [("cell_volume_per_atom", int n_attempt, int n_success), + ("cell_shear_per_rt3_atom", int n_attempt, n_success), + ("cell_stretch", int n_attempt, n_success)] with n_att and n_acc number of attempted and accepted + move for each submove type """ atoms = ns_atoms.atoms N_atoms = len(atoms) - step_size_volume = N_atoms * ns_atoms.step_size["cell_volume_per_atom"] - step_size_shear = ns_atoms.step_size["cell_shear"] + step_size_volume = ns_atoms.step_size["cell_volume_per_atom"] * N_atoms + step_size_shear = ns_atoms.step_size["cell_shear_per_rt3_atom"] * (N_atoms ** (1.0 / 3.0)) step_size_stretch = ns_atoms.step_size["cell_stretch"] min_aspect_ratio = ns_atoms.move_params["cell"]["min_aspect_ratio"] flat_V_prior = ns_atoms.move_params["cell"]["flat_V_prior"] @@ -197,7 +200,7 @@ def walk_cell(ns_atoms, Emax, rng): n_acc[move] += 1 return [("cell_volume_per_atom", n_att["volume"], n_acc["volume"]), - ("cell_shear", n_att["shear"], n_acc["shear"]), + ("cell_shear_per_rt3_atom", n_att["shear"], n_acc["shear"]), ("cell_stretch", n_att["stretch"], n_acc["stretch"])] diff --git a/pymatnext/ns_configs/ase_atoms/walks_lammps.py b/pymatnext/ns_configs/ase_atoms/walks_lammps.py index eacd24e..b02c535 100644 --- a/pymatnext/ns_configs/ase_atoms/walks_lammps.py +++ b/pymatnext/ns_configs/ase_atoms/walks_lammps.py @@ -187,9 +187,9 @@ def walk_cell(ns_atoms, Emax, rng): Returns ------- - [("cell_volume_per_atom", n_att, n_acc), - ("cell_shear", n_att, n_acc), - ("cell_stretch", n_att, n_acc)] with n_att and n_acc number of attempted and accepted + [("cell_volume_per_atom", int n_attempt, int n_success), + ("cell_shear_per_rt3_atom", int n_attempt, n_success), + ("cell_stretch", int n_attempt, n_success)] with n_att and n_acc number of attempted and accepted move for each submove type """ ns_atoms.calc.command("unfix NS") @@ -197,8 +197,9 @@ def walk_cell(ns_atoms, Emax, rng): # for LAMMPS RanMars RNG lammps_seed = rng.integers(1, 900000000) - step_size_volume = len(atoms) * ns_atoms.step_size["cell_volume_per_atom"] - step_size_shear = ns_atoms.step_size["cell_shear"] + N_atoms = len(atoms) + step_size_volume = ns_atoms.step_size["cell_volume_per_atom"] * N_atoms + step_size_shear = ns_atoms.step_size["cell_shear_per_rt3_atom"] * (N_atoms ** (1.0 / 3.0)) step_size_stretch = ns_atoms.step_size["cell_stretch"] types, pos, vel = set_lammps_from_atoms(ns_atoms) @@ -256,7 +257,7 @@ def walk_cell(ns_atoms, Emax, rng): n_acc[submove_type] = 0 return [("cell_volume_per_atom", n_att["volume"], n_acc["volume"]), - ("cell_shear", n_att["shear"], n_acc["shear"]), + ("cell_shear_per_rt3_atom", n_att["shear"], n_acc["shear"]), ("cell_stretch", n_att["stretch"], n_acc["stretch"])] def walk_type(ns_atoms, Emax, rng): diff --git a/tests/test_sample.py b/tests/test_sample.py index c603e9f..9d33431 100644 --- a/tests/test_sample.py +++ b/tests/test_sample.py @@ -158,11 +158,11 @@ def do_Morse_ASE(tmp_path, monkeypatch, using_mpi, max_iter=None): assert len(list(tmp_path.glob('Morse_ASE.test.traj.*xyz'))) == 1 assert len(list(ase.io.read(tmp_path / 'Morse_ASE.test.traj.extxyz', ':'))) == int(np.ceil(max_iter_use / traj_interval)) - # from test run 6/13/2025, when max iter was extended to 110 to catch deadlock in old buggy snapshot step_size writing + # from test run 6/18/2025, when cell_shear was replaced with cell_shear_per_rt3_atom if using_mpi: samples_fields_ref = np.asarray([1.09000000e+02, 8.02719236e+00, 1.28609799e+04, 1.60000000e+01]) else: - samples_fields_ref = np.asarray([1.09000000e+02, 8.06422068e+00, 1.29203058e+04, 1.60000000e+01]) + samples_fields_ref = np.asarray([1.09000000e+02, 8.05463203e+00, 1.35874031e+04, 1.60000000e+01]) with open(tmp_path / 'Morse_ASE.test.NS_samples') as fin: for l in fin: @@ -172,9 +172,7 @@ def do_Morse_ASE(tmp_path, monkeypatch, using_mpi, max_iter=None): # tolerance loosened so that restart, which isn't perfect due to finite precision in # saved cofig file, still passes if not np.allclose(samples_fields, samples_fields_ref, rtol=0.02): - print("final samples line test", samples_fields) - print("final samples line ref ", samples_fields_ref) - assert False + assert False, f"test {samples_fields} ref {samples_fields_ref}" # this will fail if number of steps is not divisible by N_samples interval, because # clone_hist always saves every line. Also, clone_hist is a hack that's truncated by @@ -245,9 +243,9 @@ def do_EAM_LAMMPS(tmp_path, monkeypatch, using_mpi, max_iter=None): # from test run 12/8/2022 if using_mpi: - fields_ref = np.asarray([2.99000000e+02, -3.91163426e+02, 1.08674253e+04, 1.60000000e+01, 1.87500000e-01, 8.12500000e-01]) + fields_ref = np.asarray([2.99000000e+02, -3.91054935e+02, 1.04562505e+04, 1.60000000e+01, 1.87500000e-01, 8.12500000e-01]) else: - fields_ref = np.asarray([299, -366.1823642208, 6004.3693892916, 16.0000000000, 0.2500000000, 0.7500000000]) + fields_ref = np.asarray([2.99000000e+02, -3.90472808e+02, 2.71314350e+04, 1.60000000e+01, 1.87500000e-01, 8.12500000e-01]) with open(tmp_path / 'EAM_LAMMPS.test.NS_samples') as fin: for l in fin: @@ -255,9 +253,7 @@ def do_EAM_LAMMPS(tmp_path, monkeypatch, using_mpi, max_iter=None): fields = np.asarray([float(f) for f in l.strip().split()]) if not np.allclose(fields, fields_ref): - print("final line test", fields) - print("final line ref ", fields_ref) - assert False + assert False, f"test {fields} ref {fields_ref}" def do_pressure(tmp_path, monkeypatch, using_mpi): @@ -308,7 +304,7 @@ def do_pressure(tmp_path, monkeypatch, using_mpi): Vfirst = float(lfirst.strip().split()[2]) Vlast = float(llast.strip().split()[2]) - assert Vfirst / Vlast > 20 + assert Vfirst / Vlast > 10 def do_sGC(tmp_path, monkeypatch, using_mpi): From 86fa139bf5a1a6932568712424afb242d4a6d615 Mon Sep 17 00:00:00 2001 From: Noam Bernstein Date: Wed, 18 Jun 2025 15:02:15 -0400 Subject: [PATCH 2/3] slightly loosen sGC test composition test --- tests/test_sample.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_sample.py b/tests/test_sample.py index 9d33431..e47fe0b 100644 --- a/tests/test_sample.py +++ b/tests/test_sample.py @@ -362,4 +362,4 @@ def do_sGC(tmp_path, monkeypatch, using_mpi): f_13_last, f_29_last = [float(f) for f in llast.strip().split()[4:6]] assert f_29_first / f_13_first == 1.0 - assert f_29_last / f_13_last > 5 + assert f_29_last / f_13_last > 4 From 9070a8ae2f7f73df1ff55cd89726f4d28f64d122 Mon Sep 17 00:00:00 2001 From: Noam Bernstein Date: Fri, 20 Jun 2025 13:40:57 -0400 Subject: [PATCH 3/3] Increase cell_shear_per_rt3_atom default, and update tests --- README_input_parameters.md | 2 +- pymatnext/ns_configs/ase_atoms/ase_atoms_params.py | 2 +- tests/test_sample.py | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README_input_parameters.md b/README_input_parameters.md index e44063b..403d90b 100644 --- a/README_input_parameters.md +++ b/README_input_parameters.md @@ -52,7 +52,7 @@ - [max_step_size] - pos_gmc_each_atom = -0.1 - cell_volume_per_atom = -0.05 - - cell_shear_per_rt3_atom = -0.2 + - cell_shear_per_rt3_atom = -1.0 - cell_stretch = 0.2 - [step_size] - pos_gmc_each_atom = -1.0 diff --git a/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py b/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py index 26a82fe..8dbc7fd 100644 --- a/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py +++ b/pymatnext/ns_configs/ase_atoms/ase_atoms_params.py @@ -29,7 +29,7 @@ "max_step_size": { "pos_gmc_each_atom": -0.1, "cell_volume_per_atom": -0.05, - "cell_shear_per_rt3_atom": -0.2, + "cell_shear_per_rt3_atom": -1.0, "cell_stretch": 0.2 }, diff --git a/tests/test_sample.py b/tests/test_sample.py index e47fe0b..e8b8140 100644 --- a/tests/test_sample.py +++ b/tests/test_sample.py @@ -158,11 +158,11 @@ def do_Morse_ASE(tmp_path, monkeypatch, using_mpi, max_iter=None): assert len(list(tmp_path.glob('Morse_ASE.test.traj.*xyz'))) == 1 assert len(list(ase.io.read(tmp_path / 'Morse_ASE.test.traj.extxyz', ':'))) == int(np.ceil(max_iter_use / traj_interval)) - # from test run 6/18/2025, when cell_shear was replaced with cell_shear_per_rt3_atom + # from test run 6/20/2025, when cell_shear_per_rt3_atom default was increased if using_mpi: samples_fields_ref = np.asarray([1.09000000e+02, 8.02719236e+00, 1.28609799e+04, 1.60000000e+01]) else: - samples_fields_ref = np.asarray([1.09000000e+02, 8.05463203e+00, 1.35874031e+04, 1.60000000e+01]) + samples_fields_ref = np.asarray([1.09000000e+02, 8.06422068e+00, 1.29203058e+04, 1.60000000e+01]) with open(tmp_path / 'Morse_ASE.test.NS_samples') as fin: for l in fin: @@ -241,11 +241,11 @@ def do_EAM_LAMMPS(tmp_path, monkeypatch, using_mpi, max_iter=None): assert len(list(tmp_path.glob('EAM_LAMMPS.test.traj.*xyz'))) == 1 assert len(list(ase.io.read(tmp_path / 'EAM_LAMMPS.test.traj.extxyz', ':'))) == max_iter_use // traj_interval - # from test run 12/8/2022 + # from test run 6/20/2025 when cell shear default was increased if using_mpi: - fields_ref = np.asarray([2.99000000e+02, -3.91054935e+02, 1.04562505e+04, 1.60000000e+01, 1.87500000e-01, 8.12500000e-01]) + fields_ref = np.asarray([2.99000000e+02, -3.90328931e+02, 1.99365520e+04, 1.60000000e+01, 1.87500000e-01, 8.12500000e-01]) else: - fields_ref = np.asarray([2.99000000e+02, -3.90472808e+02, 2.71314350e+04, 1.60000000e+01, 1.87500000e-01, 8.12500000e-01]) + fields_ref = np.asarray([2.99000000e+02, -3.90994404e+02, 1.45640509e+04, 1.60000000e+01, 1.87500000e-01, 8.12500000e-01]) with open(tmp_path / 'EAM_LAMMPS.test.NS_samples') as fin: for l in fin: