Skip to content

Commit 6d11907

Browse files
andyclaude
andcommitted
tests/basis: cover the basis exporters, and make their output deterministic
Nothing exercised scripts/parsers/std2<Code>, and those writers decide whether a calculation asks the code for a basis BY NAME or writes out every exponent and coefficient. Every bug found in them today was silent: a deck the code rejects, or one that quietly uses a different basis than the user chose. The new suite runs the real exporters over checked-in .gbs inputs -- the format TGBSConfig::dump() produces -- and covers the four decisions that matter: the route-card case, per-element naming in a mixed basis, the per-element ECP rule, and a configuration with no names at all. It needs only Python and perl. Self-tested by reintroducing the translated-name bug, which it reports as "emits the TRANSLATED name, not ECCE's". That self-test also turned up something the tests were not looking for: THE ELEMENT ORDER IN A GENERATED DECK WAS NONDETERMINISTIC. Perl randomises hash iteration order per process, so `foreach $atom (keys %gbs)` emitted the elements in a different order on each run -- the same calculation producing a byte-different input file every time. Chemically harmless, but it makes two generated decks impossible to diff and any golden-file test of these writers a coin toss. Confirmed directly: four consecutive runs of std2ORCA over one fixture gave "O H", "H O", "O H", "O H". Fixed by sorting the keys, in 13 loops across the four maintained writers (Gaussian 16, Gaussian 09, ORCA, NWChem). Ten consecutive runs of each now give one distinct output. wrGaussian03GBS.pm and wrGaussian98GBS.pm share the behaviour and are left alone, being retired. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFMUNbw2UpRPmgsqviAowb
1 parent 989fa87 commit 6d11907

17 files changed

Lines changed: 401 additions & 13 deletions

