Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Psi4 Integration #153

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DockerMakefiles/PythonTools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ notebook:
&& pip install backports.ssl_match_hostname backports.shutil_get_terminal_size


psi4:
build: conda install -c psi4 psi4

pyquante2:
requires:
- python_deploy_base
Expand All @@ -34,6 +37,7 @@ chem_python_requirements:
requires:
- pyscf
- chem_python_conda
- psi4

chem_python:
requires:
Expand Down
5 changes: 5 additions & 0 deletions DockerMakefiles/SymMol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ symmol_build:
symmol:
requires:
- deploybase
build: |
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
gfortran libgfortran3\
&& cleanapt
copy_from:
symmol_build:
/usr/local/bin/symmol: /usr/local/bin
3 changes: 3 additions & 0 deletions moldesign/_tests/test_symmetrizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import moldesign as mdt
import pytest

8 changes: 7 additions & 1 deletion moldesign/geom/symmetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ def __init__(self, symbol, matrix, **kwargs):
for kw, val in kwargs.items():
setattr(self, kw, val)

def __str__(self):
return 'SymmetryElement %s' % self.symbol

def __repr__(self):
return '<%s>' % self

def get_axis(self):
"""
Returns normal of the plane for Cs or axis of rotation for a Cn
Expand Down Expand Up @@ -77,7 +83,7 @@ def __init__(self, mol, symbol, rms,
self.mol = mol
self.symbol = symbol
self.rms = rms
self.orientation = mdt.utils.if_not_none(orientation, mol.atoms.position)
self.orientation = mdt.utils.if_not_none(orientation, mol.positions)
self.elems = mdt.utils.if_not_none(elems, [])
for kw, val in kwargs.items():
setattr(self, kw, val)
Expand Down
4 changes: 3 additions & 1 deletion moldesign/helpers/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
imp.find_module('nbmolviz')
except ImportError:
nbmolviz_installed = False
disp_log = print
else:
nbmolviz_installed = True
from IPython.display import display as disp_log


def _get_nbmethod(name):
Expand Down Expand Up @@ -90,4 +92,4 @@ def display_log(obj, title=None, show=False):
:param title: A name for the object (otherwise, str(obj) is used)
:return:
"""
print(obj)
disp_log(obj)
60 changes: 60 additions & 0 deletions moldesign/interfaces/psi4_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from __future__ import print_function, absolute_import, division
from future.builtins import *
from future import standard_library
standard_library.install_aliases()

# Copyright 2017 Autodesk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import imp

from moldesign import units as u
from ..utils import exports


try:
imp.find_module('psi4')
except (ImportError, OSError) as exc:
print('Psi4 not installed; using remote docker container')
force_remote = True
else:
force_remote = False


@exports
def mdt_to_psi4(mol):
import psi4
lines = []
for atom in mol.atoms:
x, y, z = atom.position.value_in(u.angstrom)
lines.append('%s %f %f %f' % (atom.symbol, x, y, z))

geom = psi4.geometry('\n'.join(lines))
return geom

@exports
def psi4_to_mdt(geom):
"""

Args:
geom (psi4.core.Molecule):

Returns:

"""
geom.update_coords()
coords = geom.geometry().to_array() * u.bohr




25 changes: 11 additions & 14 deletions moldesign/interfaces/symmol_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@

import fortranformat
import numpy as np
import pyccc

import moldesign as mdt
from .. import units as u
from .. import utils

line_writer = fortranformat.FortranRecordWriter('(a6,i2,6f9.5)')

IMAGE = 'symmol'

