import type { PileCapGeometry, MaterialProperties, DesignParameters, PileReaction, ShearCheckResult } from '../types/pileCap'; export function performShearChecks( geometry: PileCapGeometry, pileReactions: PileReaction[], materials: MaterialProperties, _design: DesignParameters ): ShearCheckResult[] { const checks: ShearCheckResult[] = []; const columnPunching = calculateColumnPunchingShear(geometry, pileReactions, materials); const pilePunching = calculateSinglePilePunchingShear(geometry, pileReactions, materials); checks.push(columnPunching, pilePunching); const oneWayX = calculateOneWayShear(geometry, pileReactions, materials, 'X'); const oneWayY = calculateOneWayShear(geometry, pileReactions, materials, 'Y'); checks.push(...oneWayX, ...oneWayY); return checks; } function calculateColumnPunchingShear( geometry: PileCapGeometry, pileReactions: PileReaction[], materials: MaterialProperties ): ShearCheckResult { const { columnLength, columnWidth, depth, coverBottom } = geometry; const d = depth - coverBottom; // effective depth in inches const b0 = 2 * ((columnLength + d) + (columnWidth + d)); const columnCenterX = geometry.length / 2; const columnCenterY = geometry.width / 2; // Critical section is at d/2 from column face. // If pile center is OUTSIDE this perimeter, it contributes to shear. // The perimeter is a rectangle (columnWidth + d) x (columnLength + d). // Half dimensions: const critX = (columnLength + d) / 12 / 2; // ft from center const critY = (columnWidth + d) / 12 / 2; // ft from center let totalReactionOutside = 0; pileReactions.forEach(pile => { const distX = Math.abs(pile.x - columnCenterX); const distY = Math.abs(pile.y - columnCenterY); // Check if pile center is outside the critical perimeter if (distX > critX || distY > critY) { totalReactionOutside += pile.reaction; } }); const demand = totalReactionOutside * 1000; // ACI 318-19 Table 22.6.5.2 const beta = Math.max(columnLength, columnWidth) / Math.min(columnLength, columnWidth); const alphaS = 40; // Interior column assumption const vc1 = (2 + 4 / beta) * Math.sqrt(materials.fc); const vc2 = (2 + alphaS * d / b0) * Math.sqrt(materials.fc); const vc3 = 4 * Math.sqrt(materials.fc); const vc = Math.min(vc1, vc2, vc3); const capacity = materials.phiShear * vc * b0 * d; return { type: 'Two-Way Shear (Column)', demand, capacity, ratio: demand / capacity, pass: demand <= capacity, location: `d/2 from column face` }; } function calculateSinglePilePunchingShear( geometry: PileCapGeometry, pileReactions: PileReaction[], materials: MaterialProperties ): ShearCheckResult { const { depth, coverBottom, pileSize, pileType } = geometry; const d = depth - coverBottom; // Critical perimeter around a single pile // For round pile: Circle at d/2? Or equivalent square? // ACI usually treats round piles as equivalent squares or uses critical perimeter at d/2. // Perimeter at d/2 from pile face. // Diameter of critical section = pileSize + d // Perimeter b0 = PI * (pileSize + d) for round // Perimeter b0 = 4 * (pileSize + d) for square let b0 = 0; if (pileType === 'Round') { b0 = Math.PI * (pileSize + d); } else { b0 = 4 * (pileSize + d); } // Demand is the maximum single pile reaction // We check the pile with the HIGHEST reaction (most critical) const maxReaction = Math.max(...pileReactions.map(p => p.reaction)); const demand = maxReaction * 1000; // Capacity // Beta for pile is 1.0 (square/round) const beta = 1.0; const alphaS = 40; // Interior pile assumption (conservative) const vc1 = (2 + 4 / beta) * Math.sqrt(materials.fc); const vc2 = (2 + alphaS * d / b0) * Math.sqrt(materials.fc); const vc3 = 4 * Math.sqrt(materials.fc); const vc = Math.min(vc1, vc2, vc3); const capacity = materials.phiShear * vc * b0 * d; return { type: 'Two-Way Shear (Single Pile)', demand, capacity, ratio: demand / capacity, pass: demand <= capacity, location: `d/2 from pile face (Max Pile)` }; } function calculateOneWayShear( geometry: PileCapGeometry, pileReactions: PileReaction[], materials: MaterialProperties, direction: 'X' | 'Y' ): ShearCheckResult[] { const { depth, coverBottom, columnLength, columnWidth, length, width } = geometry; const d = depth - coverBottom; const columnCenterX = length / 2; const columnCenterY = width / 2; const columnDim = direction === 'X' ? columnLength / 12 : columnWidth / 12; // in feet // Define critical locations (distance from center in feet) const locations = [ { name: 'at d', dist: columnDim / 2 + d / 12 }, { name: 'at Face', dist: columnDim / 2 } ]; const results: ShearCheckResult[] = []; locations.forEach(loc => { let shearDemand = 0; pileReactions.forEach(pile => { const pileDist = direction === 'X' ? Math.abs(pile.x - columnCenterX) : Math.abs(pile.y - columnCenterY); // If pile center is outside the critical section, it contributes to shear // Refinement: ACI often allows ignoring piles if their center is within d/2 of the section? // For simplicity/conservatism in this checkpoint, we use the pile center rule: // If pile center is outside the line, include it. if (pileDist > loc.dist) { shearDemand += pile.reaction; } }); const demand = shearDemand * 1000; // Convert to lbs? No, capacity is likely in lbs? // Wait, previous code: demand = shearDemand * 1000. // capacity = materials.phiShear * vc * bw * d. // vc is in psi. bw, d in inches. So capacity is in lbs. // So demand must be in lbs. shearDemand is in kips. So * 1000 is correct. const bw = direction === 'X' ? width * 12 : length * 12; // Width of the section const vc = 2 * Math.sqrt(materials.fc); const capacity = materials.phiShear * vc * bw * d; results.push({ type: `One-Way Shear (${direction})`, demand, capacity, ratio: demand / capacity, pass: demand <= capacity, location: `${loc.name} (${(loc.dist).toFixed(2)}' from center)` }); }); return results; }