Skip to content
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
37 changes: 21 additions & 16 deletions qp/analyze/checkup.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,27 @@ def check_submit_record(submit_record_path, delete_queued):
with open(submit_record_path, 'r') as f:
content = f.read()

author = extract_author(content)
queue_time = "Queue Time:" in content
run_start_time = "Run Start Time:" in content
run_end_time = "Run End Time:" in content

# if queue_time and not run_start_time:
if run_start_time and not run_end_time:
if delete_queued:
print(f"Deleting queued job record: {submit_record_path}")
os.remove(submit_record_path)
return "queue", author
elif run_start_time and not run_end_time:
return "running", author
elif run_end_time:
return "done", author

author = extract_author(content)
queue_time = "Queue Time:" in content
run_start_time = "Run Start Time:" in content
run_end_time = "Run End Time:" in content

# Queued but never started
if queue_time and not run_start_time:
if delete_queued:
print(f"Deleting queued job record: {submit_record_path}")
os.remove(submit_record_path)
return "queue", author

# Started but not finished
if run_start_time and not run_end_time:
return "running", author

# Finished
if run_end_time:
return "done", author

# Fallback (record present but missing expected markers)
return "backlog", author


Expand Down
54 changes: 53 additions & 1 deletion qp/analyze/molden.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,56 @@
import numpy as np
import os

VALENCE_ELECTRONS = {
'CA': 2, 'CD': 12, 'CO': 9, 'CU': 11, 'FE': 8, 'K': 1,
'MN': 7, 'NI': 10, 'PB': 14, 'V': 5, 'ZN': 12
}

def correct_ecp_charges_inplace(molden_file):
"""
Corrects the nuclear charge in a Molden file for elements with ECPs,
overwriting the original file.
"""
try:
with open(molden_file, 'r') as f:
lines = f.readlines()
except FileNotFoundError:
print(f"> ERROR: Molden file not found at {molden_file}")
return

modified_lines = []
in_atoms_section = False
was_modified = False
for line in lines:
if line.strip() == "[Atoms] Angs":
in_atoms_section = True
modified_lines.append(line)
continue
elif in_atoms_section and line.strip().startswith("["):
in_atoms_section = False

if in_atoms_section:
parts = line.strip().split()
if len(parts) >= 6:
element_symbol = parts[0].upper()
# Check if the element needs correction and if it hasn't been corrected already
if element_symbol in VALENCE_ELECTRONS and parts[2] != str(VALENCE_ELECTRONS[element_symbol]):
parts[2] = str(VALENCE_ELECTRONS[element_symbol])
line_to_write = ' '.join(parts) + '\n'
modified_lines.append(line_to_write)
was_modified = True
else:
modified_lines.append(line)
else:
modified_lines.append(line)
else:
modified_lines.append(line)

if was_modified:
with open(molden_file, 'w') as f:
f.writelines(modified_lines)
print(f"> Overwrote {os.path.basename(molden_file)} with ECP-corrected nuclear charges.")


def translate_to_origin(coordinates, center_of_mass):
"""Translates the molecule so the center of mass is at the origin."""
Expand Down Expand Up @@ -92,4 +144,4 @@ def center_molden(molden_file, com):
process_molden(molden_file, output_molden, com)
print(f"> Centered coordinates written to: {output_molden}")

