Skip to content

Commit d243bde

Browse files
authored
Merge pull request #505 from RagnarB83/NEW
Bugfixes and some small new features Photoelectron: CCSD(T) option, OODFT moread and outputfiles fix DLFIND interface: bugfix for TSOpt create_ML_training_data: more data files written, some restructuring (Madhur) PDBx/mmCIF file support for Fragment and OpenMMTHeory flexible_align_pdb: fix fixes for elements dictionaries: lanthanides and actinides, also dummy atom support for certain FFs pySCF: skala use fix kspies: minor fixes crest interface: support for socket CFOUR: bugfix for NEB runs (Madhur) SellaIRC option (undocumented)
2 parents 2558f59 + 91fd035 commit d243bde

19 files changed

Lines changed: 964 additions & 242 deletions

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ Interfaces to popular QM codes: ORCA, xTB, PySCF, MRCC, ccpy, Psi4, Dalton, CFou
1111
Excellent environment for writing simple or complex computational chemistry workflows.
1212

1313
**Citation**
14-
If ASH is useful in your research please cite us:
15-
`ASH: a Multi-scale, Multi-theory Modeling program <https://onlinelibrary.wiley.com/doi/10.1002/jcc.70359>`_
16-
R. Bjornsson*, J. Comput. Chem 2026, 47, e70359.
14+
If ASH is useful in your research please cite us: `ASH: a Multi-scale, Multi-theory Modeling program <https://onlinelibrary.wiley.com/doi/10.1002/jcc.70359>`_
15+
R. Bjornsson, J. Comput. Chem 2026, 47, e70359.
1716

1817
**In case of problems:**
1918
Please open an issue on Github and we will try to fix any problems as soon as possible.

ash/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@
219219
from ash.interfaces.interface_dlfind import DLFIND_optimizer,DLFIND_optimizerClass
220220

221221
# Sella
222-
from ash.interfaces.interface_sella import SellaOptimizer, SellaoptimizerClass
222+
from ash.interfaces.interface_sella import SellaOptimizer, SellaoptimizerClass, SellaIRC
223223

224224
# Other
225225
import ash.interfaces.interface_crest

ash/dictionaries_lists.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def __init__(self, name, symbol, atomnumber):
6464
self.atomnumber = atomnumber
6565

6666

