src/Bomb.js
/**
* Created by austin on 6/17/16.
*/
import Source from './Dispersion/Source';
import Atmosphere from './Dispersion/Atmosphere';
import GaussianPuff from './Dispersion/GaussianPuff';
import DynamicGaussianPuff from './Dispersion/DynamicGaussianDecayPuff';
import {SourceType} from './Dispersion/Source';
// For some reason importing Atmosphere makes Rollup unhappy
/**
* Explosive energy of tnt
* MJ/kg
* @type {number}
* @see https://en.wikipedia.org/wiki/TNT_equivalent
*/
const Q_TNT = 4.184; // One Megaton of TNT == 4.184 Petajoules
/**
* A simple bomb
*/
class Bomb {
/**
*
* @param {number} tntEqvMass - Standardized TNT equivalent kg (kg)
* @param {Atmosphere} [atmosphere=Bomb.STANDARD_ATM]
* @param {boolean} [isStatic=true] - Determines the type of puff that is used
*/
constructor(tntEqvMass, atmosphere = Bomb.STANDARD_ATM, isStatic = true) {
/**
*
* @type {number}
* @private
*/
this._mass = tntEqvMass;
/**
* A standardized measure for weapon strength
* @type {number}
* @private
*/
this._weaponYield = tntEqvMass / 1000000;
/**
*
* @type {Atmosphere}
* @private
*/
this._atm = atmosphere;
/**
*
* @type {Source}
* @private
*/
this._source = new Source(
SourceType.POINT,
Math.POSITIVE_INFINITY, // Emission rate, arb for puffs. TODO!
this.cloudHeight,
this.cloudRadius,
this.getGasTemp(this.cloudHeight),
this.getGasVelocity(this.cloudHeight)
);
if (isStatic) {
this._puff = new GaussianPuff(
atmosphere,
this._source,
this.mass // Todo: how to calculate how much mass goes into the air?
);
} else {
this._puff = new DynamicGaussianPuff(
atmosphere,
this._source,
this.mass // Todo: how to calculate how much mass goes into the air?
);
}
if (this.weaponYield > 1000) {
console.warn("WARNING: this bomb library is mean for bombs weaponYields under 1000.");
}
}
/**
*
* @param atm
* @returns {Bomb}
*/
setAtmosphere(atm) {
this._atm = atm;
return this;
}
/**
*
* @returns {Atmosphere}
*/
get atmosphere() {
return this._atm;
}
/**
*
* @returns {number}
*/
get weaponYield() {
return this._weaponYield;
}
/**
*
* @returns {Source}
*/
get source() {
return this._source;
}
/**
*
* @param mass
* @returns {Bomb}
*/
setMass(mass) {
this._mass = mass;
return this;
}
/**
*
* @returns {number}
*/
get mass() {
return this._mass;
}
/**
* Based on kilotons of tnt nuclear explosions
* @returns {number} - (m)
*/
get blastRadius() {
return 30 * Math.pow(this.weaponYield, 1/3);
}
/**
* From eq 7 of CISAC Fallout Model
* Approximating this as the top of the stem cloud
* Perhaps will change this as a combination of all three cloud alt equations
* @see http://cisac.fsi.stanford.edu/sites/default/files/geist_2014_cv.pdf
* @returns {number}
*/
get cloudHeight() {
if (this.weaponYield < 2) {
return 1740 * Math.pow(this.weaponYield, 0.229);
}
if (this.weaponYield < 20) {
return 1720 * Math.pow(this.weaponYield, 0.261);
}
return 2040 * Math.pow(this.weaponYield, 0.204);
}
/**
* Should not be used in this context. Really for nuclear bombs.
* @returns {number}
* @private
*/
_getMainCloudRadius() {
return 872 * Math.pow(this.weaponYield, 0.427);
}
/**
*
* @returns {number} - (m)
*/
get cloudRadius() {
let mainRad = this._getMainCloudRadius();
if (this.weaponYield < 20) {
return 0.5 * mainRad;
}
if (this.weaponYield <= 1000) {
return 0.5 * mainRad - 0.3 * mainRad * ((this.weaponYield - 20) / 980);
}
return 0.2 * mainRad - 0.1 * mainRad * ((this.weaponYield - 1000) / 9000);
}
/**
*
* @returns {DynamicGaussianPuff|GaussianPuff} - Depending on if the dispersion is static
*/
get dispersion() {
return this._puff;
}
/**
* @see https://www.metabunk.org/attachments/blast-effect-calculation-1-pdf.2578/ equation 3
* @param {number} r - distance from origin (m)
* @returns {number} - pressure (atm)
*/
getOverpressureAt(r) {
let a = (0.84 / r) * Math.pow(this._mass, (1/3));
let b = (2.7 / Math.pow(r, 2)) * Math.pow(this.mass, (2/3));
let c = (7 / Math.pow(r, 3)) * this.mass;
return a + b + c;
}
/**
* Velocity of gas in behind shock wave front
* @see https://www.metabunk.org/attachments/blast-effect-calculation-1-pdf.2578/ equation 5.2
* @param {number} r - distance from origin (m)
* @returns {number} velocity (m/s)
*/
getGasVelocity(r) {
let pressure = this.getOverpressureAt(r);
// Simplified for standard atmosphere
return 243 * pressure / Math.sqrt(1 + 0.86 * pressure);
}
/**
* Temperature of gas in shock wave front
* @see https://www.metabunk.org/attachments/blast-effect-calculation-1-pdf.2578/ equation 5.3
* @param {number} r - distance from origin (m)
* @returns {number} temperature (K)
*/
getGasTemp(r) {
let pressure = this.getOverpressureAt(r);
return this.atmosphere.temperature * (1 + pressure) * (7 + pressure) / (7 + 6 * pressure);
}
/**
* Positive Shock Phase Duration
* @see https://www.metabunk.org/attachments/blast-effect-calculation-1-pdf.2578/ equation 4
* @param {number} r - distance from origin (m)
* @returns {number} duration (s)
*/
getPosShockPhaseDuration(r) {
return 1.3 * Math.pow(this.mass, (1 / 6)) * Math.sqrt(r) * 0.001;
}
/**
*
* @param {number} qExp - explosive energy (MJ/kg)
* @returns {number}
*/
static tntEquivalentFactor(qExp) {
return qExp / Q_TNT;
}
/**
*
* @param {number} qExp - explosive energy (MJ/kg)
* @param {number} mass - (kg)
* @returns {number}
*/
static tntEquivalent(qExp, mass) {
return Bomb.tntEquivalentFactor(qExp) * mass;
}
}
/**
* Should probably move this to the Atmosphere class
* 0 wind
* 0 sky cover
* 65 degrees sun
* 59 degrees F / 15 degrees C
* @type {Atmosphere}
*/
Bomb.STANDARD_ATM = new Atmosphere(0, 0, 65, 288.2);
export default Bomb;