Commit 04a0c4ca authored by Malthe Kjær Bisbo's avatar Malthe Kjær Bisbo
Browse files

improved logging

parent 6af2c65a
...@@ -27,7 +27,7 @@ this (propperly modified for your setup):: ...@@ -27,7 +27,7 @@ this (propperly modified for your setup)::
source <GPAW stuff> source <GPAW stuff>
source <DFTB stuff> # if running DFTB source <DFTB stuff> # if running DFTB
mpiexec --mca mpi_warn_on_fork 0 gpaw-python run_search.py > search.log mpiexec --mca mpi_warn_on_fork 0 gpaw-python run_search.py
echo "========= Job finished at `date` ==========" echo "========= Job finished at `date` =========="
This job will be run locally in the submission folder on 10 cpu cores. This job will be run locally in the submission folder on 10 cpu cores.
......
No preview for this file type
.. _au_on_cu_search:
=======================
Au7 in Cu(111) with EMT
=======================
In this tutorial we carry out a search for Au7-clusters on
the Cu(111) surface.
In this search we will utilize an :class:`OperationConstraint`
to constraint the :class:`RattleMutation` to only rattle the
atoms within a certain box in space that we define.
...@@ -13,6 +13,7 @@ from ase.io import read, write, Trajectory ...@@ -13,6 +13,7 @@ from ase.io import read, write, Trajectory
from ase.calculators.singlepoint import SinglePointCalculator from ase.calculators.singlepoint import SinglePointCalculator
from ase.calculators.dftb import Dftb from ase.calculators.dftb import Dftb
from utils import array_to_string
from parallel_utils import split, parallel_function_eval from parallel_utils import split, parallel_function_eval
from bfgslinesearch_constrained import relax from bfgslinesearch_constrained import relax
...@@ -63,6 +64,10 @@ class GOFEE(): ...@@ -63,6 +64,10 @@ class GOFEE():
Name of trajectory to which all structures, Name of trajectory to which all structures,
evaluated during the search, is saved. evaluated during the search, is saved.
logfile: file object or str
If *logfile* is a string, a file with that name will be opened.
Use '-' for stdout.
kappa: float kappa: float
How much to weigh predicted uncertainty in the acquisition How much to weigh predicted uncertainty in the acquisition
function. function.
...@@ -104,6 +109,7 @@ class GOFEE(): ...@@ -104,6 +109,7 @@ class GOFEE():
startgenerator=None, startgenerator=None,
candidate_generator=None, candidate_generator=None,
trajectory='structures.traj', trajectory='structures.traj',
logfile='search.log',
kappa=2, kappa=2,
max_steps=200, max_steps=200,
Ninit=10, Ninit=10,
...@@ -132,7 +138,6 @@ class GOFEE(): ...@@ -132,7 +138,6 @@ class GOFEE():
assert structures is not None assert structures is not None
calc = structures[0].get_calculator() calc = structures[0].get_calculator()
assert calc is not None and not isinstance(calc, SinglePointCalculator) assert calc is not None and not isinstance(calc, SinglePointCalculator)
print('Using calculator from supplied structure(s)')
self.calc = calc self.calc = calc
if startgenerator is None: if startgenerator is None:
...@@ -177,6 +182,17 @@ class GOFEE(): ...@@ -177,6 +182,17 @@ class GOFEE():
if self.restart: if self.restart:
self.traj_name = trajectory self.traj_name = trajectory
if not self.master:
logfile = None
elif isinstance(logfile, str):
if logfile == "-":
logfile = sys.stdout
else:
logfile = open(logfile, "a")
self.logfile = logfile
self.log_msg = ''
if restart is None or not isfile(restart): if restart is None or not isfile(restart):
self.initialize() self.initialize()
...@@ -257,11 +273,14 @@ class GOFEE(): ...@@ -257,11 +273,14 @@ class GOFEE():
self.evaluate_initial_structures() self.evaluate_initial_structures()
while self.steps < self.max_steps: while self.steps < self.max_steps:
self.print_master('\n ### steps: {} ###\n'.format(self.steps)) self.log_msg += (f"\n##### STEPS: {self.steps} #####\n\n")
t0 = time()
self.train_surrogate() self.train_surrogate()
t1 = time()
self.update_population() self.update_population()
t2 = time()
relaxed_candidates = self.get_surrogate_relaxed_candidates() relaxed_candidates = self.get_surrogate_relaxed_candidates()
t3 = time()
kappa = self.kappa kappa = self.kappa
a_add = [] a_add = []
for _ in range(5): for _ in range(5):
...@@ -280,6 +299,11 @@ class GOFEE(): ...@@ -280,6 +299,11 @@ class GOFEE():
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
self.gpr.memory.save_data(a_add) self.gpr.memory.save_data(a_add)
# log timing
self.log_msg += "Timing:\n"
self.log_msg += f"{'Training':12}{'Relax pop.':12}{'Relax candidates':18}{'Evaluate':12}\n"
self.log_msg += f"{t1-t0:12.2e}\t{t2-t1:12.2e}\t{t3-t2:16.2e}\t{time()-t3:12.2e}\n\n"
# Add structure to population # Add structure to population
index_lowest = np.argmin([a.get_potential_energy() for a in a_add]) index_lowest = np.argmin([a.get_potential_energy() for a in a_add])
self.population.add([a_add[index_lowest]]) self.population.add([a_add[index_lowest]])
...@@ -287,11 +311,13 @@ class GOFEE(): ...@@ -287,11 +311,13 @@ class GOFEE():
# Save search state # Save search state
self.save_state() self.save_state()
if self.master: self.log_msg += (f"Prediction:\nenergy = {anew.info['key_value_pairs']['Epred']:.5f}eV, energy_std = {anew.info['key_value_pairs']['Epred_std']:.5f}eV\n")
print('anew pred:', anew.info['key_value_pairs']['Epred'], anew.info['key_value_pairs']['Epred_std']) self.log_msg += (f"E_true:\n{array_to_string([a.get_potential_energy() for a in a_add], unit='eV')}\n\n")
print('E_true:', [a.get_potential_energy() for a in a_add]) #self.log_msg += (f"E_true: {[a.get_potential_energy() for a in a_add]}\n")
print('pop:', [a.get_potential_energy() for a in self.population.pop]) self.log_msg += (f"Energy of population:\n{array_to_string([a.get_potential_energy() for a in self.population.pop], unit='eV')}\n")
self.log_msg += (f"Max force of ML-relaxed population:\n{array_to_string([(a.get_forces()**2).sum(axis=1).max()**0.5 for a in self.population.pop_MLrelaxed], unit='eV/A')}\n")
self.log()
self.steps += 1 self.steps += 1
def get_dualpoint(self, a, lmax=0.10, Fmax_flat=5): def get_dualpoint(self, a, lmax=0.10, Fmax_flat=5):
...@@ -315,11 +341,6 @@ class GOFEE(): ...@@ -315,11 +341,6 @@ class GOFEE():
a_dp.set_positions(pos_dp) a_dp.set_positions(pos_dp)
return a_dp return a_dp
def print_master(self, *args):
self.comm.barrier()
if self.master:
print(*args, flush=True)
def get_surrogate_relaxed_candidates(self): def get_surrogate_relaxed_candidates(self):
""" Method supplying a number of surrogate-relaxed new """ Method supplying a number of surrogate-relaxed new
candidates. The method combines the generation of new candidates. The method combines the generation of new
...@@ -338,13 +359,6 @@ class GOFEE(): ...@@ -338,13 +359,6 @@ class GOFEE():
relaxed_candidates = self.certainty_filter(relaxed_candidates) relaxed_candidates = self.certainty_filter(relaxed_candidates)
relaxed_candidates = self.population.pop_MLrelaxed + relaxed_candidates relaxed_candidates = self.population.pop_MLrelaxed + relaxed_candidates
"""
if self.master:
Epred = np.array([a.info['key_value_pairs']['Epred'] for a in relaxed_candidates])
Epred_std = np.array([a.info['key_value_pairs']['Epred_std'] for a in relaxed_candidates])
fitness = Epred - self.kappa*Epred_std
print(np.c_[Epred, Epred_std, fitness])
"""
return relaxed_candidates return relaxed_candidates
def generate_candidate(self): def generate_candidate(self):
...@@ -397,9 +411,8 @@ class GOFEE(): ...@@ -397,9 +411,8 @@ class GOFEE():
Fmax=0.001, steps=200, kappa=None) Fmax=0.001, steps=200, kappa=None)
for i in task_split[self.comm.rank]] for i in task_split[self.comm.rank]]
self.population.pop_MLrelaxed = parallel_function_eval(self.comm, func) self.population.pop_MLrelaxed = parallel_function_eval(self.comm, func)
if self.master: Fmax_pop_relaxed = [(a.get_forces()**2).sum(axis=1).max()**0.5
print('ML-relaxed pop forces:\n', for a in self.population.pop_MLrelaxed]
[(a.get_forces()**2).sum(axis=1).max()**0.5 for a in self.population.pop_MLrelaxed])
def train_surrogate(self): def train_surrogate(self):
""" Method to train the surrogate model. """ Method to train the surrogate model.
...@@ -411,13 +424,11 @@ class GOFEE(): ...@@ -411,13 +424,11 @@ class GOFEE():
# Train # Train
if self.steps < 50 or (self.steps % 10) == 0: if self.steps < 50 or (self.steps % 10) == 0:
self.gpr.optimize_hyperparameters(comm=self.comm) self.gpr.optimize_hyperparameters(comm=self.comm)
if self.master: self.log_msg += (f"lml: {self.gpr.lml}\n")
print('lml:', self.gpr.lml) self.log_msg += (f"kernel optimized:\nTheta = {[f'{x:.2e}' for x in np.exp(self.gpr.kernel.theta)]}\n\n")
print('kernel optimized:\n\tTheta =', list(np.exp(self.gpr.kernel.theta)))
else: else:
self.gpr.train() self.gpr.train()
if self.master: self.log_msg += (f"kernel fixed:\nTheta = {[f'{x:.2e}' for x in np.exp(self.gpr.kernel.theta)]}\n\n")
print('kernel fixed:\n\tTheta =', list(np.exp(self.gpr.kernel.theta)))
def select_with_acquisition(self, structures, kappa): def select_with_acquisition(self, structures, kappa):
""" Method to select single most "promizing" candidate """ Method to select single most "promizing" candidate
...@@ -450,14 +461,11 @@ class GOFEE(): ...@@ -450,14 +461,11 @@ class GOFEE():
a.set_calculator(calc_sp) a.set_calculator(calc_sp)
success = True success = True
except: except:
print('dftb failed on master', flush=True)
success = False success = False
else: else:
success = None success = None
success = self.comm.bcast(success, root=0) success = self.comm.bcast(success, root=0)
if success == False: if success == False:
write('fail.traj', a)
print('Raising error rank', self.comm.rank, flush=True)
raise RuntimeError('DFTB evaluation failed') raise RuntimeError('DFTB evaluation failed')
a = self.comm.bcast(a, root=0) a = self.comm.bcast(a, root=0)
else: else:
...@@ -506,3 +514,16 @@ class GOFEE(): ...@@ -506,3 +514,16 @@ class GOFEE():
self.gpr = GPR(template_structure=training_structures[0]) self.gpr = GPR(template_structure=training_structures[0])
self.gpr.memory.save_data(training_structures) self.gpr.memory.save_data(training_structures)
self.gpr.kernel.theta = theta self.gpr.kernel.theta = theta
def log(self):
if self.logfile is not None:
if self.steps == 0:
msg = "GOFEE"
self.logfile.write(msg)
self.logfile.write(self.log_msg)
self.logfile.flush()
self.log_msg = ''
...@@ -76,4 +76,17 @@ def get_min_distances_as_fraction_of_covalent(a, indices=None, covalent_distance ...@@ -76,4 +76,17 @@ def get_min_distances_as_fraction_of_covalent(a, indices=None, covalent_distance
# Filter away self interactions. # Filter away self interactions.
bl = bl[bl > 1e-6].reshape(bl.shape[0],bl.shape[1]-1) bl = bl[bl > 1e-6].reshape(bl.shape[0],bl.shape[1]-1)
return np.min(bl), bl.min(axis=1).argmin() return np.min(bl), bl.min(axis=1).argmin()
\ No newline at end of file
def array_to_string(arr, unit='', format='0.4f', max_line_length=80):
msg = ''
line_length_counter = 0
for i, x in enumerate(arr):
string = f'{i} = {x:{format}}{unit}, '
#string = f"{f'{i}={x:{format}}{unit},':15}"
line_length_counter += len(string)
if line_length_counter >= max_line_length:
msg += '\n'
line_length_counter = len(string)
msg += string
return msg
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment