Commit 3f568d0e authored by Malthe Kjær Bisbo's avatar Malthe Kjær Bisbo
Browse files

position constraint that both constraints mutations and surrogate relaxations

parent 04a0c4ca
...@@ -13,8 +13,9 @@ import sys ...@@ -13,8 +13,9 @@ import sys
class BFGSLineSearch_constrained(BFGSLineSearch): class BFGSLineSearch_constrained(BFGSLineSearch):
def __init__(self, atoms, pos_init=None, restart=None, logfile='-', maxstep=.2, def __init__(self, atoms, pos_init=None, restart=None, logfile='-', maxstep=.2,
trajectory=None, c1=0.23, c2=0.46, alpha=10.0, stpmax=50.0, trajectory=None, c1=0.23, c2=0.46, alpha=10.0, stpmax=50.0,
master=None, force_consistent=None, zlim=None, master=None, force_consistent=None,
blmin=None, blmax=None, max_relax_dist=4.0, rk=None): blmin=None, blmax=None, max_relax_dist=4.0,
position_constraint=None, rk=None):
""" """
add maximum displacement of single atoms to BFGSLineSearch: add maximum displacement of single atoms to BFGSLineSearch:
...@@ -24,9 +25,9 @@ class BFGSLineSearch_constrained(BFGSLineSearch): ...@@ -24,9 +25,9 @@ class BFGSLineSearch_constrained(BFGSLineSearch):
self.rk = rk # for testing self.rk = rk # for testing
self.zlim = zlim
self.blmin = blmin self.blmin = blmin
self.blmax = blmax self.blmax = blmax
self.position_constraint=position_constraint
self.max_relax_dist = max_relax_dist self.max_relax_dist = max_relax_dist
if pos_init is not None: if pos_init is not None:
...@@ -59,12 +60,12 @@ class BFGSLineSearch_constrained(BFGSLineSearch): ...@@ -59,12 +60,12 @@ class BFGSLineSearch_constrained(BFGSLineSearch):
def check_constraints(self): def check_constraints(self):
# Check if stop due to large displacement # Check if stop due to large displacement
valid_displace = self.check_displacement() valid_displace = self.check_displacement()
# Check if stop due to zlim # Check if stop due to position-constraint
valid_z = self.check_zcoord() valid_pos = self.check_positions()
# Check if stop due to invalid bondlengths # Check if stop due to invalid bondlengths
valid_bondlengths = self.check_bondlengths() valid_bondlengths = self.check_bondlengths()
if not valid_displace or not valid_z or not valid_bondlengths: if not valid_displace or not valid_pos or not valid_bondlengths:
#self.r0 = self.atoms_prior.get_positions() #self.r0 = self.atoms_prior.get_positions()
return True return True
...@@ -77,19 +78,18 @@ class BFGSLineSearch_constrained(BFGSLineSearch): ...@@ -77,19 +78,18 @@ class BFGSLineSearch_constrained(BFGSLineSearch):
valid_displace = False valid_displace = False
return valid_displace return valid_displace
def check_zcoord(self): def check_positions(self):
valid_z = True valid_pos = True
if self.zlim is not None: if self.position_constraint is not None:
# get indices of non-fixed atoms # get indices of non-fixed atoms
indices = np.arange(self.atoms.get_number_of_atoms()) indices = np.arange(self.atoms.get_number_of_atoms())
for constraint in self.atoms.constraints: for constraint in self.atoms.constraints:
if isinstance(constraint, FixAtoms): if isinstance(constraint, FixAtoms):
indices_fixed = constraint.get_indices() indices_fixed = constraint.get_indices()
indices = np.delete(np.arange(self.atoms.get_number_of_atoms()), indices_fixed) indices_not_fixed = np.delete(np.arange(self.atoms.get_number_of_atoms()), indices_fixed)
pos_z = self.atoms.positions[indices,2] pos_not_fixed = self.atoms.positions[indices_not_fixed]
if np.any(pos_z < self.zlim[0]) or np.any(pos_z > self.zlim[1]): valid_pos = self.position_constraint.check_if_valid(pos_not_fixed)
valid_z = False return valid_pos
return valid_z
def check_bondlengths(self): def check_bondlengths(self):
valid_bondlengths = True valid_bondlengths = True
...@@ -100,7 +100,7 @@ class BFGSLineSearch_constrained(BFGSLineSearch): ...@@ -100,7 +100,7 @@ class BFGSLineSearch_constrained(BFGSLineSearch):
return valid_bondlengths return valid_bondlengths
def relax(structure, calc, Fmax=0.05, steps_max=200, max_relax_dist=None): def relax(structure, calc, Fmax=0.05, steps_max=200, max_relax_dist=None, position_constraint=None):
a = structure.copy() a = structure.copy()
# Set calculator # Set calculator
a.set_calculator(calc) a.set_calculator(calc)
...@@ -111,7 +111,8 @@ def relax(structure, calc, Fmax=0.05, steps_max=200, max_relax_dist=None): ...@@ -111,7 +111,8 @@ def relax(structure, calc, Fmax=0.05, steps_max=200, max_relax_dist=None):
dyn = BFGSLineSearch_constrained(a, dyn = BFGSLineSearch_constrained(a,
logfile=None, logfile=None,
pos_init=pos_init, pos_init=pos_init,
max_relax_dist=max_relax_dist) max_relax_dist=max_relax_dist,
position_constraint=position_constraint)
dyn.run(fmax = Fmax, steps = steps_max) dyn.run(fmax = Fmax, steps = steps_max)
except Exception as err: except Exception as err:
print('Error in surrogate-relaxation:', err, flush=True) print('Error in surrogate-relaxation:', err, flush=True)
......
...@@ -95,7 +95,7 @@ class RattleMutation(OffspringOperation): ...@@ -95,7 +95,7 @@ class RattleMutation(OffspringOperation):
# Perform rattle operations in sequence. # Perform rattle operations in sequence.
for i in indices_to_rattle: for i in indices_to_rattle:
posi_0 = np.copy(a.positions[i]) posi_0 = np.copy(a.positions[i])
for _ in range(100): for _ in range(200):
# Perform rattle # Perform rattle
pos_add = pos_add_sphere(self.rattle_range) pos_add = pos_add_sphere(self.rattle_range)
a.positions[i] += pos_add a.positions[i] += pos_add
......
...@@ -118,6 +118,9 @@ class OffspringOperation(ABC): ...@@ -118,6 +118,9 @@ class OffspringOperation(ABC):
Atom {index_shortest_bond} has bond with d={d_shortest_bond}d_covalent""" Atom {index_shortest_bond} has bond with d={d_shortest_bond}d_covalent"""
warnings.warn(text) warnings.warn(text)
def set_constraints(self, constraints):
self.constraints = constraints
def check_constraints(self, indices=None): def check_constraints(self, indices=None):
if self.constraints is not None: if self.constraints is not None:
valid = self.constraints.check_if_valid(indices) valid = self.constraints.check_if_valid(indices)
...@@ -180,12 +183,18 @@ class CandidateGenerator(): ...@@ -180,12 +183,18 @@ class CandidateGenerator():
anew = op_list[to_use].finalize(anew, successfull=False) anew = op_list[to_use].finalize(anew, successfull=False)
return anew return anew
def set_constraints(self, constraints):
for op_list in self.operations:
for op in op_list:
op.set_constraints(constraints)
self.constraints = constraints
def train(self, data): def train(self, data):
""" Method to train all trainable operations in """ Method to train all trainable operations in
self.operations. self.operations.
""" """
for oplist in self.operations: for op_list in self.operations:
for operation in oplist: for operation in op_list:
operation.train(data) operation.train(data)
...@@ -231,9 +240,14 @@ class OperationConstraint(): ...@@ -231,9 +240,14 @@ class OperationConstraint():
pos = positions.reshape(-1,3) pos = positions.reshape(-1,3)
else: else:
pos = positions pos = positions
if self.box is not None: if self.box is not None:
pass p0, V = self.box
p_rel = pos - p0 # positions relative to box anchor.
V_inv = np.linalg.inv(V)
p_box = p_rel @ V_inv # positions in box-vector basis.
if (np.any(p_box < 0) or np.any(p_box > 1)):
return False
if self.xlim is not None: if self.xlim is not None:
if (np.any(pos[:,0] < self.xlim[0]) or if (np.any(pos[:,0] < self.xlim[0]) or
np.any(pos[:,0] > self.xlim[1])): np.any(pos[:,0] > self.xlim[1])):
......
No preview for this file type
...@@ -100,6 +100,11 @@ class GOFEE(): ...@@ -100,6 +100,11 @@ class GOFEE():
considdered for evaluation. (in units of the maximum possible considdered for evaluation. (in units of the maximum possible
uncertainty.) uncertainty.)
position_constraint: OperationConstraint object
Enforces constraints on the positions of the "free" atoms
in the search. The constraint is enforces both during
mutation/crossover operations and during surrogate-relaxation.
restart: str restart: str
Filename for restart file. Filename for restart file.
""" """
...@@ -108,8 +113,6 @@ class GOFEE(): ...@@ -108,8 +113,6 @@ class GOFEE():
gpr=None, gpr=None,
startgenerator=None, startgenerator=None,
candidate_generator=None, candidate_generator=None,
trajectory='structures.traj',
logfile='search.log',
kappa=2, kappa=2,
max_steps=200, max_steps=200,
Ninit=10, Ninit=10,
...@@ -118,6 +121,9 @@ class GOFEE(): ...@@ -118,6 +121,9 @@ class GOFEE():
population_size=10, population_size=10,
dualpoint=True, dualpoint=True,
min_certainty=0.7, min_certainty=0.7,
position_constraint=None,
trajectory='structures.traj',
logfile='search.log',
restart='restart.pickl'): restart='restart.pickl'):
if structures is None: if structures is None:
...@@ -146,6 +152,7 @@ class GOFEE(): ...@@ -146,6 +152,7 @@ class GOFEE():
else: else:
self.startgenerator = startgenerator self.startgenerator = startgenerator
# Determine atoms to optimize
if startgenerator is not None: if startgenerator is not None:
self.n_to_optimize = len(self.startgenerator.stoichiometry) self.n_to_optimize = len(self.startgenerator.stoichiometry)
else: else:
...@@ -156,6 +163,7 @@ class GOFEE(): ...@@ -156,6 +163,7 @@ class GOFEE():
self.n_to_optimize -= len(indices_fixed) self.n_to_optimize -= len(indices_fixed)
break break
# Set up candidate-generator if not supplied
if candidate_generator is not None: if candidate_generator is not None:
self.candidate_generator = candidate_generator self.candidate_generator = candidate_generator
else: else:
...@@ -175,8 +183,12 @@ class GOFEE(): ...@@ -175,8 +183,12 @@ class GOFEE():
self.Ncandidates = Ncandidates self.Ncandidates = Ncandidates
self.dualpoint = dualpoint self.dualpoint = dualpoint
self.min_certainty = min_certainty self.min_certainty = min_certainty
self.position_constraint = position_constraint
self.restart = restart self.restart = restart
# Add position-constraint to candidate-generator
self.candidate_generator.set_constraints(position_constraint)
if isinstance(trajectory, str): if isinstance(trajectory, str):
self.trajectory = Trajectory(filename=trajectory, mode='a', master=self.master) self.trajectory = Trajectory(filename=trajectory, mode='a', master=self.master)
if self.restart: if self.restart:
...@@ -192,7 +204,6 @@ class GOFEE(): ...@@ -192,7 +204,6 @@ class GOFEE():
self.logfile = logfile self.logfile = logfile
self.log_msg = '' self.log_msg = ''
if restart is None or not isfile(restart): if restart is None or not isfile(restart):
self.initialize() self.initialize()
...@@ -207,6 +218,7 @@ class GOFEE(): ...@@ -207,6 +218,7 @@ class GOFEE():
self.read() self.read()
self.comm.barrier() self.comm.barrier()
def initialize(self): def initialize(self):
self.get_initial_structures() self.get_initial_structures()
self.steps = 0 self.steps = 0
...@@ -373,7 +385,9 @@ class GOFEE(): ...@@ -373,7 +385,9 @@ class GOFEE():
surrogate potential. surrogate potential.
""" """
calc = self.gpr.get_calculator(kappa) calc = self.gpr.get_calculator(kappa)
a_relaxed = relax(a, calc, max_relax_dist=self.max_relax_dist, Fmax=Fmax, steps_max=steps) a_relaxed = relax(a, calc, Fmax=Fmax, steps_max=steps,
max_relax_dist=self.max_relax_dist,
position_constraint=self.position_constraint)
# Evaluate uncertainty # Evaluate uncertainty
E, Estd = self.gpr.predict_energy(a_relaxed, eval_std=True) E, Estd = self.gpr.predict_energy(a_relaxed, eval_std=True)
......
Markdown is supported
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