Spaces:
Running
Running
| /** | |
| * 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; | |