Calculators¶
stk
makes extensive use of calculator objects. Calculators are a
very general pattern for performing operations on molecules. All
calculators are objects with a special method, used to perform the
calculation. For example, an optimizer has a special method called
optimize()
, which optimizes a molecule.
Other types of calculators will have different special methods used to
perform calculations on molecules. For example, an
EnergyCalculator
will define a
get_energy()
method. This method calculates
the energy of a molecule.
All evolutionary algorithm operations are also implemented through
calculators, take for example a Mutator
such as
RandomTopologyGraph
import stk
topology_graphs = [
stk.cage.EightPlusTwelve(),
stk.cage.FourPlusSix(),
stk.cage.TwentyPlusThirty()
]
random_top = RandomTopologyGraph(topology_graphs)
cage = stk.ConstructedMolecule([bb1, bb2], stk.cage.SixPlusNine())
# Perform a mutation.
mutant = random_top.mutate(cage)
A Mutator
is a calculator which implements a
mutate()
method. This method takes a molecule and
returns a mutant, i.e. a modified version, of that molecule.
In this example, the random_top
is a Mutator
, which
replaces the topology of a molecule with a new one, in order to
generate the mutant. In this case, the new topologies would be one of
EightPlusTwelve
, FourPlusSix
or
TwentyPlusThirty
.
Calculators often support additional options to modify their behaviour.
For example, calculators of type Optimizer
or
EnergyCalculator
support caching. This means that if the
same molecule and conformer is supplied to the calculator, it will not
run the optimization or energy calculation again, it will return the
previously calculated value.
Combining Calculators¶
Calculators can be combined to create complex behaviour on the fly.
For example, we may wish to make a 2 step optimization process. First,
we perform an optimization using the MMFF force field and the run
a MacroModel molecular dynamics conformer search with
MacroModelMD
. The obvious way to do
this is to run the two optimizers in sequence
mmff = stk.MMFF()
macromodel = stk.MacroModelMD('/opt/schrodinger2017-4')
mmff.optimize(mol)
macromodel.optimize(mol)
However, there is a better way! We can use an optimizer called
OptimizerSequence
. The optimize()
method of this optimizer calls the optimize()
methods
of the optimizers it was initialized with
opt_sequence = OptimizerSequence(mmff, macromodel)
# Optimize with mmff and then with macromodel.
opt_sequence.optimize(mol)
This pattern is quite common and powerful. For example, we can take
three different Mutator
objects. Each of these defines
a different mutate()
method. We want to apply one of
these mutations at random. We can simply use RandomMutation
random_bb = stk.RandomBuildingBlock(...)
similar_bb = stk.SimilarBulidingBlock(...)
random_topology = stk.RandomTopology(...)
random_mutation = stk.RandomMutation(
random_bb,
similar_bb,
random_topology
)
# Use one of the mutate() methods of random_bb, similar_bb and
# random_topology at random.
mutant1 = random_mutation.mutate(mol)
# The next call use a different mutation to the call above.
mutant2 = random_mutation.mutate(mol)
The RandomMutation.mutate()
method randomly selects a
Mutator
it was initialized with to carry out the mutation
on its behalf.
Making New Calculators¶
New calculators can be added very simply and they can be defined in user code.
A simple example of adding a new calculator in user code.
import stk
class NewEnergyCalculator(stk.EnergyCalculator):
def get_energy(self, mol):
return 15
energy_calc = NewEnergyCalculator()
# Calculate the energy with the new calculator.
energy_calc.get_energy(mol)
Note that you can also modify the behaviour of existing calculators in your code, see the Cookbook.
Saving Calculator Results¶
If you want to save the results which your calculator found you should
set use_cache=True
and then you can simply use pickle
to dump and load the calculator object.