return output_molden
return output_molden
57 changes: 26 additions & 31 deletions qp/analyze/multiwfn.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,38 +54,34 @@ def iterate_qm_output(pdb_all, method, base_output_dir, multiwfn_path, settings_
# Copy the atmrad dir and settings.ini into the current working directory
atmrad_dest_path = os.path.join(scr_dir_path, 'atmrad/')
if not os.path.exists(atmrad_dest_path):
shutil.copytree(atmrad_path, atmrad_dest_path, dirs_exist_ok=True)
shutil.copytree(atmrad_path, atmrad_dest_path)

settings_ini_dest_path = os.path.join(scr_dir_path, 'settings.ini')
shutil.copy(settings_ini_path, settings_ini_dest_path)
# MODIFICATION: Add a check to avoid re-copying settings.ini if it already exists
if not os.path.exists(settings_ini_dest_path):
shutil.copy(settings_ini_path, settings_ini_dest_path)

cpu_count = get_cpu_count(settings_ini_dest_path)
print(f"> Using {cpu_count} threads for Multiwfn")
print(f"> Using {cpu_count} threads for Multiwfn", flush=True)

if os.path.exists(scr_dir_path):
# Move into the QM scr directory
original_dir = os.getcwd()
os.chdir(scr_dir_path)
try:
scr_dir_path = os.getcwd()

molden_files = glob.glob(f'{chain_dir}.molden')
if molden_files:
molden_file = os.path.basename(molden_files[0])
try:
task_function(scr_dir_path, molden_file, multiwfn_path, charge_scheme)
except Exception as e:
print(f"> ERROR: {current_pdb_dir} {chain_dir} failed: {e}. Skipping.")
else:
print(f"> WARNING: No molden file in {scr_dir_path}")
finally:
shutil.rmtree('atmrad/', ignore_errors=True)
try:
os.remove('settings.ini')
except FileNotFoundError:
pass
os.chdir(original_dir)
scr_dir_path = os.getcwd()

molden_files = glob.glob(f'{chain_dir}.molden')
if molden_files:
molden_file = os.path.basename(molden_files[0])
task_function(scr_dir_path, molden_file, multiwfn_path, charge_scheme)
# MODIFICATION: Comment out the deletion lines to prevent race conditions
# shutil.rmtree('atmrad/') # Delete the atmrad directory when done
# os.remove('settings.ini') # Delete the settings.ini file when done
else:
print(f"> WARNING: No molden file in {scr_dir_path}", flush=True)
os.chdir(original_dir)
else:
print(f"> WARNING: No scr directory in {method_dir_path}.")

print(f"> WARNING: No scr directory in {method_dir_path}.", flush=True)
else:
continue

Expand Down Expand Up @@ -187,7 +183,7 @@ def get_coc(scr_dir_path, molden_file, fragment_atom_indices, selected_charge_sc

# Check if the .chg file exists
if not os.path.exists(charge_file):
print(f"> Charge file {charge_file} not found. Generating it using charge_scheme function.")
print(f"> Charge file {charge_file} not found. Generating it using charge_scheme function.", flush=True)
# Generate the .chg file using the charge_scheme function
charge_scheme(scr_dir_path, molden_file, multiwfn_path, selected_charge_scheme)

Expand Down Expand Up @@ -293,9 +289,9 @@ def get_cpu_count(settings_ini_dest_path):
# Extract the first matching group (the number after 'nthreads=')
return int(match.group(1))
except FileNotFoundError:
print(f"Error: The file {settings_ini_dest_path} was not found.")
print(f"Error: The file {settings_ini_dest_path} was not found.", flush=True)
except ValueError:
print(f"Error: Unable to parse 'nthreads' in {settings_ini_dest_path}.")
print(f"Error: Unable to parse 'nthreads' in {settings_ini_dest_path}.", flush=True)

# Default to 1 if nthreads isn't found or there is an error
return 1
Expand Down Expand Up @@ -364,7 +360,7 @@ def charge_scheme(scr_dir_path, molden_file, multiwfn_path, charge_scheme):

# If the charge file already exists, skip
if os.path.exists(new_charge_file):
print(f"> Charge file {new_charge_file} already exists. Skipping {charge_scheme}...")
print(f"> Charge file {new_charge_file} already exists. Skipping {charge_scheme}...", flush=True)
return

# Run Multiwfn with the user-selected charge scheme
Expand All @@ -376,9 +372,9 @@ def charge_scheme(scr_dir_path, molden_file, multiwfn_path, charge_scheme):
charge_output_file = Path(f"{molden_base_name}.chg")
if charge_output_file.exists():
charge_output_file.rename(new_charge_file)
print(f"> {pdb_name.upper()} {chain_name} {charge_scheme} scheme computed and saved to {new_charge_file}")
print(f"> {pdb_name.upper()} {chain_name} {charge_scheme} scheme computed and saved to {new_charge_file}", flush=True)
else:
print(f"> ERROR: Multiwfn failed for {pdb_name} {chain_name} using {charge_scheme} scheme.")
print(f"> ERROR: Multiwfn failed for {pdb_name} {chain_name} using {charge_scheme} scheme.", flush=True)



Expand Down Expand Up @@ -434,4 +430,3 @@ def calc_dipole(scr_dir_path, molden_file, multiwfn_path, charge_scheme):
csv_writer.writerow(['Molecular dipole moment'] + dipole_a_u + [magnitude_a_u])

print(f"> {pdb_name.upper()} {chain_name} dipole computed using center of mass\n", flush=True)

Loading