Skip to content

Commit a3d7fc2

Browse files
Merge pull request #64 from SSCHAcode/new_cluster_v2
Add to new_cluster the new modification introduced inside the master
2 parents 856342e + e7e4fd2 commit a3d7fc2

3 files changed

Lines changed: 136 additions & 3 deletions

File tree

Modules/Cluster.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
pass
1515

1616
import numpy as np
17+
import time, datetime
1718

1819
from ase.units import Rydberg, Bohr
1920
import ase, ase.io
@@ -179,6 +180,10 @@ def __init__(self, hostname=None, pwd=None, extra_options="", workdir = "",
179180
self.timeout = 1000
180181
self.use_timeout = False
181182

183+
# Check the status of the job every TOT seconds
184+
self.check_timeout = 300
185+
self.nonblocking_command = False # True if you use a different version of slurm that does not accept blocking commands
186+
182187
# This is the number of configurations to be computed for each jub submitted
183188
# This times the self.batch_size is the total amount of configurations submitted toghether
184189
self.job_number = 1
@@ -277,7 +282,7 @@ def __setattr__(self, name, value):
277282

278283

279284

280-
def ExecuteCMD(self, cmd, raise_error = True, return_output = False):
285+
def ExecuteCMD(self, cmd, raise_error = True, return_output = False, on_cluster = False):
281286
"""
282287
EXECUTE THE CMD ON THE CLUSTER
283288
==============================
@@ -294,6 +299,8 @@ def ExecuteCMD(self, cmd, raise_error = True, return_output = False):
294299
return_output : bool, optional
295300
If True (default False) the output of the command is
296301
returned as second value.
302+
on_cluster : bool
303+
If true, the command is executed directly on the cluster through ssh
297304
298305
Returns
299306
-------
@@ -303,6 +310,9 @@ def ExecuteCMD(self, cmd, raise_error = True, return_output = False):
303310
output : string
304311
Returned only if return_output is True
305312
"""
313+
314+
if on_cluster:
315+
cmd = self.sshcmd + " {} '{}'".format(self.hostname, cmd)
306316

307317
success = False
308318
output = ""
@@ -335,6 +345,8 @@ def ExecuteCMD(self, cmd, raise_error = True, return_output = False):
335345
if return_output:
336346
return success, output
337347
return success
348+
349+
338350

339351

340352
def set_timeout(self, timeout):
@@ -661,6 +673,7 @@ def batch_submission(self, list_of_structures, calc, indices,
661673
# sys.stderr.write(cmd + ": exit with code " + str(cp_res) + "\n")
662674
# return results #[None] * N_structs
663675

676+
664677
submission = self.create_submission_script(submission_labels)
665678

666679
# Copy the submission script
@@ -674,11 +687,12 @@ def batch_submission(self, list_of_structures, calc, indices,
674687
if not cp_res:
675688
print ("Error while executing:", cmd)
676689
print ("Return code:", cp_res)
677-
sys.stderr.write(cmd + ": exit with code " + str(cp_res))
690+
sys.stderr.write(cmd + ": exit with code " + str(cp_res) + "\n")
678691
return results#[None] * N_structs
679692

680693

681694
# Run the simulation
695+
682696
sub_script_loc = os.path.join(self.workdir, label + "_" + str(indices[0]) + ".sh")
683697
cp_res, submission_output = self.submit(sub_script_loc)
684698

@@ -720,7 +734,58 @@ def batch_submission(self, list_of_structures, calc, indices,
720734
pass
721735

722736
return results
737+
738+
def get_job_id_from_submission_output(self, output):
739+
"""
740+
GET THE JOB ID
741+
742+
Retreive the job id from the output of the submission.
743+
This depends on the software employed. It works for slurm.
744+
745+
Returns None if the output contains an error
746+
"""
747+
748+
try:
749+
id = output.split()[-1]
750+
return id
751+
except:
752+
print("Error, expected a standard output, but the result of the submission was: {}".format(output))
753+
return None
723754

755+
def check_job_finished(self, job_id, verbose = True):
756+
"""
757+
Check if the job identified by the job_id is finished
758+
759+
Parameters
760+
----------
761+
job_id : string
762+
The string that identifies uniquely the job
763+
"""
764+
765+
status, output = self.ExecuteCMD("squeue -u $USER", False, return_output = True, on_cluster = True, )
766+
lines = output.split("\n")
767+
if len(lines):
768+
for l in lines:
769+
data = l.strip().split()
770+
if data[0] == job_id:
771+
if verbose:
772+
now = datetime.datetime.now()
773+
sys.stderr.write("{}/{}/{} - {}:{}:{} | job {} still running\n".format(now.year, now.month, now.day, now.hour, now.minute, now.second, job_id))
774+
sys.stderr.flush()
775+
return False
776+
777+
# If I'm here it means I did not find the job, but the command returned at least 1 line (so it was correctly executed).
778+
if verbose:
779+
now = datetime.datetime.now()
780+
sys.stderr.write("{}/{}/{} - {}:{}:{} | job {} finished\n".format(now.year, now.month, now.day, now.hour, now.minute, now.second, job_id))
781+
sys.stderr.flush()
782+
return True
783+
if verbose:
784+
now = datetime.datetime.now()
785+
sys.stderr.write("{}/{}/{} - {}:{}:{} | error while interrogating the cluster for job {}\n".format(now.year, now.month, now.day, now.hour, now.minute, now.second, job_id))
786+
sys.stderr.flush()
787+
return False
788+
724789

725790
def run_atoms(self, ase_calc, ase_atoms, label="ESP",
726791
in_extension = ".pwi", out_extension=".pwo",

Modules/Ensemble.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
Bohr = 1/__A_TO_BOHR__
8383
__RyToK__ = 157887.32400374097
8484

85+
__GPa__ = 14710.50763554043
8586

8687
__DEBUG_RHO__ = False
8788

@@ -817,6 +818,73 @@ def save_enhanced_xyz(self, filename, append_mode = True, stress_key = "virial",
817818
# Force other processors to wait for the master
818819
CC.Settings.barrier()
819820

821+
def save_raw(self, root_directory, type_dict = None):
822+
"""
823+
Save the ensemble as a set of raw files.
824+
825+
This is the default format for training with deepmd
826+
827+
Parameters
828+
----------
829+
filename : string
830+
The directory on which to save the ensemble. If it does not exist, it is create.
831+
NOTE: this will overwrite any other ensemble saved in raw format in that directory
832+
type_dict : dict
833+
The dictionary between integers and atomic types. If not provided, it is generated on the spot and returned.
834+
835+
Returns
836+
-------
837+
type_dict : dict
838+
The dictionary of the parameters
839+
"""
840+
nat = self.current_dyn.structure.N_atoms * np.prod(self.current_dyn.GetSupercell())
841+
842+
if type_dict is None:
843+
atm = np.unique(self.current_dyn.structure.atoms)
844+
type_dict = {x : i for i, x in enumerate(atm)}
845+
846+
inv_dict = {i : x for x, i in type_dict.items()}
847+
848+
849+
# Save only if the current processor is the master
850+
if Parallel.am_i_the_master():
851+
if not os.path.exists(root_directory):
852+
os.makedirs(root_directory)
853+
854+
if not os.path.isdir(root_directory):
855+
raise IOError("Error, save_raw expects a directory, but '{}' is not a directory.".format(root_directory))
856+
857+
# Save the energies
858+
np.savetxt(os.path.join(root_directory, "energy.raw"), self.energies * Rydberg)
859+
860+
# Save the positions
861+
np.savetxt(os.path.join(root_directory, "coord.raw"), self.xats.reshape((self.N, 3 * nat)))
862+
863+
# Save the box
864+
np.savetxt(os.path.join(root_directory, "box.raw"), np.tile(self.current_dyn.structure.unit_cell.ravel(), (self.N, 1)))
865+
866+
# Save the forces
867+
np.savetxt(os.path.join(root_directory, "force.raw"), self.forces.reshape((self.N, 3*nat)) * Rydberg)
868+
869+
# Save the stress
870+
np.savetxt(os.path.join(root_directory, "virial.raw"), self.stresses.reshape((self.N, 9)) * __GPa__ * 10000)
871+
872+
# Save the types
873+
ss = self.current_dyn.structure.generate_supercell(self.current_dyn.GetSupercell())
874+
875+
with open(os.path.join(root_directory, "type_map.raw"), "w") as fp:
876+
line = " ".join([inv_dict[x] for x in np.arange(len(type_dict))])
877+
fp.write(line + "\n")
878+
879+
with open(os.path.join(root_directory, "type.raw"), "w") as fp:
880+
line = " ".join([str(type_dict[x]) for x in ss.atoms])
881+
fp.write(line + "\n")
882+
883+
884+
885+
# Force other processors to wait for the master
886+
CC.Settings.barrier()
887+
820888

821889

822890

tests/test_simple_relax/test_relax_other.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_simple_relax(verbose = False):
6969
minim.min_step_struc = 0.5
7070
minim.meaningful_factor = 1e-10
7171
minim.init()
72-
minim.run()
72+
minim.run(verbose = 0)
7373
minim.finalize()
7474

7575
# Check the differences in the atomic positions

0 commit comments

Comments
 (0)