Spaces:
Sleeping
Sleeping
File size: 5,516 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 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 | import Foundation
protocol GenericVariableMixable: RawRepresentableString {
/// Soil moisture or snow depth are cumulative processes and have offests if mutliple models are mixed
var requiresOffsetCorrectionForMixing: Bool { get }
}
/// Mix differnet domains together, that offer the same or similar variable set
protocol GenericReaderMixerRaw: GenericReaderProtocol {
associatedtype Reader: GenericReaderProtocol
var reader: [Reader] { get }
init(reader: [Reader])
}
protocol GenericReaderMixer: GenericReaderMixerRaw {
associatedtype Domain: GenericDomain
static func makeReader(domain: Domain, lat: Float, lon: Float, elevation: Float, mode: GridSelectionMode, options: GenericReaderOptions) throws -> Reader?
}
struct GenericReaderMixerSameDomain<Reader: GenericReaderProtocol>: GenericReaderMixerRaw, GenericReaderProtocol {
typealias MixingVar = Reader.MixingVar
let reader: [Reader]
init(reader: [Reader]) {
self.reader = reader
}
}
extension GenericReaderMixer {
public init?(domains: [Domain], lat: Float, lon: Float, elevation: Float, mode: GridSelectionMode, options: GenericReaderOptions) throws {
/// Initiaise highest resolution domain first. If `elevation` is NaN, use the elevation of the highest domain,
var elevation = elevation
let reader: [Reader] = try domains.reversed().compactMap { domain -> (Reader?) in
guard let domain = try Self.makeReader(domain: domain, lat: lat, lon: lon, elevation: elevation, mode: mode, options: options) else {
return nil
}
if elevation.isNaN {
elevation = domain.modelElevation.numeric
}
return domain
}.reversed()
guard !reader.isEmpty else {
return nil
}
self.init(reader: reader)
}
}
extension GenericReaderMixerRaw {
var modelLat: Float {
reader.last!.modelLat
}
var modelLon: Float {
reader.last!.modelLon
}
var modelElevation: ElevationOrSea {
reader.last!.modelElevation
}
var targetElevation: Float {
reader.last!.targetElevation
}
var modelDtSeconds: Int {
reader.last!.modelDtSeconds
}
func prefetchData(variable: Reader.MixingVar, time: TimerangeDtAndSettings) throws {
for reader in reader {
try reader.prefetchData(variable: variable, time: time)
}
}
func prefetchData(variables: [Reader.MixingVar], time: TimerangeDtAndSettings) throws {
try variables.forEach { variable in
try prefetchData(variable: variable, time: time)
}
}
func getStatic(type: ReaderStaticVariable) throws -> Float? {
return try reader.last?.getStatic(type: type)
}
func get(variable: Reader.MixingVar, time: TimerangeDtAndSettings) throws -> DataAndUnit {
// Last reader return highest resolution data. therefore reverse iteration
// Integrate now lower resolution models
var data: [Float]? = nil
var unit: SiUnit? = nil
if variable.requiresOffsetCorrectionForMixing {
for r in reader.reversed() {
let d = try r.get(variable: variable, time: time)
if data == nil {
// first iteration
data = d.data
unit = d.unit
data?.deltaEncode()
} else {
data?.integrateIfNaNDeltaCoded(d.data)
}
if data?.containsNaN() == false {
break
}
}
// undo delta operation
data?.deltaDecode()
data?.greater(than: 0)
} else {
// default case, just place new data in 1:1
for r in reader.reversed() {
let d = try r.get(variable: variable, time: time)
if data == nil {
// first iteration
data = d.data
unit = d.unit
} else {
data?.integrateIfNaN(d.data)
}
if data?.containsNaN() == false {
break
}
}
}
guard let data, let unit else {
fatalError("Expected data in mixer for variable \(variable)")
}
return DataAndUnit(data, unit)
}
}
extension VariableOrDerived: GenericVariableMixable where Raw: GenericVariableMixable, Derived: GenericVariableMixable {
var requiresOffsetCorrectionForMixing: Bool {
switch self {
case .raw(let raw):
return raw.requiresOffsetCorrectionForMixing
case .derived(let derived):
return derived.requiresOffsetCorrectionForMixing
}
}
}
extension Array where Element == Float {
mutating func integrateIfNaN(_ other: [Float]) {
for x in other.indices {
if other[x].isNaN || !self[x].isNaN {
continue
}
self[x] = other[x]
}
}
mutating func integrateIfNaNDeltaCoded(_ other: [Float]) {
for x in other.indices {
if other[x].isNaN || !self[x].isNaN {
continue
}
if x > 0 {
self[x] = other[x-1] - other[x]
} else {
self[x] = other[x]
}
}
}
}
|