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 Python charge scripts. First example xtb GFN2 charges #998

Merged
merged 2 commits into from
Jul 5, 2022
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
2 changes: 1 addition & 1 deletion avogadro/calc/chargemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Array<double> ChargeModel::potentials(Core::Molecule& mol,
return potentials;
}

void ChargeModel::appendError(const std::string& errorString, bool newLine)
void ChargeModel::appendError(const std::string& errorString, bool newLine) const
{
m_error += errorString;
if (newLine)
Expand Down
4 changes: 2 additions & 2 deletions avogadro/calc/chargemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ class AVOGADROCALC_EXPORT ChargeModel
* @param errorString The error to be added.
* @param newLine Add a new line after the error string?
*/
void appendError(const std::string& errorString, bool newLine = true);
void appendError(const std::string& errorString, bool newLine = true) const;

private:
std::string m_error;
mutable std::string m_error;

float m_dielectric;
};
Expand Down
17 changes: 11 additions & 6 deletions avogadro/qtplugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ add_subdirectory(applycolors)
add_subdirectory(bondcentrictool)
add_subdirectory(bonding)
add_subdirectory(cartoons)
add_subdirectory(commandscripts)
add_subdirectory(coordinateeditor)
add_subdirectory(copypaste)
add_subdirectory(crystal)
Expand Down Expand Up @@ -147,8 +146,12 @@ endif()
add_subdirectory(apbs)
add_subdirectory(cp2kinput)
add_subdirectory(gamessinput)

# script plugins (input generators, etc.)
add_subdirectory(commandscripts)
add_subdirectory(quantuminput)
add_subdirectory(scriptfileformats)
add_subdirectory(scriptcharges)

if(USE_LIBARCHIVE)
add_subdirectory(plugindownloader)
Expand All @@ -160,18 +163,20 @@ endif()

# The scene plugins
add_subdirectory(ballandstick)
add_subdirectory(licorice)
add_subdirectory(vanderwaals)
add_subdirectory(wireframe)
add_subdirectory(closecontacts)
add_subdirectory(force)
add_subdirectory(licorice)
add_subdirectory(meshes)
add_subdirectory(closecontacts)
add_subdirectory(noncovalent)
add_subdirectory(vanderwaals)
add_subdirectory(vanderwaalsao)
add_subdirectory(wireframe)
if (USE_OPENGL)
# needs some raw OpenGL code
add_subdirectory(overlayaxes)
endif()
add_subdirectory(vanderwaalsao)

# other optional plugins
if (USE_PROTOCALL)
add_subdirectory(clientserver)
endif()
Expand Down
23 changes: 23 additions & 0 deletions avogadro/qtplugins/scriptcharges/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
set(scriptcharges_srcs
scriptchargemodel.cpp
scriptcharges.cpp
)

avogadro_plugin(ScriptCharges
"Scriptable electrostatics models"
ExtensionPlugin
scriptcharges.h
ScriptCharges
"${scriptcharges_srcs}"
""
)

target_link_libraries(ScriptCharges PRIVATE AvogadroCalc )

# Bundled format scripts:
set(charge_scripts
chargeScripts/xtb.py
)

install(PROGRAMS ${charge_scripts}
DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/scripts/charges/")
100 changes: 100 additions & 0 deletions avogadro/qtplugins/scriptcharges/chargeScripts/xtb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# This source file is part of the Avogadro project.
# This source code is released under the 3-Clause BSD License, (see "LICENSE").

import argparse
import json
import sys
import os
from shutil import which
import tempfile
import subprocess
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blacklist: Consider possible security implications associated with the subprocess module.

Reply with "@sonatype-lift help" for info about LiftBot commands.
Reply with "@sonatype-lift ignore" to tell LiftBot to leave out the above finding from this PR.
Reply with "@sonatype-lift ignoreall" to tell LiftBot to leave out all the findings from this PR and from the status bar in Github.

When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands.


Was this a good recommendation?
[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]

Check notice

Code scanning

Consider possible security implications associated with subprocess module.

Consider possible security implications associated with subprocess module.


def getMetaData():
# before we return metadata, make sure xtb is in the path
if which("xtb") is None:
return {} # Avogadro will ignore us now

metaData = {}
metaData["inputFormat"] = "xyz" # could be other formats, but this is fine
metaData["identifier"] = "GFN2"
metaData["name"] = "GFN2"
metaData["description"] = "Calculate atomic partial charges using GFN2 and xtb"
metaData["charges"] = True
metaData["potential"] = False
metaData["elements"] = "1-86" # up to Radon
return metaData


def charges():
# Avogadro will send us the sdf file as stdin
# we need to write it to a temporary file

# get the whole sdf file
xyz = sys.stdin.read()

fd, name = tempfile.mkstemp(".xyz")
os.write(fd, xyz.encode())
os.close(fd)

# run xtb
xtb = which("xtb")
if xtb is None: # we check again
return ""

# for now, ignore the output itself
tempdir = tempfile.mkdtemp()
output = subprocess.run(

Check notice

Code scanning

subprocess call - check for execution of untrusted input.

subprocess call - check for execution of untrusted input.

Check notice

Code scanning

Unused variable 'output' (unused-variable)

Unused variable 'output' (unused-variable)
[xtb, name], stdout=subprocess.PIPE, cwd=tempdir, check=True
)
# instead we read the "charges" file
result = ""
with open(tempdir + "/" + "charges", "r", encoding="utf-8") as f:
result = f.read()

# try to cleanup the temporary files
os.remove(name)
for filename in os.listdir(tempdir):
try:
os.remove(tempdir + "/" + filename)
except:

Check notice

Code scanning

Try, Except, Continue detected.

Try, Except, Continue detected.

Check notice

Code scanning

No exception type(s) specified (bare-except)

No exception type(s) specified (bare-except)
continue
# and try to cleanup the directory
try:
os.rmdir(tempdir)
except:

Check notice

Code scanning

Try, Except, Pass detected.

Try, Except, Pass detected.

Check notice

Code scanning

No exception type(s) specified (bare-except)

No exception type(s) specified (bare-except)
pass

# write the charges to stdout
return result


def potential():
# at the moment, xtb doesn't have a good way to do this
# and the method shouldn't be called anyway

# if your plugin has a potential, you can return it here
# .. you'll get JSON with the file and the set of points
# e.g. { "xyz" : "xyz file contents", "points" : [ x,y,z, x,y,z, ... ] }
# or { "sdf" : "sdf file contents", "points" : [ x,y,z, x,y,z, ... ] }
# .. and you print the list of potentials to stdout
return ""


if __name__ == "__main__":
parser = argparse.ArgumentParser("GFN2 partial charges")
parser.add_argument("--display-name", action="store_true")
parser.add_argument("--metadata", action="store_true")
parser.add_argument("--charges", action="store_true")
parser.add_argument("--potential", action="store_true")
parser.add_argument("--lang", nargs="?", default="en")
args = vars(parser.parse_args())

if args["metadata"]:
print(json.dumps(getMetaData()))
elif args["display_name"]:
print(getMetaData()["name"])
elif args["charges"]:
print(charges())
elif args["potential"]:
print(potential())
Loading