-
-
Notifications
You must be signed in to change notification settings - Fork 141
/
Icon.swift
192 lines (178 loc) · 6.26 KB
/
Icon.swift
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import Foundation
import NIOConcurrencyHelpers
import SwiftPFor2D
/**
ICON Domains including ensemble
*/
enum IconDomains: String, CaseIterable, GenericDomain {
/// hourly data until forecast hour 78, then 3 h until 180
case icon
case iconEu = "icon-eu"
case iconD2 = "icon-d2"
case iconD2_15min = "icon-d2-15min"
case iconEps = "icon-eps"
case iconEuEps = "icon-eu-eps"
case iconD2Eps = "icon-d2-eps"
var dtSeconds: Int {
if self == .iconD2_15min {
return 3600/4
}
return 3600
}
var domainRegistry: DomainRegistry {
switch self {
case .icon:
return .dwd_icon
case .iconEu:
return .dwd_icon_eu
case .iconD2:
return .dwd_icon_d2
case .iconD2_15min:
return .dwd_icon_d2_15min
case .iconEps:
return .dwd_icon_eps
case .iconEuEps:
return .dwd_icon_eu_eps
case .iconD2Eps:
return .dwd_icon_d2_eps
}
}
var domainRegistryStatic: DomainRegistry? {
switch self {
case .iconD2_15min:
return .dwd_icon_d2
default:
return domainRegistry
}
}
var hasYearlyFiles: Bool {
return false
}
var masterTimeRange: Range<Timestamp>? {
return nil
}
/// How many hourly timesteps to keep in each compressed chunk
var omFileLength: Int {
switch self {
case .icon, .iconEps:
return 180+1 + 3*24
case .iconEu, .iconEuEps:
return 120+1 + 3*24
case .iconD2, .iconD2Eps:
return 48+1 + 3*24
case .iconD2_15min:
return 48*4 + 3*24
}
}
/// All available pressure levels for the current domain
var levels: [Int] {
switch self {
case .icon:
return [30, 50, 70, 100, 150, 200, 250, 300, 400, 500, 600, 700, 800, 850, 900, 925, 950, 1000]
case .iconEu:
return [ 50, 70, 100, 150, 200, 250, 300, 400, 500, 600, 700, 800, 850, 900, 925, 950, 1000] // disabled: 775, 825, 875
case .iconD2:
return [ 200, 250, 300, 400, 500, 600, 700, 850, 950, 975, 1000]
case .iconD2_15min:
return []
case .iconEps:
return []
case .iconEuEps:
return [] // 300, 500, 850 only temperature and wind
case .iconD2Eps:
return [] // 500, 700, 850, 950, 975, 1000
}
}
/// Numer of avaialble forecast steps differs from run
/// E.g. icon global 0z has 180 as a last value, but 6z only 120
func getDownloadForecastSteps(run: Int) -> [Int] {
switch self {
case .iconEps:
// Note ICON-EPS has only 6 hourly data for 6/18z runs, not used here
// Hourly data until 48h, 3 hourly until 72, 6 hourly until 120h (same as ICON-EU-EPS) and 12 hourly until 180h
return Array(0...48) + Array(stride(from: 51, through: 72, by: 3)) + Array(stride(from: 78, through: 120, by: 6)) + Array(stride(from: 132, through: 180, by: 12))
case .icon:
if run == 6 || run == 18 {
// only up to 120
return Array(0...78) + Array(stride(from: 81, through: 120, by: 3))
} else {
// full 180
return Array(0...78) + Array(stride(from: 81, through: 180, by: 3))
}
case .iconEuEps:
// Hourly data until 48h, 3 hourly until 72, then 6 hourly until 120h (same as ICON-EPS)
// no side runs
return Array(0...48) + Array(stride(from: 51, through: 72, by: 3)) + Array(stride(from: 78, through: 120, by: 6))
case .iconEu:
if run % 6 == 0 {
return Array(0...78) + Array(stride(from: 81, through: 120, by: 3))
}
// side runs
return Array(0...30)
case .iconD2_15min:
return Array(0...48*4-1)
case .iconD2Eps:
fallthrough
case .iconD2:
return Array(0...48)
}
}
var grid: Gridable {
switch self {
case .icon:
return RegularGrid(nx: 2879, ny: 1441, latMin: -90, lonMin: -180, dx: 0.125, dy: 0.125)
case .iconEu:
return RegularGrid(nx: 1377, ny: 657, latMin: 29.5, lonMin: -23.5, dx: 0.0625, dy: 0.0625)
case .iconD2_15min:
fallthrough
case .iconD2:
return RegularGrid(nx: 1215, ny: 746, latMin: 43.18, lonMin: -3.94, dx: 0.02, dy: 0.02)
case .iconEps:
// R03B06 avg 26.5 km
return RegularGrid(nx: 1439, ny: 721, latMin: -90, lonMin: -180, dx: 0.25, dy: 0.25)
case .iconEuEps:
// R03B07 avg 13.2 km
return RegularGrid(nx: 689, ny: 329, latMin: 29.5, lonMin: -23.5, dx: 0.125, dy: 0.125)
case .iconD2Eps:
// R19B07 avg 2 km
// Note: 1px difference to use the same weights as official
return RegularGrid(nx: 1214, ny: 745, latMin: 43.18, lonMin: -3.94, dx: 0.02, dy: 0.02)
}
}
/// name in the filenames
var region: String {
switch self {
case .iconEps: fallthrough
case .icon: return "global"
case .iconEuEps: fallthrough
case .iconEu: return "europe"
case .iconD2Eps: fallthrough
case .iconD2_15min: fallthrough
case .iconD2: return "germany"
}
}
/// model level standard heights, full levels
/// icon wind level 1-90 88=98m, 87-174m
/// icon-eu 1-60 58,57
/// icon-d2 1-65.... 63=78m, 62=126m
var numberOfModelFullLevels: Int {
switch self {
case .iconEps:
fallthrough
case .icon:
return 120 // was 90
case .iconEuEps:
fallthrough
case .iconEu:
return 74 // was 60
case .iconD2Eps:
fallthrough
case .iconD2_15min:
fallthrough
case .iconD2:
return 65
}
}
/// ICON uses 1.5°C melting point temperature: https://gitlab.dkrz.de/icon/icon-model/-/blob/release-2024.01-public/src/atm_phy_nwp/mo_nh_interface_nwp.f90?ref_type=heads#L2232
static let tMelt = Float(1.5)
}