File size: 6,139 Bytes
cdfc1f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/**

 * Mathematical utility functions for quantum mechanics calculations

 */

/**

 * Validates quantum numbers according to quantum mechanics rules

 * @param {number} n - Principal quantum number (n >= 1)

 * @param {number} l - Azimuthal quantum number (0 <= l < n)

 * @param {number} m - Magnetic quantum number (-l <= m <= l)

 * @throws {Error} If quantum numbers are invalid

 */
function validateQuantumNumbers(n, l, m) {
    if (n < 1 || !Number.isInteger(n)) {
        throw new Error(`Invalid principal quantum number: n=${n} must be a positive integer`);
    }
    if (l < 0 || l >= n || !Number.isInteger(l)) {
        throw new Error(`Invalid azimuthal quantum number: l=${l} must be in range [0, ${n-1}]`);
    }
    if (Math.abs(m) > l || !Number.isInteger(m)) {
        throw new Error(`Invalid magnetic quantum number: m=${m} must be in range [${-l}, ${l}]`);
    }
    return true;
}

/**

 * Safe division with fallback for numerical stability

 * @param {number} numerator

 * @param {number} denominator

 * @param {number} fallback - Value to return if division fails

 * @returns {number}

 */
function safeDivide(numerator, denominator, fallback = 0) {
    if (denominator === 0 || !isFinite(denominator)) {
        console.warn('Division by zero or non-finite denominator');
        return fallback;
    }
    const result = numerator / denominator;
    return isFinite(result) ? result : fallback;
}

/**

 * Factorial function with memoization

 */
const factorialCache = {};
function factorial(n) {
    if (n < 0) return 0;
    if (n === 0 || n === 1) return 1;
    if (factorialCache[n]) return factorialCache[n];
    
    let result = 1;
    for (let i = 2; i <= n; i++) {
        result *= i;
    }
    factorialCache[n] = result;
    return result;
}

/**

 * Log factorial for numerical stability with large numbers

 * @param {number} n

 * @returns {number} ln(n!)

 */
function logFactorial(n) {
    if (n < 0) return -Infinity;
    if (n === 0 || n === 1) return 0;
    
    let result = 0;
    for (let i = 2; i <= n; i++) {
        result += Math.log(i);
    }
    return result;
}

/**

 * Associated Laguerre polynomial L_n^k(x)

 * Used in radial wave function calculation

 * @param {number} n - Degree

 * @param {number} k - Order

 * @param {number} x - Argument

 * @returns {number}

 */
function laguerrePolynomial(n, k, x) {
    if (n === 0) return 1;
    if (n === 1) return 1 + k - x;
    
    // Use recurrence relation for numerical stability
    let L_prev2 = 1;
    let L_prev1 = 1 + k - x;
    let L_current = 0;
    
    for (let i = 2; i <= n; i++) {
        L_current = ((2 * i - 1 + k - x) * L_prev1 - (i - 1 + k) * L_prev2) / i;
        L_prev2 = L_prev1;
        L_prev1 = L_current;
    }
    
    return L_current;
}

/**

 * Associated Legendre polynomial P_l^m(x)

 * Used in angular wave function calculation

 * @param {number} l - Degree

 * @param {number} m - Order

 * @param {number} x - Argument (typically cos(theta))

 * @returns {number}

 */
function legendrePolynomial(l, m, x) {
    const absM = Math.abs(m);
    
    if (absM > l) return 0;
    
    // Compute P_l^m using recurrence relations
    // Start with P_m^m
    let pmm = 1.0;
    if (absM > 0) {
        const somx2 = Math.sqrt((1 - x) * (1 + x));
        let fact = 1.0;
        for (let i = 1; i <= absM; i++) {
            pmm *= -fact * somx2;
            fact += 2.0;
        }
    }
    
    if (l === absM) {
        return pmm;
    }
    
    // Compute P_{m+1}^m
    let pmmp1 = x * (2 * absM + 1) * pmm;
    
    if (l === absM + 1) {
        return pmmp1;
    }
    
    // Compute P_l^m for l > m+1 using recurrence
    let pll = 0;
    for (let ll = absM + 2; ll <= l; ll++) {
        pll = (x * (2 * ll - 1) * pmmp1 - (ll + absM - 1) * pmm) / (ll - absM);
        pmm = pmmp1;
        pmmp1 = pll;
    }
    
    return pll;
}

/**

 * Spherical harmonic Y_l^m(theta, phi) - Real form

 * Uses real spherical harmonics for proper px, py, pz orbital shapes

 * @param {number} l - Azimuthal quantum number

 * @param {number} m - Magnetic quantum number

 * @param {number} theta - Polar angle (0 to π)

 * @param {number} phi - Azimuthal angle (0 to 2π)

 * @returns {number} Real spherical harmonic value

 */
function sphericalHarmonic(l, m, theta, phi) {
    const absM = Math.abs(m);
    
    // Normalization constant
    const norm = Math.sqrt(
        ((2 * l + 1) * factorial(l - absM)) / 
        (4 * CONSTANTS.PI * factorial(l + absM))
    );
    
    // Associated Legendre polynomial
    const legendre = legendrePolynomial(l, absM, Math.cos(theta));
    
    // Real spherical harmonics:
    // For m = 0: no phi dependence (pz orbital)
    // For m > 0: use sqrt(2)*cos(m*phi) (px orbital for m=1)
    // For m < 0: use sqrt(2)*sin(|m|*phi) (py orbital for m=-1)
    let angular;
    if (m === 0) {
        angular = 1;
    } else if (m > 0) {
        angular = Math.sqrt(2) * Math.cos(m * phi);
    } else {
        // For negative m, use sin with absolute value
        angular = Math.sqrt(2) * Math.sin(absM * phi);
    }
    
    return norm * legendre * angular;
}

/**

 * Spherical harmonic squared (for probability density)

 * This is used in CDF sampling where we need positive values

 * @param {number} l - Azimuthal quantum number

 * @param {number} m - Magnetic quantum number

 * @param {number} theta - Polar angle (0 to π)

 * @param {number} phi - Azimuthal angle (0 to 2π)

 * @returns {number} |Y_l^m|^2

 */
function sphericalHarmonicSquared(l, m, theta, phi) {
    const Y = sphericalHarmonic(l, m, theta, phi);
    return Y * Y;
}


// Make functions available globally
window.validateQuantumNumbers = validateQuantumNumbers;
window.safeDivide = safeDivide;
window.factorial = factorial;
window.logFactorial = logFactorial;
window.legendrePolynomial = legendrePolynomial;
window.sphericalHarmonic = sphericalHarmonic;
window.sphericalHarmonicSquared = sphericalHarmonicSquared;