67-
element_dict_atname = {'h': Element('hydrogen', 'H', 1), 'he': Element('helium', 'He', 2),
67+
element_dict_atname = {'m': Element('dummy', 'M', 0), 'h': Element('hydrogen', 'H', 1), 'he': Element('helium', 'He', 2),
6868
'li': Element('lithium', 'Li', 3),
6969
'be': Element('beryllium', 'Be', 4), 'b': Element('boron', 'B', 5),
7070
'c': Element('carbon', 'C', 6), 'n': Element('nitrogen', 'N', 7),
@@ -93,16 +93,31 @@ def __init__(self, name, symbol, atomnumber):
9393
'sb': Element('antimony', 'Sb', 51), 'te': Element('tellurium', 'Te', 52),
9494
'i': Element('iodine', 'I', 53), 'xe': Element('xenon', 'Xe', 54),
9595
'cs': Element('cesium', 'Cs', 55), 'ba': Element('barium', 'Ba', 56),
96+
'la': Element('lanthanum', 'La', 57), 'ce': Element('cerium', 'Ce', 58), 'pr': Element('praseodymium', 'Pr', 59),
97+
'nd': Element('neodymium', 'Nd', 60), 'pm': Element('promethium', 'Pm', 61), 'sm': Element('samarium', 'Sm', 62),
98+
'eu': Element('europium', 'Eu', 63), 'gd': Element('gadolinium', 'Gd', 64), 'tb': Element('terbium', 'Tb', 65),
99+
'dy': Element('dysprosium', 'Dy', 66), 'ho': Element('holmium', 'Ho', 67), 'er': Element('erbium', 'Er', 68),
100+
'tm': Element('thulium', 'Tm', 69), 'yb': Element('ytterbium', 'Yb', 70), 'lu': Element('lutetium', 'Lu', 71),
96101
'hf': Element('hafnium', 'Hf', 72), 'ta': Element('tantalum', 'Ta', 73),
97102
'w': Element('tungsten', 'W', 74), 're': Element('rhenium', 'Re', 75),
98103
'os': Element('osmium', 'Os', 76),
99104
'ir': Element('iridium', 'Ir', 77), 'pt': Element('platinum', 'Pt', 78),
100105
'au': Element('gold', 'Au', 79), 'hg': Element('mercury', 'Hg', 80), 'tl': Element('thallium', 'Tl', 81),
101106
'pb': Element('lead', 'Pb', 82),'bi': Element('bismuth', 'Bi', 83),'po': Element('polonium', 'Po', 84),
102-
'at': Element('astatine', 'At', 85), 'rn': Element('radon', 'Rn', 86)}
107+
'at': Element('astatine', 'At', 85), 'rn': Element('radon', 'Rn', 86), 'fr': Element('francium', 'Fr', 87),
108+
'ra': Element('radium', 'Ra', 88), 'ac': Element('actinium', 'Ac', 89),
109+
'th': Element('thorium', 'Th', 90), 'pa': Element('protactinium', 'Pa', 91), 'u': Element('uranium', 'U', 92),
110+
'np': Element('neptunium', 'Np', 93), 'pu': Element('plutonium', 'Pu', 94), 'am': Element('americium', 'Am', 95),
111+
'cm': Element('curium', 'Cm', 96), 'bk': Element('berkelium', 'Bk', 97), 'cf': Element('californium', 'Cf', 98),
112+
'es': Element('einsteinium', 'Es', 99), 'fm': Element('fermium', 'Fm', 100), 'md': Element('mendelevium', 'Md', 101),
113+
'no': Element('nobelium', 'No', 102), 'lr': Element('lawrencium', 'Lr', 103), 'rf': Element('rutherfordium', 'Rf', 104),
114+
'db': Element('dubnium', 'Db', 105), 'sg': Element('seaborgium', 'Sg', 106), 'bh': Element('bohrium', 'Bh', 107),
115+
'hs': Element('hassium', 'Hs', 108), 'mt': Element('meitnerium', 'Mt', 109), 'ds': Element('darmstadtium', 'Ds', 110),
116+
'rg': Element('roentgenium', 'Rg', 111), 'cn': Element('copernicium', 'Cn', 112), 'nh': Element('nihonium', 'Nh', 113),
117+
'fl': Element('flerovium', 'Fl', 114), 'mc': Element('moscovium', 'Mc', 115), 'lv': Element('livermorium', 'Lv', 116),
118+
'ts': Element('tennessine', 'Ts', 117), 'og': Element('oganesson', 'Og', 118)}
103119

