Skip to content

Commit 90791c5

Browse files
andyclaude
andcommitted
tests: a coordination complex, rotors and all
Hexammine cobalt with its ammonia rotors deliberately unaligned -- which is what makes it a test rather than a demonstration, since that is exactly what costs the real molecule its octahedral symmetry. Checks the whole ligand field construction: that it is built in Oh at all, that the metal gives eg + t2g, a1g and t1u, that the ligands appear as six sigma donors rather than twenty-four atoms' worth of atomic orbitals, that those donors span a1g + eg + t1u, that t2g has no partner among them, that twelve donor electrons arrive, and that cobalt(III) comes out d6. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFMUNbw2UpRPmgsqviAowb
1 parent 50fdfe2 commit 90791c5

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

‎tests/symmetry/testMoFragments.C‎

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,112 @@ int main(int argc, char** argv) {
10831083
if (!refused) bad++;
10841084
}
10851085

1086+
// --- a coordination complex ---------------------------------------
1087+
//
1088+
// THE SKELETON IS WHAT IS CLASSIFIED, AND THE LIGANDS ARE DONORS.
1089+
//
1090+
// Hexammine cobalt's donor set is a perfect octahedron and the
1091+
// complex is not: six ammonia rotors cannot all be, so a
1092+
// whole-molecule symmetry search calls it Th at best and C1 in
1093+
// practice. Every course draws it in Oh regardless, because the
1094+
// thing being classified is the coordination skeleton. The
1095+
// rotors here are deliberately NOT aligned with anything, which
1096+
// is what makes this a test rather than a demonstration.
1097+
{
1098+
printf("\n A coordination complex\n");
1099+
1100+
vector<double> coords;
1101+
vector<string> elements;
1102+
elements.push_back("Co");
1103+
coords.push_back(0.0); coords.push_back(0.0); coords.push_back(0.0);
1104+
1105+
for (int axis = 0; axis < 3; axis++) {
1106+
for (int sign = 1; sign >= -1; sign -= 2) {
1107+
double n[3] = { 0.0, 0.0, 0.0 };
1108+
n[axis] = sign*2.0;
1109+
elements.push_back("N");
1110+
for (int k = 0; k < 3; k++) coords.push_back(n[k]);
1111+
1112+
static const double OFFSET[3][2] =
1113+
{ { 0.94, 0.0 }, { -0.47, 0.82 }, { -0.47, -0.82 } };
1114+
for (int h = 0; h < 3; h++) {
1115+
double p[3] = { 0.0, 0.0, 0.0 };
1116+
p[axis] = sign*2.4;
1117+
p[(axis + 1) % 3] = OFFSET[h][0];
1118+
p[(axis + 2) % 3] = OFFSET[h][1];
1119+
elements.push_back("H");
1120+
for (int k = 0; k < 3; k++) coords.push_back(p[k]);
1121+
}
1122+
}
1123+
}
1124+
1125+
MoColumn left, right;
1126+
string note;
1127+
const bool built = MoFragments::build(coords, elements, "OH", 3,
1128+
left, right, note);
1129+
printf(" %-46s %s\n",
1130+
"Co(NH3)6 3+: built in Oh despite the rotors",
1131+
built ? "ok" : ("FAIL: " + note).c_str());
1132+
if (!built) { bad++; }
1133+
else {
1134+
// The metal offers a1g (4s), t1u (4p), eg + t2g (3d) -- read
1135+
// off the character table, with no electrostatic argument.
1136+
bool eg = false, t2g = false, a1g = false, t1u = false;
1137+
for (size_t i = 0; i < left.levels.size(); i++) {
1138+
const string& r = left.levels[i].irrep;
1139+
if (r == "EG") eg = true;
1140+
if (r == "T2G") t2g = true;
1141+
if (r == "A1G") a1g = true;
1142+
if (r == "T1U") t1u = true;
1143+
}
1144+
printf(" %-46s %s\n", "the metal gives eg + t2g, a1g and t1u",
1145+
(eg && t2g && a1g && t1u) ? "ok" : "FAIL");
1146+
if (!(eg && t2g && a1g && t1u)) bad++;
1147+
1148+
// And six sigma donors span a1g + eg + t1u, whatever the
1149+
// ligand is. Not the nitrogens' 2s and 2p: a diagram built
1150+
// from every atomic orbital on twenty-four atoms is correct
1151+
// and unreadable.
1152+
int donors = 0;
1153+
double electrons = 0.0;
1154+
bool sigma = true, hasT2g = false;
1155+
map<string,int> span;
1156+
for (size_t i = 0; i < right.levels.size(); i++) {
1157+
donors += right.levels[i].degeneracy;
1158+
electrons += right.levels[i].occupancy;
1159+
span[right.levels[i].irrep] += right.levels[i].degeneracy;
1160+
if (right.levels[i].label.find("sigma") == string::npos) sigma = false;
1161+
if (right.levels[i].irrep == "T2G") hasT2g = true;
1162+
}
1163+
1164+
printf(" %-46s %s\n", "the ligands are sigma donors, not their basis",
1165+
(sigma && donors == 6) ? "ok" : "FAIL");
1166+
if (!(sigma && donors == 6)) bad++;
1167+
1168+
const bool gamma = (span["A1G"] == 1 && span["EG"] == 2 &&
1169+
span["T1U"] == 3 && span.size() == 3);
1170+
printf(" %-46s %s\n", "and they span a1g + eg + t1u",
1171+
gamma ? "ok" : "FAIL");
1172+
if (!gamma) bad++;
1173+
1174+
printf(" %-46s %s\n", "t2g has no partner in the donor set",
1175+
!hasT2g ? "ok" : "FAIL");
1176+
if (hasT2g) bad++;
1177+
1178+
// A donor brings a pair: twelve electrons fill the bonding set
1179+
// before any of the metal's own.
1180+
checkd("six donors bring twelve electrons", electrons, 12.0, 1e-9);
1181+
1182+
// Cobalt(III) is d6. The charge oxidises the metal; it does
1183+
// not reduce it.
1184+
double metal = 0.0;
1185+
for (size_t i = 0; i < left.levels.size(); i++) {
1186+
metal += left.levels[i].occupancy;
1187+
}
1188+
checkd("and cobalt(III) is d6", metal, 6.0, 1e-9);
1189+
}
1190+
}
1191+
10861192
printf("\n %s\n", bad ? "FAIL" : "PASS");
10871193
return bad ? 1 : 0;
10881194
}

0 commit comments

Comments
 (0)