open-wether / Sources /App /Nbm /NbmVariableDownloadable.swift
soiz1's picture
Migrated from GitHub
6ee917b verified
/// Required additions to a GFS variable to make it downloadable
protocol NbmVariableDownloadable: GenericVariable {
func gribIndexName(for domain: NbmDomain, timestep: Int, previousTimestep: Int, run: Int) -> String?
func multiplyAdd(domain: NbmDomain) -> (multiply: Float, add: Float)?
}
extension NbmSurfaceVariable: NbmVariableDownloadable {
func gribIndexName(for domain: NbmDomain, timestep: Int, previousTimestep: Int, run: Int) -> String? {
// Note: Aggregations are only available every 6 hours, while instant values are 3 hourly after hour 40
/// NBM uses 6 hourly models below. Probabilities are emited to 6 hours alignments
let relTime = timestep + run % 6
switch self {
case .temperature_2m:
return ":TMP:2 m above ground:\(timestep) hour fcst:"
case .surface_temperature:
return ":TMP:surface:\(timestep) hour fcst:"
case .cape:
return ":CAPE:surface:\(timestep) hour fcst:"
case .shortwave_radiation:
if timestep - previousTimestep > 1 {
// Instantaneous radiation can be processed with 1-hour intervals. Disregard the 3-hourly and 6-hourly data.
return nil
}
return ":DSWRF:surface:\(timestep) hour fcst:"
case .precipitation:
if timestep > 36 {
return relTime % 6 != 0 ? nil : ":APCP:surface:\(timestep-6)-\(timestep) hour acc fcst:"
}
return ":APCP:surface:\(timestep-1)-\(timestep) hour acc fcst:"
case .relative_humidity_2m:
return ":RH:2 m above ground:\(timestep) hour fcst:"
case .cloud_cover:
return ":TCDC:surface:\(timestep) hour fcst:"
case .wind_speed_10m:
return ":WIND:10 m above ground:\(timestep) hour fcst:"
case .wind_speed_80m:
return ":WIND:80 m above ground:\(timestep) hour fcst:"
case .wind_direction_10m:
return ":WDIR:10 m above ground:\(timestep) hour fcst:"
case .wind_direction_80m:
return ":WDIR:80 m above ground:\(timestep) hour fcst:"
case .snowfall:
if timestep > 36 {
return relTime % 6 != 0 ? nil : ":ASNOW:surface:\(timestep-6)-\(timestep) hour acc fcst:"
}
return ":ASNOW:surface:\(timestep-1)-\(timestep) hour acc fcst:"
case .wind_gusts_10m:
return ":GUST:10 m above ground:\(timestep) hour fcst:"
case .visibility:
return timestep > 78 ? nil : ":VIS:surface:\(timestep) hour fcst:"
case .thunderstorm_probability:
if timestep > 36 {
return relTime % 6 != 0 || relTime >= 192 ? nil : ":TSTM:surface:\(timestep-6)-\(timestep) hour acc fcst:probability forecast"
}
return ":TSTM:surface:\(timestep-1)-\(timestep) hour acc fcst:probability forecast"
case .precipitation_probability:
if timestep > 36 {
return relTime % 6 != 0 ? nil : ":APCP:surface:\(timestep-6)-\(timestep) hour acc fcst:prob >0.254:prob fcst 255/255"
}
return ":APCP:surface:\(timestep-1)-\(timestep) hour acc fcst:prob >0.254:prob fcst 255/255"
case .rain_probability:
// PTYPE codes: https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-201.shtml
return ":PTYPE:surface:\(timestep) hour fcst:prob >=1 <2:prob fcst 1/1"
case .freezing_rain_probability:
return ":PTYPE:surface:\(timestep) hour fcst:prob >=3 <4:prob fcst 1/1"
case .ice_pellets_probability:
return ":PTYPE:surface:\(timestep) hour fcst:prob >=8 <9:prob fcst 1/1"
case .snowfall_probability:
return ":PTYPE:surface:\(timestep) hour fcst:prob >=5 <7:prob fcst 1/1"
}
}
func multiplyAdd(domain: NbmDomain) -> (multiply: Float, add: Float)? {
switch self {
case .temperature_2m, .surface_temperature:
return (1, -273.15)
case .snowfall:
return (100, 0)
default:
return nil
}
}
}
extension NbmPressureVariable: NbmVariableDownloadable {
func gribIndexName(for domain: NbmDomain, timestep: Int, previousTimestep: Int, run: Int) -> String? {
return nil
}
func skipHour0(for domain: NbmDomain) -> Bool {
return false
}
func multiplyAdd(domain: NbmDomain) -> (multiply: Float, add: Float)? {
switch variable {
case .temperature:
return (1, -273.15)
default:
return nil
}
}
}