{"spec_id":"line-tanabe-sugano","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// line-tanabe-sugano: Tanabe-Sugano Diagram for Crystal Field Theory\n// Library: highcharts 12.6.0 | JavaScript 22.23.3\n// Quality: 92/100 | Created: 2026-10-01\n\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Eigenvalues of a symmetric matrix (cyclic Jacobi rotations) ------------\n// The core Highcharts bundle ships no linear algebra, and the term energies are\n// the eigenvalues of the Tanabe-Sugano matrices, so a short solver is needed.\nconst eigenvalues = (matrix) => {\n    const n = matrix.length;\n    const a = matrix.map((row) => row.slice());\n\n    for (let sweep = 0; sweep < 40; sweep++) {\n        let offDiagonal = 0;\n        for (let p = 0; p < n; p++) {\n            for (let q = p + 1; q < n; q++) offDiagonal += a[p][q] * a[p][q];\n        }\n        if (offDiagonal < 1e-20) break;\n\n        for (let p = 0; p < n; p++) {\n            for (let q = p + 1; q < n; q++) {\n                if (a[p][q] === 0) continue;\n                const angle = 0.5 * Math.atan2(2 * a[p][q], a[q][q] - a[p][p]);\n                const cos = Math.cos(angle);\n                const sin = Math.sin(angle);\n                for (let k = 0; k < n; k++) {\n                    const kp = a[k][p];\n                    const kq = a[k][q];\n                    a[k][p] = cos * kp - sin * kq;\n                    a[k][q] = sin * kp + cos * kq;\n                }\n                for (let k = 0; k < n; k++) {\n                    const pk = a[p][k];\n                    const qk = a[q][k];\n                    a[p][k] = cos * pk - sin * qk;\n                    a[q][k] = sin * pk + cos * qk;\n                }\n            }\n        }\n    }\n    return a.map((row, i) => row[i]).sort((x, y) => x - y);\n};\n\n// --- Data: d³ (Cr³⁺) term energies from the Tanabe-Sugano matrices ----------\n// Energies are in units of the Racah parameter B (so B = 1 and the Racah A,\n// taken as constant, drops out); the Racah C/B ratio is the usual textbook 4.5.\n// Each block is the strong-field matrix over the t₂³ / t₂²e / t₂e² / e³ basis\n// states of that symmetry; diagonalizing it keeps same-symmetry terms as\n// avoided crossings instead of letting them cross.\nconst C = 4.5;\nconst SQRT2 = Math.SQRT2;\nconst SQRT3 = Math.sqrt(3);\n\nconst termEnergies = (dq) => {\n    const groundTerm = -12 * dq - 15; // ⁴A₂g(t₂³) — the energy zero of the chart\n\n    const quartetT1 = eigenvalues([\n        [-2 * dq - 3, 6],\n        [6, 8 * dq - 12],\n    ]);\n    const doubletE = eigenvalues([\n        [-12 * dq - 6 + 3 * C, -6 * SQRT2, -3 * SQRT2, 0],\n        [-6 * SQRT2, -2 * dq + 8 + 6 * C, 10, SQRT3 * (2 + C)],\n        [-3 * SQRT2, 10, -2 * dq - 1 + 3 * C, 2 * SQRT3],\n        [0, SQRT3 * (2 + C), 2 * SQRT3, 18 * dq - 8 + 4 * C],\n    ]);\n    const doubletT1 = eigenvalues([\n        [-12 * dq - 6 + 3 * C, -3, 3, 0, -2 * SQRT3],\n        [-3, -2 * dq + 3 * C, -3, 3, 3 * SQRT3],\n        [3, -3, -2 * dq - 6 + 3 * C, -3, -SQRT3],\n        [0, 3, -3, 8 * dq - 6 + 3 * C, 2 * SQRT3],\n        [-2 * SQRT3, 3 * SQRT3, -SQRT3, 2 * SQRT3, 8 * dq - 2 + 3 * C],\n    ]);\n    const doubletT2 = eigenvalues([\n        [-12 * dq + 5 * C, -3 * SQRT3, -5 * SQRT3, 4 + 2 * C, 2],\n        [-3 * SQRT3, -2 * dq - 6 + 3 * C, 3, -3 * SQRT3, -3 * SQRT3],\n        [-5 * SQRT3, 3, -2 * dq + 4 + 3 * C, -SQRT3, SQRT3],\n        [4 + 2 * C, -3 * SQRT3, -SQRT3, 8 * dq + 6 + 5 * C, 10],\n        [2, -3 * SQRT3, SQRT3, 10, 8 * dq - 2 + 3 * C],\n    ]);\n\n    return {\n        quartetA2: 0,\n        quartetT2: -2 * dq - 15 - groundTerm,\n        quartetT1F: quartetT1[0] - groundTerm,\n        quartetT1P: quartetT1[1] - groundTerm,\n        doubletE: doubletE[0] - groundTerm,\n        doubletT1: doubletT1[0] - groundTerm,\n        doubletT2: doubletT2[0] - groundTerm,\n        doubletA1: -2 * dq - 11 + 3 * C - groundTerm,\n    };\n};\n\n// Imprint palette order; spin-allowed = the ⁴A₂g ground term's multiplicity.\n// labelShift nudges the two labels apart where ²Eg and ²T₁g nearly coincide.\nconst terms = [\n    { key: \"quartetA2\", symbol: \"<sup>4</sup>A<sub>2g</sub>\", spinAllowed: true, labelShift: 0 },\n    { key: \"quartetT2\", symbol: \"<sup>4</sup>T<sub>2g</sub>\", spinAllowed: true, labelShift: 0 },\n    { key: \"quartetT1F\", symbol: \"<sup>4</sup>T<sub>1g</sub>(F)\", spinAllowed: true, labelShift: 0 },\n    { key: \"quartetT1P\", symbol: \"<sup>4</sup>T<sub>1g</sub>(P)\", spinAllowed: true, labelShift: 0 },\n    { key: \"doubletE\", symbol: \"<sup>2</sup>E<sub>g</sub>\", spinAllowed: false, labelShift: 15 },\n    { key: \"doubletT1\", symbol: \"<sup>2</sup>T<sub>1g</sub>\", spinAllowed: false, labelShift: -15 },\n    { key: \"doubletT2\", symbol: \"<sup>2</sup>T<sub>2g</sub>\", spinAllowed: false, labelShift: 0 },\n    { key: \"doubletA1\", symbol: \"<sup>2</sup>A<sub>1g</sub>\", spinAllowed: false, labelShift: 0 },\n];\n\nconst curves = new Map(terms.map((term) => [term.key, []]));\nfor (let fieldStrength = 0; fieldStrength <= 40; fieldStrength += 0.125) {\n    const energies = termEnergies(fieldStrength / 10); // Δ_o/B = 10 Dq/B\n    for (const term of terms) curves.get(term.key).push([fieldStrength, energies[term.key]]);\n}\n\n// --- Chart -----------------------------------------------------------------\nHighcharts.chart(\"container\", {\n    chart: {\n        type: \"line\",\n        backgroundColor: \"transparent\",\n        animation: false,\n        marginRight: 112, // room for the term labels past the right edge\n        spacingBottom: 18,\n        style: { fontFamily: \"inherit\" },\n    },\n    credits: { enabled: false },\n    colors: t.palette,\n    title: {\n        text: \"line-tanabe-sugano · javascript · highcharts · anyplot.ai\",\n        style: { color: t.ink, fontSize: \"24px\", fontWeight: \"600\" },\n    },\n    subtitle: {\n        useHTML: true,\n        text:\n            \"d<sup>3</sup> (Cr<sup>3+</sup>), octahedral · C/B = 4.5 · \" +\n            \"solid = spin-allowed, dashed = spin-forbidden\",\n        style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    xAxis: {\n        min: 0,\n        max: 40,\n        tickInterval: 5,\n        lineColor: t.grid,\n        tickColor: t.grid,\n        gridLineWidth: 1,\n        gridLineColor: t.grid,\n        labels: { style: { color: t.inkSoft, fontSize: \"15px\" } },\n        title: {\n            useHTML: true,\n            text: \"Ligand-field strength Δ<sub>o</sub>/B\",\n            style: { color: t.inkSoft, fontSize: \"17px\" },\n        },\n    },\n    yAxis: {\n        min: 0,\n        max: 90,\n        tickInterval: 10,\n        lineWidth: 0,\n        gridLineColor: t.grid,\n        labels: { style: { color: t.inkSoft, fontSize: \"15px\" } },\n        title: {\n            text: \"Term energy E/B\",\n            style: { color: t.inkSoft, fontSize: \"17px\" },\n        },\n    },\n    legend: { enabled: false },\n    tooltip: {\n        useHTML: true,\n        headerFormat: \"\",\n        pointFormat:\n            \"<b>{series.name}</b><br>Δ<sub>o</sub>/B {point.x:.1f} · E/B {point.y:.1f}\",\n        backgroundColor: t.elevatedBg,\n        borderColor: t.grid,\n        style: { color: t.ink, fontSize: \"14px\" },\n    },\n    plotOptions: {\n        series: {\n            animation: false,\n            marker: { enabled: false },\n            states: { inactive: { opacity: 1 } },\n        },\n    },\n    series: terms.map((term, index) => {\n        const curve = curves.get(term.key);\n        const rightEdge = curve[curve.length - 1];\n        return {\n            name: term.symbol,\n            lineWidth: term.spinAllowed ? 4 : 2.2,\n            dashStyle: term.spinAllowed ? \"Solid\" : \"ShortDash\",\n            data: [\n                ...curve.slice(0, -1),\n                {\n                    x: rightEdge[0],\n                    y: rightEdge[1],\n                    dataLabels: {\n                        enabled: true,\n                        useHTML: true,\n                        format: \"{series.name}\",\n                        align: \"left\",\n                        verticalAlign: \"middle\",\n                        x: 12,\n                        y: term.labelShift,\n                        crop: false,\n                        overflow: \"allow\",\n                        style: {\n                            color: t.palette[index],\n                            fontSize: \"19px\",\n                            fontWeight: \"600\",\n                            textOutline: \"none\",\n                        },\n                    },\n                },\n            ],\n        };\n    }),\n});\n"}