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

Add some additional classes including cjson and connect #1427

Merged
merged 2 commits into from
Dec 29, 2023
Merged
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: 3 additions & 1 deletion python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions python/avogadro/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from . import cjson

Check notice on line 1 in python/avogadro/__init__.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

python/avogadro/__init__.py#L1

'.cjson' imported but unused (F401)
from . import connect

Check notice on line 2 in python/avogadro/__init__.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

python/avogadro/__init__.py#L2

'.connect' imported but unused (F401)
from . import core
from . import io
70 changes: 45 additions & 25 deletions python/avogadro/cjson.py
Original file line number Diff line number Diff line change
@@ -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
[
Expand All @@ -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)
return self.__to_cjson(data)
99 changes: 99 additions & 0 deletions python/avogadro/connect.py
Original file line number Diff line number Diff line change
@@ -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

Check notice on line 12 in python/avogadro/connect.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

python/avogadro/connect.py#L12

'sys' imported but unused (F401)

Check warning on line 12 in python/avogadro/connect.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

python/avogadro/connect.py#L12

Unused 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={}):

Check warning on line 45 in python/avogadro/connect.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

python/avogadro/connect.py#L45

Dangerous default value {} as argument
"""
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()
3 changes: 3 additions & 0 deletions python/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <avogadro/quantumio/mopacaux.h>
#include <avogadro/quantumio/nwchemjson.h>
#include <avogadro/quantumio/nwchemlog.h>
#include <avogadro/quantumio/orca.h>

namespace py = pybind11;

Expand Down Expand Up @@ -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_<ffm>(m, "FileFormatManager")
Expand Down
Loading