Skip to content

Commit a8226e0

Browse files
andyclaude
andcommitted
MOPAC: re-enable IR intensities, with an End that cannot starve (#103)
The parser was already written and verified. It was disabled on 2026-09-22 because its End anchor, "SYMMETRY NUMBER FOR POINT-GROUP", is not printed for every molecule or runtype, and eccejobmonitor treats reaching EOF inside an open block as fatal: FATAL ERROR: JobOutputGet: end of file encountered while reading parse type VIBIR Two traps stood between that and a working End, and both were hit here rather than reasoned about: "FORCE CONSTANT" appears INSIDE every VIBRATION stanza, so ending on it closes the block after the first mode and reports one mode's intensity as though it were all of them. The withdrawal note predicted exactly this for a blank line; the stanza field has the same shape. "SYMBOL FORCE CONSTANT" -- the header of the table that follows -- cannot match a stanza line and is present exactly once in every fixture that has a vibrations block. But with THERMO that table is a long way past the stanzas (438 to 647 in ch4_geovib) and the thermochemistry table sits between them at 492, so ending there swallowed [ENTHALPY]...'s Begin and stopped MOPAC thermochemistry being extracted at all. The suite caught it immediately: "expected parse type [ENTHALPY]... never delivered a block". The End is therefore an alternation. "SYSTEM IS A GROUND STATE" is printed straight after the stanzas whenever THERMO is on, closing the block before the thermochemistry; the table header closes it otherwise. Units say "arb. units" and mean it. MOPAC prints a transition dipole in Debye -- confirmed against its own manual, which gives the formula and explicitly offers no conversion -- and IR intensity goes as the square. The stored value is that square on a relative scale, so peak positions and relative heights are right and the y-axis is not comparable with another code's km/mol. The geovib case now pins VIBIR at length 9 alongside the thermochemistry, so the two cannot silently trade places again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFMUNbw2UpRPmgsqviAowb
1 parent d95da94 commit a8226e0

5 files changed

Lines changed: 102 additions & 48 deletions

File tree

‎scripts/parsers/mopac.desc‎

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -273,62 +273,70 @@ Frequency=last
273273
End=MASS-WEIGHTED COORDINATE ANALYSIS
274274
[END]
275275

276-
# IR INTENSITIES ARE DISABLED. The entry below is kept, commented out,
277-
# because the parser (mopac.vibir) is correct and verified -- what is
278-
# wrong is the framing, and the fix belongs with someone who can test it
279-
# live.
276+
# IR intensities, from the DESCRIPTION OF VIBRATIONS block. Without
277+
# VIBIR (or VIBRAM) NModePanel::fillGraph() returns false and
278+
# initialize() falls back to showTable(), which is why MOPAC alone showed
279+
# a table where every other code shows the spectrum plot.
280280
#
281-
# Shipped briefly on 2026-09-22 and withdrawn the same evening: it made
282-
# eccejobmonitor die on every MOPAC job.
281+
# THE End IS THE WHOLE STORY HERE. This shipped on 2026-09-22 anchored
282+
# on "SYMMETRY NUMBER FOR POINT-GROUP" and was withdrawn the same
283+
# evening, because it made eccejobmonitor die on every MOPAC job:
283284
#
284285
# FATAL ERROR: JobOutputGet: end of file encountered while reading
285286
# parse type VIBIR
286-
# eccejobmonitor terminating abnormally, exit status 1
287287
#
288-
# eccejobmonitor tails a file that is STILL BEING WRITTEN. If it matches
289-
# a Begin and then reaches EOF before finding the End, that is fatal --
290-
# not "try again next poll". So an End must be a string that is
291-
# guaranteed to appear, and "SYMMETRY NUMBER FOR POINT-GROUP" is not:
292-
# MOPAC does not print it for every molecule or every runtype.
288+
# The monitor tails a file that is still being written. Matching a
289+
# Begin and then reaching EOF without the End is fatal, not "try again
290+
# next poll", so an End has to be a string that reliably appears --
291+
# and MOPAC does not print that one for every molecule or runtype.
293292
#
294-
# This is invisible to tests/parsers, which replays COMPLETE files and
295-
# therefore always finds the End. Any End-anchored entry needs thinking
296-
# about at EOF, not just on a finished output.
293+
# The obvious replacement is a trap: "FORCE CONSTANT" appears INSIDE
294+
# every VIBRATION stanza, so ending on it would close the block after
295+
# the first mode and report one mode's intensity as though it were all
296+
# of them -- worse than nothing, and exactly what the withdrawal note
297+
# warned a blank line would do.
297298
#
298-
# A fix needs an End that always follows the block. A blank line will
299-
# not do -- MOPAC separates the individual VIBRATION stanzas with blank
300-
# lines, so it would close after the first one and report a single
301-
# mode's intensity as though it were all of them, which is worse than
302-
# nothing. See issue #103.
299+
# "SYMBOL FORCE CONSTANT" is the header of the force-constant table
300+
# that follows the block, and cannot match a stanza line (those read
301+
# " FORCE CONSTANT 0.2475", with no SYMBOL). Checked across every
302+
# MOPAC fixture: present exactly once in each output that has a
303+
# DESCRIPTION OF VIBRATIONS block, always after it, and absent from
304+
# every output that has no such block.
303305
#
304-
# # IR intensities, from the DESCRIPTION OF VIBRATIONS block. Without
305-
# # VIBIR (or VIBRAM) NModePanel::fillGraph() returns false and
306-
# # initialize() falls back to showTable(), which is why MOPAC alone showed
307-
# # a table where every other code shows the spectrum plot.
308-
# #
309-
# # Boundary check, per the End-line starvation hazard this file's header
310-
# # warns about: Begin is DESCRIPTION OF VIBRATIONS, which lies after
311-
# # [VIBFREQ]'s End (MASS-WEIGHTED COORDINATE ANALYSIS), so nothing is
312-
# # eaten from under it. End is SYMMETRY NUMBER FOR POINT-GROUP, which is
313-
# # no other entry's Begin -- [PNTGRP] anchors on MOLECULAR POINT GROUP,
314-
# # and every occurrence of that in a real FORCE or GEO+VIB run comes
315-
# # BEFORE this block, so consuming this line starves nothing.
316-
# #
317-
# # Skip=1 starts feeding at the line after Begin, which is blank;
318-
# # mopac.vibir scans for its own markers and ignores everything else, so
319-
# # the usual off-by-one that silently produces zero output does not apply.
320-
# #
321-
# # The block collapses degenerate modes (4 stanzas for CH4's 9 modes);
322-
# # mopac.vibir expands them back out. See its header for why that is
323-
# # structural rather than a frequency match, and for why the value is the
324-
# # square of the transition dipole on a relative scale.
325-
# [VIBIR]
326-
# Script=mopac.vibir
327-
# Begin=DESCRIPTION OF VIBRATIONS
328-
# Skip=1
329-
# Frequency=last
330-
# End=SYMMETRY NUMBER FOR POINT-GROUP
331-
# [END]
306+
# It is NOT sufficient on its own, and the alternation is not
307+
# decoration. With THERMO the force-constant table comes a long way
308+
# after the stanzas -- 438 to 647 in the ch4_geovib fixture -- and the
309+
# thermochemistry table sits between them at 492. Ending only on the
310+
# table header therefore swallowed [ENTHALPY]...'s Begin and starved it
311+
# completely: MOPAC thermochemistry stopped being extracted the moment
312+
# this entry was re-enabled. "SYSTEM IS A GROUND STATE" is printed
313+
# immediately after the stanzas whenever THERMO is on, so the
314+
# alternation closes the block before the thermochemistry in that case
315+
# and on the table header otherwise.
316+
#
317+
# Boundary check per this file's header: Begin lies after [VIBFREQ]'s
318+
# End (MASS-WEIGHTED COORDINATE ANALYSIS), so nothing is eaten from
319+
# under it, and the End line is no other entry's Begin.
320+
#
321+
# Skip=1 starts feeding at the line after Begin, which is blank;
322+
# mopac.vibir scans for its own markers and ignores everything else, so
323+
# the usual off-by-one that silently produces zero output does not
324+
# apply.
325+
#
326+
# The block collapses degenerate modes (4 stanzas for CH4's 9 modes);
327+
# mopac.vibir expands them back out, so VIBIR comes out the same length
328+
# as VIBFREQ. The value is the square of the transition dipole on a
329+
# relative scale: MOPAC prints a transition dipole in Debye and gives no
330+
# conversion to km/mol, so the units say "arb. units" rather than
331+
# claiming an absolute intensity. Peak positions and relative heights
332+
# are right; the y-axis is not comparable with another code's.
333+
[VIBIR]
334+
Script=mopac.vibir
335+
Begin=DESCRIPTION OF VIBRATIONS
336+
Skip=1
337+
Frequency=last
338+
End=SYSTEM IS A GROUND STATE|SYMBOL\s+FORCE CONSTANT
339+
[END]
332340

333341
# " ZERO POINT ENERGY 10.115 KCAL/MOL" -- already in
334342
# kcal/mol, the Energy converter's base unit, so no conversion.

‎tests/parsers/cases.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,19 @@
10071007
# H.O.F. - RT. The two values below differ by exactly
10081008
# RT = 1.987204259 * 298.00 / 1000 kcal/mol, which is ORCA's
10091009
# own "H = U + kB*T" seen from the other side.
1010+
# VIBIR must be exactly as long as VIBFREQ. MOPAC's
1011+
# DESCRIPTION OF VIBRATIONS collapses degenerate modes --
1012+
# four stanzas for CH4's nine modes -- and mopac.vibir
1013+
# expands them back out; a short vector would silently
1014+
# misalign the spectrum against the frequencies.
1015+
#
1016+
# Its presence here is also the regression guard for the
1017+
# End that had to be an alternation: ending only on the
1018+
# force-constant table header swallowed the thermochemistry
1019+
# entry's Begin on this very fixture, so if VIBIR appears
1020+
# and the thermochemistry below does not, the End has drifted
1021+
# too far again.
1022+
'VIBIR': dict(blocks=1, keys={'VIBIR': {'size': '9'}}),
10101023
'ENTHALPY][ETHERM][ENTROPY][HEATCAP][THERMOTEMP': dict(
10111024
blocks=1, keys={
10121025
'ENTHALPY': {'values': '-0.022954235107774',

‎tests/parsers/expected/mopac-ch4-force-nothermo.txt‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,17 @@ args: '.' 'Vibration' 'SE' 'PM7' '0'
119119
values: 0.000000000000000e+00 0.000000000000000e+00 -0.000000000000000e+00 -0.000000000000000e+00 -1.829000000000000e-01 -1.705000000000000e-01 -1.724000000000000e-01 -6.080000000000000e-02 1.705000000000000e-01 2.254000000000000e-01 2.330000000000000e-02 -1.055000000000000e-01 -5.300000000000000e-02 2.204000000000000e-01 1.055000000000000e-01 0.000000000000000e+00 -0.000000000000000e+00 0.000000000000000 ...[3036 chars]
120120
units: Angstrom
121121

122+
[VIBIR] Script=mopac.vibir Freq=last Skip=1 Lines=-
123+
Begin=DESCRIPTION OF VIBRATIONS
124+
End=SYSTEM IS A GROUND STATE|SYMBOL\s+FORCE CONSTANT
125+
begins=1 delivered=1 buffered_away=0
126+
block 1: begin-line=435 fed=38 rc=0 first-fed=''
127+
key: VIBIR
128+
size: 9
129+
rowlabels: 1 2 3 4 5 6 7 8 9
130+
values: 0.0000 0.0000 1.0935 1.0935 1.0935 0.4556 0.4556 0.4556 0.0000
131+
units: arb. units
132+
122133
[EZEROPT] Script=mopac.ezeropt Freq=last Skip=0 Lines=1
123134
Begin=ZERO POINT ENERGY
124135
begins=1 delivered=1 buffered_away=0

‎tests/parsers/expected/mopac-ch4-force.txt‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,17 @@ args: '.' 'Vibration' 'SE' 'RPM7' '0'
8888
values: -0.000000000000000e+00 -0.000000000000000e+00 -0.000000000000000e+00 0.000000000000000e+00 2.495000000000000e-01 1.590000000000000e-02 2.352000000000000e-01 8.320000000000000e-02 -1.590000000000000e-02 -1.306000000000000e-01 -1.571000000000000e-01 1.440000000000000e-01 -1.046000000000000e-01 -1.755000000000000e-01 -1.440000000000000e-01 -0.000000000000000e+00 0.000000000000000e+00 0.00000000000000 ...[3034 chars]
8989
units: Angstrom
9090

91+
[VIBIR] Script=mopac.vibir Freq=last Skip=1 Lines=-
92+
Begin=DESCRIPTION OF VIBRATIONS
93+
End=SYSTEM IS A GROUND STATE|SYMBOL\s+FORCE CONSTANT
94+
begins=1 delivered=1 buffered_away=0
95+
block 1: begin-line=254 fed=38 rc=0 first-fed=''
96+
key: VIBIR
97+
size: 9
98+
rowlabels: 1 2 3 4 5 6 7 8 9
99+
values: 0.0000 0.0000 1.0920 1.0920 1.0920 0.4558 0.4558 0.4558 0.0000
100+
units: arb. units
101+
91102
[EZEROPT] Script=mopac.ezeropt Freq=last Skip=0 Lines=1
92103
Begin=ZERO POINT ENERGY
93104
begins=1 delivered=1 buffered_away=0

‎tests/parsers/expected/mopac-ch4-geovib.txt‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,17 @@ args: '.' 'GeoVib' 'SE' 'RPM7' '0'
140140
values: 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 -0.000000000000000e+00 -2.127000000000000e-01 -1.314000000000000e-01 -2.005000000000000e-01 -7.090000000000000e-02 1.314000000000000e-01 2.076000000000000e-01 6.590000000000000e-02 -1.228000000000000e-01 -7.100000000000000e-03 2.177000000000000e-01 1.228000000000000e-01 0.000000000000000e+00 0.000000000000000e+00 -0.000000000000000e ...[3035 chars]
141141
units: Angstrom
142142

143+
[VIBIR] Script=mopac.vibir Freq=last Skip=1 Lines=-
144+
Begin=DESCRIPTION OF VIBRATIONS
145+
End=SYSTEM IS A GROUND STATE|SYMBOL\s+FORCE CONSTANT
146+
begins=1 delivered=1 buffered_away=0
147+
block 1: begin-line=438 fed=38 rc=0 first-fed=''
148+
key: VIBIR
149+
size: 9
150+
rowlabels: 1 2 3 4 5 6 7 8 9
151+
values: 0.0000 0.0000 1.0922 1.0922 1.0922 0.4556 0.4556 0.4556 0.0000
152+
units: arb. units
153+
143154
[EZEROPT] Script=mopac.ezeropt Freq=last Skip=0 Lines=1
144155
Begin=ZERO POINT ENERGY
145156
begins=1 delivered=1 buffered_away=0

0 commit comments

Comments
 (0)