‎scripts/parsers/wrGaussian09GBS.pm‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,12 @@ sub writeGaussian09{
293293
$name_coordinants = delete $name_gbs{"coordinants"};
294294
$oldbasis = "";
295295
$useRouteCard = 1;
296-
foreach $atom (keys %name_gbs) {
296+
# sort: Perl randomises hash iteration order per process, so without
297+
# this the elements come out in a different order every run and the
298+
# same calculation generates a byte-different input file each time.
299+
# Harmless to the codes, but it makes two generated decks impossible
300+
# to diff and any golden-file test of this writer nondeterministic.
301+
foreach $atom (sort keys %name_gbs) {
297302
$lib_gbs = lc $name_gbs{$atom};
298303
# strip off quotes
299304
$lib_gbs =~ s/^\"//;
@@ -337,7 +342,7 @@ sub writeGaussian09{
337342
#
338343
# Write out information for each atom in the gbs
339344
#
340-
foreach $atom (keys %gbs)
345+
foreach $atom (sort keys %gbs)
341346
{
342347
#
343348
# Each atom has a list of orbitals associated with it.
@@ -451,7 +456,7 @@ sub writeGaussian09{
451456
# Write out information for each atom in the ecp
452457
#
453458
print "\n";
454-
foreach $atom (keys %ecp)
459+
foreach $atom (sort keys %ecp)
455460
{
456461
#
457462
# Each atom has a set of data associated with it

‎scripts/parsers/wrGaussian16GBS.pm‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,12 @@ sub writeGaussian16{
293293
$name_coordinants = delete $name_gbs{"coordinants"};
294294
$oldbasis = "";
295295
$useRouteCard = 1;
296-
foreach $atom (keys %name_gbs) {
296+
# sort: Perl randomises hash iteration order per process, so without
297+
# this the elements come out in a different order every run and the
298+
# same calculation generates a byte-different input file each time.
299+
# Harmless to the codes, but it makes two generated decks impossible
300+
# to diff and any golden-file test of this writer nondeterministic.
301+
foreach $atom (sort keys %name_gbs) {
297302
$lib_gbs = lc $name_gbs{$atom};
298303
# strip off quotes
299304
$lib_gbs =~ s/^\"//;
@@ -343,7 +348,7 @@ sub writeGaussian16{
343348
#
344349
# Write out information for each atom in the gbs
345350
#
346-
foreach $atom (keys %gbs)
351+
foreach $atom (sort keys %gbs)
347352
{
348353
#
349354
# Each atom has a list of orbitals associated with it.
@@ -485,7 +490,7 @@ sub writeGaussian16{
485490
# Write out information for each atom in the ecp
486491
#
487492
print "\n";
488-
foreach $atom (keys %ecp)
493+
foreach $atom (sort keys %ecp)
489494
{
490495
#
491496
# Each atom has a set of data associated with it

‎scripts/parsers/wrNWChemGBS.pm‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,12 @@ sub wrNWChem{
293293
%gbs = %{$bs{"name_gbs"}};
294294
}
295295
my $atom;
296-
foreach $atom (keys %gbs)
296+
# sort: Perl randomises hash iteration order per process, so without
297+
# this the elements come out in a different order every run and the
298+
# same calculation generates a byte-different input file each time.
299+
# Harmless to the codes, but it makes two generated decks impossible
300+
# to diff and any golden-file test of this writer nondeterministic.
301+
foreach $atom (sort keys %gbs)
297302
{
298303
if (!($atom =~ /coordinants/)) {
299304
if ($gbs{$atom} =~ /^\s*\"\s*aug/i ||
@@ -338,7 +343,7 @@ sub wrNWChem{
338343
}
339344
340345
my $atom;
341-
foreach $atom (keys %gbs)
346+
foreach $atom (sort keys %gbs)
342347
{
343348
my @orbitalList = @{$gbs{$atom}};
344349
@@ -367,7 +372,7 @@ sub wrNWChem{
367372
}
368373
#####ECP#######################
369374
print "ECP\n" if (exists $bs{"ecp"});
370-
foreach $atom (keys %ecp)
375+
foreach $atom (sort keys %ecp)
371376
{
372377
#
373378
# Each atom has a set of data associated with it
@@ -432,7 +437,7 @@ sub wrNWChem{
432437
}
433438
434439
my $atom;
435-
foreach $atom (keys %gbs)
440+
foreach $atom (sort keys %gbs)
436441
{
437442
print(" $atom library $gbs{$atom}\n");
438443
}

‎scripts/parsers/wrORCAGBS.pm‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ sub writeORCA{
4444
delete $name_gbs{"coordinants"};
4545
my $oldbasis = "";
4646
$useRouteCard = 1;
47-
foreach my $atom (keys %name_gbs) {
47+
# sort: Perl randomises hash iteration order per process, so without
48+
# this the elements come out in a different order every run and the
49+
# same calculation generates a byte-different input file each time.
50+
# Harmless to the codes, but it makes two generated decks impossible
51+
# to diff and any golden-file test of this writer nondeterministic.
52+
foreach my $atom (sort keys %name_gbs) {
4853
my $lib_gbs = lc $name_gbs{$atom};
4954
$lib_gbs =~ s/^\"//;
5055
$lib_gbs =~ s/\"$//;
@@ -66,7 +71,7 @@ sub writeORCA{
6671
print "%basis\n";
6772

6873
my $atom;
69-
foreach $atom (keys %gbs) {
74+
foreach $atom (sort keys %gbs) {
7075
my @orbitalList = @{$gbs{$atom}};
7176
my $en = $atom;
7277
$en = ucfirst(lc($en));
@@ -115,7 +120,7 @@ sub writeORCA{
115120
# Write out ECP information (unverified -- see header note)
116121
########################################
117122
@shells = ("s", "p", "d", "f", "g", "h", "i");
118-
foreach $atom (keys %ecp) {
123+
foreach $atom (sort keys %ecp) {
119124
my @ecpData = @{$ecp{$atom}};
120125
my $numElectrons = $ecpData[0];
121126
my @ecpComponents = @{$ecpData[1]};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
H 0
2+
6-31G*
3+
****
4+
PT 0
5+
S 2 1.00
6+
2.63000000 -2.60878100
7+
1.72000000 3.16705300
8+
****
9+
10+
PT 0
11+
PT-ECP 1 60
12+
p potential
13+
1
14+
2 52.86801740 -298.31781350
15+
s-p potential
16+
1
17+
0 409.44373580 2.73342180
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
H 0
2+
S 3 1.00
3+
18.73113700 0.03349460
4+
2.82539370 0.23472700
5+
0.64012170 0.81375730
6+
****
7+
O 0
8+
6-31G*
9+
****
10+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
useRouteCard 6-31G* cartesian
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
%basis
2+
NewGTO H "6-31G*" end
3+
NewGTO Pt
4+
S 2
5+
1 2.63000000 -2.60878100
6+
2 1.72000000 3.16705300
7+
end
8+
NewECP Pt
9+
N_core 60
10+
lmax p
11+
p 1
12+
52.8680174 -298.31781350 2.00000000
13+
s 1
14+
409.4437358 2.73342180 0.00000000
15+
end
16+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
%basis
2+
NewGTO H
3+
S 3
4+
1 18.73113700 0.03349460
5+
2 2.82539370 0.23472700
6+
3 0.64012170 0.81375730
7+
end
8+
NewGTO O "6-31G*" end
9+
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
%basis
2+
NewGTO H
3+
S 3
4+
1 18.73113700 0.03349460
5+
2 2.82539370 0.23472700
6+
3 0.64012170 0.81375730
7+
end
8+
NewGTO O
9+
S 3
10+
1 5484.67170000 0.00183110
11+
2 825.23495000 0.01395010
12+
3 188.04696000 0.06844510
13+
S 2
14+
1 15.53961600 -0.11077750
15+
2 3.59993360 -0.14802630
16+
P 2
17+
1 15.53961600 0.07087430
18+
2 3.59993360 0.33975280
19+
S 1
20+
1 0.27000580 1.00000000
21+
P 1
22+
1 0.27000580 1.00000000
23+
end
24+
end

0 commit comments

Comments
 (0)