forked from JuliaMolSim/ASE.jl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjulip.py
91 lines (73 loc) · 3.29 KB
/
julip.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""
ASE to JuLIP.jl interface
Requires `pyjulia` package from https://github.com/JuliaPy/pyjulia
"""
import numpy as np
from ase.calculators.calculator import Calculator
from ase.optimize.optimize import Optimizer
from julia import Julia
julia = Julia()
julia.using("JuLIP")
julia.using("ASE")
# Workaround limitiation in PyCall that does not allow types to be called
# https://github.com/JuliaPy/PyCall.jl/issues/319
ASEAtoms = julia.eval('ASEAtoms(a) = ASE.ASEAtoms(a)')
ASECalculator = julia.eval('ASECalculator(c) = ASE.ASECalculator(c)')
fixedcell = julia.eval('fixedcell(a) = JuLIP.Constraints.FixedCell(a)')
variablecell = julia.eval('variablecell(a) = JuLIP.Constraints.VariableCell(a)')
class JulipCalculator(Calculator):
"""
ASE-compatible Calculator that calls JuLIP.jl for forces and energy
"""
implemented_properties = ['forces', 'energy', 'stress']
default_parameters = {}
name = 'JulipCalculator'
def __init__(self, julip_calculator):
Calculator.__init__(self)
self.julip_calculator = julia.eval(julip_calculator)
def calculate(self, atoms, properties, system_changes):
Calculator.calculate(self, atoms, properties, system_changes)
julia_atoms = ASEAtoms(atoms)
self.results = {}
if 'energy' in properties:
self.results['energy'] = julia.energy(self.julip_calculator, julia_atoms)
if 'forces' in properties:
self.results['forces'] = np.array(julia.forces(self.julip_calculator, julia_atoms))
if 'stress' in properties:
self.results['stress'] = np.array(julia.stress(self.julip_calculator, julia_atoms))
class JulipOptimizer(Optimizer):
"""
Geometry optimize a structure using JuLIP.jl and Optim.jl
"""
def __init__(self, atoms, restart=None, logfile='-',
trajectory=None, master=None, variable_cell=False,
optimizer='JuLIP.Solve.ConjugateGradient'):
"""Parameters:
atoms: Atoms object
The Atoms object to relax.
restart, logfile ,trajector master : as for ase.optimize.optimize.Optimzer
variable_cell : bool
If true optimize the cell degresses of freedom as well as the
atomic positions. Default is False.
"""
Optimizer.__init__(self, atoms, restart, logfile, trajectory, master)
self.optimizer = julia.eval(optimizer)
self.variable_cell = variable_cell
def run(self, fmax=0.05):
"""
Run the optimizer to convergence
"""
julia_atoms = ASEAtoms(self.atoms)
# FIXME - if calculator is actually implemented in Julia, can skip this
julia_calc = ASECalculator(self.atoms.get_calculator())
julia.set_calculator_b(julia_atoms, julia_calc)
if self.atoms.constraints != []:
raise NotImplementedError("No support for ASE constraints yet!")
if self.variable_cell:
julia.set_constraint_b(julia_atoms, variablecell(julia_atoms))
else:
julia.set_constraint_b(julia_atoms, fixedcell(julia_atoms))
# FIXME - should send output to logfile, and also add callback hook
# to write frames to trajectory (Callbacks not yet supported by Optim.jl).
results = julia.minimise_b(julia_atoms, gtol=fmax, verbose=2)
return results