Source: calc/params.js

/**
 * Sounding diagnostics without drawing: parcels, downdraft CAPE, effective inflow layer and the
 * kinematic parameters (SRH, bulk shear, mean wind, EHI) for every layer the chart shows.
 * The chart engine and analyze() both use these, so they always agree.
 */
import {
  LiftParcel, GetDCape, EffInflowCalc, GetLayerByPres, GetLayerByHght, GetLayerByTemp,
  GetSRH, GetParams, WindShearFindP, ConvertPolartoCart,
} from './skewtCalcs.js';

/**
 * Surface-based, mixed-layer (lowest 100 mb) and most-unstable parcels, DCAPE and the effective
 * inflow layer. The storm motion used for ECAPE comes from UpdateCSMotStorm().
 * @param {object} Sound  sounding.Sounding
 */
export function computeThermo(Sound) {
  const SBParcel = LiftParcel(Sound, "SB");
  const MLParcel = LiftParcel(Sound, "ML");
  const MUParcel = LiftParcel(Sound, "MU", 767);
  const DCapeProf = GetDCape(Sound);
  const StartSlice = GetLayerByPres(Sound, "SFC");
  let EffInflow = false;
  if (SBParcel.Path != false) {
    EffInflow = EffInflowCalc(Sound, StartSlice);
  }
  return { DCapeProf, EffInflow, SBParcel, MLParcel, MUParcel };
}

/**
 * Wind parameters keyed the way the chart panels read them, e.g.
 *   SRH01 / BWD01 / EHI01 / MeanWind.M01   (0-1 km AGL)
 *   SRHEb03 …                               (0-3 km above the effective inflow base)
 *   ESRH, EBWD, BWDE, EHIE, LCLELSHR, BRM
 *
 * @param {object} Sounding  full sounding object ({ Sounding: {...} })
 * @param {object} Info      result of computeThermo()
 * @param {object|null} DP   the displayed parcel (SB/ML/MU/custom)
 * @param {{x:number,y:number}} CSMot  storm motion (chart convention: y points south)
 * @param {object} [WindParams]  object to fill (the engine reuses one); a new one by default
 */