104-
# NOTE: redundant? remove?
105-
element_dict_atnum = {1: Element('hydrogen', 'H', 1), 2: Element('helium', 'He', 2), 3: Element('lithium', 'Li', 3),
120+
element_dict_atnum = {0: Element('dummy', 'M', 0), 1: Element('hydrogen', 'H', 1), 2: Element('helium', 'He', 2), 3: Element('lithium', 'Li', 3),
106121
4: Element('beryllium', 'Be', 4), 5: Element('boron', 'B', 5), 6: Element('carbon', 'C', 6),
107122
7: Element('nitrogen', 'N', 7),
108123
8: Element('oxygen', 'O', 8), 9: Element('fluorine', 'F', 9), 10: Element('neon', 'Ne', 10),
@@ -129,11 +144,31 @@ def __init__(self, name, symbol, atomnumber):
129144
52: Element('tellurium', 'Te', 52), 53: Element('iodine', 'I', 53),
130145
54: Element('xenon', 'Xe', 54),
131146
55: Element('cesium', 'Cs', 55), 56: Element('barium', 'Ba', 56),
147+
57: Element('lanthanum', 'La', 57), 58: Element('cerium', 'Ce', 58), 59: Element('praseodymium', 'Pr', 59),
148+
60: Element('neodymium', 'Nd', 60), 61: Element('promethium', 'Pm', 61), 62: Element('samarium', 'Sm', 62),
149+
63: Element('europium', 'Eu', 63), 64: Element('gadolinium', 'Gd', 64),
150+
65: Element('terbium', 'Tb', 65), 66: Element('dysprosium', 'Dy', 66),
151+
67: Element('holmium', 'Ho', 67), 68: Element('erbium', 'Er', 68),
152+
69: Element('thulium', 'Tm', 69), 70: Element('ytterbium', 'Yb', 70),
153+
71: Element('lutetium', 'Lu', 71),
132154
72: Element('hafnium', 'Hf', 72), 73: Element('tantalum', 'Ta', 73),
133155
74: Element('tungsten', 'W', 74), 75: Element('rhenium', 'Re', 75),
134156
76: Element('osmium', 'Os', 76),
135157
77: Element('iridium', 'Ir', 77), 78: Element('platinum', 'Pt', 78),
136-
79: Element('gold', 'Au', 79), 80: Element('mercury', 'Hg', 80)}
158+
79: Element('gold', 'Au', 79), 80: Element('mercury', 'Hg', 80),
159+
81: Element('thallium', 'Tl', 81), 82: Element('lead', 'Pb', 82),
160+
83: Element('bismuth', 'Bi', 83), 84: Element('polonium', 'Po', 84), 85: Element('astatine', 'At', 85),
161+
86: Element('radon', 'Rn', 86), 87: Element('francium', 'Fr', 87), 88: Element('radium', 'Ra', 88),
162+
89: Element('actinium', 'Ac', 89), 90: Element('thorium', 'Th', 90), 91: Element('protactinium', 'Pa', 91), 92: Element('uranium', 'U', 92),
163+
93: Element('neptunium', 'Np', 93), 94: Element('plutonium', 'Pu', 94), 95: Element('americium', 'Am', 95),
164+
96: Element('curium', 'Cm', 96), 97: Element('berkelium', 'Bk', 97), 98: Element('californium', 'Cf', 98),
165+
99: Element('einsteinium', 'Es', 99), 100: Element('fermium', 'Fm', 100), 101: Element('mendelevium', 'Md', 101),
166+
102: Element('nobelium', 'No', 102), 103: Element('lawrencium', 'Lr', 103), 104: Element('rutherfordium', 'Rf', 104),
167+
105: Element('dubnium', 'Db', 105), 106: Element('seaborgium', 'Sg', 106), 107: Element('bohrium', 'Bh', 107),
168+
108: Element('hassium', 'Hs', 108), 109: Element('meitnerium', 'Mt', 109), 110: Element('darmstadtium', 'Ds', 110),
169+
111: Element('roentgenium', 'Rg', 111), 112: Element('copernicium', 'Cn', 112), 113: Element('nihonium', 'Nh', 113),
170+
114: Element('flerovium', 'Fl', 114), 115: Element('moscovium', 'Mc', 115), 116: Element('livermorium', 'Lv', 116),
171+
117: Element('tennessine', 'Ts', 117), 118: Element('oganesson', 'Og', 118)}
137172

