Skip to content

Commit

Permalink
Merge pull request #21 from davidkastner/bug-fix
Browse files Browse the repository at this point in the history
Fixed a number of bugs
  • Loading branch information
davidkastner authored Feb 13, 2024
2 parents 06d24d4 + 15507b9 commit b23ddff
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 50 deletions.
45 changes: 32 additions & 13 deletions pyef/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import pandas as pd
import scipy.linalg as la
from collections import deque
from importlib import resources
from distutils.dir_util import copy_tree

class Electrostatics:
Expand Down Expand Up @@ -99,7 +100,7 @@ def fix_ECPmolden(self):

with open('final_optim.molden', 'w') as file:
file.write(content)
print(" > Molden file is fixed")
print(" > Molden file is fixed\n")
os.chdir(owd)

def prepData(self):
Expand Down Expand Up @@ -713,24 +714,29 @@ def getESPData(self, charge_types, ESPdata_filename):
[sorted_distances, sorted_esps, cum_esps] = Electrostatics.esp_bydistance(atom_idx, full_file_path)
ESP_fcoord = self.esp_first_coord(atom_idx, full_file_path, path_to_xyz)
ESP_scoord = self.esp_second_coord(atom_idx, full_file_path, path_to_xyz)

except Exception as e:
print('The Exception is: ' + str(e))
print(traceback.format_exc())
print('Error when trying to access electrostatic information: Attemtping to re-compute partial charges of type: ' + str(key))

