Spaces:
Sleeping
Sleeping
File size: 5,842 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 | import Foundation
extension Zensun {
/// Calculate DNI based on zenith angle
public static func calculateInstantDNI(directRadiation: [Float], latitude: Float, longitude: Float, timerange: TimerangeDt) -> [Float] {
var out = [Float]()
out.reserveCapacity(directRadiation.count)
for (dhi, timestamp) in zip(directRadiation, timerange) {
// direct horizontal irradiation
if dhi.isNaN {
out.append(.nan)
continue
}
if dhi <= 0 {
out.append(0)
continue
}
let decang = timestamp.getSunDeclination()
let eqtime = timestamp.getSunEquationOfTime()
let latsun=decang
/// universal time
let ut = timestamp.hourWithFraction
let t1 = (90-latsun).degreesToRadians
let lonsun = -15.0*(ut-12.0+eqtime)
/// longitude of sun
let p1 = lonsun.degreesToRadians
let t0=(90-latitude).degreesToRadians
/// longitude of point
let p0 = longitude.degreesToRadians
let zz = cos(t0)*cos(t1)+sin(t0)*sin(t1)*cos(p1-p0)
if zz <= 0 {
out.append(0)
continue
}
let b = max(zz, cos(Float(85).degreesToRadians))
let dni = dhi / b
out.append(dni)
}
return out
}
/// Calculate DNI using super sampling
public static func calculateBackwardsDNISupersampled(directRadiation: [Float], latitude: Float, longitude: Float, timerange: TimerangeDt, samples: Int = 60) -> [Float] {
// Shift timerange by dt and increase time resolution
let dtNew = timerange.dtSeconds / samples
let timeSuperSampled = timerange.range.add(-timerange.dtSeconds + dtNew).range(dtSeconds: dtNew)
let dhiBackwardsSuperSamled = directRadiation.interpolateSolarBackwards(timeOld: timerange, timeNew: timeSuperSampled, latitude: latitude, longitude: longitude, scalefactor: 1000)
let averagedToInstant = backwardsAveragedToInstantFactor(time: timeSuperSampled, latitude: latitude, longitude: longitude)
let dhiSuperSamled = zip(dhiBackwardsSuperSamled, averagedToInstant).map(*)
let dniSuperSampled = calculateInstantDNI(directRadiation: dhiSuperSamled, latitude: latitude, longitude: longitude, timerange: timeSuperSampled)
/// return instant values
//return (0..<timerange.count).map { dhiBackwardsSuperSamled[Swift.min($0 * samples + samples, dhiBackwardsSuperSamled.count-1)] }
let dni = dniSuperSampled.mean(by: samples)
return dni
}
/// Calculate DNI based on zenith angle
public static func calculateBackwardsDNI(directRadiation: [Float], latitude: Float, longitude: Float, timerange: TimerangeDt, convertToInstant: Bool = false) -> [Float] {
//return calculateBackwardsDNISupersampled(directRadiation: directRadiation, latitude: latitude, longitude: longitude, timerange: timerange)
return zip(directRadiation, timerange).map { (dhi, timestamp) in
if dhi.isNaN {
return .nan
}
if dhi <= 0 {
return 0
}
/// DNI is typically limted to 85° zenith. We apply 5° to the parallax in addition to atmospheric refraction
/// The parallax is then use to limit integral coefficients to sun rise/set
let alpha = Float(0.83333 - 5).degreesToRadians
let decang = timestamp.getSunDeclination()
let eqtime = timestamp.getSunEquationOfTime()
let latsun=decang
/// universal time
let ut = timestamp.hourWithFraction
let t1 = (90-latsun).degreesToRadians
let lonsun = -15.0*(ut-12.0+eqtime)
/// longitude of sun
let p1 = lonsun.degreesToRadians
let ut0 = ut - (Float(timerange.dtSeconds)/3600)
let lonsun0 = -15.0*(ut0-12.0+eqtime)
let p10 = lonsun0.degreesToRadians
let t0=(90-latitude).degreesToRadians
/// longitude of point
var p0 = longitude.degreesToRadians
if p0 < p1 - .pi {
p0 += 2 * .pi
}
if p0 > p1 + .pi {
p0 -= 2 * .pi
}
// limit p1 and p10 to sunrise/set
let arg = -(sin(alpha)+cos(t0)*cos(t1))/(sin(t0)*sin(t1))
let carg = arg > 1 || arg < -1 ? .pi : acos(arg)
let sunrise = p0 + carg
let sunset = p0 - carg
let p1_l = min(sunrise, p10)
let p10_l = max(sunset, p1)
// solve integral to get sun elevation dt
// integral(cos(t0) cos(t1) + sin(t0) sin(t1) cos(p - p0)) dp = sin(t0) sin(t1) sin(p - p0) + p cos(t0) cos(t1) + constant
let left = sin(t0) * sin(t1) * sin(p1_l - p0) + p1_l * cos(t0) * cos(t1)
let right = sin(t0) * sin(t1) * sin(p10_l - p0) + p10_l * cos(t0) * cos(t1)
let zzBackwards = (left-right) / (p1_l - p10_l)
let dni = dhi / zzBackwards
// Prevent possible division by zero
// See https://github.com/open-meteo/open-meteo/discussions/395
if zzBackwards <= 0.0001 {
return dhi
}
/// Instant sun elevation
let zzInstant = cos(t0)*cos(t1)+sin(t0)*sin(t1)*cos(p1-p0)
return convertToInstant ? dni * max(zzInstant, 0) / zzBackwards : dni
}
}
}
|