//wtcsim.com | Simplified WTC Floor Collapse Simulator
//JS numeric model version 1.0.0
//(C) 2024 Ing. Jan Kolar
//general variables:
var simTimer, timeSeconds, dt, simInterval, pause, floorCounter;
//building characteristics:
var floorThickness;
var floorDamageEnergy;
//falling block of debris:
var weight, bottomSideHeight, velocity, startFloor;
//falling upper part:
var upperWeight, upperTopSideHeight, upperVelocity, upperMaterialLoss, upperAcceleration, initialWeight, upperCoreWeight;
//constants:
const g = 9.81;
const floorHeight = 3.791;
//HTML elements:
var velocityIndicator = document.getElementById("velocity");
var timeIndicator = document.getElementById("time");
var floorIndicator = document.getElementById("floors");
var startFloorEntry = document.getElementById("startFloor");
var initialWeightEntry = document.getElementById("initialWeight");
var upperCoreWeightEntry = document.getElementById("upperCoreWeight");
var floorDamageEnergyEntry = document.getElementById("floorDamageEnergy");
var floorThicknessEntry = document.getElementById("floorThickness");
var upperMaterialLossPctEntry = document.getElementById("upperMaterialLoss");
var upperAccelerationEntry = document.getElementById("upperAcceleration");
//SVG elements
var upperPartRect = document.getElementById("upperPart");
var debrisBlockRect = document.getElementById("debrisBlock");
var remainingFloorsRect = document.getElementById("remainingFloors");
var pUpperFloors = document.getElementById("pUpperFloors");
//*****************************************************************
// Numeric model
//*****************************************************************
function onTimer() {
if (pause == 1) return;
var ad, E, dh, dw, fp;
//*** time counting ***
timeSeconds = timeSeconds + dt; // dt = 0.02 s
//*** upper part ***
//something still remains in the upper part?
if (upperWeight > 0) {
//upper part fall velocity
upperVelocity = upperVelocity + upperAcceleration * dt;
//upper part fall added distance
ad = upperVelocity * dt;
//move the upper part by the added distance
upperTopSideHeight = upperTopSideHeight - ad;
//upper part portion falling into the block of debris
dh = topSideHeight() - upperBottomSideHeight();
if (dh > 0) {
//the portion weight
dw = dh / floorHeight;
floorCounter = floorCounter + dw;
//subtract from the upper part weight
upperWeight = upperWeight - dw;
if (upperWeight < 0) upperWeight = 0;
//add the core weight portion
dw = dw + dh * upperCoreWeight / floorHeight;
//add the portion into the block of debris
addToFallingDebrisBlock(dw*(1-upperMaterialLoss), upperVelocity);
}
}
//*** block of debris ***
//fall velocity
velocity = velocity + g * dt;
if (weight < initialWeight) velocity = 0;
//fall added distance
ad = velocity * dt;
if (bottomSideHeight <= 0) ad = 0; //debris reached ground
//move the collapse front by the added distance
bottomSideHeight = bottomSideHeight - ad;
//kinetic energy
E = (weight * velocity * velocity) / 2; // E = 1/2 * mv^2
//damaged floor portion
fp = ad / floorHeight;
//subtract energy required for the floor damage
E = E - floorDamageEnergy * fp;
if (E < 0) {
velocity = 0;
} else {
//new velocity after the floor portion damage
velocity = Math.sqrt(2 * E / weight); // v = sqrt(2 * E / m)
//add the damaged floor portion into the block of debris
addToFallingDebrisBlock(fp, 0);
floorCounter = floorCounter + fp;
}
//display results
updateGUI();
//simulation ends?
if (((bottomSideHeight <= 0) || (velocity <= 0)) && (upperWeight == 0)) {
stopSimulation();
}
}
//*****************************************************************
//Misc.
function addToFallingDebrisBlock(addedWeight, itsVelocity) {
var p1 = weight * velocity;
var p2 = addedWeight * itsVelocity;
weight = weight + addedWeight; // m = m1 + m2
velocity = (p1 + p2) / weight; // v = (p1+p2)/(m1+m2)
}
function topSideHeight() {
return bottomSideHeight + weight * floorThickness;
}
function upperBottomSideHeight() {
return upperTopSideHeight - upperWeight * floorHeight;
}
//GUI Misc.
function presetSelect() {
var p = document.getElementById("preset");
var v = p.value;
if ( v==1 ) {
startFloorEntry.value = 94;
initialWeightEntry.value = 2;
floorDamageEnergyEntry.value = 3.0;
floorThicknessEntry.value = 0.2;
upperAccelerationEntry.value = 7;
upperMaterialLossPctEntry.value = 0;
}
if ( v==2 ) {
startFloorEntry.value = 78;
initialWeightEntry.value = 1;
floorDamageEnergyEntry.value = 3.0;
floorThicknessEntry.value = 0.2;
upperAccelerationEntry.value = 7;
upperMaterialLossPctEntry.value = 10;
}
}
function updateGUI() {
//text output
updateIndicators();
//graphic output
var y, h;
debrisBlockRect.setAttribute("y", 417 - topSideHeight());
debrisBlockRect.setAttribute("height", weight * floorThickness);
if (bottomSideHeight < 0) bottomSideHeight = 0;
remainingFloorsRect.setAttribute("y", 417 - bottomSideHeight);
remainingFloorsRect.setAttribute("height", bottomSideHeight);
y = 417 - upperTopSideHeight;
upperPartRect.setAttribute("y", y);
h = upperWeight * floorHeight;
if (h < 0) h = 0;
upperPartRect.setAttribute("height", h);
pUpperFloors.patternTransform.baseVal.getItem(0).setTranslate(0,y);
}
function updateIndicators() {
floorIndicator.innerHTML = Math.round(floorCounter);
velocityIndicator.innerHTML = velocity.toFixed(1)+" m/s";
timeIndicator.innerHTML = timeSeconds.toFixed(2)+" s";
}
//GUI Buttons
function startSimulation() {
resetSimulation();
if (velocity > 0) return;
simInterval = 20;
dt = simInterval / 1000;
simTimer = setInterval(onTimer, simInterval);
}
function stopSimulation() {
velocity = 0;
clearInterval(simTimer);
pause = 0;
}
function pauseSimulation() {
pause = 1;
}
function resetSimulation() {
pause = 0;
if (velocity > 0) return;
startFloor = parseInt(startFloorEntry.value);
floorThickness = parseFloat(floorThicknessEntry.value);
initialWeight = parseInt(initialWeightEntry.value);
floorDamageEnergy = parseFloat(floorDamageEnergyEntry.value) * g * floorHeight;
upperAcceleration = parseFloat(upperAccelerationEntry.value);
upperMaterialLoss = parseInt(upperMaterialLossPctEntry.value) / 100;
upperCoreWeight = parseFloat(upperCoreWeightEntry.value);
bottomSideHeight = floorHeight * startFloor;
upperWeight = 110 - startFloor ;
upperTopSideHeight = 417;
weight = 0;
timeSeconds = 0;
floorHeightCounter = 0;
floorCounter = 0;
velocity = 0;
upperVelocity = 0;
updateIndicators();
debrisBlockRect.setAttribute("height", 0);
remainingFloorsRect.setAttribute("height", 417);
remainingFloorsRect.setAttribute("y", 0);
upperPartRect.setAttribute("height", upperWeight * floorHeight);
upperPartRect.setAttribute("y", 0);
pUpperFloors.patternTransform.baseVal.getItem(0).setTranslate(0,0);
}
resetSimulation();