open-wether / Sources /App /GfsGraphCast /GfsGraphCastReader.swift
soiz1's picture
Migrated from GitHub
6ee917b verified
enum GfsGraphCastVariableDerivedSurface: String, CaseIterable, GenericVariableMixable {
case windspeed_10m
case winddirection_10m
case wind_speed_10m
case wind_direction_10m
case surface_pressure
case weathercode
case weather_code
case is_day
case rain
case snowfall
case showers
case cloudcover
case cloudcover_low
case cloudcover_mid
case cloudcover_high
var requiresOffsetCorrectionForMixing: Bool {
return false
}
}
/**
Types of pressure level variables
*/
enum GfsGraphCastPressureVariableDerivedType: String, CaseIterable {
case windspeed
case winddirection
case dewpoint
case wind_speed
case wind_direction
case dew_point
case relativehumidity
case cloudcover
case cloud_cover
}
/**
A pressure level variable on a given level in hPa / mb
*/
struct GfsGraphCastPressureVariableDerived: PressureVariableRespresentable, GenericVariableMixable {
let variable: GfsGraphCastPressureVariableDerivedType
let level: Int
var requiresOffsetCorrectionForMixing: Bool {
return false
}
}
typealias GfsGraphCastVariableDerived = SurfaceAndPressureVariable<GfsGraphCastVariableDerivedSurface, GfsGraphCastPressureVariableDerived>
typealias GfsGraphCastVariableCombined = VariableOrDerived<GfsGraphCastVariable, GfsGraphCastVariableDerived>
struct GfsGraphCastReader: GenericReaderDerived, GenericReaderProtocol {
typealias Domain = GfsGraphCastDomain
typealias Variable = GfsGraphCastVariable
typealias Derived = GfsGraphCastVariableDerived
typealias MixingVar = GfsGraphCastVariableCombined
let reader: GenericReaderCached<GfsGraphCastDomain, GfsGraphCastVariable>
let options: GenericReaderOptions
public init?(domain: Domain, lat: Float, lon: Float, elevation: Float, mode: GridSelectionMode, options: GenericReaderOptions) throws {
guard let reader = try GenericReader<Domain, Variable>(domain: domain, lat: lat, lon: lon, elevation: elevation, mode: mode) else {
return nil
}
self.reader = GenericReaderCached(reader: reader)
self.options = options
}
public init(domain: Domain, gridpoint: Int, options: GenericReaderOptions) throws {
let reader = try GenericReader<Domain, Variable>(domain: domain, position: gridpoint)
self.reader = GenericReaderCached(reader: reader)
self.options = options
}
func get(raw: GfsGraphCastVariable, time: TimerangeDtAndSettings) throws -> DataAndUnit {
return try reader.get(variable: raw, time: time)
}
func prefetchData(raw: GfsGraphCastVariable, time: TimerangeDtAndSettings) throws {
try reader.prefetchData(variable: raw, time: time)
}
func prefetchData(variable: GfsGraphCastSurfaceVariable, time: TimerangeDtAndSettings) throws {
try prefetchData(variable: .raw(.surface(variable)), time: time)
}
func get(raw: GfsGraphCastSurfaceVariable, time: TimerangeDtAndSettings) throws -> DataAndUnit {
return try get(variable: .raw(.surface(raw)), time: time)
}
func prefetchData(derived: GfsGraphCastVariableDerived, time: TimerangeDtAndSettings) throws {
switch derived {
case .surface(let surface):
switch surface {
case .wind_speed_10m, .windspeed_10m, .wind_direction_10m, .winddirection_10m:
try prefetchData(variable: .wind_u_component_10m, time: time)
try prefetchData(variable: .wind_v_component_10m, time: time)
case .surface_pressure:
try prefetchData(variable: .pressure_msl, time: time)
try prefetchData(variable: .temperature_2m, time: time)
case .weather_code, .weathercode:
try prefetchData(variable: .cloud_cover, time: time)
try prefetchData(variable: .precipitation, time: time)
try prefetchData(variable: .temperature_2m, time: time)
case .is_day:
break
case .cloudcover:
try prefetchData(variable: .cloud_cover, time: time)
case .cloudcover_low:
try prefetchData(variable: .cloud_cover_low, time: time)
case .cloudcover_mid:
try prefetchData(variable: .cloud_cover_mid, time: time)
case .cloudcover_high:
try prefetchData(variable: .cloud_cover_high, time: time)
case .rain, .snowfall:
try prefetchData(variable: .precipitation, time: time)
try prefetchData(variable: .temperature_2m, time: time)
case .showers:
try prefetchData(variable: .precipitation, time: time)
}
case .pressure(let v):
switch v.variable {
case .windspeed, .wind_speed:
fallthrough
case .winddirection, .wind_direction:
try prefetchData(raw: .pressure(GfsGraphCastPressureVariable(variable: .wind_u_component, level: v.level)), time: time)
try prefetchData(raw: .pressure(GfsGraphCastPressureVariable(variable: .wind_v_component, level: v.level)), time: time)
case .dewpoint, .dew_point:
try prefetchData(raw: .pressure(GfsGraphCastPressureVariable(variable: .temperature, level: v.level)), time: time)
try prefetchData(raw: .pressure(GfsGraphCastPressureVariable(variable: .relative_humidity, level: v.level)), time: time)
case .relativehumidity:
try prefetchData(raw: .pressure(GfsGraphCastPressureVariable(variable: .relative_humidity, level: v.level)), time: time)
case .cloudcover, .cloud_cover:
try prefetchData(raw: .pressure(GfsGraphCastPressureVariable(variable: .relative_humidity, level: v.level)), time: time)
}
}
}
func get(derived: GfsGraphCastVariableDerived, time: TimerangeDtAndSettings) throws -> DataAndUnit {
switch derived {
case .surface(let variableDerivedSurface):
switch variableDerivedSurface {
case .windspeed_10m, .wind_speed_10m:
let u = try get(raw: .wind_u_component_10m, time: time).data
let v = try get(raw: .wind_v_component_10m, time: time).data
let speed = zip(u,v).map(Meteorology.windspeed)
return DataAndUnit(speed, .metrePerSecond)
case .winddirection_10m, .wind_direction_10m:
let u = try get(raw: .wind_u_component_10m, time: time).data
let v = try get(raw: .wind_v_component_10m, time: time).data
let direction = Meteorology.windirectionFast(u: u, v: v)
return DataAndUnit(direction, .degreeDirection)
case .surface_pressure:
let temperature = try get(raw: .temperature_2m, time: time).data
let pressure = try get(raw: .pressure_msl, time: time)
return DataAndUnit(Meteorology.surfacePressure(temperature: temperature, pressure: pressure.data, elevation: reader.targetElevation), pressure.unit)
case .weathercode, .weather_code:
let cloudcover = try get(raw: .cloud_cover, time: time).data
let precipitation = try get(raw: .precipitation, time: time).data
let snowfall = try get(derived: .surface(.snowfall), time: time).data
return DataAndUnit(WeatherCode.calculate(
cloudcover: cloudcover,
precipitation: precipitation,
convectivePrecipitation: nil,
snowfallCentimeters: snowfall,
gusts: nil,
cape: nil,
liftedIndex: nil,
visibilityMeters: nil,
categoricalFreezingRain: nil,
modelDtSeconds: time.dtSeconds), .wmoCode
)
case .is_day:
return DataAndUnit(Zensun.calculateIsDay(timeRange: time.time, lat: reader.modelLat, lon: reader.modelLon), .dimensionlessInteger)
case .cloudcover:
return try get(raw: .cloud_cover, time: time)
case .cloudcover_low:
return try get(raw: .cloud_cover_low, time: time)
case .cloudcover_mid:
return try get(raw: .cloud_cover_mid, time: time)
case .cloudcover_high:
return try get(raw: .cloud_cover_high, time: time)
case .snowfall:
let temperature = try get(raw: .temperature_2m, time: time)
let precipitation = try get(raw: .precipitation, time: time)
return DataAndUnit(zip(temperature.data, precipitation.data).map({ $1 * ($0 >= 0 ? 0 : 0.7) }), .centimetre)
case .rain:
let temperature = try get(raw: .temperature_2m, time: time)
let precipitation = try get(raw: .precipitation, time: time)
return DataAndUnit(zip(temperature.data, precipitation.data).map({ $1 * ($0 >= 0 ? 1 : 0) }), .millimetre)
case .showers:
let precipitation = try get(raw: .precipitation, time: time)
return DataAndUnit(precipitation.data.map({min($0, 0)}), precipitation.unit)
}
case .pressure(let v):
switch v.variable {
case .windspeed, .wind_speed:
let u = try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .wind_u_component, level: v.level)), time: time)
let v = try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .wind_v_component, level: v.level)), time: time)
let speed = zip(u.data,v.data).map(Meteorology.windspeed)
return DataAndUnit(speed, u.unit)
case .winddirection, .wind_direction:
let u = try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .wind_u_component, level: v.level)), time: time).data
let v = try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .wind_v_component, level: v.level)), time: time).data
let direction = Meteorology.windirectionFast(u: u, v: v)
return DataAndUnit(direction, .degreeDirection)
case .dewpoint, .dew_point:
let temperature = try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .temperature, level: v.level)), time: time)
let rh = try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .relative_humidity, level: v.level)), time: time)
return DataAndUnit(zip(temperature.data, rh.data).map(Meteorology.dewpoint), temperature.unit)
case .cloudcover, .cloud_cover:
let rh = try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .relative_humidity, level: v.level)), time: time)
return DataAndUnit(rh.data.map({Meteorology.relativeHumidityToCloudCover(relativeHumidity: $0, pressureHPa: Float(v.level))}), .percentage)
case .relativehumidity:
return try get(raw: .pressure(GfsGraphCastPressureVariable(variable: .relative_humidity, level: v.level)), time: time)
}
}
}
}