Skip to content

Commit afd6054

Browse files
author
David W. Kastner
authored
Merge pull request #91 from davidkastner/correct-molden
Fix ECP in molden
2 parents 6f3d946 + c523072 commit afd6054

3 files changed

Lines changed: 100 additions & 48 deletions

File tree

qp/analyze/checkup.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,27 @@ def check_submit_record(submit_record_path, delete_queued):
5353
with open(submit_record_path, 'r') as f:
5454
content = f.read()
5555

56-
author = extract_author(content)
57-
queue_time = "Queue Time:" in content
58-
run_start_time = "Run Start Time:" in content
59-
run_end_time = "Run End Time:" in content
60-
61-
# if queue_time and not run_start_time:
62-
if run_start_time and not run_end_time:
63-
if delete_queued:
64-
print(f"Deleting queued job record: {submit_record_path}")
65-
os.remove(submit_record_path)
66-
return "queue", author
67-
elif run_start_time and not run_end_time:
68-
return "running", author
69-
elif run_end_time:
70-
return "done", author
71-
56+
author = extract_author(content)
57+
queue_time = "Queue Time:" in content
58+
run_start_time = "Run Start Time:" in content
59+
run_end_time = "Run End Time:" in content
60+
61+
# Queued but never started
62+
if queue_time and not run_start_time:
63+
if delete_queued:
64+
print(f"Deleting queued job record: {submit_record_path}")
65+
os.remove(submit_record_path)
66+
return "queue", author
67+
68+
# Started but not finished
69+
if run_start_time and not run_end_time:
70+
return "running", author
71+
72+
# Finished
73+
if run_end_time:
74+
return "done", author
75+
76+
# Fallback (record present but missing expected markers)
7277
return "backlog", author
7378

7479

qp/analyze/molden.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,56 @@
11
import numpy as np
2+
import os
3+
4+
VALENCE_ELECTRONS = {
5+
'CA': 2, 'CD': 12, 'CO': 9, 'CU': 11, 'FE': 8, 'K': 1,
6+
'MN': 7, 'NI': 10, 'PB': 14, 'V': 5, 'ZN': 12
7+
}
8+
9+
def correct_ecp_charges_inplace(molden_file):
10+
"""
11+
Corrects the nuclear charge in a Molden file for elements with ECPs,
12+
overwriting the original file.
13+
"""
14+
try:
15+
with open(molden_file, 'r') as f:
16+
lines = f.readlines()
17+
except FileNotFoundError:
18+
print(f"> ERROR: Molden file not found at {molden_file}")
19+
return
20+
21+
modified_lines = []
22+
in_atoms_section = False
23+
was_modified = False
24+
for line in lines:
25+
if line.strip() == "[Atoms] Angs":
26+
in_atoms_section = True
27+
modified_lines.append(line)
28+
continue
29+
elif in_atoms_section and line.strip().startswith("["):
30+
in_atoms_section = False
31+
32+
if in_atoms_section:
33+
parts = line.strip().split()
34+
if len(parts) >= 6:
35+
element_symbol = parts[0].upper()
36+
# Check if the element needs correction and if it hasn't been corrected already
37+
if element_symbol in VALENCE_ELECTRONS and parts[2] != str(VALENCE_ELECTRONS[element_symbol]):
38+
parts[2] = str(VALENCE_ELECTRONS[element_symbol])
39+
line_to_write = ' '.join(parts) + '\n'
40+
modified_lines.append(line_to_write)
41+
was_modified = True
42+
else:
43+
modified_lines.append(line)
44+
else:
45+
modified_lines.append(line)
46+
else:
47+
modified_lines.append(line)
48+
49+
if was_modified:
50+
with open(molden_file, 'w') as f:
51+
f.writelines(modified_lines)
52+
print(f"> Overwrote {os.path.basename(molden_file)} with ECP-corrected nuclear charges.")
53+
254

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

95-
return output_molden
147+
return output_molden

qp/analyze/multiwfn.py

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,38 +54,34 @@ def iterate_qm_output(pdb_all, method, base_output_dir, multiwfn_path, settings_
5454
# Copy the atmrad dir and settings.ini into the current working directory
5555
atmrad_dest_path = os.path.join(scr_dir_path, 'atmrad/')
5656
if not os.path.exists(atmrad_dest_path):
57-
shutil.copytree(atmrad_path, atmrad_dest_path, dirs_exist_ok=True)
57+
shutil.copytree(atmrad_path, atmrad_dest_path)
5858

5959
settings_ini_dest_path = os.path.join(scr_dir_path, 'settings.ini')
60-
shutil.copy(settings_ini_path, settings_ini_dest_path)
60+
# MODIFICATION: Add a check to avoid re-copying settings.ini if it already exists
61+
if not os.path.exists(settings_ini_dest_path):
62+
shutil.copy(settings_ini_path, settings_ini_dest_path)
63+
6164
cpu_count = get_cpu_count(settings_ini_dest_path)
62-
print(f"> Using {cpu_count} threads for Multiwfn")
65+
print(f"> Using {cpu_count} threads for Multiwfn", flush=True)
6366

6467
if os.path.exists(scr_dir_path):
68+
# Move into the QM scr directory
6569
original_dir = os.getcwd()
6670
os.chdir(scr_dir_path)
67-
try:
68-
scr_dir_path = os.getcwd()
69-
70-
molden_files = glob.glob(f'{chain_dir}.molden')
71-
if molden_files:
72-
molden_file = os.path.basename(molden_files[0])
73-
try:
74-
task_function(scr_dir_path, molden_file, multiwfn_path, charge_scheme)
75-
except Exception as e:
76-
print(f"> ERROR: {current_pdb_dir} {chain_dir} failed: {e}. Skipping.")
77-
else:
78-
print(f"> WARNING: No molden file in {scr_dir_path}")
79-
finally:
80-
shutil.rmtree('atmrad/', ignore_errors=True)
81-
try:
82-
os.remove('settings.ini')
83-
except FileNotFoundError:
84-
pass
85-
os.chdir(original_dir)
71+
scr_dir_path = os.getcwd()
72+
73+
molden_files = glob.glob(f'{chain_dir}.molden')
74+
if molden_files:
75+
molden_file = os.path.basename(molden_files[0])
76+
task_function(scr_dir_path, molden_file, multiwfn_path, charge_scheme)
77+
# MODIFICATION: Comment out the deletion lines to prevent race conditions
78+
# shutil.rmtree('atmrad/') # Delete the atmrad directory when done
79+
# os.remove('settings.ini') # Delete the settings.ini file when done
80+
else:
81+
print(f"> WARNING: No molden file in {scr_dir_path}", flush=True)
82+
os.chdir(original_dir)
8683
else:
87-
print(f"> WARNING: No scr directory in {method_dir_path}.")
88-
84+
print(f"> WARNING: No scr directory in {method_dir_path}.", flush=True)
8985
else:
9086
continue
9187

@@ -187,7 +183,7 @@ def get_coc(scr_dir_path, molden_file, fragment_atom_indices, selected_charge_sc
187183

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

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

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

365361
# If the charge file already exists, skip
366362
if os.path.exists(new_charge_file):
367-
print(f"> Charge file {new_charge_file} already exists. Skipping {charge_scheme}...")
363+
print(f"> Charge file {new_charge_file} already exists. Skipping {charge_scheme}...", flush=True)
368364
return
369365

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

383379

384380

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

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

0 commit comments

Comments
 (0)