Skip to content

Commit

Permalink
New indices and corrections to existing indices
Browse files Browse the repository at this point in the history
  • Loading branch information
kocatepedogu committed Sep 21, 2023
1 parent 0673618 commit ba69571
Show file tree
Hide file tree
Showing 6 changed files with 492 additions and 105 deletions.
36 changes: 32 additions & 4 deletions src/renderer/diagram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export class SoundingPlot {
private rP: number = NaN;
private rT: number = NaN;

private dataChanged = true;
private effectiveInflowLayers: Array<[number,number]> = [];

private functions: PlotFunction[] = [
{func: (p: number) => {
return this.sounding.getValueAt(p, "temp");
Expand All @@ -94,6 +97,9 @@ export class SoundingPlot {
];

private curves: PlotCurve[] = [
{func: () => {this.drawEffectiveInflow();},
color: "yellow", id:"effectiveinflow", name: "Effective Inflow Layers", enabled: false},

{func: () => {this.drawPressureLines();},
color: 'lightgray', id: 'pressure', name: 'Pressure levels', enabled: true},

Expand All @@ -107,7 +113,7 @@ export class SoundingPlot {
color: "brown", id:"dryadiabats", name: "Dry adiabats", enabled: false},

{func: () => {this.drawParcelTemperature();},
color: "gray", id:"parceltemperature", name: "Parcel temperature", enabled: true}
color: "gray", id:"parceltemperature", name: "Parcel temperature", enabled: true},
];

constructor(sounding: data.Sounding) {
Expand All @@ -117,6 +123,7 @@ export class SoundingPlot {
this.determineCanvasSize();

this.sounding.addObserver(() => {
this.dataChanged = true;
this.update();
});

Expand Down Expand Up @@ -269,7 +276,7 @@ export class SoundingPlot {
private findPlotBoundaries() {
this.plotWidth = this.width * 0.8;
this.plotHeight = this.height * 0.9;
this.plotX = this.width * 0.1;
this.plotX = this.width * 0.10;
this.plotY = this.height * 0.05;
this.lastX = this.plotX + this.plotWidth - 1;
this.lastY = this.plotY + this.plotHeight - 1;
Expand Down Expand Up @@ -299,8 +306,8 @@ export class SoundingPlot {
}
});

minT += 5;
maxT += 5;
minT += 10;
maxT += 10;

this.tmin = Math.floor(minT);
this.tmax = Math.ceil(maxT);
Expand Down Expand Up @@ -436,6 +443,27 @@ export class SoundingPlot {
return LCL;
}

/**
* Computes and draws effective inflow layers.
* Each layer is drawn as a yellow rectangle in the background.
*/
private drawEffectiveInflow() {
if (this.dataChanged) {
const fnTemp = (p: number) => this.sounding.getValueAt(p, 'temp');
const fnDewp = (p: number) => this.sounding.getValueAt(p, 'dewpt');
this.effectiveInflowLayers = numerical.computeEffectiveInflow(fnTemp, fnDewp,
this.sounding.first().pressure, this.sounding.last().pressure);
this.dataChanged = false;
}

for (const [bottom, top] of this.effectiveInflowLayers) {
const bottomY = this.computeCoordinates(bottom, 0)[1];
const topY = this.computeCoordinates(top, 0)[1];
this.ctx.fillStyle = "rgb(252, 251, 207)";
this.ctx.fillRect(this.plotX, bottomY, this.plotWidth, topY - bottomY + 1);
}
}

/**
* Draws given function curves (temperature, dew point)
*/
Expand Down
234 changes: 159 additions & 75 deletions src/renderer/indices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,71 +30,171 @@ type SoundingIndex = {

export class IndexTable {
private sounding: data.Sounding;

private indices: SoundingIndex[] = [
{func: () => {return numerical.computeCAPE(this.fnTemp(), this.fnDewp(),
this.sounding.first().pressure, this.sounding.last().pressure)}, name: "CAPE"},

{func: () => {return numerical.computeLiftedIndex(this.fnTemp(), this.fnDewp(), this.sounding.first().pressure)},
name: "Lifted Index"},

{func: () => {return numerical.computeK(this.fnTemp(), this.fnDewp())},
name: "K Index"},

{func: () => {return numerical.computeTT(this.fnTemp(), this.fnDewp())},
name: "Totals Totals"},

{func: () => {return numerical.computeBoyden(this.fnTemp(), this.fnHeight())},
name: "Boyden Index"},

{func: () => {return numerical.computeVT(this.fnTemp())},
name: "Vertical Totals"},

{func: () => {return numerical.computeCT(this.fnTemp(), this.fnDewp())},
name: "Cross Totals"},

{func: () => {return numerical.computeMJI(this.fnTemp(), this.fnDewp())},
name: "Modified Jefferson Index"},

{func: () => {return numerical.computeRackliff(this.fnTemp(), this.fnDewp())},
name: "Rackliff Index"},

{func: () => {return numerical.computeThompson(this.fnTemp(), this.fnDewp(), this.sounding.first().pressure)},
name: "Thompson Index"},

{func: () => {return numerical.computeShowalter(this.fnTemp(), this.fnDewp())},
name: "Showalter Index"},

{func: () => {return numerical.computeModifiedK(this.fnTemp(), this.fnDewp(), this.sounding.first().pressure)},
name: "Modified K Index"},

{func: () => {return numerical.computeModifiedTT(this.fnTemp(), this.fnDewp(), this.sounding.first().pressure)},
name: "Modified Totals Totals"},

{func: () => {return numerical.computeCII(this.fnTemp(), this.fnDewp(), this.sounding.first().pressure)},
name: "Convective Instability Index"},

{func: () => {return numerical.computeFSI(this.fnTemp(), this.fnDewp(), this.fnSpd(), this.sounding.first().pressure)},
name: "Fog Stability Index"},

{func: () => {return numerical.computeDCI(this.fnTemp(), this.fnDewp(), this.sounding.first().pressure)},
name: "Deep Convective Index"},

{func: () => {return numerical.computeKo(this.fnTemp(), this.fnDewp())},
name: "Ko Index"},

{func: () => {return numerical.computePII(this.fnTemp(), this.fnDewp(), this.fnHeight())},
name: "Potential Instability Index"},

{func: () => {return numerical.computeHumidityIndex(this.fnTemp(), this.fnDewp())},
name: "Humidity Index"},
];
private indices: SoundingIndex[];

constructor(sounding: data.Sounding) {
this.sounding = sounding;
this.sounding.addObserver(() => {
this.update();
});

const fnTemp = (p: number) => this.sounding.getValueAt(p, 'temp');
const fnDewp = (p: number) => this.sounding.getValueAt(p, 'dewpt');
const fnSpd = (p: number) => this.sounding.getValueAt(p, 'windspd');
const fnDir = (p: number) => this.sounding.getValueAt(p, 'winddir');
const fnHeight = (p: number) => this.sounding.getValueAt(p, 'height');
const fnWind = (h: number) => this.sounding.getWindAt(h);
const pBegin = () => this.sounding.first().pressure;
const pEnd = () => this.sounding.last().pressure;
const zBegin = () => this.sounding.first().height;
const zEnd = () => this.sounding.last().height;

fnTemp;fnDewp;fnSpd;fnDir;fnHeight;pBegin;pEnd;zBegin;zEnd;

let CAPE: number = NaN;
let CIN: number = NaN;
let LFC: number = NaN;
let EL: number = NaN;

let inflow: [number, number] = [NaN, NaN];

this.indices = [
{func: () => {
[CAPE, CIN, LFC, EL] = numerical.computeCAPE(fnTemp, fnDewp, pBegin(), pEnd());
return CAPE;
}, name: "CAPE"},

{func: () => CIN,
name: "CIN"},

{func: () => Math.sqrt(2 * CAPE),
name: "Maximum updraft (m/s)"},

{func: () => sounding.getValueAt(LFC, 'height'),
name: "Level of Free Convection"},

{func: () => sounding.getValueAt(EL, 'height'),
name: "Equilibrium Level"},

{func: () => numerical.computeLiftedIndex(fnTemp, fnDewp, pBegin()),
name: "Lifted Index"},

{func: () => numerical.computePW(fnDewp, pBegin(), pEnd()),
name: "Precipitable Water"},

{func: () => numerical.computeK(fnTemp, fnDewp),
name: "K Index"},

{func: () => numerical.computeTT(fnTemp, fnDewp),
name: "Totals Totals"},

{func: () => numerical.computeBoyden(fnTemp, fnHeight),
name: "Boyden Index"},

{func: () => numerical.computeVT(fnTemp),
name: "Vertical Totals"},

{func: () => numerical.computeCT(fnTemp, fnDewp),
name: "Cross Totals"},

{func: () => numerical.computeMJI(fnTemp, fnDewp),
name: "Modified Jefferson Index"},

{func: () => numerical.computeRackliff(fnTemp, fnDewp),
name: "Rackliff Index"},

{func: () => numerical.computeThompson(fnTemp, fnDewp, pBegin()),
name: "Thompson Index"},

{func: () => numerical.computeShowalter(fnTemp, fnDewp),
name: "Showalter Index"},

{func: () => numerical.computeModifiedK(fnTemp, fnDewp, pBegin()),
name: "Modified K Index"},

{func: () => numerical.computeModifiedTT(fnTemp, fnDewp, pBegin()),
name: "Modified Totals Totals"},

{func: () => numerical.computeCII(fnTemp, fnDewp, pBegin()),
name: "Convective Instability Index"},

{func: () => numerical.computeFSI(fnTemp, fnDewp, fnSpd, pBegin()),
name: "Fog Stability Index"},

{func: () => numerical.computeDCI(fnTemp, fnDewp, pBegin()),
name: "Deep Convective Index"},

{func: () => numerical.computeKo(fnTemp, fnDewp),
name: "Ko Index"},

{func: () => numerical.computePII(fnTemp, fnDewp, fnHeight),
name: "Potential Instability Index"},

{func: () => numerical.computeHumidityIndex(fnTemp, fnDewp),
name: "Humidity Index"},

{func: () => numerical.computeShear(fnWind, zBegin(), zBegin() + 1000) / 1.852,
name: "Bulk Shear 0-1 km (kt)"},

{func: () => numerical.computeShear(fnWind, zBegin(), zBegin() + 3000) / 1.852,
name: "Bulk Shear 0-3 km (kt)"},

{func: () => numerical.computeShear(fnWind, zBegin(), zBegin() + 6000) / 1.852,
name: "Bulk Shear 0-6 km (kt)"},

{func: () => numerical.computeShear(fnWind, zBegin(), zBegin() + 8000) / 1.852,
name: "Bulk Shear 0-8 km (kt)"},

{func: () => {
let inflowLayer: [number, number]|undefined = undefined;
/* Find the longest effective inflow layer */
let lastThickness = 0;
numerical.computeEffectiveInflow(fnTemp, fnDewp, pBegin(), pEnd()).forEach(
(layer) => {
const [bottom, top] = layer;
const thickness = numerical.hypsometricEquation(bottom, top, fnTemp, fnDewp);
if (thickness > lastThickness) {
inflowLayer = layer;
lastThickness = thickness;
}
}
);

/* Get start and end altitudes of the longest inflow layer */
inflow = [sounding.getValueAt(inflowLayer![0], 'height'),
sounding.getValueAt(inflowLayer![1], 'height')];

/* Compute shear of inflow layer */
return numerical.computeShear(fnWind, inflow[0], inflow[1]) / 1.852;
}, name: "Bulk Shear Inflow (kt)"},

{func: () => numerical.computeSTM(fnWind, zBegin(), 1)[0] / 1.852,
name: "Bunkers STM (R) (kt)"},

{func: () => numerical.computeSTM(fnWind, zBegin(), -1)[0] / 1.852,
name: "Bunkers STM (L) (kt)"},

{func: () => numerical.computeSREH(fnWind, zBegin(), zBegin() + 1000),
name: "SReH (0-1 km) (m^2/s^2)"},

{func: () => numerical.computeSREH(fnWind, zBegin(), zBegin() + 3000),
name: "SReH (0-3 km) (m^2/s^2)"},

{func: () => numerical.computeSREH(fnWind, inflow![0], inflow![1] + 3000),
name: "SReH (Inflow) (m^2/s^2)"},

{func: () => numerical.computeEHI(fnHeight, fnTemp, fnDewp, fnWind, pBegin(), pEnd(), 1000),
name: "Energy Helicity Index 0-1km"},

{func: () => numerical.computeEHI(fnHeight, fnTemp, fnDewp, fnWind, pBegin(), pEnd(), 3000),
name: "Energy Helicity Index 0-3km"},

{func: () => numerical.computeEHI(fnHeight, fnTemp, fnDewp, fnWind, pBegin(), pEnd(), inflow![1]),
name: "Energy Helicity Index Inflow"},

{func: () => numerical.computeSWEAT(fnTemp, fnDewp, fnSpd, fnDir),
name: "SWEAT Index"}
];
}

/* Computes and prints indices */
Expand Down Expand Up @@ -123,20 +223,4 @@ export class IndexTable {
update() {
this.printIndices();
}

private fnTemp() {
return (p: number) => {return this.sounding.getValueAt(p, 'temp');}
}

private fnDewp() {
return (p: number) => {return this.sounding.getValueAt(p, 'dewpt');}
}

private fnSpd() {
return (p: number) => {return this.sounding.getValueAt(p, 'windspd');}
}

private fnHeight() {
return (p: number) => {return this.sounding.getValueAt(p, 'height');}
}
}
Loading

0 comments on commit ba69571

Please sign in to comment.