#@doi('10.1107/S0021889898002180')
def run_symmol(mol,
tolerance=0.1 * u.angstrom,
image='symmol',
engine=None):
def run_symmol(mol, tolerance=0.1 * u.angstrom):
infile = ['1.0 1.0 1.0 90.0 90.0 90.0', # line 1: indicates XYZ coordinates
# line 2: numbers indicate: mass weighted moment of inertia,
# tolerance interpretation, tolerance value,
Expand All @@ -47,15 +47,11 @@ def run_symmol(mol,
command = 'symmol < sym.in'
inputs = {'sym.in': '\n'.join(infile)}

# TODO: this boilerplate has to go
engine = utils.if_not_none(engine, mdt.compute.get_engine())
imagename = mdt.compute.get_image_path(image)
job = engine.launch(imagename,
command,
inputs=inputs,
name="symmol, %s" % mol.name)
mdt.display.display_log(job.get_display_object(), "symmol, %s"%mol.name)
job.wait()
job = pyccc.Job(image=mdt.compute.get_image_path(IMAGE),
command=command,
inputs=inputs,
name="symmol, %s" % mol.name)
job = mdt.compute.run_job(job)

data = parse_output(job.get_output('symmol.out'))
symm = mdt.geom.MolecularSymmetry(
Expand Down Expand Up @@ -176,9 +172,10 @@ def _string_to_matrix(string):
mat.append(row)
return np.array(mat)


def get_aligned_coords(mol, data):
com = mol.com
centerpos = mol.atoms.position - com
centerpos = mol.positions - com
orthcoords = (centerpos.T.ldot(data.orthmat)).T
return orthcoords

Expand Down
55 changes: 55 additions & 0 deletions moldesign/models/psi4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from __future__ import print_function, absolute_import, division
from future.builtins import *
from future import standard_library

standard_library.install_aliases()

# Copyright 2017 Autodesk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from .. import units as u
from ..compute import runsremotely
from ..interfaces import psi4_interface
from .base import QMBase

THEORIES = {'rhf':'scf'}

BASES = {}


class Psi4Potential(QMBase):
def prep(self):
return True

@runsremotely(enable=psi4_interface.force_remote)
def calculate(self, requests):
import psi4
geom = psi4_interface.mdt_to_psi4(self.mol)
psi4.set_options({'basis': BASES.get(self.params.basis, self.params.basis)})
props = {'potential_energy':
self._get_runmethod(requests)(self._gettheorystring()) * u.hartree}
psi4.core.clean()
psi4.core.clean_options()
return props

@staticmethod
def _get_runmethod(requests):
import psi4
# TODO: actually deal with requests
return psi4.energy

def _gettheorystring(self):
return THEORIES.get(self.params.theory, self.params.theory)


17 changes: 16 additions & 1 deletion moldesign/molecules/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,23 @@ def copy(self, mol):
props['mol'] = mol
return self.__class__(**props)

def __getattr__(self, item):
val = super().__getattr__(item)
return self._tryconvert(val)

def __getitem__(self, item):
val = super().__getitem__(item)
return self._tryconvert(val)

@staticmethod
def _tryconvert(val):
if hasattr(val, 'defunits'):
return val.defunits()
else:
return val

def geometry_matches(self, mol):
"""Returns:
bool: True if the molecule's ``position`` is the same as these properties' ``position``
"""
return np.array_equal(self.positions, mol.positions)
return np.array_equal(self.positions, mol.positions)
2 changes: 1 addition & 1 deletion moldesign/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def named_dict(l):
])


QMTHEORIES = ['rhf', 'rks', 'mp2', 'casscf', 'casci', 'fci']
QMTHEORIES = ['rhf', 'rks', 'uhf', 'uks', 'mp2', 'casscf', 'casci', 'fci']
BASISSETS = ['3-21g', '4-31g', '6-31g', '6-31g*', '6-31g**',
'6-311g', '6-311g*', '6-311g+', '6-311g*+',
'sto-3g', 'sto-6g', 'minao', 'weigend',
Expand Down
60 changes: 34 additions & 26 deletions moldesign/utils/callsigs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import inspect
import os
from functools import wraps

import collections
import warnings

import funcsigs


from .utils import if_not_none
from .docparsers import GoogleDocArgumentInjector

Expand Down Expand Up @@ -105,38 +107,44 @@ def args_from(original_function,

def decorator(f):
"""Modify f's call signature (using the `__signature__` attribute)"""
if wraps:
fname = original_function.__name__
f = functools.wraps(original_function)(f)
f.__name__ = fname # revert name change
else:
fname = f.__name__
f.__signature__ = sig

if update_docstring_args or inject_kwargs:
if not update_docstring_args:
argument_docstrings = GoogleDocArgumentInjector(f.__doc__).args
docs = GoogleDocArgumentInjector(f.__doc__)
docs.args = argument_docstrings

if not hasattr(f, '__orig_docs'):
f.__orig_docs = []
f.__orig_docs.append(f.__doc__)
try:
if wraps:
fname = original_function.__name__
f = functools.wraps(original_function)(f)
f.__name__ = fname # revert name change
else:
fname = f.__name__
f.__signature__ = sig

if update_docstring_args or inject_kwargs:
if not update_docstring_args:
argument_docstrings = GoogleDocArgumentInjector(f.__doc__).args
docs = GoogleDocArgumentInjector(f.__doc__)
docs.args = argument_docstrings

if not hasattr(f, '__orig_docs'):
f.__orig_docs = []
f.__orig_docs.append(f.__doc__)

f.__doc__ = docs.new_docstring()

# Only for building sphinx documentation:
if os.environ.get('SPHINX_IS_BUILDING_DOCS', ""):
sigstring = '%s%s\n' % (fname, sig)
if hasattr(f, '__doc__') and f.__doc__ is not None:
f.__doc__ = sigstring + f.__doc__
else:
f.__doc__ = sigstring

f.__doc__ = docs.new_docstring()
except Exception as exc:
warnings.warn('Failed to create call signature object for %s: %s' % (f, exc))

# Only for building sphinx documentation:
if os.environ.get('SPHINX_IS_BUILDING_DOCS', ""):
sigstring = '%s%s\n' % (fname, sig)
if hasattr(f, '__doc__') and f.__doc__ is not None:
f.__doc__ = sigstring + f.__doc__
else:
f.__doc__ = sigstring
return f

return decorator



def kwargs_from(reference_function, mod_docs=True):
""" Replaces ``**kwargs`` in a call signature with keyword arguments from another function.

Expand Down