export function computeWindParams(Sounding, Info, DP, CSMot, WindParams = { MeanWind: {} }) {
  let Sound = Sounding.Sounding;
  if (!WindParams.MeanWind) WindParams.MeanWind = {};

  let ESRH = 0;
  let EffInfBot =GetLayerByPres(Sound, 'SFC');
  if (Info.EffInflow != false) {
    ESRH = GetSRH(Sound, Info.EffInflow.Bot, Info.EffInflow.Top, "Pres");
    ESRH = ESRH.SRHP + ESRH.SRHM;
    EffInfBot = GetLayerByPres(Sound, Info.EffInflow.Bot);
  }

  const Surfaces = {
    'SFC': GetLayerByHght(Sound, "SFC"),
    '500': GetLayerByHght(Sound, 500, true),
    '1000': GetLayerByHght(Sound, 1000, true),
    '3000': GetLayerByHght(Sound, 3000, true),
    '6000': GetLayerByHght(Sound, 6000, true),

    'EbSFC': GetLayerByHght(Sound, EffInfBot.H, false),
    'Eb500': GetLayerByHght(Sound, EffInfBot.H + 500, false),
    'Eb1000': GetLayerByHght(Sound, EffInfBot.H + 1000, false),
    'Eb3000': GetLayerByHght(Sound, EffInfBot.H + 3000, false),
    'Eb6000': GetLayerByHght(Sound, EffInfBot.H + 6000, false),

    '9000': GetLayerByHght(Sound, 9000, true),
    '12000': GetLayerByHght(Sound, 12000, true),
    'SBLCL': GetLayerByHght(Sound, Info.SBParcel.LCL == -9999 ? 1500 : Info.SBParcel.LCL, true),
    'MLLFC': GetLayerByHght(Sound, Info.MLParcel.LFC == -9999 ? 1500 : Info.MLParcel.LFC, true),
    '500mb': GetLayerByPres(Sound, 500),
    '700mb': GetLayerByPres(Sound, 700),
    '300mb': GetLayerByPres(Sound, 300),
    'T-18': GetLayerByTemp(Sound, -18),
    'T-12': GetLayerByTemp(Sound, -12),
    'T-10': GetLayerByTemp(Sound, -10),
    'T-30': GetLayerByTemp(Sound, -30),
  }

  WindParams['ESRH'] = ESRH;

  if (Info.EffInflow != false) {
    WindParams['EHIE'] = GetParams({ DP: DP !== null ? DP : Info.SBParcel, SRHAmn: (ESRH) }, "EHI");
    WindParams['BWDE'] = WindShearFindP(Sound, Info.EffInflow.Bot, Info.EffInflow.Top)
    WindParams['MeanWind']['ME'] = WindShearFindP(Sound, Info.EffInflow.Bot, Info.EffInflow.Top, true)
  } else {
    WindParams['EHIE'] = "--";
    WindParams['BWDE'] = "--";
    WindParams['MeanWind']['ME'] = "--";
  }

  let ELVal = DP !== null ? DP.EL : Info.SBParcel.EL;
  let LCLVal = DP !== null ? DP.LCL : Info.SBParcel.LCL;

  if (Info.EffInflow != false) {
    let EBWD = {}
    if (ELVal == -9999 || ELVal == false){
      WindParams['EBWD'] = "--";
    } else {
      let LCLSHR = ConvertPolartoCart(GetLayerByPres(Sound, Info.EffInflow.Bot));
      let ELSHR = ConvertPolartoCart(GetLayerByHght(Sound, (GetLayerByPres(Sound, Info.EffInflow.Bot).H + ELVal)/2, true));
      let P = {
        x: -(LCLSHR.x - ELSHR.x),
        y: -(LCLSHR.y - ELSHR.y),
      };
      WindParams['EBWD'] = P;
    }
  } else {
    WindParams['EBWD'] = "--";
  }

  if (ELVal == -9999 || ELVal == false){
    WindParams['LCLELSHR'] = "--";
  } else {
    let LCLSHR = ConvertPolartoCart(GetLayerByHght(Sound, LCLVal, true));
    let ELSHR = ConvertPolartoCart(GetLayerByHght(Sound, ELVal, true));
    let P = {
      x: -(LCLSHR.x - ELSHR.x),
      y: -(LCLSHR.y - ELSHR.y),
    };
    WindParams['LCLELSHR'] = P;
  }

  // Wind Params
  for (const x of [
    ['005', 'SFC', "500"],
    ['01', 'SFC', "1000"],
    ['03', 'SFC', "3000"],
    ['06', 'SFC', "6000"],

    ['Eb005', 'EbSFC', "Eb500"],
    ['Eb01', 'EbSFC', "Eb1000"],
    ['Eb03', 'EbSFC', "Eb3000"],
    ['Eb06', 'EbSFC', "Eb6000"],

    ['0MLLFC', 'SFC', 'MLLFC'],
    ['0SBLCL', 'SFC', 'SBLCL'],
    ['13', "1000", "3000"],
    ['36', "3000", "6000"],
    ['09', 'SFC', "9000"],
    ['012', 'SFC', "12000"],
    ['912', "9000", "12000"],

    ['0mb300', 'SFC', '300mb'],
    ['0mb500', 'SFC', '500mb'],
    ['0mb700', 'SFC', '700mb'],

    ['DGZ', 'T-12', 'T-18'],
    ['0T18', 'SFC', 'T-18'],
    ['HGZ', 'T-10', 'T-30'],
  ]) {
    if (Surfaces[x[1]].isAbove == true || Surfaces[x[2]].isAbove == true){
      WindParams[`SRH${x[0]}`] = GetSRH(Sound, Surfaces[x[1]].P, Surfaces[x[2]].P-1, "Pres", CSMot);
      WindParams[`BWD${x[0]}`] = WindShearFindP(Sound, Surfaces[x[1]].P, Surfaces[x[2]].P-1);
      WindParams[`EHI${x[0]}`] = GetParams({ DP: DP !== null ? DP : Info.SBParcel, SRHAmn: (WindParams[`SRH${x[0]}`].SRHP + WindParams[`SRH${x[0]}`].SRHM) }, "EHI")
      WindParams['MeanWind'][`M${x[0]}`] = WindShearFindP(Sound, Surfaces[x[1]].P, Surfaces[x[2]].P-1, true);
    } else {
      WindParams[`SRH${x[0]}`] = GetSRH(Sound, Surfaces[x[1]].P, Surfaces[x[2]].P, "Pres", CSMot);
      WindParams[`BWD${x[0]}`] = WindShearFindP(Sound, Surfaces[x[1]].P, Surfaces[x[2]].P);
      WindParams[`EHI${x[0]}`] = GetParams({ DP: DP !== null ? DP : Info.SBParcel, SRHAmn: (WindParams[`SRH${x[0]}`].SRHP + WindParams[`SRH${x[0]}`].SRHM) }, "EHI")
      WindParams['MeanWind'][`M${x[0]}`] = WindShearFindP(Sound, Surfaces[x[1]].P, Surfaces[x[2]].P, true);
    }
  }

  WindParams['BRM'] = CSMot;

  return WindParams;
}