MultiOptPy 1.20.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multioptpy/Calculator/__init__.py +0 -0
- multioptpy/Calculator/ase_calculation_tools.py +424 -0
- multioptpy/Calculator/ase_tools/__init__.py +0 -0
- multioptpy/Calculator/ase_tools/fairchem.py +28 -0
- multioptpy/Calculator/ase_tools/gamess.py +19 -0
- multioptpy/Calculator/ase_tools/gaussian.py +165 -0
- multioptpy/Calculator/ase_tools/mace.py +28 -0
- multioptpy/Calculator/ase_tools/mopac.py +19 -0
- multioptpy/Calculator/ase_tools/nwchem.py +31 -0
- multioptpy/Calculator/ase_tools/orca.py +22 -0
- multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
- multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
- multioptpy/Calculator/emt_calculation_tools.py +458 -0
- multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
- multioptpy/Calculator/lj_calculation_tools.py +314 -0
- multioptpy/Calculator/psi4_calculation_tools.py +334 -0
- multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
- multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
- multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
- multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
- multioptpy/Calculator/tblite_calculation_tools.py +352 -0
- multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
- multioptpy/Constraint/__init__.py +0 -0
- multioptpy/Constraint/constraint_condition.py +834 -0
- multioptpy/Coordinate/__init__.py +0 -0
- multioptpy/Coordinate/polar_coordinate.py +199 -0
- multioptpy/Coordinate/redundant_coordinate.py +638 -0
- multioptpy/IRC/__init__.py +0 -0
- multioptpy/IRC/converge_criteria.py +28 -0
- multioptpy/IRC/dvv.py +544 -0
- multioptpy/IRC/euler.py +439 -0
- multioptpy/IRC/hpc.py +564 -0
- multioptpy/IRC/lqa.py +540 -0
- multioptpy/IRC/modekill.py +662 -0
- multioptpy/IRC/rk4.py +579 -0
- multioptpy/Interpolation/__init__.py +0 -0
- multioptpy/Interpolation/adaptive_interpolation.py +283 -0
- multioptpy/Interpolation/binomial_interpolation.py +179 -0
- multioptpy/Interpolation/geodesic_interpolation.py +785 -0
- multioptpy/Interpolation/interpolation.py +156 -0
- multioptpy/Interpolation/linear_interpolation.py +473 -0
- multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
- multioptpy/Interpolation/spline_interpolation.py +353 -0
- multioptpy/MD/__init__.py +0 -0
- multioptpy/MD/thermostat.py +185 -0
- multioptpy/MEP/__init__.py +0 -0
- multioptpy/MEP/pathopt_bneb_force.py +443 -0
- multioptpy/MEP/pathopt_dmf_force.py +448 -0
- multioptpy/MEP/pathopt_dneb_force.py +130 -0
- multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
- multioptpy/MEP/pathopt_gpneb_force.py +512 -0
- multioptpy/MEP/pathopt_lup_force.py +113 -0
- multioptpy/MEP/pathopt_neb_force.py +225 -0
- multioptpy/MEP/pathopt_nesb_force.py +205 -0
- multioptpy/MEP/pathopt_om_force.py +153 -0
- multioptpy/MEP/pathopt_qsm_force.py +174 -0
- multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
- multioptpy/ModelFunction/__init__.py +7 -0
- multioptpy/ModelFunction/avoiding_model_function.py +29 -0
- multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
- multioptpy/ModelFunction/conical_model_function.py +26 -0
- multioptpy/ModelFunction/opt_meci.py +50 -0
- multioptpy/ModelFunction/opt_mesx.py +47 -0
- multioptpy/ModelFunction/opt_mesx_2.py +49 -0
- multioptpy/ModelFunction/seam_model_function.py +27 -0
- multioptpy/ModelHessian/__init__.py +0 -0
- multioptpy/ModelHessian/approx_hessian.py +147 -0
- multioptpy/ModelHessian/calc_params.py +227 -0
- multioptpy/ModelHessian/fischer.py +236 -0
- multioptpy/ModelHessian/fischerd3.py +360 -0
- multioptpy/ModelHessian/fischerd4.py +398 -0
- multioptpy/ModelHessian/gfn0xtb.py +633 -0
- multioptpy/ModelHessian/gfnff.py +709 -0
- multioptpy/ModelHessian/lindh.py +165 -0
- multioptpy/ModelHessian/lindh2007d2.py +707 -0
- multioptpy/ModelHessian/lindh2007d3.py +822 -0
- multioptpy/ModelHessian/lindh2007d4.py +1030 -0
- multioptpy/ModelHessian/morse.py +106 -0
- multioptpy/ModelHessian/schlegel.py +144 -0
- multioptpy/ModelHessian/schlegeld3.py +322 -0
- multioptpy/ModelHessian/schlegeld4.py +559 -0
- multioptpy/ModelHessian/shortrange.py +346 -0
- multioptpy/ModelHessian/swartd2.py +496 -0
- multioptpy/ModelHessian/swartd3.py +706 -0
- multioptpy/ModelHessian/swartd4.py +918 -0
- multioptpy/ModelHessian/tshess.py +40 -0
- multioptpy/Optimizer/QHAdam.py +61 -0
- multioptpy/Optimizer/__init__.py +0 -0
- multioptpy/Optimizer/abc_fire.py +83 -0
- multioptpy/Optimizer/adabelief.py +58 -0
- multioptpy/Optimizer/adabound.py +68 -0
- multioptpy/Optimizer/adadelta.py +65 -0
- multioptpy/Optimizer/adaderivative.py +56 -0
- multioptpy/Optimizer/adadiff.py +68 -0
- multioptpy/Optimizer/adafactor.py +70 -0
- multioptpy/Optimizer/adam.py +65 -0
- multioptpy/Optimizer/adamax.py +62 -0
- multioptpy/Optimizer/adamod.py +83 -0
- multioptpy/Optimizer/adamw.py +65 -0
- multioptpy/Optimizer/adiis.py +523 -0
- multioptpy/Optimizer/afire_neb.py +282 -0
- multioptpy/Optimizer/block_hessian_update.py +709 -0
- multioptpy/Optimizer/c2diis.py +491 -0
- multioptpy/Optimizer/component_wise_scaling.py +405 -0
- multioptpy/Optimizer/conjugate_gradient.py +82 -0
- multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
- multioptpy/Optimizer/coordinate_locking.py +405 -0
- multioptpy/Optimizer/dic_rsirfo.py +1015 -0
- multioptpy/Optimizer/ediis.py +417 -0
- multioptpy/Optimizer/eve.py +76 -0
- multioptpy/Optimizer/fastadabelief.py +61 -0
- multioptpy/Optimizer/fire.py +77 -0
- multioptpy/Optimizer/fire2.py +249 -0
- multioptpy/Optimizer/fire_neb.py +92 -0
- multioptpy/Optimizer/gan_step.py +486 -0
- multioptpy/Optimizer/gdiis.py +609 -0
- multioptpy/Optimizer/gediis.py +203 -0
- multioptpy/Optimizer/geodesic_step.py +433 -0
- multioptpy/Optimizer/gpmin.py +633 -0
- multioptpy/Optimizer/gpr_step.py +364 -0
- multioptpy/Optimizer/gradientdescent.py +78 -0
- multioptpy/Optimizer/gradientdescent_neb.py +52 -0
- multioptpy/Optimizer/hessian_update.py +433 -0
- multioptpy/Optimizer/hybrid_rfo.py +998 -0
- multioptpy/Optimizer/kdiis.py +625 -0
- multioptpy/Optimizer/lars.py +21 -0
- multioptpy/Optimizer/lbfgs.py +253 -0
- multioptpy/Optimizer/lbfgs_neb.py +355 -0
- multioptpy/Optimizer/linesearch.py +236 -0
- multioptpy/Optimizer/lookahead.py +40 -0
- multioptpy/Optimizer/nadam.py +64 -0
- multioptpy/Optimizer/newton.py +200 -0
- multioptpy/Optimizer/prodigy.py +70 -0
- multioptpy/Optimizer/purtubation.py +16 -0
- multioptpy/Optimizer/quickmin_neb.py +245 -0
- multioptpy/Optimizer/radam.py +75 -0
- multioptpy/Optimizer/rfo_neb.py +302 -0
- multioptpy/Optimizer/ric_rfo.py +842 -0
- multioptpy/Optimizer/rl_step.py +627 -0
- multioptpy/Optimizer/rmspropgrave.py +65 -0
- multioptpy/Optimizer/rsirfo.py +1647 -0
- multioptpy/Optimizer/rsprfo.py +1056 -0
- multioptpy/Optimizer/sadam.py +60 -0
- multioptpy/Optimizer/samsgrad.py +63 -0
- multioptpy/Optimizer/tr_lbfgs.py +678 -0
- multioptpy/Optimizer/trim.py +273 -0
- multioptpy/Optimizer/trust_radius.py +207 -0
- multioptpy/Optimizer/trust_radius_neb.py +121 -0
- multioptpy/Optimizer/yogi.py +60 -0
- multioptpy/OtherMethod/__init__.py +0 -0
- multioptpy/OtherMethod/addf.py +1150 -0
- multioptpy/OtherMethod/dimer.py +895 -0
- multioptpy/OtherMethod/elastic_image_pair.py +629 -0
- multioptpy/OtherMethod/modelfunction.py +456 -0
- multioptpy/OtherMethod/newton_traj.py +454 -0
- multioptpy/OtherMethod/twopshs.py +1095 -0
- multioptpy/PESAnalyzer/__init__.py +0 -0
- multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
- multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
- multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
- multioptpy/PESAnalyzer/pca_analysis.py +314 -0
- multioptpy/Parameters/__init__.py +0 -0
- multioptpy/Parameters/atomic_mass.py +20 -0
- multioptpy/Parameters/atomic_number.py +22 -0
- multioptpy/Parameters/covalent_radii.py +44 -0
- multioptpy/Parameters/d2.py +61 -0
- multioptpy/Parameters/d3.py +63 -0
- multioptpy/Parameters/d4.py +103 -0
- multioptpy/Parameters/dreiding.py +34 -0
- multioptpy/Parameters/gfn0xtb_param.py +137 -0
- multioptpy/Parameters/gfnff_param.py +315 -0
- multioptpy/Parameters/gnb.py +104 -0
- multioptpy/Parameters/parameter.py +22 -0
- multioptpy/Parameters/uff.py +72 -0
- multioptpy/Parameters/unit_values.py +20 -0
- multioptpy/Potential/AFIR_potential.py +55 -0
- multioptpy/Potential/LJ_repulsive_potential.py +345 -0
- multioptpy/Potential/__init__.py +0 -0
- multioptpy/Potential/anharmonic_keep_potential.py +28 -0
- multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
- multioptpy/Potential/electrostatic_potential.py +69 -0
- multioptpy/Potential/flux_potential.py +30 -0
- multioptpy/Potential/gaussian_potential.py +101 -0
- multioptpy/Potential/idpp.py +516 -0
- multioptpy/Potential/keep_angle_potential.py +146 -0
- multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
- multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
- multioptpy/Potential/keep_potential.py +99 -0
- multioptpy/Potential/mechano_force_potential.py +74 -0
- multioptpy/Potential/nanoreactor_potential.py +52 -0
- multioptpy/Potential/potential.py +896 -0
- multioptpy/Potential/spacer_model_potential.py +221 -0
- multioptpy/Potential/switching_potential.py +258 -0
- multioptpy/Potential/universal_potential.py +34 -0
- multioptpy/Potential/value_range_potential.py +36 -0
- multioptpy/Potential/void_point_potential.py +25 -0
- multioptpy/SQM/__init__.py +0 -0
- multioptpy/SQM/sqm1/__init__.py +0 -0
- multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
- multioptpy/SQM/sqm2/__init__.py +0 -0
- multioptpy/SQM/sqm2/calc_tools.py +95 -0
- multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
- multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
- multioptpy/SQM/sqm2/sqm2_core.py +303 -0
- multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
- multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
- multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
- multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
- multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
- multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
- multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
- multioptpy/Thermo/__init__.py +0 -0
- multioptpy/Thermo/normal_mode_analyzer.py +865 -0
- multioptpy/Utils/__init__.py +0 -0
- multioptpy/Utils/bond_connectivity.py +264 -0
- multioptpy/Utils/calc_tools.py +884 -0
- multioptpy/Utils/oniom.py +96 -0
- multioptpy/Utils/pbc.py +48 -0
- multioptpy/Utils/riemann_curvature.py +208 -0
- multioptpy/Utils/symmetry_analyzer.py +482 -0
- multioptpy/Visualization/__init__.py +0 -0
- multioptpy/Visualization/visualization.py +156 -0
- multioptpy/WFAnalyzer/MO_analysis.py +104 -0
- multioptpy/WFAnalyzer/__init__.py +0 -0
- multioptpy/Wrapper/__init__.py +0 -0
- multioptpy/Wrapper/autots.py +1239 -0
- multioptpy/Wrapper/ieip_wrapper.py +93 -0
- multioptpy/Wrapper/md_wrapper.py +92 -0
- multioptpy/Wrapper/neb_wrapper.py +94 -0
- multioptpy/Wrapper/optimize_wrapper.py +76 -0
- multioptpy/__init__.py +5 -0
- multioptpy/entrypoints.py +916 -0
- multioptpy/fileio.py +660 -0
- multioptpy/ieip.py +340 -0
- multioptpy/interface.py +1086 -0
- multioptpy/irc.py +529 -0
- multioptpy/moleculardynamics.py +432 -0
- multioptpy/neb.py +1267 -0
- multioptpy/optimization.py +1553 -0
- multioptpy/optimizer.py +709 -0
- multioptpy-1.20.2.dist-info/METADATA +438 -0
- multioptpy-1.20.2.dist-info/RECORD +246 -0
- multioptpy-1.20.2.dist-info/WHEEL +5 -0
- multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
- multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
- multioptpy-1.20.2.dist-info/top_level.txt +1 -0
multioptpy/fileio.py
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from scipy.signal import argrelextrema
|
|
7
|
+
from multioptpy.Utils.calc_tools import calc_RMS
|
|
8
|
+
|
|
9
|
+
def save_bias_pot_info(file_path, energy, gradient, bias_pot_id):
|
|
10
|
+
max_grad = np.max(np.abs(gradient))
|
|
11
|
+
rms_grad = calc_RMS(gradient)
|
|
12
|
+
save_path = file_path+"bias_pot_info_"+str(bias_pot_id)+".log"
|
|
13
|
+
|
|
14
|
+
if not os.path.exists(save_path):
|
|
15
|
+
with open(save_path, "w") as f:
|
|
16
|
+
f.write("Energy, MaxGrad, RMSGrad\n")
|
|
17
|
+
|
|
18
|
+
with open(save_path, "a") as f:
|
|
19
|
+
f.write(str(energy)+","+str(max_grad)+","+str(rms_grad)+"\n")
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
def save_bias_param_grad_info(file_path, gradient, bias_pot_id):
|
|
23
|
+
save_path = file_path+"bias_param_grad_info_"+str(bias_pot_id)+".log"
|
|
24
|
+
|
|
25
|
+
if not os.path.exists(save_path):
|
|
26
|
+
with open(save_path, "w") as f:
|
|
27
|
+
f.write("Gradient\n")
|
|
28
|
+
with open(save_path, "a") as f:
|
|
29
|
+
f.write(str(gradient)+"\n")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
def read_software_path(file_path="./software_path.conf"):
|
|
33
|
+
print("Reading software path from", file_path)
|
|
34
|
+
with open(file_path, "r") as f:
|
|
35
|
+
words = f.read().splitlines()
|
|
36
|
+
software_path_dict = {}
|
|
37
|
+
for word in words:
|
|
38
|
+
tmp_split = word.split("::")
|
|
39
|
+
soft_name = tmp_split[0]
|
|
40
|
+
soft_path = tmp_split[1]
|
|
41
|
+
software_path_dict[soft_name] = soft_path
|
|
42
|
+
return software_path_dict
|
|
43
|
+
|
|
44
|
+
def xyz2list(file_path, args_electric_charge_and_multiplicity):
|
|
45
|
+
pattern_cs = get_pattern_cs()
|
|
46
|
+
pattern_xyz = get_pattern_xyz()
|
|
47
|
+
electric_charge_and_multiplicity = None
|
|
48
|
+
element_list = []
|
|
49
|
+
with open(file_path, "r") as f:
|
|
50
|
+
words = f.read().splitlines()
|
|
51
|
+
geometry_list = []
|
|
52
|
+
for word in words:
|
|
53
|
+
if re.match(pattern_cs, word):
|
|
54
|
+
electric_charge_and_multiplicity = list(map(str, word.split()))
|
|
55
|
+
if re.match(pattern_xyz, word):
|
|
56
|
+
geometry_list.append(word.split()[1:4])
|
|
57
|
+
element_list.append(word.split()[0])
|
|
58
|
+
if electric_charge_and_multiplicity is None:
|
|
59
|
+
electric_charge_and_multiplicity = args_electric_charge_and_multiplicity
|
|
60
|
+
|
|
61
|
+
return geometry_list, element_list, electric_charge_and_multiplicity
|
|
62
|
+
import re
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _parse_gamess(lines):
|
|
67
|
+
"""Internal function to parse GAMESS input."""
|
|
68
|
+
pattern_atom = get_pattern_gamess_atom()
|
|
69
|
+
element_list = []
|
|
70
|
+
geometry_list = []
|
|
71
|
+
|
|
72
|
+
is_data_section = False
|
|
73
|
+
for line in lines:
|
|
74
|
+
if "$DATA" in line.upper(): is_data_section = True; continue
|
|
75
|
+
if "$END" in line.upper() and is_data_section: break
|
|
76
|
+
if is_data_section:
|
|
77
|
+
match = pattern_atom.match(line)
|
|
78
|
+
if match:
|
|
79
|
+
element_list.append(match.group(1))
|
|
80
|
+
geometry_list.append([match.group(2), match.group(3), match.group(4)])
|
|
81
|
+
return geometry_list, element_list
|
|
82
|
+
|
|
83
|
+
def _parse_orca(lines):
|
|
84
|
+
"""Internal function to parse ORCA input."""
|
|
85
|
+
pattern_atom = get_pattern_orca_atom()
|
|
86
|
+
element_list = []
|
|
87
|
+
geometry_list = []
|
|
88
|
+
electric_charge_and_multiplicity = ["0", "1"] # Default
|
|
89
|
+
|
|
90
|
+
is_coord_section = False
|
|
91
|
+
for line in lines:
|
|
92
|
+
# Check for start of coordinate block, e.g., *xyz 0 1
|
|
93
|
+
if line.strip().startswith("*xyz"):
|
|
94
|
+
is_coord_section = True
|
|
95
|
+
parts = line.strip().split()
|
|
96
|
+
if len(parts) == 3:
|
|
97
|
+
electric_charge_and_multiplicity = [parts[1], parts[2]]
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
# Check for end of coordinate block
|
|
101
|
+
if is_coord_section and line.strip() == "*":
|
|
102
|
+
break
|
|
103
|
+
|
|
104
|
+
if is_coord_section:
|
|
105
|
+
match = pattern_atom.match(line)
|
|
106
|
+
if match:
|
|
107
|
+
element_list.append(match.group(1))
|
|
108
|
+
geometry_list.append([match.group(2), match.group(3), match.group(4)])
|
|
109
|
+
return geometry_list, element_list, electric_charge_and_multiplicity
|
|
110
|
+
|
|
111
|
+
def _parse_qchem(lines):
|
|
112
|
+
"""Internal function to parse Q-Chem input."""
|
|
113
|
+
pattern_atom = get_pattern_qchem_atom()
|
|
114
|
+
element_list = []
|
|
115
|
+
geometry_list = []
|
|
116
|
+
electric_charge_and_multiplicity = ["0", "1"] # Default
|
|
117
|
+
|
|
118
|
+
is_molecule_section = False
|
|
119
|
+
for line in lines:
|
|
120
|
+
if "$molecule" in line.lower():
|
|
121
|
+
is_molecule_section = True
|
|
122
|
+
# Read charge and multiplicity from the next line
|
|
123
|
+
charge_mult_line = next(iter(lines), "").strip()
|
|
124
|
+
parts = charge_mult_line.split()
|
|
125
|
+
if len(parts) == 2:
|
|
126
|
+
electric_charge_and_multiplicity = parts
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
if "$end" in line.lower() and is_molecule_section:
|
|
130
|
+
break
|
|
131
|
+
|
|
132
|
+
if is_molecule_section:
|
|
133
|
+
# Skip the charge/multiplicity line itself
|
|
134
|
+
if re.match(r"^\s*[+-]?\d+\s+[+-]?\d+\s*$", line.strip()):
|
|
135
|
+
continue
|
|
136
|
+
match = pattern_atom.match(line)
|
|
137
|
+
if match:
|
|
138
|
+
element_list.append(match.group(1))
|
|
139
|
+
geometry_list.append([match.group(2), match.group(3), match.group(4)])
|
|
140
|
+
return geometry_list, element_list, electric_charge_and_multiplicity
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def inp2list(file_path, args_electric_charge_and_multiplicity=["0", "1"]):
|
|
144
|
+
"""
|
|
145
|
+
Automatically detects the input file format (GAMESS, ORCA, Q-Chem)
|
|
146
|
+
and parses the atomic coordinates.
|
|
147
|
+
"""
|
|
148
|
+
with open(file_path, 'r') as f:
|
|
149
|
+
content = f.read()
|
|
150
|
+
|
|
151
|
+
lines = content.splitlines()
|
|
152
|
+
|
|
153
|
+
# --- Format Detection Logic ---
|
|
154
|
+
detected_format = None
|
|
155
|
+
if "$CONTRL" in content and "$DATA" in content:
|
|
156
|
+
detected_format = "gamess"
|
|
157
|
+
elif re.search(r"^\s*!", content, re.MULTILINE) and "*xyz" in content:
|
|
158
|
+
detected_format = "orca"
|
|
159
|
+
elif "$molecule" in content:
|
|
160
|
+
detected_format = "qchem"
|
|
161
|
+
else:
|
|
162
|
+
print("Error: Could not determine the file format.")
|
|
163
|
+
return [], [], None, None
|
|
164
|
+
|
|
165
|
+
# --- Parsing based on detected format ---
|
|
166
|
+
if detected_format == "gamess":
|
|
167
|
+
print("Detected format: GAMESS")
|
|
168
|
+
geometry_list, element_list = _parse_gamess(lines)
|
|
169
|
+
# GAMESS does not have a standard charge/multiplicity line in $DATA
|
|
170
|
+
electric_charge_and_multiplicity = args_electric_charge_and_multiplicity
|
|
171
|
+
|
|
172
|
+
elif detected_format == "orca":
|
|
173
|
+
print("Detected format: ORCA")
|
|
174
|
+
geometry_list, element_list, electric_charge_and_multiplicity = _parse_orca(lines)
|
|
175
|
+
|
|
176
|
+
elif detected_format == "qchem":
|
|
177
|
+
print("Detected format: Q-Chem")
|
|
178
|
+
geometry_list, element_list, electric_charge_and_multiplicity = _parse_qchem(lines)
|
|
179
|
+
|
|
180
|
+
return geometry_list, element_list, electric_charge_and_multiplicity
|
|
181
|
+
|
|
182
|
+
def mol2list(file_path, args_electric_charge_and_multiplicity):
|
|
183
|
+
"""Parses a MOL file (.mol)."""
|
|
184
|
+
pattern_atom = get_pattern_mol_atom()
|
|
185
|
+
|
|
186
|
+
element_list = []
|
|
187
|
+
geometry_list = []
|
|
188
|
+
electric_charge_and_multiplicity = args_electric_charge_and_multiplicity
|
|
189
|
+
|
|
190
|
+
with open(file_path, "r") as f:
|
|
191
|
+
lines = f.readlines()
|
|
192
|
+
|
|
193
|
+
# Get the number of atoms from the counts line (4th line)
|
|
194
|
+
try:
|
|
195
|
+
num_atoms = int(lines[3].strip().split()[0])
|
|
196
|
+
except (IndexError, ValueError):
|
|
197
|
+
# Return empty lists for an invalid format
|
|
198
|
+
return [], [], electric_charge_and_multiplicity
|
|
199
|
+
|
|
200
|
+
# Process the atom block
|
|
201
|
+
atom_block_lines = lines[4 : 4 + num_atoms]
|
|
202
|
+
for line in atom_block_lines:
|
|
203
|
+
match = pattern_atom.match(line)
|
|
204
|
+
if match:
|
|
205
|
+
# MOL format order is: X, Y, Z, Symbol
|
|
206
|
+
geometry_list.append([match.group(1), match.group(2), match.group(3)])
|
|
207
|
+
element_list.append(match.group(4))
|
|
208
|
+
|
|
209
|
+
return geometry_list, element_list, electric_charge_and_multiplicity
|
|
210
|
+
|
|
211
|
+
def mol22list(file_path, args_electric_charge_and_multiplicity):
|
|
212
|
+
"""Parses a MOL2 file (.mol2)."""
|
|
213
|
+
pattern_atom = get_pattern_mol2_atom()
|
|
214
|
+
|
|
215
|
+
element_list = []
|
|
216
|
+
geometry_list = []
|
|
217
|
+
electric_charge_and_multiplicity = args_electric_charge_and_multiplicity
|
|
218
|
+
|
|
219
|
+
is_atom_section = False
|
|
220
|
+
with open(file_path, "r") as f:
|
|
221
|
+
for line in f:
|
|
222
|
+
# Detect the start of the ATOM section
|
|
223
|
+
if "@<TRIPOS>ATOM" in line:
|
|
224
|
+
is_atom_section = True
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
# End processing if another section starts
|
|
228
|
+
if is_atom_section and "@<TRIPOS>" in line:
|
|
229
|
+
break
|
|
230
|
+
|
|
231
|
+
# Process atom lines within the ATOM section
|
|
232
|
+
if is_atom_section:
|
|
233
|
+
match = pattern_atom.match(line)
|
|
234
|
+
if match:
|
|
235
|
+
# Extract the element symbol from the atom name (e.g., "C1", "Oa")
|
|
236
|
+
atom_name = match.group(1)
|
|
237
|
+
element = "".join(filter(str.isalpha, atom_name))
|
|
238
|
+
element_list.append(element)
|
|
239
|
+
|
|
240
|
+
# MOL2 format order is: Name, X, Y, Z
|
|
241
|
+
geometry_list.append([match.group(2), match.group(3), match.group(4)])
|
|
242
|
+
|
|
243
|
+
return geometry_list, element_list, electric_charge_and_multiplicity
|
|
244
|
+
|
|
245
|
+
def traj2list(file_path, args_electric_charge_and_multiplicity):
|
|
246
|
+
pattern_cs = get_pattern_cs()
|
|
247
|
+
pattern_xyz = get_pattern_xyz()
|
|
248
|
+
|
|
249
|
+
electric_charge_and_multiplicity = None
|
|
250
|
+
cs_flag = True
|
|
251
|
+
|
|
252
|
+
with open(file_path, "r") as f:
|
|
253
|
+
words = f.read().splitlines()
|
|
254
|
+
|
|
255
|
+
geometry_list = []
|
|
256
|
+
element_list = []
|
|
257
|
+
geometries = []
|
|
258
|
+
elements = []
|
|
259
|
+
for word in words:
|
|
260
|
+
if re.match(pattern_cs, word) and cs_flag:
|
|
261
|
+
electric_charge_and_multiplicity = list(map(str, word.split()))
|
|
262
|
+
cs_flag = False
|
|
263
|
+
if re.match(pattern_xyz, word):
|
|
264
|
+
geometry_list.append(word.split()[1:4])
|
|
265
|
+
element_list.append(word.split()[0])
|
|
266
|
+
else:
|
|
267
|
+
if len(geometry_list) != 0:
|
|
268
|
+
geometries.append(geometry_list)
|
|
269
|
+
if len(element_list) != 0:
|
|
270
|
+
elements.append(element_list)
|
|
271
|
+
|
|
272
|
+
geometry_list = []
|
|
273
|
+
element_list = []
|
|
274
|
+
|
|
275
|
+
if electric_charge_and_multiplicity is None:
|
|
276
|
+
electric_charge_and_multiplicity = args_electric_charge_and_multiplicity
|
|
277
|
+
|
|
278
|
+
return geometries, elements, electric_charge_and_multiplicity
|
|
279
|
+
|
|
280
|
+
def get_pattern_xyz():
|
|
281
|
+
pattern_xyz = re.compile(r"\s*([A-Za-z]+)\s+([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?)(?:\s+([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?))(?:\s+([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?))\s*")
|
|
282
|
+
return pattern_xyz
|
|
283
|
+
|
|
284
|
+
def get_pattern_cs():
|
|
285
|
+
pattern_cs = re.compile(r"-*[0-9]+\s+-*[0-9]+\s*")
|
|
286
|
+
return pattern_cs
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def get_pattern_qchem_atom():
|
|
290
|
+
"""Returns a regex pattern for Q-Chem atom lines."""
|
|
291
|
+
coordinate_pattern = r"([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?)"
|
|
292
|
+
pattern = re.compile(
|
|
293
|
+
r"^\s*([A-Za-z]+)\s+" # Element
|
|
294
|
+
+ coordinate_pattern + r"\s+" # X
|
|
295
|
+
+ coordinate_pattern + r"\s+" # Y
|
|
296
|
+
+ coordinate_pattern + r"\s*" # Z
|
|
297
|
+
)
|
|
298
|
+
return pattern
|
|
299
|
+
|
|
300
|
+
def get_pattern_orca_atom():
|
|
301
|
+
"""Returns a regex pattern for ORCA atom lines."""
|
|
302
|
+
coordinate_pattern = r"([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?)"
|
|
303
|
+
pattern = re.compile(
|
|
304
|
+
r"^\s*([A-Za-z]+)\s+" # Element
|
|
305
|
+
+ coordinate_pattern + r"\s+" # X
|
|
306
|
+
+ coordinate_pattern + r"\s+" # Y
|
|
307
|
+
+ coordinate_pattern + r"\s*" # Z
|
|
308
|
+
)
|
|
309
|
+
return pattern
|
|
310
|
+
|
|
311
|
+
def get_pattern_gamess_atom():
|
|
312
|
+
"""
|
|
313
|
+
Returns a regex pattern to match atom lines in a GAMESS input file.
|
|
314
|
+
Supports both decimal and scientific notation for coordinates.
|
|
315
|
+
"""
|
|
316
|
+
# Example: "O 8.0 0.0 0.0 0.0" or "C 6.0 1.234e+01 -5.67E-02 8.9"
|
|
317
|
+
coordinate_pattern = r"([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?)"
|
|
318
|
+
|
|
319
|
+
pattern = re.compile(
|
|
320
|
+
r"^\s*([A-Za-z]+)\s+" # Element
|
|
321
|
+
r"[+-]?\d+\.\d+\s+" # Atomic Number
|
|
322
|
+
+ coordinate_pattern + r"\s+" # X coordinate
|
|
323
|
+
+ coordinate_pattern + r"\s+" # Y coordinate
|
|
324
|
+
+ coordinate_pattern + r"\s*" # Z coordinate
|
|
325
|
+
)
|
|
326
|
+
return pattern
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def get_pattern_mol_atom():
|
|
330
|
+
"""
|
|
331
|
+
Returns a regex pattern to match atom lines in a MOL/SDF file.
|
|
332
|
+
Supports both decimal and scientific notation for coordinates.
|
|
333
|
+
"""
|
|
334
|
+
# Example: " 0.0000 0.0000 0.0000 O ..." or " 1.23e-05 -4.56E+00 7.89 O ..."
|
|
335
|
+
coordinate_pattern = r"([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?)"
|
|
336
|
+
|
|
337
|
+
pattern = re.compile(
|
|
338
|
+
r"^\s*" + coordinate_pattern + r"\s+" # X coordinate
|
|
339
|
+
+ coordinate_pattern + r"\s+" # Y coordinate
|
|
340
|
+
+ coordinate_pattern + r"\s+" # Z coordinate
|
|
341
|
+
r"([A-Za-z]+)\s+.*" # Element
|
|
342
|
+
)
|
|
343
|
+
return pattern
|
|
344
|
+
|
|
345
|
+
def get_pattern_mol2_atom():
|
|
346
|
+
"""
|
|
347
|
+
Returns a regex pattern to match atom lines in a MOL2 file.
|
|
348
|
+
Supports both decimal and scientific notation for coordinates.
|
|
349
|
+
"""
|
|
350
|
+
# Example: " 1 O 0.0000 0.0000 0.0000 O.3 ..." or " 2 C 1.2e1 -3.4E-1 5.6 C.ar ..."
|
|
351
|
+
coordinate_pattern = r"([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?)"
|
|
352
|
+
|
|
353
|
+
pattern = re.compile(
|
|
354
|
+
r"^\s*\d+\s+" # Atom ID
|
|
355
|
+
r"([A-Za-z]+)\w*\s+" # Atom Name
|
|
356
|
+
+ coordinate_pattern + r"\s+" # X coordinate
|
|
357
|
+
+ coordinate_pattern + r"\s+" # Y coordinate
|
|
358
|
+
+ coordinate_pattern + r"\s+.*" # Z coordinate
|
|
359
|
+
)
|
|
360
|
+
return pattern
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class FileIO:
|
|
364
|
+
def __init__(self, folder_dir, file):
|
|
365
|
+
self.work_directory = folder_dir
|
|
366
|
+
self.START_FILE = file
|
|
367
|
+
self.NOEXT_START_FILE = os.path.splitext(os.path.basename(self.START_FILE))[0]
|
|
368
|
+
self.is_save_gjf_file = True
|
|
369
|
+
return
|
|
370
|
+
|
|
371
|
+
def make_geometry_list(self, args_electric_charge_and_multiplicity):
|
|
372
|
+
"""Load initial structure"""
|
|
373
|
+
tmp_geometry_list, element_list, electric_charge_and_multiplicity = xyz2list(self.START_FILE, args_electric_charge_and_multiplicity)
|
|
374
|
+
natoms = len(tmp_geometry_list)
|
|
375
|
+
|
|
376
|
+
# Create start_data with list comprehension instead of for loop
|
|
377
|
+
start_data = [
|
|
378
|
+
str(natoms),
|
|
379
|
+
electric_charge_and_multiplicity,
|
|
380
|
+
*[[element_list[j]] + tmp_geometry_list[j] for j in range(len(tmp_geometry_list))]
|
|
381
|
+
]
|
|
382
|
+
|
|
383
|
+
return [start_data], element_list, electric_charge_and_multiplicity
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def print_geometry_list(self, new_geometry, element_list, electric_charge_and_multiplicity):
|
|
387
|
+
"""load structure updated geometry for next QM calculation"""
|
|
388
|
+
new_geometry = new_geometry.tolist()
|
|
389
|
+
print("\n")
|
|
390
|
+
|
|
391
|
+
# Process all geometries at once with list comprehension
|
|
392
|
+
formatted_geometries = []
|
|
393
|
+
for num, geometry in enumerate(new_geometry):
|
|
394
|
+
element = element_list[num]
|
|
395
|
+
formatted_geometry = [element] + list(map(str, geometry))
|
|
396
|
+
formatted_geometries.append(formatted_geometry)
|
|
397
|
+
print(f"{element:2} {float(geometry[0]):>17.12f} {float(geometry[1]):>17.12f} {float(geometry[2]):>17.12f}")
|
|
398
|
+
|
|
399
|
+
geometry_list = [[electric_charge_and_multiplicity, *formatted_geometries]]
|
|
400
|
+
print("")
|
|
401
|
+
|
|
402
|
+
return geometry_list
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def make_psi4_input_file(self, geometry_list, iter):#geometry_list: ang.
|
|
406
|
+
"""structure updated geometry is saved."""
|
|
407
|
+
file_directory = self.work_directory+"samples_"+self.NOEXT_START_FILE+"_"+str(iter)
|
|
408
|
+
tmp_cs = ["SAMPLE"+str(iter), ""]
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
float_pattern = r"([+-]?(?:\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?)"
|
|
412
|
+
|
|
413
|
+
os.makedirs(file_directory, exist_ok=True)
|
|
414
|
+
|
|
415
|
+
for y, geometry in enumerate(geometry_list):
|
|
416
|
+
tmp_geometry = []
|
|
417
|
+
for geom in geometry:
|
|
418
|
+
if len(geom) == 4 \
|
|
419
|
+
and re.match(r"[A-Za-z]+", str(geom[0])) \
|
|
420
|
+
and all(re.match(float_pattern, str(x)) for x in geom[1:]):
|
|
421
|
+
tmp_geometry.append(geom)
|
|
422
|
+
|
|
423
|
+
if len(geom) == 2 and re.match(r"-*\d+", str(geom[0])) and re.match(r"-*\d+", str(geom[1])):
|
|
424
|
+
tmp_cs = geom
|
|
425
|
+
|
|
426
|
+
with open(file_directory+"/"+self.NOEXT_START_FILE+"_"+str(y)+".xyz","w") as w:
|
|
427
|
+
w.write(str(len(tmp_geometry))+"\n")
|
|
428
|
+
w.write(str(tmp_cs[0])+" "+str(tmp_cs[1])+"\n")
|
|
429
|
+
for rows in tmp_geometry:
|
|
430
|
+
w.write(f"{rows[0]:2} {float(rows[1]):>17.12f} {float(rows[2]):>17.12f} {float(rows[3]):>17.12f}\n")
|
|
431
|
+
return file_directory
|
|
432
|
+
|
|
433
|
+
def read_gjf_file(self, args_electric_charge_and_multiplicity=None):
|
|
434
|
+
geometry_list = []
|
|
435
|
+
element_list = []
|
|
436
|
+
with open(self.START_FILE, 'r') as f:
|
|
437
|
+
lines = f.read().splitlines()
|
|
438
|
+
read_flag = False
|
|
439
|
+
pattern = r"-*[0-9]+\s+-*[0-9]+\s*"
|
|
440
|
+
repatter = re.compile(pattern)
|
|
441
|
+
for i in range(len(lines)):
|
|
442
|
+
if bool(re.match(repatter, lines[i])) is True:
|
|
443
|
+
electric_charge_and_multiplicity = lines[i].split()
|
|
444
|
+
read_flag = True
|
|
445
|
+
geometry_list.append("dummy")
|
|
446
|
+
geometry_list.append(lines[i].split())
|
|
447
|
+
continue
|
|
448
|
+
if read_flag and len(lines[i]) == 0:
|
|
449
|
+
break
|
|
450
|
+
if read_flag:
|
|
451
|
+
element_list.append(lines[i].split()[0])
|
|
452
|
+
geometry_list.append(lines[i].split())
|
|
453
|
+
geometry_list = [geometry_list]
|
|
454
|
+
return geometry_list, element_list, electric_charge_and_multiplicity
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def read_mol_file(self, args_electric_charge_and_multiplicity=None):
|
|
459
|
+
"""
|
|
460
|
+
Reads a .mol file, formats output to match the gjf reader structure.
|
|
461
|
+
"""
|
|
462
|
+
# Call the internal parser to get clean lists
|
|
463
|
+
coords_list, elements, charge_multiplicity = mol2list(
|
|
464
|
+
self.START_FILE, args_electric_charge_and_multiplicity
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
if not elements:
|
|
468
|
+
return [[]], [], charge_multiplicity
|
|
469
|
+
|
|
470
|
+
# Reconstruct the geometry_list to match the target format
|
|
471
|
+
# [["dummy", [charge, mult], [element, x, y, z], ...]]
|
|
472
|
+
output_geometry_list = ["dummy", charge_multiplicity]
|
|
473
|
+
for i, element in enumerate(elements):
|
|
474
|
+
full_atom_line = [element] + coords_list[i]
|
|
475
|
+
output_geometry_list.append(full_atom_line)
|
|
476
|
+
|
|
477
|
+
return [output_geometry_list], elements, charge_multiplicity
|
|
478
|
+
|
|
479
|
+
def read_mol2_file(self, args_electric_charge_and_multiplicity=None):
|
|
480
|
+
"""
|
|
481
|
+
Reads a .mol2 file, formats output to match the gjf reader structure.
|
|
482
|
+
"""
|
|
483
|
+
# Call the internal parser to get clean lists
|
|
484
|
+
coords_list, elements, charge_multiplicity = mol22list(
|
|
485
|
+
self.START_FILE, args_electric_charge_and_multiplicity
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
if not elements:
|
|
489
|
+
return [[]], [], charge_multiplicity
|
|
490
|
+
|
|
491
|
+
# Reconstruct the geometry_list to match the target format
|
|
492
|
+
output_geometry_list = ["dummy", charge_multiplicity]
|
|
493
|
+
for i, element in enumerate(elements):
|
|
494
|
+
full_atom_line = [element] + coords_list[i]
|
|
495
|
+
output_geometry_list.append(full_atom_line)
|
|
496
|
+
|
|
497
|
+
return [output_geometry_list], elements, charge_multiplicity
|
|
498
|
+
|
|
499
|
+
def read_gamess_inp_file(self, args_electric_charge_and_multiplicity=None):
|
|
500
|
+
"""
|
|
501
|
+
Reads a .inp file, formats output to match the gjf reader structure.
|
|
502
|
+
"""
|
|
503
|
+
# Call the internal parser to get clean lists
|
|
504
|
+
coords_list, elements, charge_multiplicity = inp2list(
|
|
505
|
+
self.START_FILE, args_electric_charge_and_multiplicity
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
if not elements:
|
|
509
|
+
return [[]], [], charge_multiplicity
|
|
510
|
+
|
|
511
|
+
# Reconstruct the geometry_list to match the target format
|
|
512
|
+
output_geometry_list = ["dummy", charge_multiplicity]
|
|
513
|
+
for i, element in enumerate(elements):
|
|
514
|
+
full_atom_line = [element] + coords_list[i]
|
|
515
|
+
output_geometry_list.append(full_atom_line)
|
|
516
|
+
|
|
517
|
+
return [output_geometry_list], elements, charge_multiplicity
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def save_gjf_file(self, geometry_list):
|
|
522
|
+
with open(self.work_directory+self.NOEXT_START_FILE+".gjf","w") as f:
|
|
523
|
+
f.write("%mem=4GB\n")
|
|
524
|
+
f.write("%nprocshared=4\n")
|
|
525
|
+
f.write("#p B3LYP/6-31G* opt freq\n")
|
|
526
|
+
f.write("\n")
|
|
527
|
+
f.write("Title Card Required\n")
|
|
528
|
+
f.write("\n")
|
|
529
|
+
f.write("0 1\n")# This line is required for fixing the charge and multiplicity.
|
|
530
|
+
for geometry in geometry_list:
|
|
531
|
+
f.write(geometry)
|
|
532
|
+
f.write("\n")
|
|
533
|
+
f.write("\n\n\n")
|
|
534
|
+
return
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def make_traj_file(self, name=""):
|
|
538
|
+
"""optimized path is saved."""
|
|
539
|
+
print("\nprocessing geometry collection ...\n")
|
|
540
|
+
if name == "":
|
|
541
|
+
file_list = sum([sorted(glob.glob(os.path.join(self.work_directory, f"samples_*_" + "[0-9]" * i, "*.xyz")))
|
|
542
|
+
for i in range(1, 7)], [])
|
|
543
|
+
else:
|
|
544
|
+
file_list = sum([sorted(glob.glob(os.path.join(self.work_directory, f"samples_*_{name}_" + "[0-9]" * i, "*.xyz")))
|
|
545
|
+
for i in range(1, 7)], [])
|
|
546
|
+
step_num = len(file_list)
|
|
547
|
+
|
|
548
|
+
for m, file in enumerate(file_list):
|
|
549
|
+
sample = []
|
|
550
|
+
tmp_geometry_list, element_list, _ = xyz2list(file, None)
|
|
551
|
+
for j in range(len(element_list)):
|
|
552
|
+
sample.append(f"{element_list[j]:2} {float(tmp_geometry_list[j][0]):>17.12f} {float(tmp_geometry_list[j][1]):>17.12f} {float(tmp_geometry_list[j][2]):>17.12f}")
|
|
553
|
+
with open(self.work_directory+self.NOEXT_START_FILE+"_traj.xyz","a") as w:
|
|
554
|
+
atom_num = len(sample)
|
|
555
|
+
w.write(str(atom_num)+"\n")
|
|
556
|
+
w.write("Frame "+str(m)+"\n")
|
|
557
|
+
for i in sample:
|
|
558
|
+
if "\n" == i or "" == i:
|
|
559
|
+
continue
|
|
560
|
+
w.write(i+"\n")
|
|
561
|
+
|
|
562
|
+
if m == step_num - 1:
|
|
563
|
+
if self.is_save_gjf_file:
|
|
564
|
+
self.save_gjf_file(sample)
|
|
565
|
+
with open(self.work_directory+self.NOEXT_START_FILE+"_optimized.xyz","a") as w2:
|
|
566
|
+
w2.write(str(atom_num)+"\n")
|
|
567
|
+
w2.write("OptimizedStructure\n")
|
|
568
|
+
for i in sample:
|
|
569
|
+
if "\n" == i or "" == i:
|
|
570
|
+
continue
|
|
571
|
+
w2.write(i+"\n")
|
|
572
|
+
print("\ngeometry collection was completed...\n")
|
|
573
|
+
return
|
|
574
|
+
|
|
575
|
+
def xyz_file_save_for_IRC(self, element_list, geometry_list):
|
|
576
|
+
count = 0
|
|
577
|
+
for geometry in geometry_list:
|
|
578
|
+
with open(self.work_directory+"IRC_path.xyz","a") as w:
|
|
579
|
+
atom_num = len(geometry)
|
|
580
|
+
w.write(str(atom_num)+"\n")
|
|
581
|
+
w.write("Frame "+str(count)+"\n")
|
|
582
|
+
for i in range(len(geometry)):
|
|
583
|
+
w.write(f"{element_list[i]:2} {float(geometry[i][0]):>17.12f} {float(geometry[i][1]):>17.12f} {float(geometry[i][2]):>17.12f}\n")
|
|
584
|
+
count += 1
|
|
585
|
+
print("\ngeometry collection for IRC was completed...\n")
|
|
586
|
+
return
|
|
587
|
+
|
|
588
|
+
def make_traj_file_for_DM(self, img_1="reactant", img_2="product"):
|
|
589
|
+
"""optimized path is saved."""
|
|
590
|
+
print("\nprocessing geometry collection ...\n")
|
|
591
|
+
file_list = sum(
|
|
592
|
+
[sorted(glob.glob(self.work_directory + f"samples_*_{str(img_1)}_{'[0-9]' * i}/*.xyz"))
|
|
593
|
+
for i in range(1, 7)],
|
|
594
|
+
[]
|
|
595
|
+
) + sum(
|
|
596
|
+
[sorted(glob.glob(self.work_directory + f"samples_*_{str(img_2)}_{'[0-9]' * i}/*.xyz"))[::-1]
|
|
597
|
+
for i in range(6, 0, -1)],
|
|
598
|
+
[]
|
|
599
|
+
)
|
|
600
|
+
for m, file in enumerate(file_list[1:-1]):
|
|
601
|
+
sample = []
|
|
602
|
+
tmp_geometry_list, element_list, _ = xyz2list(file, None)
|
|
603
|
+
for j in range(len(element_list)):
|
|
604
|
+
sample.append(f"{element_list[j]:2} {float(tmp_geometry_list[j][0]):>17.12f} {float(tmp_geometry_list[j][1]):>17.12f} {float(tmp_geometry_list[j][2]):>17.12f}")
|
|
605
|
+
|
|
606
|
+
with open(self.work_directory+self.NOEXT_START_FILE+"_traj.xyz","a") as w:
|
|
607
|
+
atom_num = len(sample)
|
|
608
|
+
w.write(str(atom_num)+"\n")
|
|
609
|
+
w.write("Frame "+str(m)+"\n")
|
|
610
|
+
for i in sample:
|
|
611
|
+
if "\n" == i or "" == i:
|
|
612
|
+
continue
|
|
613
|
+
w.write(i+"\n")
|
|
614
|
+
print("\ngeometry collection was completed...\n")
|
|
615
|
+
return
|
|
616
|
+
|
|
617
|
+
def argrelextrema_txt_save(self, save_list, name, min_max):
|
|
618
|
+
NUM_LIST = [i for i in range(len(save_list))]
|
|
619
|
+
if min_max == "max":
|
|
620
|
+
local_max_energy_list_index = argrelextrema(np.array(save_list), np.greater)
|
|
621
|
+
with open(self.work_directory+name+".txt","w") as f:
|
|
622
|
+
for j in local_max_energy_list_index[0].tolist():
|
|
623
|
+
f.write(str(NUM_LIST[j])+"\n")
|
|
624
|
+
elif min_max == "min":
|
|
625
|
+
inverse_energy_list = (-1)*np.array(save_list, dtype="float64")
|
|
626
|
+
local_min_energy_list_index = argrelextrema(np.array(inverse_energy_list), np.greater)
|
|
627
|
+
with open(self.work_directory+name+".txt","w") as f:
|
|
628
|
+
for j in local_min_energy_list_index[0].tolist():
|
|
629
|
+
f.write(str(NUM_LIST[j])+"\n")
|
|
630
|
+
else:
|
|
631
|
+
print("error")
|
|
632
|
+
|
|
633
|
+
return
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def write_xyz_file(element_list, coords, file_path, comment="save"):# element_list: list of element symbols, coords: np.array of coordinates (ang.)
|
|
638
|
+
with open(file_path, 'w') as f:
|
|
639
|
+
f.write(str(len(element_list)) + '\n')
|
|
640
|
+
f.write(comment+'\n')
|
|
641
|
+
for i in range(len(element_list)):
|
|
642
|
+
f.write(element_list[i] + ' ' + ' '.join([str(j) for j in coords[i]]) + '\n')
|
|
643
|
+
return
|
|
644
|
+
|
|
645
|
+
def make_workspace(directory):
|
|
646
|
+
if not os.path.exists(directory):
|
|
647
|
+
os.makedirs(directory)
|
|
648
|
+
return directory
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def stack_path(directory):
|
|
652
|
+
file_list = glob.glob(directory + '/*_[0-9].xyz') + glob.glob(directory + '/*_[0-9][0-9].xyz') + glob.glob(directory + '/*_[0-9][0-9][0-9].xyz') + glob.glob(directory + '/*_[0-9][0-9][0-9][0-9].xyz') + glob.glob(directory + '/*_[0-9][0-9][0-9][0-9][0-9].xyz') + glob.glob(directory + '/*_[0-9][0-9][0-9][0-9][0-9][0-9].xyz') + glob.glob(directory + '/*_[0-9][0-9][0-9][0-9][0-9][0-9][0-9].xyz')
|
|
653
|
+
|
|
654
|
+
with open(directory + '/path.xyz', 'w') as f:
|
|
655
|
+
for file in file_list:
|
|
656
|
+
with open(file, 'r') as g:
|
|
657
|
+
lines = g.read().splitlines()
|
|
658
|
+
for line in lines:
|
|
659
|
+
f.write(line + '\n')
|
|
660
|
+
return
|