Spaces:
Sleeping
Sleeping
File size: 4,763 Bytes
6ee917b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | import Foundation
import OmFileFormat
/**
Generic domain that is required for the reader
*/
protocol GenericDomain {
/// The grid definition. Could later be replaced with a more generic implementation
var grid: Gridable { get }
/// Domain name used as data directory
var domainRegistry: DomainRegistry { get }
/// Domain which is used for static files. E.g. 15minutes domains refer to the 1-hourly domain
var domainRegistryStatic: DomainRegistry? { get }
/// Time resoltuion of the deomain. 3600 for hourly, 10800 for 3-hourly
var dtSeconds: Int { get }
/// How often a domain is updated in seconds. `3600` for updates every hour
var updateIntervalSeconds: Int { get }
/// If true, domain has yearly files
var hasYearlyFiles: Bool { get }
/// If present, the timerange that is available in a master file
var masterTimeRange: Range<Timestamp>? { get }
/// The time length of each compressed time series file
var omFileLength: Int { get }
}
extension GenericDomain {
var dtHours: Int { dtSeconds / 3600 }
/// Temporary directory to download data
var downloadDirectory: String {
return "\(OpenMeteo.tempDirectory)download-\(domainRegistry.rawValue)/"
}
/// The the file containing static information for elevation of soil types
func getStaticFile(type: ReaderStaticVariable) -> OmFileReaderArray<MmapFile, Float>? {
guard let domainRegistryStatic else {
return nil
}
switch type {
case .soilType:
return try? OmFileManager.get(
.staticFile(domain: domainRegistryStatic, variable: "soil_type", chunk: nil)
)
case .elevation:
return try? OmFileManager.get(
.staticFile(domain: domainRegistryStatic, variable: "HSURF", chunk: nil)
)
}
}
func getMetaJson() throws -> ModelUpdateMetaJson? {
return try MetaFileManager.get(OmFileManagerReadable.meta(domain: domainRegistry))
}
/// Filename of the surface elevation file
var surfaceElevationFileOm: OmFileManagerReadable {
.staticFile(domain: domainRegistry, variable: "HSURF", chunk: nil)
}
var soilTypeFileOm: OmFileManagerReadable {
.staticFile(domain: domainRegistry, variable: "soil_type", chunk: nil)
}
}
/**
Generic variable for the reader implementation
*/
protocol GenericVariable: GenericVariableMixable {
/// The filename of the variable. Typically just `temperature_2m`. Level is used to store mutliple levels or ensemble members in one file
/// NOTE: `level` has been replaced with `ensembleMemberLevel` in settings
var omFileName: (file: String, level: Int) { get }
/// The scalefactor to compress data
var scalefactor: Float { get }
/// Kind of interpolation for this variable. Used to interpolate from 1 to 3 hours
var interpolation: ReaderInterpolation { get }
/// SI unit of this variable
var unit: SiUnit { get }
/// If true, temperature will be corrected by 0.65°K per 100 m
var isElevationCorrectable: Bool { get }
/// If true, forecasts from the previous model runs will be preserved
var storePreviousForecast: Bool { get }
}
enum ReaderInterpolation {
/// Simple linear interpolation
case linear
/// Interpolate 0-360° values
case linearDegrees
/// Hermite interpolation for more smooth interpolation for temperature
case hermite(bounds: ClosedRange<Float>?)
/// Solar fluxes are properly backwards averaged during model run time. Always be the case with weather models
case solar_backwards_averaged
/// Solar flux is backwards averaged, but values after missing values are not averaged correctly.
/// This happens with satellite data and missing time steps
case solar_backwards_missing_not_averaged
/// Take the next hour, and devide by `dt` to preserve sums like precipitation
case backwards_sum
/// Replicate value backwards. E.g. min/max of previous hours
case backwards
/// How many timesteps on the left and right side are used for interpolation
var padding: Int {
switch self {
case .linear, .linearDegrees:
return 1
case .hermite:
return 2
case .solar_backwards_averaged, .solar_backwards_missing_not_averaged:
return 2
case .backwards_sum, .backwards:
return 1
}
}
var bounds: ClosedRange<Float>? {
switch self {
case .hermite(let bounds):
return bounds
default:
return nil
}
}
}
|