diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 1d4f466537..2cb3d0e17b 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -59,7 +59,9 @@ if (NOT WIN32) INSTALL_RPATH ${_rpath_value}) endif() -install(FILES avogadro/__init__.py COMPONENT python DESTINATION "${_python_module_install_dir}") +# Install the python files. +FILE(GLOB PY_SRCS "avogadro/*.py") +install(FILES ${PY_SRCS} COMPONENT python DESTINATION "${_python_module_install_dir}") # Set the output directory so the python modules can be used from the build # tree. diff --git a/python/avogadro/__init__.py b/python/avogadro/__init__.py index dbae4884e1..3f7f59e25d 100644 --- a/python/avogadro/__init__.py +++ b/python/avogadro/__init__.py @@ -1,2 +1,4 @@ +from . import cjson +from . import connect from . import core from . import io diff --git a/python/avogadro/cjson.py b/python/avogadro/cjson.py index 7d07468d63..096eff734a 100644 --- a/python/avogadro/cjson.py +++ b/python/avogadro/cjson.py @@ -1,29 +1,41 @@ """ /****************************************************************************** This source file is part of the Avogadro project. - This source code is released under the New BSD License, (the "License"). + This source code is released under the 3-clause BSD License, (the "License"). ******************************************************************************/ """ import json -class Cjson: + + +class cjson: """ - This Class is intended to read Cjson files - with python libraries and perform certain - methods on files and convert them back to Cjson - files as required + This class is intended to read and write cjson (chemical json) files + and help manipulate them (e.g., change coordinates, elements, etc.) + + The cjson format is a JSON format for chemical information. It is + intended to be a common exchange and storage format for chemical + information that is both human and machine readable. It is intended + to be easily extended to support new features and data types. + + More information and the schema can be found at: + https://github.com/OpenChemistry/chemicaljson """ + def __init__(self): pass + def __from_cjson(self, filePath): - '''Use to read CJson formats by converting them to python dictionaries''' - with open(filePath, 'r') as cjsonFile: + """Use to read cjson formats by converting them to python dictionaries""" + with open(filePath, "r") as cjsonFile: py_dict_data = json.load(cjsonFile) return py_dict_data + def __to_cjson(self, cjson_dict_file): - '''It converts python dictionaries to CJson format''' + """It converts python dictionaries to cjson format""" cjsonData = json.dumps(cjson_dict_file, indent=4) - return (cjsonData) - def get_atoms_coords(self,filePath): + return cjsonData + + def get_atoms_coords(self, filePath): """ It helps to get the co-ordinates of individual elements/atoms in the format [ @@ -36,44 +48,52 @@ def get_atoms_coords(self,filePath): data = self.__from_cjson(filePath) coords = data["atoms"]["coords"]["3d"] elements = data["atoms"]["elements"]["number"] - element_coords = [(*coords[i*3:(i+1)*3], elements[i]) for i in range(0, int(len(coords) / 3))] - cjson_dict = {"element-coordinates" :element_coords} + element_coords = [ + (*coords[i * 3 : (i + 1) * 3], elements[i]) + for i in range(0, int(len(coords) / 3)) + ] + cjson_dict = {"element-coordinates": element_coords} return self.__to_cjson(cjson_dict) + def get_elements(self, filePath): - ''' + """ returns all the elements present in cjson file - ''' + """ data = self.__from_cjson(filePath) elements = data["atoms"]["elements"]["number"] return elements - def get_coordinates(self,filePath): - ''' + + def get_coordinates(self, filePath): + """ returns the coordinate array - ''' + """ data = self.__from_cjson(filePath) coords = data["atoms"]["coords"]["3d"] return coords + def set_atoms_coordinates(self, filePath, coords_array): - ''' + """ it updates the coordinates array in cjson file - ''' + """ data = self.__from_cjson(filePath) data["atoms"]["coords"]["3d"] = coords_array return self.__to_cjson(data) + def set_elements(self, filePath, elements_array): - ''' + """ It sets all the elements present in the cjson file where elements are set/recognized by their atomic numbers - ''' + """ data = self.__from_cjson(filePath) data["atoms"]["elements"]["number"] = elements_array return self.__to_cjson(data) + def set_coordinates(self, filePath, coords_array): - ''' + """ It helps to set all coordinates of the cjson file where coordinates of all elements can be changed by an input array of coords_array - ''' + """ data = self.__from_cjson(filePath) data["atoms"]["coords"]["3d"] = coords_array - return self.__to_cjson(data) \ No newline at end of file + return self.__to_cjson(data) diff --git a/python/avogadro/connect.py b/python/avogadro/connect.py new file mode 100755 index 0000000000..3e441a0681 --- /dev/null +++ b/python/avogadro/connect.py @@ -0,0 +1,99 @@ +""" +/****************************************************************************** + This source file is part of the Avogadro project. + This source code is released under the 3-clause BSD License, (the "License"). +******************************************************************************/ +""" + +import json +import os +import socket +import struct +import sys +import tempfile + + +class connect: + """ + Send JSON-RPC requests to Avogadro through a named pipe. + + This class is intended to be used by external scripts that are + run on the same machine as Avogadro. + + The named pipe is created by Avogadro and is named "avogadro". + If it does not exist, Avogadro is not running. + """ + + def __init__(self, name="avogadro"): + """ + Connect to the local named pipe + + :param name: The name of the named pipe. + """ + # create socket and connect + try: + if os.name == "nt": + self.sock = open("//./pipe/" + name, "w+b", 0) + else: + self.sock.connect(tempfile.gettempdir() + "/" + name) + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + + except Exception as exception: + print("error while connecting: " + str(exception)) + print("Is Avogadro running?") + + def __json(self, method, params={}): + """ + Send a JSON-RPC request to the named pipe. + :param method: The JSON-RPC request method. + Send a message to the named pipe + :param file: file corresponding to method. + + """ + if method == "receive_message": + size = 1024 + if os.name == "nt": + packet = self.sock.read(size) + else: + packet = self.sock.recv(size) + + try: + return json.loads(str(packet[4:])) + except Exception as exception: + print("error: " + str(exception)) + return {} + else: + msg = { + "jsonrpc": "2.0", + "id": 0, + "method": method, + "params": params, + } + json_msg = json.dumps(msg) + size = len(json_msg) + header = struct.pack(">I", size) + packet = header + json_msg.encode("ascii") + if os.name == "nt": + self.sock.write(packet) + self.sock.seek(0) + else: + self.sock.send(packet) + + def open_file(self, file): + """Opens file""" + # param: file is filename input by the user in string + method = "openFile" + params = {"filename": file} + self.__json(method, params) + self.__json("receive_message") + + def save_graphic(self, file): + """Save Graphic""" + method = "saveGraphic" + params = {"filename": file} + self.__json(method, params) + self.__json("receive_message") + + def close(self): + """Close the socket to the named pipe""" + self.sock.close() diff --git a/python/io.cpp b/python/io.cpp index 50b94a922c..700f659d82 100644 --- a/python/io.cpp +++ b/python/io.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace py = pybind11; @@ -68,12 +69,14 @@ PYBIND11_MODULE(io, m) /// Add the quantum IO formats, we should probably move them over soon, but /// get things working for now... + Io::FileFormatManager::registerFormat(new GAMESSUSOutput); Io::FileFormatManager::registerFormat(new GaussianFchk); Io::FileFormatManager::registerFormat(new GaussianCube); Io::FileFormatManager::registerFormat(new MoldenFile); Io::FileFormatManager::registerFormat(new MopacAux); Io::FileFormatManager::registerFormat(new NWChemJson); Io::FileFormatManager::registerFormat(new NWChemLog); + Io::FileFormatManager::registerFormat(new ORCAOutput); /// This class uses a singleton pattern, make it accessible through Python. py::class_(m, "FileFormatManager")