Skip to content

Commit aac4c8d

Browse files
authored
Merge pull request #504 from madhursharmaa/NEW
bug-fix
2 parents 98a653f + cb3fad2 commit aac4c8d

1 file changed

Lines changed: 68 additions & 39 deletions

File tree

ash/modules/module_machine_learning.py

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,45 @@
1414
# Collection of functions related to machine learning and data analysis
1515
# Also helper tools for Torch and MLatom interfaces
1616

17+
# Helper that writes a training-data file in the MACE extended-XYZ format.
18+
# atom_energies/energies are expected in Hartree and gradients in Hartree/Bohr;
19+
# all are converted to MACE units (eV and eV/Å) on the fly.
20+
def write_mace_xyz_file(filename, atom_energies, energies, gradients, fragments, Grad=True):
21+
#TODO: Nmols, comp, molindex ?
22+
Nmols="1"
23+
comp="xxx"
24+
molindex=0
25+
with open(filename, "w") as mace_file:
26+
print(f"Writing isolated atom reference energies to {filename} ....")
27+
for el, an_at in atom_energies.items():
28+
en_ev = an_at * 27.211386245988
29+
mace_file.write("1\n")
30+
if Grad:
31+
mace_file.write(f"Properties=species:S:1:pos:R:3:forces_REF:R:3 config_type=IsolatedAtom energy_REF={en_ev} pbc='F F F'\n")
32+
mace_file.write(f"{el:2s}{0.0:17.8f}{0.0:17.8f}{0.0:17.8f}"
33+
f"{-0.0:17.8f}{-0.0:17.8f}{-0.0:17.8f}\n")
34+
else:
35+
mace_file.write(f"Properties=species:S:1:pos:R:3 config_type=IsolatedAtom energy_REF={en_ev} pbc='F F F'\n")
36+
mace_file.write(f"{el:2s}{0.0:17.8f}{0.0:17.8f}{0.0:17.8f}\n")
37+
38+
for i in range(len(energies)):
39+
# Converting energy to eV
40+
frag = fragments[i]
41+
energy_ev = energies[i]*27.211386245988
42+
mace_file.write(f"{frag.numatoms}\n")
43+
if Grad:
44+
force = -1 * np.array(gradients[i]) * 51.42206747
45+
mace_file.write(f"Properties=species:S:1:pos:R:3:molID:I:1:forces_REF:R:3 Nmols={Nmols} Comp={comp} energy_REF={energy_ev} pbc='F F F'\n")
46+
for j in range(frag.numatoms): # Bug fix: inner loop variable was i, now j
47+
mace_file.write(f"{frag.elems[j]:2s}{frag.coords[j][0]:17.8f}{frag.coords[j][1]:17.8f}{frag.coords[j][2]:17.8f}"
48+
f"{molindex:9d}{force[j][0]:17.8f}{force[j][1]:17.8f}{force[j][2]:17.8f}\n")
49+
else:
50+
# Write without forces
51+
mace_file.write(f"Properties=species:S:1:pos:R:3:molID:I:1 Nmols={Nmols} Comp={comp} energy_REF={energy_ev} pbc='F F F'\n")
52+
for j in range(frag.numatoms): # Bug fix: inner loop variable was i, now j
53+
mace_file.write(f"{frag.elems[j]:2s}{frag.coords[j][0]:17.8f}{frag.coords[j][1]:17.8f}{frag.coords[j][2]:17.8f}"
54+
f"{molindex:9d}\n")
55+
1756
# Function to create ML training data given XYZ-files and 2 ASH theories
1857
def create_ML_training_data(xyz_dir=None, dcd_trajectory=None, xyz_trajectory=None, xyz_files=None, num_snapshots=None, random_snapshots=True,
1958
dcd_pdb_topology=None, nth_frame_in_traj=1, printlevel=2,
@@ -196,7 +235,8 @@ def create_ML_training_data(xyz_dir=None, dcd_trajectory=None, xyz_trajectory=No
196235
# Remove old files if present
197236
for f in ["train_data.xyz", "train_data.energies", "train_data.gradients", "train_data_mace.xyz",
198237
"train_data_theory1.energies", "train_data_theory2.energies",
199-
"train_data_theory1.gradients", "train_data_theory2.gradients"]:
238+
"train_data_theory1.gradients", "train_data_theory2.gradients",
239+
"train_data_mace_theory1.xyz", "train_data_mace_theory2.xyz"]:
200240
try:
201241
os.remove(f)
202242
except:
@@ -213,9 +253,10 @@ def create_ML_training_data(xyz_dir=None, dcd_trajectory=None, xyz_trajectory=No
213253
gradients_theory1=[]
214254
gradients_theory2=[]
215255

216-
# Removing
256+
# Removing
217257
theory_1.cleanup()
218-
theory_2.cleanup()
258+
if theory_2 is not None:
259+
theory_2.cleanup()
219260

220261
if runmode=="serial":
221262
print("Runmode is serial!")
@@ -325,6 +366,9 @@ def create_ML_training_data(xyz_dir=None, dcd_trajectory=None, xyz_trajectory=No
325366
gradients.append(gradient)
326367

327368
# Calculate energies for atoms
369+
# Per-theory isolated-atom reference energies, needed to write per-theory MACE files
370+
energies_atoms_dict_theory1={}
371+
energies_atoms_dict_theory2={}
328372
if energies_atoms_dict is None:
329373
print("\nNow calculating isolated atom reference energies for each element in the training set")
330374
energies_atoms_dict={}
@@ -340,13 +384,15 @@ def create_ML_training_data(xyz_dir=None, dcd_trajectory=None, xyz_trajectory=No
340384
theory_1.cleanup()
341385
result_1 = Singlepoint(theory=theory_1, fragment=atomfrag, printlevel=0,
342386
result_write_to_disk=False)
387+
energies_atoms_dict_theory1[uniq_el] = result_1.energy
343388
if delta is True:
344389
theory_2.printlevel=0
345390
# Running theory 2
346391
print("Now running Theory 2 for atom:", uniq_el)
347392
theory_2.cleanup()
348393
result_2 = Singlepoint(theory=theory_2, fragment=atomfrag, printlevel=0,
349394
result_write_to_disk=False)
395+
energies_atoms_dict_theory2[uniq_el] = result_2.energy
350396
# Delta energy
351397
atomenergy = result_2.energy - result_1.energy
352398
else:
@@ -406,42 +452,23 @@ def create_ML_training_data(xyz_dir=None, dcd_trajectory=None, xyz_trajectory=No
406452
print("\nNow writing data in MACE-format with energies in units of eV and forces in eV/Å")
407453
print("Fragments labels:",[frag.label for frag in fragments])
408454
print("energies:", energies)
409-
# Write data file that MACE uses
455+
# Write combined data file that MACE uses (delta data when two theories are used,
456+
# otherwise the single-theory data)
457+
write_mace_xyz_file("train_data_mace.xyz", energies_atoms_dict, energies, gradients,
458+
fragments, Grad=Grad)
410459

411-
with open("train_data_mace.xyz", "w") as mace_file:
412-
print("Writing isolated atom reference energies....")
413-
for el, an_at in energies_atoms_dict.items():
414-
en_ev = an_at * 27.211386245988
415-
mace_file.write("1\n")
416-
if Grad:
417-
mace_file.write(f"Properties=species:S:1:pos:R:3:forces_REF:R:3 config_type=IsolatedAtom energy_REF={en_ev} pbc='F F F'\n")
418-
mace_file.write(f"{el:2s}{0.0:17.8f}{0.0:17.8f}{0.0:17.8f}"
419-
f"{-0.0:17.8f}{-0.0:17.8f}{-0.0:17.8f}\n")
420-
else:
421-
mace_file.write(f"Properties=species:S:1:pos:R:3 config_type=IsolatedAtom energy_REF={en_ev} pbc='F F F'\n")
422-
mace_file.write(f"{el:2s}{0.0:17.8f}{0.0:17.8f}{0.0:17.8f}\n")
423-
#TODO: Nmols, comp, molindex ?
424-
Nmols="1"
425-
comp="xxx"
426-
molindex=0
427-
428-
for i in range(len(energies)):
429-
# Converting energy to eV
430-
frag = fragments[i]
431-
energy_ev = energies[i]*27.211386245988
432-
mace_file.write(f"{frag.numatoms}\n")
433-
if Grad:
434-
force = -1 * np.array(gradients[i]) * 51.42206747
435-
mace_file.write(f"Properties=species:S:1:pos:R:3:molID:I:1:forces_REF:R:3 Nmols={Nmols} Comp={comp} energy_REF={energy_ev} pbc='F F F'\n")
436-
for j in range(frag.numatoms): # Bug fix: inner loop variable was i, now j
437-
mace_file.write(f"{frag.elems[j]:2s}{frag.coords[j][0]:17.8f}{frag.coords[j][1]:17.8f}{frag.coords[j][2]:17.8f}"
438-
f"{molindex:9d}{force[j][0]:17.8f}{force[j][1]:17.8f}{force[j][2]:17.8f}\n")
439-
else:
440-
# Write without forces
441-
mace_file.write(f"Properties=species:S:1:pos:R:3:molID:I:1 Nmols={Nmols} Comp={comp} energy_REF={energy_ev} pbc='F F F'\n")
442-
for j in range(frag.numatoms): # Bug fix: inner loop variable was i, now j
443-
mace_file.write(f"{frag.elems[j]:2s}{frag.coords[j][0]:17.8f}{frag.coords[j][1]:17.8f}{frag.coords[j][2]:17.8f}"
444-
f"{molindex:9d}\n")
460+
# When delta-learning (two theories), also write a MACE-format file for each
461+
# individual theory level, using that theory's own atomic reference energies.
462+
if delta is True:
463+
if energies_atoms_dict_theory1 and energies_atoms_dict_theory2:
464+
write_mace_xyz_file("train_data_mace_theory1.xyz", energies_atoms_dict_theory1,
465+
energies_theory1, gradients_theory1, fragments, Grad=Grad)
466+
write_mace_xyz_file("train_data_mace_theory2.xyz", energies_atoms_dict_theory2,
467+
energies_theory2, gradients_theory2, fragments, Grad=Grad)
468+
else:
469+
print("Warning: per-theory isolated-atom reference energies are not available "
470+
"(user-provided energies_atoms_dict only holds the combined values).")
471+
print("Skipping per-theory MACE files train_data_mace_theory1.xyz / train_data_mace_theory2.xyz")
445472

446473
print("All done! Files created:\ntrain_data.xyz\ntrain_data.energies\ntrain_data_mace.xyz")
447474
if Grad:
@@ -450,6 +477,8 @@ def create_ML_training_data(xyz_dir=None, dcd_trajectory=None, xyz_trajectory=No
450477
print("train_data_theory1.energies\ntrain_data_theory2.energies")
451478
if Grad:
452479
print("train_data_theory1.gradients\ntrain_data_theory2.gradients")
480+
if energies_atoms_dict_theory1 and energies_atoms_dict_theory2:
481+
print("train_data_mace_theory1.xyz\ntrain_data_mace_theory2.xyz")
453482
print("Number of user-chosen snapshots:", num_snapshots)
454483
print("Number of successfully generated datapoints:", len(energies))
455484

@@ -842,4 +871,4 @@ def coulomb_matrix(fragment):
842871
else:
843872
# avoid divide-by-zero
844873
C[i, j] = Z[i] * Z[j] / D[i, j]
845-
return C
874+
return C

0 commit comments

Comments
 (0)