Commit 5de4909f authored by Malthe Kjær Bisbo's avatar Malthe Kjær Bisbo
Browse files

Added further documentation - autodoc still does not work

parent bc3a9b91
from gofee import GOFEE
__all__ = ['GOFEE']
\ No newline at end of file
from __future__ import print_function
# ******NOTICE***************
# optimize.py module by Travis E. Oliphant
#
# You may copy and use this module as you see fit with no
# guarantee implied provided you keep this notice in all copies.
# *****END NOTICE************
import time
import numpy as np
from numpy import eye, absolute, sqrt, isinf
from ase.utils.linesearch import LineSearch
from ase.optimize.bfgslinesearch import BFGSLineSearch
from ase.constraints import FixAtoms
from ase.utils import basestring
from ase.data import covalent_radii
from ase.ga.utilities import get_mic_distance
# These have been copied from Numeric's MLab.py
# I don't think they made the transition to scipy_core
# Modified from scipy_optimize
abs = absolute
pymin = min
pymax = max
__version__ = '0.1'
class BFGSLineSearch_zlim(BFGSLineSearch):
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,
master=None, force_consistent=None, zlim=None, dmax_cov=1.5, rk=None):
"""
add maximum displacement of single atoms to BFGSLineSearch:
dmax_cov: maximum distance the atom is alowed to move from it's initial position.
in units of it's covalent distance.
"""
self.rk = rk # for testing
self.zlim = zlim
self.dmax_cov = dmax_cov
self.d_cov = 2*np.array([covalent_radii[num] for num in atoms.get_atomic_numbers()])
if pos_init is not None:
self.pos_init = pos_init
else:
self.pos_init = np.copy(atoms.positions)
self.cell = atoms.get_cell()
self.pbc = atoms.get_pbc()
BFGSLineSearch.__init__(self, atoms, restart=restart, logfile=logfile, maxstep=maxstep,
trajectory=trajectory, c1=c1, c2=c2, alpha=alpha, stpmax=stpmax,
master=master, force_consistent=force_consistent)
def converged(self, forces=None):
"""Did the optimization converge?"""
if forces is None:
forces = self.atoms.get_forces()
if hasattr(self.atoms, 'get_curvature'):
return ((forces**2).sum(axis=1).max() < self.fmax**2 and
self.atoms.get_curvature() < 0.0)
# Check if stop due to large displacement
if self.dmax_cov is not None:
#max_covDisplace = np.max(np.sqrt(((self.pos_init-self.atoms.positions)**2).sum(axis=1))/self.d_cov)
d = np.array([get_mic_distance(p1,p2,self.cell,self.pbc) for p1,p2 in zip(self.pos_init,self.atoms.get_positions())])
max_covDisplace = np.max(d/self.d_cov)
if max_covDisplace > self.dmax_cov:
print('RELAX STOPPED on rank {} - max relax dist (>{} covDist) reached.'.format(self.rk, self.dmax_cov), flush=True)
return True
# Check if stop due to zlim
if self.zlim is not None:
# get indices of non-fixed atoms
indices = np.arange(self.atoms.get_number_of_atoms())
for constraint in self.atoms.constraints:
if isinstance(constraint, FixAtoms):
indices_fixed = constraint.get_indices()
indices = np.delete(np.arange(self.atoms.get_number_of_atoms()), indices_fixed)
pos_z = self.atoms.positions[indices,2]
if np.any(pos_z < self.zlim[0]) or np.any(pos_z > self.zlim[1]):
print('RELAXATION STOPPED on rank {} due to - zlim crossed.'.format(self.rk))
return True
return (forces**2).sum(axis=1).max() < self.fmax**2
......@@ -5,7 +5,7 @@ from ase.geometry import get_distances
from ase.visualize import view
from candidate_operations.candidate_generation import CandidateGenerator
from candidate_operations.candidate_generation import OffspringOperation
def pos_add_sphere(rattle_strength):
......@@ -31,7 +31,7 @@ def pos_add_sphere_shell(rmin, rmax):
np.cos(phi)])
return pos_add
class RattleMutation(CandidateGenerator):
class RattleMutation(OffspringOperation):
"""Class to perform rattle mutations on structures.
Parameters:
......@@ -57,7 +57,7 @@ class RattleMutation(CandidateGenerator):
"""
def __init__(self, n_top, Nrattle=3, rattle_range=3,
description='RattleMutation', *args, **kwargs):
CandidateGenerator.__init__(self, *args, **kwargs)
OffspringOperation.__init__(self, *args, **kwargs)
self.description = description
self.n_top = n_top
self.probability = Nrattle/n_top
......@@ -108,7 +108,7 @@ class RattleMutation(CandidateGenerator):
# of trials, return initial structure.
return None
class RattleMutation2(CandidateGenerator):
class RattleMutation2(OffspringOperation):
"""Class to perform rattle mutations on structures.
Parameters:
......@@ -130,7 +130,7 @@ class RattleMutation2(CandidateGenerator):
"""
def __init__(self, n_top, Nrattle=3, description='RattleMutation',
*args, **kwargs):
CandidateGenerator.__init__(self, *args, **kwargs)
OffspringOperation.__init__(self, *args, **kwargs)
self.description = description
self.n_top = n_top
self.probability = Nrattle/n_top
......@@ -187,7 +187,7 @@ class RattleMutation2(CandidateGenerator):
return None
class PermutationMutation(CandidateGenerator):
class PermutationMutation(OffspringOperation):
"""Class to perform permutation mutations on structures.
Parameters:
......@@ -210,7 +210,7 @@ class PermutationMutation(CandidateGenerator):
def __init__(self, n_top, Npermute=3,
description='PermutationMutation', *args, **kwargs):
CandidateGenerator.__init__(self, *args, **kwargs)
OffspringOperation.__init__(self, *args, **kwargs)
self.description = description
self.n_top = n_top
self.probability = Npermute/n_top
......
......@@ -9,7 +9,7 @@ from utils import check_valid_bondlengths, get_min_distances_as_fraction_of_cova
import warnings
class CandidateGenerator(ABC):
class OffspringOperation(ABC):
"""Baseclass for mutation and crossover operations as well
as the startgenerator.
......@@ -118,8 +118,7 @@ class CandidateGenerator(ABC):
Atom {index_shortest_bond} has bond with d={d_shortest_bond}d_covalent"""
warnings.warn(text)
class OperationSelector():
class CandidateGenerator():
"""Class to produce new candidates by applying one of the
candidate generation operations which is supplied in the
'operations'-list. The operations are drawn randomly according
......@@ -238,7 +237,7 @@ class OperationConstraint():
return True
class StartGenerator(CandidateGenerator):
class StartGenerator(OffspringOperation):
""" Class used to generate random initial candidates.
The candidates are generated by iteratively adding in
one atom at a time within the box described.
......@@ -271,7 +270,7 @@ class StartGenerator(CandidateGenerator):
def __init__(self, slab, stoichiometry, box_to_place_in,
cluster=False, description='StartGenerator',
*args, **kwargs):
CandidateGenerator.__init__(self, *args, **kwargs)
OffspringOperation.__init__(self, *args, **kwargs)
self.slab = slab
self.stoichiometry = stoichiometry
self.box = box_to_place_in
......
================
=====
GOFEE
================
=====
The following is an introduction to the structure determination method
:class:`GOFEE`, which relies on Gaussian Process Regression model to
......@@ -10,20 +10,14 @@ When initializing the search, the two most important pieces of
information, is what system to optimize and what energy expression
(in the form of an ase.calculator) to optimize with respect to.
There two ways of supplying the information of what system to optimize.
1) The search can be initialized with one or more :class:`ase.Atoms`
objects, and the search will optimize all non-fixed atoms in the
system.
The calculator is supplied using the ``calc`` keyword.
2) By defining a :class:`StartGenerator`, that randomly initializes
the atoms, to be optimized, within a user-specified box. Using this
box, the atoms ate placed within the cell of a supplied
:class:`ase.Atoms` objects, which potentially contains fixed slab
atoms.
One way of supplying what system to optimize (although not the typically
recomended one) is to use the ``structures`` keyword to supply
:class:`GOFEE` with a number of pre-prepared ASE :class:`Atoms` objects.
Initializing the search with just a structure and a calculator looks
like this::
Initializing the search with just a structure and a calculator, and running
the search for 200 iterations using ``max_steps``, can then look like this::
# Creates: structures.traj
import numpy as np
......@@ -61,23 +55,108 @@ like this::
max_steps=200)
search.run()
Specifying the system - supplying Structures
============================================
The search must be run in parallel using MPI. To run the code in
parallel on grendel, use a .job script like the following::
#!/bin/bash
#SBATCH --job-name=C6H6_0
#SBATCH --partition=q12,q16,q16l,q20
#SBATCH --mem=30G
#SBATCH --nodes=1
#SBATCH --time=6:00:00
##SBATCH --exclusive
#SBATCH --ntasks-per-node=10
#SBATCH --cpus-per-task=1
#SBATCH --no-requeue
#SBATCH --array=0-2%3
echo "========= Job started at `date` =========="
# Go to the directory where this job was submitted
cd $SLURM_SUBMIT_DIR
source /home/mkb/.gpaw_py3
source /home/mkb/.GOFEE
# Setup directories to run the search in
base_path=$SLURM_SUBMIT_DIR/runs0/run$SLURM_ARRAY_TASK_ID
mkdir $base_path
# Copy files
cp run_search.py $base_path
cp slab.traj $base_path
# Go to execution directory and run code
cd $base_path
mpiexec --mca mpi_warn_on_fork 0 gpaw-python run_search.py > search.log
echo "========= Job finished at `date` =========="
Other possible keywords are: ``gpr``, ``startgenerator``,
``candidate_generator``, ``trajectory``, ``kappa``, ``Ninit``, ``dmax_cov``,
``Ncandidates``, ``population_size``, ``dualpoint``, ``min_certainty``,
``restart``, which will all be discussed below.
Specifying the system - structures or startgenerator
====================================================
There two ways of supplying the information of what system to optimize.
Specifying the system - supplying StartGenerator
================================================
1) As illustrated above, the search can be initialized with one or more
:class:`Atoms` objects, and the search will optimize all non-fixed
atoms in the system.
2) (The recomended approach) By defining a :class:`StartGenerator`,
that randomly initializes the atoms, to be optimized, within a
user-specified box. Using this box, the atoms are placed within
the cell of a supplied :class:`Atoms` objects, which potentially
contains a number of fixed 'template' atoms. As an example, a
:class:`StartGenerator` for a search for benzene can be mase as::
Basic settings
==============
import numpy as np
from candidate_operations.candidate_generation import StartGenerator
# make slab
slab = Atoms('',
cell=[20,20,20],
pbc=[0, 0, 0])
# Stoichiometry of atoms to be placed
stoichiometry = 6*[1]+6*[6]
# Box in which to place atoms
v = 3*np.eye(3)
p0 = np.array((8.5, 8.5, 8.5))
box = [p0, v]
# initialize startgenerator
sg = StartGenerator(slab, stoichiometry, box)
candidate_generator
===================
Used to customize what operations are used to generate new candidates
during the search. Continuing from the :class:`StartGenerator` example
above, a :class:`CandidateGenerator` which generates candidates using
the :class:`StartGenerator` with 20% probability, the
:class:`PermutationMutation` with 20% probability and
:class:`RattleMutation` with 60% probability, is done like this::
from candidate_operations.candidate_generation import CandidateGenerator
from candidate_operations.basic_mutations import RattleMutation, PermutationMutation
n_to_optimize = len(stoichiometry)
parmut = PermutationMutation(n_to_optimize, Npermute=3)
rattle = RattleMutation(n_to_optimize, Nrattle=3, rattle_range=3)
candidate_generator = CandidateGenerator([0.2, 0.2, 0.6],
[sg, permut, rattle])
Gaussian Process Regression (GPR) model
=======================================
CandidateGenerator
==================
.. module:: gofee
:synopsis: GOFEE
.. autoclass:: GOFEE
Gaussian Process Regression (GPR) model
=======================================
.. autoclass:: gofee.GOFEE
.. class:: GOFEE
\ No newline at end of file
......@@ -10,8 +10,8 @@ Welcome to GOFEE's documentation!
:maxdepth: 2
:caption: Contents:
#modules
test
modules
getting_started
Indices and tables
==================
......
gofee
.. _gofee:
=====
.. toctree::
:maxdepth: 4
gofee
surrogate
candidate_operations
\ No newline at end of file
......@@ -81,7 +81,8 @@
<p class="caption"><span class="caption-text">Contents:</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="test.html">GOFEE</a></li>
<li class="toctree-l1"><a class="reference internal" href="gofee.html">gofee package</a></li>
<li class="toctree-l1"><a class="reference internal" href="getting_started.html">GOFEE</a></li>
</ul>
......@@ -148,8 +149,21 @@
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
<a href="#G"><strong>G</strong></a>
</div>
<h2 id="G">G</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="getting_started.html#gofee.GOFEE">GOFEE (class in gofee)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="getting_started.html#module-gofee">gofee (module)</a>
</li>
</ul></td>
</tr></table>
</div>
......
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