# Re-run multiwfn computation of partial charge
proc = subprocess.Popen(command_A, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
calc_command = self.dict_of_calcs[key]
commands = ['7', calc_command, '1', 'y', '0', 'q'] # for atomic charge type corresponding to dict key

if key == 'CHELPG':
commands = ['7', calc_command, '1','\n', 'y', '0', 'q']
output = proc.communicate("\n".join(commands).encode())
new_name = 'final_optim_' +key+'.txt'
os.rename('final_optim.chg', new_name)

if self.inGaCageBool:
# With newly analyzed partial charges, re-compute ESP data
[ESP_all, ESP_just_ligand, ESP_just_cage, atom_type] = Electrostatics.ESP_all_calcs(full_file_path, atom_idx, self.inGaCageBool)
else:
[ESP_all, atom_type] = Electrostatics.ESP_all_calcs(full_file_path, atom_idx, self.inGaCageBool)

[total_charge,partial_charge_atom] = Electrostatics.charge_atom(full_file_path, atom_idx)
[sorted_distances, sorted_esps, cum_esps] = Electrostatics.esp_bydistance(atom_idx, full_file_path)
ESP_fcoord = self.esp_first_coord(atom_idx, full_file_path, path_to_xyz)
Expand Down Expand Up @@ -770,6 +776,7 @@ def getESPData(self, charge_types, ESPdata_filename):

# input_bond_indices is a list of a list of tuples
def getEFieldData(self, Efield_data_filename, input_bond_indices=[]):

metal_idxs = self.lst_of_tmcm_idx
folder_to_molden = self.folder_to_file_path
list_of_file = self.lst_of_folders
Expand All @@ -784,30 +791,39 @@ def getEFieldData(self, Efield_data_filename, input_bond_indices=[]):
bool_manual_mode = True

for f in list_of_file:
load_multiwfn = "module load multiwfn/noGUI"
atom_idx = metal_idxs[counter]
os.chdir(owd)
os.chdir(f + folder_to_molden)
subprocess.call("module load multiwfn/noGUI", shell=True)
# First For this to work, the .molden file should be named: f.molden
subprocess.call(load_multiwfn, shell=True)

command_A = '/opt/Multiwfn_3.7_bin_Linux_noGUI/Multiwfn '+ 'final_optim.molden'
results_dir = os.getcwd() + '/'
# First For this to work, the .molden file should be named: f.molden
results_dict = {}
results_dict['Name'] = f
Command_Polarization = '/opt/Multiwfn_3.7_bin_Linux_noGUI/Multiwfn '+ 'final_optim.molden >' + 'final_optim_polarization.txt'
multiwfn_path = "/opt/Multiwfn_3.7_bin_Linux_noGUI/Multiwfn"
molden_filename = "final_optim.molden"
final_structure_file = "final_optim.xyz"
polarization_file = "final_optim_polarization.txt"

# Dynamically get path to package settings.ini file
with resources.path('pyef.resources', 'settings.ini') as ini_path:
path_to_ini_file = str(ini_path)
Command_Polarization = f"{multiwfn_path} {molden_filename} -set {path_to_ini_file} > {polarization_file}"

# Check if the atomic polarizations have been computed
path_to_pol = os.getcwd() + '/' + 'final_optim_polarization.txt'
xyz_file_path = os.getcwd() + '/' 'final_optim.xyz'
print('Attempting this path to the polarization file')
path_to_pol = os.path.join(os.getcwd(), polarization_file)
xyz_file_path = os.path.join(os.getcwd(), final_structure_file)
print(f"Attempting polarization file path: {path_to_pol}")

if os.path.exists(path_to_pol):
print(' > Polarization file: ' + f + "!!")
print(f" > Polarization file: {f}!!")
else:
print('Starting to run polarization calculation!')
# Now Run the calculation for atomic dipole and quadrupole moment
print(f" > Submitting Multiwfn job using: {Command_Polarization}")
proc = subprocess.Popen(Command_Polarization, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
polarization_commands = ['15', '-1', '3', '2', '0', 'q'] # for atomic dipole and quadrupole moment of Hirshfeld-I type
output = proc.communicate("\n".join(polarization_commands).encode())
polarization_commands = ['15', '-1', '3', '2', '0', 'q'] # For atomic dipole and quadrupole moment of Hirshfeld-I type
proc.communicate("\n".join(polarization_commands).encode())

try:
# If bond_indices is longer then one, default to manually entry mode
Expand All @@ -817,20 +833,23 @@ def getEFieldData(self, Efield_data_filename, input_bond_indices=[]):
# Otherwise, automatically sense atoms bonded to metal and output E-fields of those
else:
[proj_Efields, bondedAs, bonded_idx, bond_lens] = self.E_proj_first_coord(atom_idx,xyz_file_path, path_to_pol)

results_dict['Max Eproj'] = max(abs(np.array(proj_Efields)))
results_dict['Projected_Efields V/Angstrom'] = proj_Efields
results_dict['Bonded Atoms'] = bondedAs
results_dict['Bonded Indices'] = bonded_idx
results_dict['Bond Lengths']= bond_lens

except Exception as e:
print(repr(e))

counter = counter + 1

# Probably want to add other bonds to this list!
allspeciesdict.append(results_dict)

os.chdir(owd)
df = pd.DataFrame(allspeciesdict)
df.to_csv(Efield_data_filename +'.csv')
df.to_csv(f"{Efield_data_filename}.csv")


52 changes: 24 additions & 28 deletions pyef/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Command-line interface (CLI) entry point"""

import os
import pyef
import click

Expand All @@ -22,9 +23,10 @@ def welcome():
print("Default programmed actions for the pyEF package.")
print("GitHub: https://github.com/davidkastner/pyef")
print("Documentation: https://pyef.readthedocs.io")
print("• Command for electric field analysis: pyef ef --run")
print("• Command for electrostatic analysis: pyef esp --run")
print("• Example annotated job file: github.com/davidkastner/pyEF/tree/main/demo/jobs.in\n")
print("• Command for electric field analysis: pyef ef -i path/to/jobs.in")
print("• Command for electrostatic analysis: pyef esp -i path/to/jobs.in")
print("• Example annotated job file: github.com/davidkastner/pyEF/tree/main/demo/jobs.in")
print("• Update the settings.ini file in pyef.resources, especially the `nthreads` variable\n")

# Welcome even if no flags
welcome()
Expand All @@ -35,41 +37,35 @@ def cli():
pass

@cli.command()
@click.option("--run", is_flag=True, help="Analyze electric fields.")
def ef(run):
@click.option("-i", "--input", "job_path", required=True, type=str, help="Path to pyEF job file.")
def ef(job_path):
"""Analyzes electric fields"""
click.echo("Calculate the projected electric field.")
if run:
click.echo("Importing dependencies...")
from pyef.run import main
from pyef.manage import parse_job_batch_file
click.echo("Importing dependencies...")
from pyef.run import main
from pyef.manage import parse_job_batch_file

geom_flag = False # Perform a geometry check
esp_flag = False # Perform analysis of electrostatics
geom_flag = False # Perform a geometry check
esp_flag = False # Perform analysis of electrostatics

job_path = input(" > Path to pyEF job file: ")
jobs, metal_indices, bond_indices = parse_job_batch_file(job_path)

pyef.run.main(jobs, metal_indices, bond_indices, geom_flag, esp_flag)
# Now using the job_path provided by the -i option
jobs, metal_indices, bond_indices = parse_job_batch_file(job_path)
job_name = os.path.splitext(job_path)[0]
pyef.run.main(job_name, jobs, metal_indices, bond_indices, geom_flag, esp_flag)

# THE ESP SECTION IS UNDERCONSTRUCTION AND MAY NOT WORK
@cli.command()
@click.option("--run", is_flag=True, help="Perform an action")
def esp(run):
@click.option("-i", "--input", "job_path", required=True, type=str, help="Path to pyEF job file.")
def esp(job_path):
"""Analyzes electrostatic potential"""
click.echo("Performing electrostatic analysis.")
if run:
click.echo("Importing dependencies...")
from pyef.run import main
from pyef.run import main
from pyef.manage import parse_job_batch_file

geom_flag = False # Perform a geometry check
esp_flag = True # Perform analysis of electrostatics
geom_flag = False # Perform a geometry check
esp_flag = True # Perform analysis of electrostatics

job_paths = input(" > Paths to jobs separated by commas: ")
jobs = [job.strip() for job in job_paths.split(",")]
metal_indices = input(" > Indices of your metals, separated by commas: ")
metal_indices = [int(metal.strip()) for metal in metal_indices.split(",")]
pyef.run.main(jobs, geom_flag, esp_flag, metal_indices)
jobs, metal_indices, bond_indices = parse_job_batch_file(job_path)
pyef.run.main(jobs, geom_flag, esp_flag, metal_indices)


if __name__ == '__main__':
Expand Down
7 changes: 4 additions & 3 deletions pyef/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,16 @@ def parse_job_batch_file(file_path):

with open(file_path, 'r') as file:
for line in file:
# Skip empty lines and lines starting with '#'
# Skip empty lines and comments '#'
if line.strip() == '' or line.strip().startswith('#'):
continue

columns = line.strip().split(',')
columns = [col.strip() for col in line.strip().split(',')]
# Extracting and appending data to respective lists
jobs.append(columns[0])
metal_index = int(columns[1])
bonded_atom_index = int(columns[2])
metal_indices.append(metal_index)
column_pairs.append((metal_index, columns[2]))
column_pairs.append([(metal_index, bonded_atom_index)])

return jobs, metal_indices, column_pairs
Empty file added pyef/resources/__init__.py
Empty file.
Loading

0 comments on commit b23ddff

Please sign in to comment.