Skip to content

Commit 96af2ae

Browse files
andyclaude
andcommitted
Built-in basis names: extend ORCA and Gaussian 16 tables, verified by energy (#118)
ORCA +47 names (pc-n/pcseg/pcSseg and aug-, cc-pCV/pwCV families, -DK, IGLO-II/III, Ahlrichs SV/SVP/TZV, MINI/MIDI, 3-21GSP, 4-22GSP, UGBS); Gaussian 16 +7 and 6-31G(3df,3pd) removed (differs from ECCE's set by 3.4e-3 Eh). tests/basisload/named_basis_check.py runs each keyword and ECCE's own primitives (TGBSConfig::dump via std2<Code>) in the real code and compares function count and RHF energy; results file kept. tests/basis pins a representative mapping and the names that must not map. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFPK14wBcLD1s4dYUowpP4
1 parent a1d9f9b commit 96af2ae

7 files changed

Lines changed: 716 additions & 3 deletions

File tree

‎scripts/parsers/wrGaussian16GBS.pm‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,21 @@ sub setupBasisTranslation {
587587
$NameToBasis{"def2-tzvpp"} = "def2TZVPP"; # nbf 59
588588
$NameToBasis{"def2-qzvp"} = "def2QZVP"; # nbf 117
589589

590+
# Added for #118 from tests/basisload/named_basis_check.py: same function
591+
# count and an RHF energy within 1e-5 Eh of ECCE's own primitives on
592+
# water, under Gaussian 16 (5D 7F). 6-31G(3df,3pd) was removed from
593+
# this table: Gaussian's set differs from ECCE's by 3.4e-3 Eh, so
594+
# naming it silently changed the basis. Gaussian rejects cc-pVDZ-DK,
595+
# cc-pCV*/cc-pwCV*, d-aug-cc-p*, pc-*/pcseg-*, IGLO-* and cc-pV(X+d)Z.
596+
597+
$NameToBasis{"6-31++g*"} = "6-31++G*";
598+
$NameToBasis{"ahlrichs tzv"} = "TZV";
599+
$NameToBasis{"ahlrichs vdz"} = "SV";
600+
$NameToBasis{"ahlrichs pvdz"} = "SVP";
601+
$NameToBasis{"dzp (dunning)"} = "D95**";
602+
$NameToBasis{"sto-6g"} = "STO-6G";
603+
$NameToBasis{"ugbs"} = "UGBS";
604+
590605
$NameToBasis{"sto-2g"} = "sto-2g";
591606
$NameToBasis{"sto-3g"} = "sto-3g";
592607
$NameToBasis{"sto-3g*"} = "sto-3g*";
@@ -601,7 +616,6 @@ sub setupBasisTranslation {
601616
$NameToBasis{"6-31g"} = "6-31g";
602617
$NameToBasis{"6-31++g"} = "6-31++g";
603618
$NameToBasis{"6-31++g**"} = "6-31++g**";
604-
$NameToBasis{"6-31g(3df,3pd)"} = "6-31G(3df,3pd)";
605619
$NameToBasis{"6-311g"} = "6-311G";
606620
$NameToBasis{"6-311++g"} = "6-311++G";
607621
$NameToBasis{"6-311++g(3df,3pd)"} = "6-311++G(3df,3pd)";

‎tests/basis/fixtures/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ optionally an `ECP` block.
55

66
* `water_same.gbs` every atom on one mappable basis -- the route-card case
77
* `water_mixed.gbs` O on 6-31G* (mappable), H on IGLO-II (not) -- per-element
8+
* `water_gencontr.gbs` / `water_mixed_orca.gbs` general contraction (cc-pVDZ-style s); H on 6-31++G, which ORCA rejects (IGLO-II is now mappable in ORCA).
89
* `water_numonly.gbs` no `NameBasis` section at all -- everything explicit
910
* `water_aug_ccpvdz.gbs`, `water_aug_pwcvdz.gbs` one shared name each, to
1011
exercise NWChem's name blocklist (named vs. forced explicit)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
NameBasis
2+
basis "ao basis" cartesian print
3+
O library "6-31G*"
4+
H library "6-31++G"
5+
END
6+
EndNameBasis
7+
8+
9+
NumericalBasis
10+
basis "ao basis" cartesian print
11+
O S
12+
5484.6717000 0.0018311
13+
825.2349500 0.0139501
14+
188.0469600 0.0684451
15+
O SP
16+
15.5396160 -0.1107775 0.0708743
17+
3.5999336 -0.1480263 0.3397528
18+
O SP
19+
0.2700058 1.0000000 1.0000000
20+
H S
21+
18.7311370 0.0334946
22+
2.8253937 0.2347270
23+
0.6401217 0.8137573
24+
END
25+
EndNumericalBasis

‎tests/basis/run_tests.py‎

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
),
7979
dict(
8080
name="orca-mixed-per-element",
81-
fixture="water_mixed.gbs",
81+
fixture="water_mixed_orca.gbs",
8282
exporter="std2ORCA",
8383
expect=[("no route card", lambda t: "useRouteCard" not in t),
8484
("O named via NewGTO",
@@ -95,6 +95,18 @@
9595
("Pt explicit", lambda t: "2.63000000" in t),
9696
("ECP still written", lambda t: "PT-ECP" in t)],
9797
),
98+
dict(
99+
name="orca-general-contraction",
100+
fixture="water_gencontr.gbs",
101+
exporter="std2ORCA",
102+
# cc-pVDZ oxygen s is 8 primitives and TWO contractions. The ORCA
103+
# writer once wrote only the first, so water had 23 functions
104+
# instead of 24 and the energy was 0.3 Eh off.
105+
expect=[("both contractions of the 8-primitive s shell are written",
106+
lambda t: t.count(" S 8") == 2),
107+
("the second column's coefficients are there",
108+
lambda t: "-0.00016000" in t and "0.55736800" in t)],
109+
),
98110
dict(
99111
name="orca-ecp-per-element",
100112
fixture="pth_ecp.gbs",
@@ -355,6 +367,59 @@ def orca_ecp_rows_shape_ok(text):
355367
return True
356368

357369

370+
def name_tables():
371+
"""%NameToBasis in the ORCA and Gaussian 16 writers.
372+
373+
Every entry was run against the real code (tests/basisload/
374+
named_basis_check.py) and judged by energy against ECCE's own
375+
primitives. This pins a representative set -- a name that silently
376+
maps to a different basis, or a verified name that goes missing --
377+
without needing either code. ECCE name -> keyword, exactly.
378+
"""
379+
def table(path):
380+
text = open(os.path.join(PARSERS, path)).read()
381+
return dict(re.findall(r'\$NameToBasis\{"([^"]+)"\}\s*=\s*"([^"]+)"',
382+
text))
383+
384+
must = {
385+
"wrORCAGBS.pm": {
386+
"6-31g*": "6-31G*", "cc-pvdz": "cc-pVDZ",
387+
"aug-cc-pvtz": "aug-cc-pVTZ", "cc-pvtz-dk": "cc-pVTZ-DK",
388+
"cc-pwcvtz": "cc-pwCVTZ", "aug-cc-pcvdz": "aug-cc-pCVDZ",
389+
"pc-2": "pc-2", "pcseg-2": "pcseg-2", "iglo-ii": "IGLO-II",
390+
"def2-svp(p)": "def2-SV(P)", "ahlrichs vdz": "SV",
391+
"3-21gsp": "3-21GSP", "def2-tzvp": "def2-TZVP"},
392+
"wrGaussian16GBS.pm": {
393+
"6-31g*": "6-31G*", "6-31++g*": "6-31++G*",
394+
"cc-pvdz": "cc-pvdz", "def2-svp": "def2SVP",
395+
"def2-svp(p)": "def2SVPP", "midi!": "midix",
396+
"dz (dunning)": "d95", "ahlrichs vdz": "SV",
397+
"dzp (dunning)": "D95**", "sto-6g": "STO-6G"},
398+
}
399+
# Rejected by the code or a different basis from ECCE's under the
400+
# same name: naming any of these must stay impossible.
401+
never = {
402+
"wrORCAGBS.pm": ["pcseg-0", "3-21g*", "4-31g", "6-31++g",
403+
"6-31++g*", "d-aug-cc-pvdz", "cc-pv(d+d)z",
404+
"dz (dunning)"],
405+
"wrGaussian16GBS.pm": ["6-31g(3df,3pd)", "cc-pvdz-dk", "cc-pcvdz",
406+
"d-aug-cc-pvdz", "pc-2", "iglo-ii"],
407+
}
408+
problems = []
409+
for path in must:
410+
got = table(path)
411+
for name, kw in must[path].items():
412+
if got.get(name) != kw:
413+
problems.append("%s: %r should map to %r, maps to %r"
414+
% (path, name, kw, got.get(name)))
415+
for name in never[path]:
416+
if name in got:
417+
problems.append("%s: %r is in the table but the code "
418+
"rejects it or its set differs from ECCE's"
419+
% (path, name))
420+
return problems
421+
422+
358423
def run(case):
359424
path = os.path.join(PARSERS, case["exporter"])
360425
with open(os.path.join(FIXTURES, case["fixture"])) as handle:
@@ -422,6 +487,10 @@ def main():
422487
for finding in mo_ordering_matches_the_code():
423488
failures.append("MO ordering: " + finding)
424489

490+
checks += 1
491+
for finding in name_tables():
492+
failures.append("name tables: " + finding)
493+
425494
checks += 1
426495
for finding in basis_name_rules(args.verbose):
427496
failures.append("basis name rules: " + finding)

‎tests/basisload/loadBasis.C‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* --dump and --complete are described where they are defined.
3939
*/
4040
#include <string.h>
41+
#include <stdlib.h>
4142
#include <iostream>
4243
#include <strstream>
4344
#include <string>
@@ -212,7 +213,11 @@ static int dumpOne(int argc, char** argv)
212213
TGBSGroup* group = new TGBSGroup();
213214
group->insertOrbitalGBS(argv[3], list, true);
214215
config.insertGBSGroup(argv[5], group);
215-
const char* text = config.dump("NWChem", string(argv[6]) == "named");
216+
// LOADBASIS_CODE picks the code whose GBSExport script formats the
217+
// text (default NWChem), so a deck can be built for ORCA or Gaussian.
218+
const char* codeName = getenv("LOADBASIS_CODE");
219+
const char* text = config.dump(codeName ? codeName : "NWChem",
220+
string(argv[6]) == "named");
216221
cout << text << endl;
217222
delete [] text;
218223
return 0;

0 commit comments

Comments
 (0)