138173
# Ground-state spin multiplicities of all elements
139174
atom_spinmults = {'H': 2, 'He': 1, 'Li': 2, 'Be': 1, 'B': 2, 'C': 3, 'N': 4, 'O': 3, 'F': 2, 'Ne': 1, 'Na': 2, 'Mg': 1,

ash/interfaces/interface_CFour.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,16 @@ def __init__(self, cfourdir=None, printlevel=2, cfouroptions=None, numcores=1,
151151
except shutil.SameFileError:
152152
pass
153153

154-
154+
#Storing absolute path of staged GENBAS file and caching ECPDATA contents in memory.
155+
#Necessary because some job-types (e.g. NEB via Knarr, Singlepoint_parallel) change into
156+
#subdirectories (image_N, worker dirs) before calling run(). The run-method will
157+
#restore these files in the current working directory if they are missing there.
158+
#Note: ECPDATA must be cached in memory since cleanup() (called below) deletes the file.
159+
self.genbas_path = os.path.abspath('GENBAS') if os.path.isfile('GENBAS') else None
160+
self.ecpdata_content = None
161+
if os.path.isfile('ECPDATA'):
162+
with open('ECPDATA') as ecpfile:
163+
self.ecpdata_content = ecpfile.read()
155164

156165
#Clean-up of possible old Cfour files before beginning
157166
#TODO: Skip cleanup of chosen files?
@@ -321,6 +330,25 @@ def run(self, current_coords=None, current_MM_coords=None, MMcharges=None, qm_el
321330
print("no current_coords")
322331
ashexit()
323332

333+
#Making sure GENBAS (and ECPDATA if available) is present in current working directory.
334+
#Jobs like NEB (Knarr) and Singlepoint_parallel change into subdirectories (image_N, worker dirs)
335+
#where the basis files staged by __init__ are not present, causing xcfour to crash.
336+
if self.genbas_path is not None and not os.path.isfile("GENBAS"):
337+
if os.path.isfile(self.genbas_path):
338+
print(f"No GENBAS file found in current directory. Copying GENBAS from: {self.genbas_path}")
339+
shutil.copyfile(self.genbas_path, "GENBAS")
340+
if self.ecpdata_content is not None and not os.path.isfile("ECPDATA"):
341+
with open("ECPDATA", "w") as ecpfile:
342+
ecpfile.write(self.ecpdata_content)
343+
344+
#Resetting EXTERN_POT for this run unless pointcharges are used (QM/MM job).
345+
#Energy-only runs with PROP active set self.EXTERN_POT='ON' (dummy-pcharges trick to prevent
346+
#reorientation), which is sticky on the object. A subsequent gradient run in a fresh directory
347+
#(e.g. NEB image dirs) would then require a pcharges file that does not exist there,
348+
#crashing CFour (mkvmol.f: End of file reading pcharges).
349+
if PC is not True:
350+
self.EXTERN_POT='OFF'
351+
324352
if self.DBOC is True:
325353
DBOC=True
326354

ash/interfaces/interface_ORCA.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,7 @@ def create_orca_input_pc(name,elems,coords,orcasimpleinput,orcablockinput,charge
18281828
orcafile.write('! Freq' + '\n')
18291829
if moreadfile is not None:
18301830
print("MOREAD option active. Will read orbitals from file:", moreadfile)
1831-
orcafile.write('! MOREAD' + '\n')
1831+
orcafile.write('\n! MOREAD' + '\n')
18321832
orcafile.write('%moinp \"{}\"'.format(moreadfile) + '\n')
18331833
orcafile.write('%pointcharges "{}"\n'.format(pcfile))
18341834
orcafile.write(orcablockinput + '\n')
@@ -1904,7 +1904,7 @@ def create_orca_input_plain(name,elems,coords,orcasimpleinput,orcablockinput,cha
19041904
orcafile.write('! Freq' + '\n')
19051905
if moreadfile is not None:
19061906
print("MOREAD option active. Will read orbitals from file:", moreadfile)
1907-
orcafile.write('! MOREAD' + '\n')
1907+
orcafile.write('\n! MOREAD' + '\n')
19081908
orcafile.write('%moinp \"{}\"'.format(moreadfile) + '\n')
19091909
orcafile.write(orcablockinput)
19101910
if atomstoflip is not None:
@@ -2000,6 +2000,7 @@ def grabspinpop_ORCA(chargemodel,outputfile):
20002000
coordgrab=False
20012001
spinpops=[]
20022002
BS=False #if broken-symmetry job
2003+
numatoms = int(pygrep('Number of atoms ...', outputfile)[-1])
20032004
#if
20042005
if len(pygrep2("WARNING: Broken symmetry calculations", outputfile)):
20052006
BS=True
@@ -2032,7 +2033,9 @@ def grabspinpop_ORCA(chargemodel,outputfile):
20322033
#If BS then we have grabbed charges for both high-spin and BS solution
20332034
if BS is True:
20342035
print("Broken-symmetry job detected. Only taking BS-state populations")
2035-
spinpops=spinpops[int(len(spinpops)/2):]
2036+
#spinpops=spinpops[int(len(spinpops)/2):]
2037+
if len(spinpops) != numatoms:
2038+
spinpops=spinpops[-numatoms:]
20362039
#if len(spinpops) == 0:
20372040
# print("Warning: No spinpopulations were found in ORCA outputfile")
20382041
return spinpops

0 commit comments

Comments
 (0)