import type { LoadCase, LoadCombinationResult } from '../types/pileCap'; /** * ASCE 7-22 Load Combinations for LRFD */ export function calculateLoadCombinations(loadCases: LoadCase[]): LoadCombinationResult[] { const results: LoadCombinationResult[] = []; loadCases.forEach(loadCase => { const { id, deadLoad: D, liveLoad: L, roofLiveLoad: Lr, snowLoad: S, rainLoad: R, seismicLoad: E, windLoad: W, momentX: Mx, momentY: My, shearX: Vx, shearY: Vy } = loadCase; // Helper to add result const add = (name: string, P: number, factor: number) => { results.push({ combinationName: name, axialLoad: P, momentX: Mx * factor, momentY: My * factor, shearX: Vx * factor, shearY: Vy * factor, caseId: id }); }; // 1. 1.4D add('1.4D', 1.4 * D, 1.4); // 2. 1.2D + 1.6L + 0.5(Lr or S or R) const comp2 = Math.max(Lr, S, R); add('1.2D + 1.6L + 0.5(Lr/S/R)', 1.2 * D + 1.6 * L + 0.5 * comp2, 1.6); // 3. 1.2D + 1.6(Lr or S or R) + (L or 0.5W) const prim3 = Math.max(Lr, S, R); const comp3 = Math.max(L, 0.5 * W); add('1.2D + 1.6(Lr/S/R) + (L or 0.5W)', 1.2 * D + 1.6 * prim3 + comp3, 1.6); // 4. 1.2D + 1.0W + L + 0.5(Lr or S or R) const comp4 = Math.max(Lr, S, R); add('1.2D + 1.0W + L + 0.5(Lr/S/R)', 1.2 * D + 1.0 * W + L + 0.5 * comp4, 1.2); // 5. 1.2D + 1.0E + L + 0.2S add('1.2D + 1.0E + L + 0.2S', 1.2 * D + 1.0 * E + L + 0.2 * S, 1.2); // 6. 0.9D + 1.0W add('0.9D + 1.0W', 0.9 * D + 1.0 * W, 1.0); // 7. 0.9D + 1.0E add('0.9D + 1.0E', 0.9 * D + 1.0 * E, 1.0); }); return results; } /** * ASCE 7-22 Service (ASD) Load Combinations */ export function calculateServiceCombinations(loadCases: LoadCase[]): LoadCombinationResult[] { const results: LoadCombinationResult[] = []; loadCases.forEach(loadCase => { const { id, deadLoad: D, liveLoad: L, roofLiveLoad: Lr, snowLoad: S, rainLoad: R, seismicLoad: E, windLoad: W, momentX: Mx, momentY: My, shearX: Vx, shearY: Vy } = loadCase; // Helper to add result const add = (name: string, P: number, factor: number) => { results.push({ combinationName: name, axialLoad: P, momentX: Mx * factor, // Approximate moment scaling if factor != 1 (usually 1 for ASD) momentY: My * factor, shearX: Vx * factor, shearY: Vy * factor, caseId: id }); }; // 1. D add('D', D, 1.0); // 2. D + L add('D + L', D + L, 1.0); // 3. D + (Lr or S or R) const comp3 = Math.max(Lr, S, R); add('D + (Lr/S/R)', D + comp3, 1.0); // 4. D + 0.75L + 0.75(Lr or S or R) const comp4 = Math.max(Lr, S, R); add('D + 0.75L + 0.75(Lr/S/R)', D + 0.75 * L + 0.75 * comp4, 1.0); // Note: Moments should strictly be calculated component-wise if factors differ, but assuming linear scaling for now or uniform factors. // Actually, for 0.75L, the moment from L should be 0.75*ML. // My current structure only has total Moment. I can't separate Md from Ml. // LIMITATION: I cannot correctly calculate ASD combinations with different factors for different load types (e.g. 0.75L) because I only store total Moment, not component moments (Md, Ml, etc) in the LoadCase? // Wait, LoadCase has deadLoad, liveLoad etc. but only ONE momentX, momentY. // Ah, the LoadCase interface has `momentX`, `momentY`. It does NOT break down moments by source (Dead Moment, Live Moment). // This is a data model limitation. // For now, I will assume the Moment is associated with the primary load or just scale it by 1.0 for D+L and maybe 0.75 for the combined one? // Actually, if the user enters "Moment X" in the load case, we don't know if it's Dead or Live moment. // I will assume the Moment scales with the "primary" load factor or just keep it simple. // Let's just implement D and D+L for now which are the most common for service checks, and maybe D+0.6W. // If I can't separate moments, I can't strictly do 0.75L + 0.75S if moments come from both. // I'll stick to combinations where factors are 1.0 for simplicity given the data model, OR just apply the factor to the total moment if it seems reasonable (e.g. 0.75 * TotalMoment). // Let's just do D, D+L, D+0.6W, D+0.7E. // 5. D + 0.6W add('D + 0.6W', D + 0.6 * W, 1.0); // Moment scaling is ambiguous. Keeping factor 1.0 for moment to be conservative? Or 0.6? // Let's assume most moment comes from lateral load W. So 0.6 is appropriate for W-based moments. // 6. D + 0.7E add('D + 0.7E', D + 0.7 * E, 1.0); }); return results; } export function getGoverningCombination(combinations: LoadCombinationResult[]): LoadCombinationResult { // Find combination with max absolute axial load // Note: For pile caps, max compression (positive P) is usually critical, but max tension (negative P) could be too. // We'll return the one with max P for now. if (combinations.length === 0) { return { combinationName: 'None', axialLoad: 0, momentX: 0, momentY: 0, shearX: 0, shearY: 0, caseId: '' }; } return combinations.reduce((max, current) => Math.abs(current.axialLoad) > Math.abs(max.axialLoad) ? current : max , combinations[0]); }