|
| 1 | +#!/usr/bin/env perl |
| 2 | +################################################################################ |
| 3 | +# |
| 4 | +# Purpose: |
| 5 | +# Parses MOPAC's GRAPHF file (<job>.mgf) into MO/ORBENG/ORBOCC/ORBSYM and |
| 6 | +# the UHF beta counterparts. |
| 7 | +# |
| 8 | +# WHY THIS RATHER THAN THE PRINTED EIGENVECTORS. mopac.mo reads the |
| 9 | +# human-readable "EIGENVECTORS" block out of the main output, which works |
| 10 | +# but is the weaker source in three ways: |
| 11 | +# |
| 12 | +# precision the printed block carries four decimals; this file carries |
| 13 | +# Fortran D-notation doubles. |
| 14 | +# occupancy the printed block has none, so mopac.orbocc has to |
| 15 | +# reconstruct it from the atomic orbital populations plus |
| 16 | +# the open-shell count ECCE passes in argv. Here it is |
| 17 | +# simply the second field of each ORBITAL line. |
| 18 | +# shape the printed block is fixed-width and column-grouped, the |
| 19 | +# exact shape that merged two coefficients into one for |
| 20 | +# ORCA (issue #108). This file is free-form. |
| 21 | +# |
| 22 | +# Real MOPAC 23.1.2 shape: |
| 23 | +# |
| 24 | +# 3 MOPAC-Graphical data Version 23.1.2 |
| 25 | +# 8 0.0000000 -0.0000030 0.1149079 -0.6491 <- Z x y z charge |
| 26 | +# 1 0.0000000 0.7594689 -0.4644394 0.3246 |
| 27 | +# 1 -0.0000000 -0.7594570 -0.4644300 0.3246 |
| 28 | +# 5.9723090 2.3490170 0.0000000 <- zeta s p d |
| 29 | +# 1.2602370 0.0000000 0.0000000 |
| 30 | +# 1.2602370 0.0000000 0.0000000 |
| 31 | +# ORBITAL 2 1a1 -29.6190 <- occ sym E(eV) |
| 32 | +# 0.85155170D+00-0.28614321D-16 ... <- coefficients |
| 33 | +# |
| 34 | +# The atom count on line 1 gives the size of BOTH leading blocks: one |
| 35 | +# coordinate line and one exponent line per atom. The exponent lines are |
| 36 | +# the Slater exponents of the valence basis, which is what a future |
| 37 | +# 3-D orbital renderer would need; nothing reads them yet, so they are |
| 38 | +# deliberately not emitted as a property rather than shipped unused. |
| 39 | +# |
| 40 | +# Energies are eV here and Hartree in ECCE (MoPanel hard-codes the column |
| 41 | +# label), so they are converted. |
| 42 | +# |
| 43 | +# The symmetry label is a single token such as "1a1", "2b2" or "1a''" -- |
| 44 | +# an index within the irrep followed by the irrep itself. Only the irrep |
| 45 | +# is kept: MoPanel::fillGraph() groups the orbital plot by this string, |
| 46 | +# so keeping the index would put every orbital in a group of its own. |
| 47 | +# |
| 48 | +################################################################################ |
| 49 | + |
| 50 | +$| = 1; |
| 51 | + |
| 52 | +use constant EV_PER_HARTREE => 27.211386245988; |
| 53 | + |
| 54 | +my @lines; |
| 55 | +while (<STDIN>) { chomp; push(@lines, $_); } |
| 56 | + |
| 57 | +exit(0) if (!@lines); |
| 58 | + |
| 59 | +# Line 1 begins with the atom count. |
| 60 | +my $natoms = 0; |
| 61 | +$natoms = $1 if ($lines[0] =~ /^\s*(\d+)\s/); |
| 62 | +exit(0) if ($natoms <= 0); |
| 63 | + |
| 64 | +# Coordinates then exponents, one line each per atom. The number of |
| 65 | +# basis functions is not stated anywhere, so it is taken from the |
| 66 | +# coefficient count of the first orbital instead. |
| 67 | +my $idx = 1 + 2 * $natoms; |
| 68 | + |
| 69 | +my (@occ, @eng, @sym, @coeff); |
| 70 | +while ($idx <= $#lines) { |
| 71 | + my $line = $lines[$idx]; |
| 72 | + if ($line =~ /^\s*ORBITAL\s+(\S+)\s+(\S+)\s+(-?[\d.]+)\s*$/) { |
| 73 | + my ($o, $s, $e) = ($1, $2, $3); |
| 74 | + # "1a1" -> "a1"; "2a''" -> "a''". A label that is nothing but the |
| 75 | + # irrep is left alone. |
| 76 | + $s =~ s/^\d+//; |
| 77 | + push(@occ, $o); |
| 78 | + push(@eng, $e / EV_PER_HARTREE); |
| 79 | + push(@sym, $s); |
| 80 | + push(@coeff, []); |
| 81 | + $idx++; |
| 82 | + # Coefficients run until the next line that is not purely numbers. |
| 83 | + # |
| 84 | + # Terminating on a whitelist of what a coefficient line looks like, |
| 85 | + # rather than a blacklist of what follows, because more follows than |
| 86 | + # is obvious: after the last orbital MOPAC writes an |
| 87 | + # "INVERSE_MATRIX[6x6]=" block of the same D-notation numbers, and a |
| 88 | + # loop that stopped only at "ORBITAL" or "Keywords:" swallowed all |
| 89 | + # of it into the final orbital -- 27 coefficients where the other |
| 90 | + # five had 6. That then failed the rectangular check below and the |
| 91 | + # script emitted nothing at all, silently. |
| 92 | + while ($idx <= $#lines && |
| 93 | + $lines[$idx] =~ /^[-+\s\d.DdEe]+$/ && |
| 94 | + $lines[$idx] =~ /\d[DdEe][-+]?\d/) { |
| 95 | + my $text = $lines[$idx]; |
| 96 | + # Fortran D exponents, and values packed without separators: |
| 97 | + # "-0.63614535D-01-0.45803202D-14" is two numbers, not one. |
| 98 | + while ($text =~ /(-?\d*\.\d+[DdEe][-+]?\d+)/g) { |
| 99 | + my $v = $1; |
| 100 | + $v =~ s/[Dd]/e/; |
| 101 | + push(@{$coeff[-1]}, $v + 0); |
| 102 | + } |
| 103 | + $idx++; |
| 104 | + } |
| 105 | + next; |
| 106 | + } |
| 107 | + $idx++; |
| 108 | +} |
| 109 | + |
| 110 | +my $nmo = scalar(@eng); |
| 111 | +exit(0) if ($nmo == 0); |
| 112 | + |
| 113 | +my $nbas = scalar(@{$coeff[0]}); |
| 114 | +exit(0) if ($nbas == 0); |
| 115 | +# Every orbital must have the same number of coefficients, or the table |
| 116 | +# is not rectangular and PropTable rejects it on load with "input vector |
| 117 | +# length does not match rows*columns" -- which presents as the Orbitals |
| 118 | +# panel refusing to compute, not as a parse error. |
| 119 | +foreach my $c (@coeff) { |
| 120 | + exit(0) if (scalar(@$c) != $nbas); |
| 121 | +} |
| 122 | + |
| 123 | +# UHF writes the alpha set then the beta set into the same file, so twice |
| 124 | +# the basis size means both are present. |
| 125 | +my $half = ($nmo == 2 * $nbas) ? $nbas : $nmo; |
| 126 | + |
| 127 | +sub emitSet { |
| 128 | + my ($from, $count, $moKey, $engKey, $occKey, $symKey) = @_; |
| 129 | + return if ($count <= 0); |
| 130 | + |
| 131 | + print "key: $engKey\n"; |
| 132 | + print "size:\n$count\n"; |
| 133 | + print "values:\n"; |
| 134 | + printf("%.8f ", $eng[$from + $_]) foreach (0 .. $count - 1); |
| 135 | + print "\nunits:\nHartree\n"; |
| 136 | + print "END\n"; |
| 137 | + |
| 138 | + print "key: $occKey\n"; |
| 139 | + print "size:\n$count\n"; |
| 140 | + print "values:\n"; |
| 141 | + printf("%.4f ", $occ[$from + $_]) foreach (0 .. $count - 1); |
| 142 | + print "\nunits:\nelectrons\n"; |
| 143 | + print "END\n"; |
| 144 | + |
| 145 | + print "key: $symKey\n"; |
| 146 | + print "size:\n$count\n"; |
| 147 | + print "rowlabels:\n"; |
| 148 | + print " " . ($_ + 1) . " " foreach (0 .. $count - 1); |
| 149 | + print "\nvalues:\n"; |
| 150 | + print join(" ", @sym[$from .. $from + $count - 1]), "\n"; |
| 151 | + print "END\n"; |
| 152 | + |
| 153 | + print "key: $moKey\n"; |
| 154 | + print "size:\n$count $nbas\n"; |
| 155 | + print "rowlabels:\n"; |
| 156 | + for my $m (1 .. $count) { |
| 157 | + print "MO-$m "; |
| 158 | + print "\n" if (($m % 8) == 0); |
| 159 | + } |
| 160 | + print "\ncolumnlabels:\n"; |
| 161 | + for my $b (1 .. $nbas) { |
| 162 | + print "BasFun-$b "; |
| 163 | + print "\n" if (($b % 6) == 0); |
| 164 | + } |
| 165 | + print "\nvalues:\n"; |
| 166 | + for my $m (0 .. $count - 1) { |
| 167 | + my $row = $coeff[$from + $m]; |
| 168 | + printf("%.8f ", $_) foreach (@$row); |
| 169 | + print "\n"; |
| 170 | + } |
| 171 | + print "END\n"; |
| 172 | +} |
| 173 | + |
| 174 | +&emitSet(0, $half, "MO", "ORBENG", "ORBOCC", "ORBSYM"); |
| 175 | +&emitSet($half, $nmo - $half, |
| 176 | + "MOBETA", "ORBENGBETA", "ORBOCCBETA", "ORBSYMBETA") |
| 177 | + if ($nmo > $half); |
| 178 | +exit(0); |
0 commit comments