/** * ROLLCALC V14 - STANDARD DEVIATION CALCULATOR * ============================================= * Calculates diameter & weight ranges (±2σ = 95% confidence) * For use with article-data.json containing thickness_stddev & area_weight_stddev */ class StdDevCalculator { /** * Calculate weight ranges from area weight and stddev * @param {number} areaWeight - Area weight in g/m² * @param {number} areaWeightStddev - Standard deviation in g/m² * @param {number} length - Product length in meters * @param {number} width - Roll width in meters * @returns {Object} {min, nom, max, aw_min, aw_nom, aw_max} */ static calculateWeightRanges(areaWeight, areaWeightStddev, length, width) { if (!areaWeight || !length || !width) return null; const sigma = areaWeightStddev || 0; const aw_min = Math.max(0, areaWeight - 2 * sigma); // Worst case (lightest) const aw_nom = areaWeight; // Nominal const aw_max = areaWeight + 2 * sigma; // Best case (heaviest) return { min: (aw_min * length * width / 1000), // kg nom: (aw_nom * length * width / 1000), // kg max: (aw_max * length * width / 1000), // kg aw_min: aw_min, aw_nom: aw_nom, aw_max: aw_max }; } /** * Calculate diameter ranges from thickness and stddev * NOTE: Thinner product → larger roll, thicker product → smaller roll * @param {number} coreDiameter - Core diameter in mm * @param {number} thickness - Product thickness in mm * @param {number} thicknessStddev - Standard deviation in mm * @param {number} productLength - Product length in meters * @returns {Object} {min, nom, max, t_min, t_nom, t_max} */ static calculateDiameterRanges(coreDiameter, thickness, thicknessStddev, productLength) { if (!coreDiameter || !thickness || !productLength) return null; const d = coreDiameter; const L = productLength * 1000; // Convert to mm const sigma = thicknessStddev || 0; // ±2σ = 95% confidence interval const t_min = Math.max(0, thickness - 2 * sigma); // Thinnest product const t_nom = thickness; // Nominal const t_max = thickness + 2 * sigma; // Thickest product // D = √(d² + 4Lt/π) // Thinner product → larger D // Thicker product → smaller D const d_nom = Math.sqrt(d*d + (4*L*t_nom)/Math.PI); const d_thin = Math.sqrt(d*d + (4*L*t_min)/Math.PI); // LARGEST roll const d_thick = Math.sqrt(d*d + (4*L*t_max)/Math.PI); // SMALLEST roll return { min: d_thick, // SMALLEST (thickest product) nom: d_nom, // NOMINAL max: d_thin, // LARGEST (thinnest product) t_min: t_min, t_nom: t_nom, t_max: t_max }; } /** * Format diameter result with ±2σ ranges and tolerance */ static formatDiameterResult(diameterRanges, tolerance = 0) { if (!diameterRanges) return ''; const d_low = Math.min(diameterRanges.min, diameterRanges.max); const d_high = Math.max(diameterRanges.min, diameterRanges.max); let text = `${diameterRanges.nom.toFixed(1)} mm (±2σ: ${d_low.toFixed(1)}–${d_high.toFixed(1)} mm)`; if (tolerance && tolerance > 0) { const d_tol_min = diameterRanges.nom - tolerance; const d_tol_max = diameterRanges.nom + tolerance; text += ` | Tolerance: ${d_tol_min.toFixed(1)}–${d_tol_max.toFixed(1)} mm`; } return text; } /** * Format weight result */ static formatWeightResult(weightRanges) { if (!weightRanges) return ''; return `${weightRanges.nom.toFixed(1)} kg (${weightRanges.min.toFixed(1)}–${weightRanges.max.toFixed(1)} kg)`; } /** * Get worst-case weight for safety checks (Forklift, etc.) */ static getWorstCaseWeight(weightRanges) { if (!weightRanges) return 0; return Math.max(weightRanges.nom, weightRanges.max); } /** * Get nominal weight for capacity planning */ static getNominalWeight(weightRanges) { if (!weightRanges) return 0; return weightRanges.nom; } } // Export globally window.StdDevCalculator = StdDevCalculator; console.log('[StdDevCalculator] Module loaded');