open-wether / Sources /App /GfsGraphCast /GfsGraphCastVariable.swift
soiz1's picture
Migrated from GitHub
6ee917b verified
import Foundation
/// Required additions to a GFS variable to make it downloadable
protocol GfsGraphCastVariableDownloadable: GenericVariable {
var multiplyAdd: (multiply: Float, add: Float)? { get }
}
enum GfsGraphCastSurfaceVariable: String, CaseIterable, GenericVariableMixable, GfsGraphCastVariableDownloadable {
case temperature_2m
case cloud_cover
case cloud_cover_low
case cloud_cover_mid
case cloud_cover_high
case pressure_msl
case wind_v_component_10m
case wind_u_component_10m
case precipitation
var storePreviousForecast: Bool {
switch self {
case .temperature_2m: return true
case .precipitation: return true
case .pressure_msl: return true
case .cloud_cover: return true
case .wind_u_component_10m, .wind_v_component_10m: return true
default: return false
}
}
var requiresOffsetCorrectionForMixing: Bool {
return false
}
var multiplyAdd: (multiply: Float, add: Float)? {
switch self {
case .temperature_2m:
return (1, -273.15)
case .pressure_msl:
return (1/100, 0)
default:
return nil
}
}
var omFileName: (file: String, level: Int) {
return (rawValue, 0)
}
var scalefactor: Float {
switch self {
case .temperature_2m: return 20
case .cloud_cover: return 1
case .cloud_cover_low: return 1
case .cloud_cover_mid: return 1
case .cloud_cover_high: return 1
case .precipitation: return 10
case .wind_v_component_10m: return 10
case .wind_u_component_10m: return 10
case .pressure_msl: return 10
}
}
var interpolation: ReaderInterpolation {
switch self {
case .temperature_2m:
return .hermite(bounds: nil)
case .cloud_cover:
return .linear
case .cloud_cover_low:
return .linear
case .cloud_cover_mid:
return .linear
case .cloud_cover_high:
return .linear
case .pressure_msl:
return .hermite(bounds: nil)
case .precipitation:
return .backwards_sum
case .wind_v_component_10m:
return .hermite(bounds: nil)
case .wind_u_component_10m:
return .hermite(bounds: nil)
}
}
var unit: SiUnit {
switch self {
case .temperature_2m: return .celsius
case .cloud_cover: return .percentage
case .cloud_cover_low: return .percentage
case .cloud_cover_mid: return .percentage
case .cloud_cover_high: return .percentage
case .precipitation: return .millimetre
case .wind_v_component_10m: return .metrePerSecond
case .wind_u_component_10m: return .metrePerSecond
case .pressure_msl: return .hectopascal
}
}
var isElevationCorrectable: Bool {
switch self {
case .temperature_2m:
return true
default:
return false
}
}
}
/**
Types of pressure level variables
*/
enum GfsGraphCastPressureVariableType: String, CaseIterable {
case temperature
case wind_u_component
case wind_v_component
case geopotential_height
case vertical_velocity
case relative_humidity
case specific_humdity
}
/**
A pressure level variable on a given level in hPa / mb
*/
struct GfsGraphCastPressureVariable: PressureVariableRespresentable, Hashable, GenericVariableMixable, GfsGraphCastVariableDownloadable {
let variable: GfsGraphCastPressureVariableType
let level: Int
var storePreviousForecast: Bool {
return false
}
var requiresOffsetCorrectionForMixing: Bool {
return false
}
var multiplyAdd: (multiply: Float, add: Float)? {
switch variable {
case .temperature:
return (1, -273.15)
case .specific_humdity:
return (1000, 0)
default:
return nil
}
}
var omFileName: (file: String, level: Int) {
return (rawValue, 0)
}
var scalefactor: Float {
// Upper level data are more dynamic and that is bad for compression. Use lower scalefactors
switch variable {
case .temperature:
// Use scalefactor of 2 for everything higher than 300 hPa
return (2..<10).interpolated(atFraction: (300..<1000).fraction(of: Float(level)))
case .wind_u_component:
fallthrough
case .wind_v_component:
// Use scalefactor 3 for levels higher than 500 hPa.
return (3..<10).interpolated(atFraction: (500..<1000).fraction(of: Float(level)))
case .geopotential_height:
return (0.05..<1).interpolated(atFraction: (0..<500).fraction(of: Float(level)))
case .relative_humidity:
return (0.2..<1).interpolated(atFraction: (0..<800).fraction(of: Float(level)))
case .vertical_velocity:
return (20..<100).interpolated(atFraction: (0..<500).fraction(of: Float(level)))
case .specific_humdity:
fatalError("should never be written to disk")
}
}
var interpolation: ReaderInterpolation {
switch variable {
case .temperature:
return .hermite(bounds: nil)
case .wind_u_component:
return .hermite(bounds: nil)
case .wind_v_component:
return .hermite(bounds: nil)
case .geopotential_height:
return .linear
case .relative_humidity:
return .hermite(bounds: 0...100)
case .vertical_velocity:
return .hermite(bounds: nil)
case .specific_humdity:
return .hermite(bounds: nil)
}
}
var unit: SiUnit {
switch variable {
case .temperature:
return .celsius
case .wind_u_component:
return .metrePerSecond
case .wind_v_component:
return .metrePerSecond
case .geopotential_height:
return .metre
case .relative_humidity:
return .percentage
case .vertical_velocity:
return .metrePerSecondNotUnitConverted
case .specific_humdity:
return .gramPerKilogram
}
}
var isElevationCorrectable: Bool {
return false
}
}
/**
Combined surface and pressure level variables with all definitions for downloading and API
*/
typealias GfsGraphCastVariable = SurfaceAndPressureVariable<GfsGraphCastSurfaceVariable, GfsGraphCastPressureVariable>