From 64d31693a20d8b6ddeb38974ea9920df04a2a5a6 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Fri, 10 Jan 2020 00:52:26 +0530 Subject: [PATCH 01/22] initial_commit_Boyle_Coello_soiling_model --- pvlib/pvl_soiling_hsu.py | 307 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 pvlib/pvl_soiling_hsu.py diff --git a/pvlib/pvl_soiling_hsu.py b/pvlib/pvl_soiling_hsu.py new file mode 100644 index 0000000000..48ca27d253 --- /dev/null +++ b/pvlib/pvl_soiling_hsu.py @@ -0,0 +1,307 @@ +import argparse +import time, datetime +import numpy as np +from scipy import integrate, special + +def accumarray(Indx, value): + n = np.max(Indx)+1 + if(np.isscalar(value)): + value = np.repeat(value, len(Indx)) + + A = np.zeros((n,)) + for i in range(n): + A[i]=np.sum(value[Indx[:]==i]) + return A + +def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType = 2, RainAccPeriod = 1, LUC = 8, WindSpeed = 2, Temperature = 12): +''' + PVL_SOILING_HSU Calculates soiling rate over time given particulate and rain data + + Parameters + ---------- + + Time : Time_Structure + Time values for the soiling function do not need to be + regularly spaced, although large gaps in timing are discouraged. (datetime) + + Rain : numeric + Rainfall values should be in mm of rainfall. Programmatically, rain + is accumulated over a given time period, and cleaning is applied + immediately after a time period where the cleaning threshold is + reached. (mm) + + RainThresh : numeric + RainThresh is a scalar for the amount of rain, in mm, in an accumulation + period needed to clean the PV modules. In periods where the + accumulated rain meets or exceeds RainThresh, the panels are assumed + to be cleaned immediately after the accumulation period + [1] suggests a good RainThresh could be 1mm, but the time period is + not specified. Rain accumulation period length can be adjusted in the + optional input RainAccPeriod. (mm) + + Tilt : numeric + Tilt is a scalar or vector for the tilt of the PV panels. Changing tilt + angles (e.g. in tracking cases) can be accomodated, and tilt angles + are correlated with the entries in Time.(degree) + + PM2_5 : numeric + PM2_5 is the concentration of airborne particulate matter (PM) with diameter less + than 2.5 microns. (g/m^3) + + PM10 : numeric + PM10 is the concentration of airborne particulate matter (PM) with diameter less than + 10 microns, in g/m^3. + + ModelType : numeric + ModelType is an optional input to the function to determine the + the model type to be used in the soiling model, see [1]. A + value of "1" indicates that the Variable Deposition Velocity model + shall be used, a value of "2" indicates that the Fixed Settling + Velocity model shall be used, and a value of "3" indicates that the + Fixed Deposition Velocity model shall be used. [1] indicates that the + Fixed Settling Velocity model performs best under a wide range of + conditions, and thus "2" is the default ModelType if ModelType is omitted. + Validation efforts by Sandia National Laboratories + confirm these findings. If an incorrect ModelType is provided, the + Fixed Settling Velocity (type 2) will be used (with a warning). + + RainAccPeriod : numeric + RainAccPeriod is an optional input that specifies the period, in hours, + over which to accumulate rainfall totals before checking against the + rain cleaning threshold. For example, if the rain threshold is + 0.5 mm per hour, then RainThresh should be 0.5 and RainAccPeriod + should be 1. If the threshold is 1 mm per hour, then the values + should be 1 and 1, respectively. The minimum RainAccPeriod is 1 + hour. The default value is 1, indicating hourly rain accumulation. + Accumulation periods exceeding 24 (daily accumulation) are not + recommended. (mm per hour) + + LUC : numeric + LUC is an optional input to the function, but it is required for the + Variable Deposition Model. LUC is the Land Use Category as specified + in Table 19.2 of [2]. LUC must be a numeric scalar with value 1, 4, + 6, 8, or 10, corresponding to land with evergreen trees, deciduous + trees, grass, desert, or shrubs with interrupted woodlands. If + omitted, the default value of 8 (desert) is used. + WindSpeed : numeric + WindSpeed is an optional input to the function, but is required for the + Variable Deposition Model. WindSpeed is a scalar or vector value with + the same number of elements as Time, and must be in meters per + second. If WindSpeed is omitted, the value of 2 m/s is used as + default. (m/s) + + Temperature : numeric + Temperature is an optional input to the function, but is required for + the Variable Deposition Model. Temperature is a scalar or vector + value with the same number of Elements as Time and must be in degrees + C. If Temperature is omitted, the value of 12 C is used as default. (Celcius) + + Returns + ------- + SR : numeric + The soiling ratio (SR) of a tilted PV panel, this is a number + between 0 and 1. SR is a time series where each element of SR + correlates with the accumulated soiling and rain cleaning at the times + specified in Time. + + Notes + ------ + The following default values + + ============================ ================ + Parameter Value + ============================ ================ + ModelType 2 + Temperature at zero altitude 288.15 K + RainAccPeriod 1 mm per hour + LUC 2 + WindSpeed 2 m/s + Temperature 12 C + ============================ ================ + + References + ----------- + .. [1] M. Coello and L. Boyle, "Simple Model For Predicting Time Series + Soiling of Photovoltaic Panels," in IEEE Journal of Photovoltaics. + doi: 10.1109/JPHOTOV.2019.2919628 + .. [2] Atmospheric Chemistry and Physics: From Air Pollution to Climate + Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. + + ''' + + # Time is datetime structure + TimeAsDatenum = time.mktime(Time.timetuple()) + + RainAccAsDatenum = np.floor(TimeAsDatenum * 24/ RainAccPeriod) + + ##### Doubt ###### + + [RainAccTimes, UnqRainAccFrstVal, UnqRainAccIndx] = np.unique(RainAccAsDatenum,return_index=True, return_inverse=True) + + RainAtAccTimes = accumarray(UnqRainAccIndx, Rain); + ##### Doubt ###### + + AccumRain = np.zeros_like(Rain) + AccumRain[UnqRainAccFrstVal[1:]-1] = RainAtAccTimes[1:-1] + AccumRain[-1] = RainAtAccTimes[-1] + + vd_switch = { + + 1:depo_veloc(Temperature, WindSpeed, LUC), # case 1 Variable Deposition Velocity + + 2:np.array([0.0009,0.004]), # case 2 % Fixed Settling Velocity in m/s + + 3:np.array([0.0015,0.0917]) # case 3 % Fixed Deposition Velcoity in m/s + + } + + try: + vd = vd_switch[ModelType] + except: + print("Unknown MoodelType, assuming MoodelType to 2.") + MoodelType = 2 + vd = vd_switch[ModelType] + + + + PMConcentration=np.zeros(len(np.ravel(TimeAsDatenum)),2) # pre-allocate with NAN + + + PMConcentration[:,0] = PM2_5 # fill PM2.5 data in column 1 + PMConcentration[:,1] = PM10 - PM2_5 # fill in PM2.5-PM10 data in column 2 + + PMConcentration[PM10 - PM2_5 < 0 , 1] = 0 + + PMConcentration = PMConcentration * 10**-6; + + F = PMConcentration * vd # g * m^-2 * s^-1, by particulate size + HorizontalTotalMassRate = F[:,0]+ F[:,2] # g * m^-2 * s^-1, total + + TiltedMassRate = HorizontalTotalMassRate * np.cosd(mp.pi*Tilt/180) + + TiltedMassNoRain = integrate.cumtrapz(TimeAsDatenum*86400, TiltedMassRate) + + TiltedMass = TiltedMassNoRain; + + for cntr1 in range(0,len(RainAtAccTimes)): + if (RainAtAccTimes[cntr1] >= RainThresh): + TiltedMass[UnqRainAccFrstVal[cntr1+1]:end] = TiltedMass[UnqRainAccFrstVal[cntr1+1]:end]-TiltedMass[UnqRainAccFrstVal[cntr1+1]-1] + + + SoilingRate = 34.37 * special.erf(0.17*TiltedMass**0.8473) + + SR = (100 - SoilingRate)/100 + + +def depo_velocity(T, WindSpeed, LUC): + + # convert temperature into Kelvin + T = T + 273.15 + + # save wind data + if(np.isscalar(WindSpeed)): + u = np.array([WindSpeed]) + else: + u = WindSpeed + + g=9.81 #gravity in m/s^2 + Na=6.022*10**23 #avagadros number + R=8.314 #Universal gas consant in m3Pa/Kmol + k=1.38*10**-23 #Boltzmann's constant in m^2kg/sK + P=101300 #pressure in Pa + rhoair= 1.2041 #density of air in kg/m3 + z0=1 + rhop=1500 #Assume density of particle in kg/m^3 + + switcher={ + 1:0.56, + 4:0.56, + 6:0.54, + 8:0.54, + 10:0.54, + } + + try: + gamma = switcher[LUC] + except: + print("Unknown Land Use Category (LUC), assuming LUC 8.") + LUC = 8 + gamma = switcher[LUC] + + + # Diameter of particle in um + Dpum=np.array([2.5,10]) + Dpm=Dpum*10**-6 # Diameter of particle in m + + # Calculations + mu=1.8*10**-5*(T/298)**0.85 #viscosity of air in kg/m s + nu=mu/rhoair + lambda1=2*mu/(P*(8.*0.0288/(np.pi*R*T))**(0.5)) #mean free path + ll = np.array([lambda1,lambda1]) + Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) #slip correction coefficient + + ########################################################################### + # Calculate vs + ########################################################################### + vs = rhop*Dpm**2*(g*Cc/(mu*18)) # particle settling velocity + + ########################################################################### + # Calculate rb + ########################################################################### + ustar = np.zeros_like(u, dtype=float) # pre-allocate ustar + # Equation 11.66 in Ramaswami (and 16.67 and Sienfeld &Pandis) + ustar[u > 0] = 0.4 * u[u > 0]/np.log(10/z0) + ustar[u<=0] = 0.001 + + D=k*T*(Cc/(3*np.pi*mu*Dpm)) + + Sc=nu/D + # gamma=0.56 #for urban + # alpha=1.5 #for urban + EB=Sc**(-1 * gamma) + St = vs*(ustar ** 2)/ (g*nu) + + EIM=10.0**(-3.0/St) #For smooth surfaces + #EIM=((St)./(0.82+St)).^2 + + R1=np.exp(-St**(0.5)) # percentage of particles that stick + + rb=1/(3*(EB+EIM)*ustar*R1) + + ########################################################################### + # Calculate ra + ########################################################################### + a=np.array([-0.096,-0.037,-0.002,0,0.004,0.035]) + b=np.array([0.029,0.029,0.018,0,-0.018,-0.036]) + + # For wind speeds <= 3, use a = -0.037 and b = 0.029 + # For wind speeds >3 and <=5, use a = -.002, b = 0.018 + # For wind speeds > 5, use a = 0, b = 0 + avals = a[1]*np.ones_like(u, dtype=float) + avals[u>3] = a[2] + avals[u>5] = a[3] + + bvals = b[1]*np.ones_like(u, dtype=float) + bvals[u>3] = b[2] + bvals[u>5] = b[3] + + L = 1/(avals + bvals* np.log(z0)) + + zeta0=z0/L + zeta=10.0/L + eta = ((1-15*zeta)**(0.25)) + eta0 = ((1-15*zeta0)**(0.25)) + + ra = np.zeros_like(zeta, dtype=float) # Preallocate memory + ra[zeta == 0] = (1/ (0.4* ustar[zeta == 0]))* np.log(10.0/z0) + ra[zeta > 0] = (1/(0.4*ustar[zeta > 0]))*(np.log(10.0/z0) + 4.7*(zeta[zeta > 0]-zeta0[zeta > 0])) + ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0]))* (np.log(10.0/ z0) + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 / ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + 2 * (np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) + + ########################################################################### + # Calculate vd and mass flux + ########################################################################### + + vd=1/(ra+rb)+vs + + return vd + From 501d48c4845325b0f2fcbfe979748462db982584 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Sat, 11 Jan 2020 01:10:02 +0530 Subject: [PATCH 02/22] stickler-ci_correction --- pvlib/pvl_soiling_hsu.py | 446 +++++++++++++++++++++------------------ 1 file changed, 240 insertions(+), 206 deletions(-) diff --git a/pvlib/pvl_soiling_hsu.py b/pvlib/pvl_soiling_hsu.py index 48ca27d253..7d3c34431d 100644 --- a/pvlib/pvl_soiling_hsu.py +++ b/pvlib/pvl_soiling_hsu.py @@ -1,100 +1,106 @@ import argparse -import time, datetime +import time +import datetime import numpy as np +import warnings from scipy import integrate, special + def accumarray(Indx, value): - n = np.max(Indx)+1 - if(np.isscalar(value)): - value = np.repeat(value, len(Indx)) - A = np.zeros((n,)) - for i in range(n): - A[i]=np.sum(value[Indx[:]==i]) - return A + n = np.max(Indx)+1 + if(np.isscalar(value)): + value = np.repeat(value, len(Indx)) + + A = np.zeros((n,)) + for i in range(n): + A[i] = np.sum(value[Indx[:] == i]) + return A + + +def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, + ModelType=2, RainAccPeriod=1, LUC=8, + WindSpeed=2, Temperature=12 + ): -def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType = 2, RainAccPeriod = 1, LUC = 8, WindSpeed = 2, Temperature = 12): -''' - PVL_SOILING_HSU Calculates soiling rate over time given particulate and rain data + + """ + PVL_SOILING_HSU Calculates soiling rate over time given particulate and + rain data Parameters ---------- - + Time : Time_Structure Time values for the soiling function do not need to be - regularly spaced, although large gaps in timing are discouraged. (datetime) - + regularly spaced, although large gaps in timing are + discouraged. (datetime) + Rain : numeric Rainfall values should be in mm of rainfall. Programmatically, rain is accumulated over a given time period, and cleaning is applied immediately after a time period where the cleaning threshold is reached. (mm) - + RainThresh : numeric - RainThresh is a scalar for the amount of rain, in mm, in an accumulation - period needed to clean the PV modules. In periods where the - accumulated rain meets or exceeds RainThresh, the panels are assumed - to be cleaned immediately after the accumulation period - [1] suggests a good RainThresh could be 1mm, but the time period is - not specified. Rain accumulation period length can be adjusted in the - optional input RainAccPeriod. (mm) - + RainThresh is a scalar for the amount of rain, in mm, in an + accumulation period needed to clean the PV modules. (mm) + Tilt : numeric - Tilt is a scalar or vector for the tilt of the PV panels. Changing tilt - angles (e.g. in tracking cases) can be accomodated, and tilt angles - are correlated with the entries in Time.(degree) + Tilt is a scalar or vector for the tilt of the PV panels. (degree) PM2_5 : numeric - PM2_5 is the concentration of airborne particulate matter (PM) with diameter less - than 2.5 microns. (g/m^3) + PM2_5 is the concentration of airborne particulate matter (PM) with + diameter less than 2.5 microns. (g/m^3) PM10 : numeric - PM10 is the concentration of airborne particulate matter (PM) with diameter less than - 10 microns, in g/m^3. + PM10 is the concentration of airborne particulate matter (PM) with + diameter less than 10 microns. (g/m^3) - ModelType : numeric - ModelType is an optional input to the function to determine the + ModelType : numeric, optional + ModelType is an optional input to the function to determine the the model type to be used in the soiling model, see [1]. A value of "1" indicates that the Variable Deposition Velocity model shall be used, a value of "2" indicates that the Fixed Settling Velocity model shall be used, and a value of "3" indicates that the Fixed Deposition Velocity model shall be used. [1] indicates that the Fixed Settling Velocity model performs best under a wide range of - conditions, and thus "2" is the default ModelType if ModelType is omitted. - Validation efforts by Sandia National Laboratories + conditions, and thus "2" is the default ModelType if ModelType + is omitted. Validation efforts by Sandia National Laboratories confirm these findings. If an incorrect ModelType is provided, the Fixed Settling Velocity (type 2) will be used (with a warning). - RainAccPeriod : numeric - RainAccPeriod is an optional input that specifies the period, in hours, - over which to accumulate rainfall totals before checking against the - rain cleaning threshold. For example, if the rain threshold is - 0.5 mm per hour, then RainThresh should be 0.5 and RainAccPeriod - should be 1. If the threshold is 1 mm per hour, then the values - should be 1 and 1, respectively. The minimum RainAccPeriod is 1 - hour. The default value is 1, indicating hourly rain accumulation. + RainAccPeriod : numeric, optional + RainAccPeriod is an optional input that specifies the period, + in hours, over which to accumulate rainfall totals before checking + against the rain cleaning threshold. For example, if the rain + threshold is 0.5 mm per hour, then RainThresh should be 0.5 and + RainAccPeriod should be 1. If the threshold is 1 mm per hour, then + the values should be 1 and 1, respectively. The minimum RainAccPeriod + is 1hour. The default value is 1, indicating hourly rain accumulation. Accumulation periods exceeding 24 (daily accumulation) are not recommended. (mm per hour) - - LUC : numeric + + LUC : numeric, optional LUC is an optional input to the function, but it is required for the Variable Deposition Model. LUC is the Land Use Category as specified in Table 19.2 of [2]. LUC must be a numeric scalar with value 1, 4, 6, 8, or 10, corresponding to land with evergreen trees, deciduous - trees, grass, desert, or shrubs with interrupted woodlands. If - omitted, the default value of 8 (desert) is used. - WindSpeed : numeric - WindSpeed is an optional input to the function, but is required for the - Variable Deposition Model. WindSpeed is a scalar or vector value with - the same number of elements as Time, and must be in meters per + trees, grass, desert, or shrubs with interrupted woodlands. If + omitted, the default value of 8 (desert) is used. + + WindSpeed : numeric, optional + WindSpeed is an optional input to the function, but is required for + the Variable Deposition Model. WindSpeed is a scalar or vector value + with the same number of elements as Time, and must be in meters per second. If WindSpeed is omitted, the value of 2 m/s is used as default. (m/s) - - Temperature : numeric + + Temperature : numeric, optional Temperature is an optional input to the function, but is required for the Variable Deposition Model. Temperature is a scalar or vector value with the same number of Elements as Time and must be in degrees - C. If Temperature is omitted, the value of 12 C is used as default. (Celcius) + C. By Default, the value of 12 C is used as default. (Celcius) Returns ------- @@ -106,7 +112,7 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType = 2, Ra Notes ------ - The following default values + The following are default values ============================ ================ Parameter Value @@ -121,187 +127,215 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType = 2, Ra References ----------- - .. [1] M. Coello and L. Boyle, "Simple Model For Predicting Time Series + .. [1] M. Coello and L. Boyle, "Simple Model For Predicting Time Series Soiling of Photovoltaic Panels," in IEEE Journal of Photovoltaics. doi: 10.1109/JPHOTOV.2019.2919628 .. [2] Atmospheric Chemistry and Physics: From Air Pollution to Climate - Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. + Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. + + """ + assert isinstance(Time, datetime.datetime), \ + "Time variable is not datetime instance" + assert np.char.isnumeric(str(Rain)) and (Rain >= 0), \ + "Error with the Rain value" + assert np.char.isnumeric(str(RainThresh)) and np.isscalar(RainThresh) and \ + (RainThresh >= 0), "Error with the RainThresh value" + assert np.char.isnumeric(str(Tilt)), "Error with the Tilt value" + assert np.char.isnumeric(str(PM2_5)) and (PM2_5 >= 0), \ + "Error with the PM2_5 value" + assert np.char.isnumeric(str(PM10)) and (PM10 >= 0), \ + "Error with the PM10 value" + # optional variables + assert np.isscalar(ModelType), "Error with the ModelType value" + assert np.char.isnumeric(str(RainAccPeriod)) and \ + np.isscalar(RainAccPeriod) and (RainAccPeriod >= 1), \ + "Error with the RainAccPeriod value" + assert np.isscalar(LUC), "Error with the LUC value" + assert np.char.isnumeric(str(WindSpeed)) and (WindSpeed >= 0), \ + "Error with the WindSpeed value" + assert np.char.isnumeric(str(Temperature)) and (Temperature >= 0), \ + "Error with the Temperature value" + + # Time is datetime structure + TimeAsDatenum = time.mktime(Time.timetuple()) - ''' + RainAccAsDatenum = np.floor(TimeAsDatenum * 24 / RainAccPeriod) - # Time is datetime structure - TimeAsDatenum = time.mktime(Time.timetuple()) + # Doubt - RainAccAsDatenum = np.floor(TimeAsDatenum * 24/ RainAccPeriod) + [RainAccTimes, UnqRainAccFrstVal, UnqRainAccIndx] = np.unique( + RainAccAsDatenum, + return_index=True, + return_inverse=True + ) - ##### Doubt ###### + RainAtAccTimes = accumarray(UnqRainAccIndx, Rain) + # Doubt - [RainAccTimes, UnqRainAccFrstVal, UnqRainAccIndx] = np.unique(RainAccAsDatenum,return_index=True, return_inverse=True) + AccumRain = np.zeros_like(Rain) + AccumRain[UnqRainAccFrstVal[1:]-1] = RainAtAccTimes[1:-1] + AccumRain[-1] = RainAtAccTimes[-1] - RainAtAccTimes = accumarray(UnqRainAccIndx, Rain); - ##### Doubt ###### + vd_switch = { - AccumRain = np.zeros_like(Rain) - AccumRain[UnqRainAccFrstVal[1:]-1] = RainAtAccTimes[1:-1] - AccumRain[-1] = RainAtAccTimes[-1] + 1 : depo_veloc(Temperature, WindSpeed, LUC), + # case 1 Variable Deposition Velocity + + 2 : np.array([0.0009,0.004]), + # case 2 % Fixed Settling Velocity in m/s + + 3 : np.array([0.0015,0.0917]) + # case 3 % Fixed Deposition Velcoity in m/s - vd_switch = { - - 1:depo_veloc(Temperature, WindSpeed, LUC), # case 1 Variable Deposition Velocity - - 2:np.array([0.0009,0.004]), # case 2 % Fixed Settling Velocity in m/s - - 3:np.array([0.0015,0.0917]) # case 3 % Fixed Deposition Velcoity in m/s + } - } + try: + vd = vd_switch[ModelType] + except Exception as e: + warnings.warn("Unknown ModelType, assuming MoodelType to 2.") + ModelType = 2 + vd = vd_switch[ModelType] - try: - vd = vd_switch[ModelType] - except: - print("Unknown MoodelType, assuming MoodelType to 2.") - MoodelType = 2 - vd = vd_switch[ModelType] + PMConcentration = np.zeros(len(np.ravel(TimeAsDatenum)), 2) - PMConcentration=np.zeros(len(np.ravel(TimeAsDatenum)),2) # pre-allocate with NAN + PMConcentration[:, 0] = PM2_5 # fill PM2.5 data in column 1 + PMConcentration[:, 1] = PM10 - PM2_5 # fill in PM2.5-PM10 in column 2 - PMConcentration[:,0] = PM2_5 # fill PM2.5 data in column 1 - PMConcentration[:,1] = PM10 - PM2_5 # fill in PM2.5-PM10 data in column 2 + PMConcentration[PM10 - PM2_5 < 0, 1] = 0 - PMConcentration[PM10 - PM2_5 < 0 , 1] = 0 + PMConcentration = PMConcentration * 10**-6 - PMConcentration = PMConcentration * 10**-6; + F = PMConcentration * vd # g * m^-2 * s^-1, by particulate size + HorizontalTotalMassRate = F[:, 0] + F[:, 2] # g * m^-2 * s^-1, total - F = PMConcentration * vd # g * m^-2 * s^-1, by particulate size - HorizontalTotalMassRate = F[:,0]+ F[:,2] # g * m^-2 * s^-1, total + TiltedMassRate = HorizontalTotalMassRate * np.cosd(mp.pi * Tilt / 180) - TiltedMassRate = HorizontalTotalMassRate * np.cosd(mp.pi*Tilt/180) + TiltedMassNoRain = integrate.cumtrapz(TimeAsDatenum*86400, TiltedMassRate) - TiltedMassNoRain = integrate.cumtrapz(TimeAsDatenum*86400, TiltedMassRate) + TiltedMass = TiltedMassNoRain - TiltedMass = TiltedMassNoRain; + for cntr1 in range(0, len(RainAtAccTimes)): + if (RainAtAccTimes[cntr1] >= RainThresh): + TiltedMass[UnqRainAccFrstVal[cntr1 + 1] : end] = \ + TiltedMass[UnqRainAccFrstVal[cntr1 + 1] : end] - \ + TiltedMass[UnqRainAccFrstVal[cntr1 + 1] - 1 ] - for cntr1 in range(0,len(RainAtAccTimes)): - if (RainAtAccTimes[cntr1] >= RainThresh): - TiltedMass[UnqRainAccFrstVal[cntr1+1]:end] = TiltedMass[UnqRainAccFrstVal[cntr1+1]:end]-TiltedMass[UnqRainAccFrstVal[cntr1+1]-1] - - SoilingRate = 34.37 * special.erf(0.17*TiltedMass**0.8473) + SoilingRate = 34.37 * special.erf(0.17*TiltedMass**0.8473) - SR = (100 - SoilingRate)/100 + SR = (100 - SoilingRate)/100 + return SR def depo_velocity(T, WindSpeed, LUC): - # convert temperature into Kelvin - T = T + 273.15 - - # save wind data - if(np.isscalar(WindSpeed)): - u = np.array([WindSpeed]) - else: - u = WindSpeed - - g=9.81 #gravity in m/s^2 - Na=6.022*10**23 #avagadros number - R=8.314 #Universal gas consant in m3Pa/Kmol - k=1.38*10**-23 #Boltzmann's constant in m^2kg/sK - P=101300 #pressure in Pa - rhoair= 1.2041 #density of air in kg/m3 - z0=1 - rhop=1500 #Assume density of particle in kg/m^3 - - switcher={ - 1:0.56, - 4:0.56, - 6:0.54, - 8:0.54, - 10:0.54, - } - - try: - gamma = switcher[LUC] - except: - print("Unknown Land Use Category (LUC), assuming LUC 8.") - LUC = 8 - gamma = switcher[LUC] - - - # Diameter of particle in um - Dpum=np.array([2.5,10]) - Dpm=Dpum*10**-6 # Diameter of particle in m - - # Calculations - mu=1.8*10**-5*(T/298)**0.85 #viscosity of air in kg/m s - nu=mu/rhoair - lambda1=2*mu/(P*(8.*0.0288/(np.pi*R*T))**(0.5)) #mean free path - ll = np.array([lambda1,lambda1]) - Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) #slip correction coefficient - - ########################################################################### - # Calculate vs - ########################################################################### - vs = rhop*Dpm**2*(g*Cc/(mu*18)) # particle settling velocity - - ########################################################################### - # Calculate rb - ########################################################################### - ustar = np.zeros_like(u, dtype=float) # pre-allocate ustar - # Equation 11.66 in Ramaswami (and 16.67 and Sienfeld &Pandis) - ustar[u > 0] = 0.4 * u[u > 0]/np.log(10/z0) - ustar[u<=0] = 0.001 - - D=k*T*(Cc/(3*np.pi*mu*Dpm)) - - Sc=nu/D - # gamma=0.56 #for urban - # alpha=1.5 #for urban - EB=Sc**(-1 * gamma) - St = vs*(ustar ** 2)/ (g*nu) - - EIM=10.0**(-3.0/St) #For smooth surfaces - #EIM=((St)./(0.82+St)).^2 - - R1=np.exp(-St**(0.5)) # percentage of particles that stick - - rb=1/(3*(EB+EIM)*ustar*R1) - - ########################################################################### - # Calculate ra - ########################################################################### - a=np.array([-0.096,-0.037,-0.002,0,0.004,0.035]) - b=np.array([0.029,0.029,0.018,0,-0.018,-0.036]) - - # For wind speeds <= 3, use a = -0.037 and b = 0.029 - # For wind speeds >3 and <=5, use a = -.002, b = 0.018 - # For wind speeds > 5, use a = 0, b = 0 - avals = a[1]*np.ones_like(u, dtype=float) - avals[u>3] = a[2] - avals[u>5] = a[3] - - bvals = b[1]*np.ones_like(u, dtype=float) - bvals[u>3] = b[2] - bvals[u>5] = b[3] - - L = 1/(avals + bvals* np.log(z0)) - - zeta0=z0/L - zeta=10.0/L - eta = ((1-15*zeta)**(0.25)) - eta0 = ((1-15*zeta0)**(0.25)) - - ra = np.zeros_like(zeta, dtype=float) # Preallocate memory - ra[zeta == 0] = (1/ (0.4* ustar[zeta == 0]))* np.log(10.0/z0) - ra[zeta > 0] = (1/(0.4*ustar[zeta > 0]))*(np.log(10.0/z0) + 4.7*(zeta[zeta > 0]-zeta0[zeta > 0])) - ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0]))* (np.log(10.0/ z0) + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 / ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + 2 * (np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) - - ########################################################################### - # Calculate vd and mass flux - ########################################################################### - - vd=1/(ra+rb)+vs - - return vd + # convert temperature into Kelvin + T = T + 273.15 + + # save wind data + if(np.isscalar(WindSpeed)): + u = np.array([WindSpeed]) + else: + u = WindSpeed + + g = 9.81 # gravity in m/s^2 + Na = 6.022 * 10**23 # avagadros number + R = 8.314 # Universal gas consant in m3Pa/Kmol + k = 1.38 * 10**-23 # Boltzmann's constant in m^2kg/sK + P = 101300 # pressure in Pa + rhoair = 1.2041 # density of air in kg/m3 + z0 = 1 + rhop = 1500 # Assume density of particle in kg/m^3 + + switcher = { + 1 : 0.56, + 4 : 0.56, + 6 : 0.54, + 8 : 0.54, + 10 : 0.54, + } + + try: + gamma = switcher[LUC] + except Exception as e: + warnings.warn("Unknown Land Use Category (LUC), assuming LUC 8.") + LUC = 8 + gamma = switcher[LUC] + + + # Diameter of particle in um + Dpum = np.array([2.5, 10]) + Dpm = Dpum*10**-6 # Diameter of particle in m + + # Calculations + mu = 1.8*10**-5*(T/298)**0.85 # viscosity of air in kg/m s + nu = mu/rhoair + lambda1 = 2*mu/(P*(8.*0.0288/(np.pi*R*T))**(0.5)) # mean free path + ll = np.array([lambda1, lambda1]) + Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) + # slip correction coefficient + + # Calculate vs + vs = rhop*Dpm**2*(g*Cc/(mu*18)) # particle settling velocity + + # Calculate rb + ustar = np.zeros_like(u, dtype=float) # pre-allocate ustar + # Equation 11.66 in Ramaswami (and 16.67 and Sienfeld &Pandis) + ustar[u > 0] = 0.4 * u[u > 0]/np.log(10/z0) + ustar[u <= 0] = 0.001 + + D = k*T*(Cc/(3*np.pi*mu*Dpm)) + + Sc = nu/D + # gamma=0.56 # for urban + # alpha=1.5 # for urban + EB = Sc**(-1 * gamma) + St = vs*(ustar ** 2)/ (g*nu) + + EIM = 10.0**(-3.0/St) # For smooth surfaces + # EIM =((St)./(0.82+St)).^2 + + R1 = np.exp(-St**(0.5)) # percentage of particles that stick + + rb = 1/(3*(EB+EIM)*ustar*R1) + + # Calculate ra + a = np.array([-0.096, -0.037, -0.002, 0, 0.004, 0.035]) + b = np.array([0.029, 0.029, 0.018, 0, -0.018, -0.036]) + + # For wind speeds <= 3, use a = -0.037 and b = 0.029 + # For wind speeds >3 and <=5, use a = -.002, b = 0.018 + # For wind speeds > 5, use a = 0, b = 0 + avals = a[1]*np.ones_like(u, dtype=float) + avals[u>3] = a[2] + avals[u>5] = a[3] + + bvals = b[1]*np.ones_like(u, dtype=float) + bvals[u > 3] = b[2] + bvals[u > 5] = b[3] + + L = 1/(avals + bvals*np.log(z0)) + + zeta0 = z0/L + zeta = 10.0/L + eta = ((1-15*zeta)**(0.25)) + eta0 = ((1-15*zeta0)**(0.25)) + + ra = np.zeros_like(zeta, dtype=float) # Preallocate memory + ra[zeta == 0] = (1/ (0.4* ustar[zeta == 0]))* np.log(10.0/z0) + ra[zeta > 0] = (1/(0.4*ustar[zeta > 0]))*(np.log(10.0/z0) + 4.7*(zeta[zeta > 0]-\ + zeta0[zeta > 0])) + ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0]))* (np.log(10.0/ z0) +\ + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 /\ + ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + 2 * \ + (np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) + + # Calculate vd and mass flux + + vd = 1/(ra+rb)+vs + + return vd From c63ca7c99581e627ee2396e8cdd863251a5641cb Mon Sep 17 00:00:00 2001 From: nappaillav Date: Sat, 11 Jan 2020 02:21:36 +0530 Subject: [PATCH 03/22] E128_Error --- pvlib/pvl_soiling_hsu.py | 108 +++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 62 deletions(-) diff --git a/pvlib/pvl_soiling_hsu.py b/pvlib/pvl_soiling_hsu.py index 7d3c34431d..0dca1f6853 100644 --- a/pvlib/pvl_soiling_hsu.py +++ b/pvlib/pvl_soiling_hsu.py @@ -1,8 +1,7 @@ -import argparse import time import datetime import numpy as np -import warnings +import warnings from scipy import integrate, special @@ -18,21 +17,20 @@ def accumarray(Indx, value): return A -def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, - ModelType=2, RainAccPeriod=1, LUC=8, +def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, + ModelType=2, RainAccPeriod=1, LUC=8, WindSpeed=2, Temperature=12 ): - """ - PVL_SOILING_HSU Calculates soiling rate over time given particulate and + PVL_SOILING_HSU Calculates soiling rate over time given particulate and rain data Parameters ---------- Time : Time_Structure - Time values for the soiling function do not need to be + Time values for the soiling function do not need to be regularly spaced, although large gaps in timing are discouraged. (datetime) @@ -96,7 +94,7 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, second. If WindSpeed is omitted, the value of 2 m/s is used as default. (m/s) - Temperature : numeric, optional + Temperature : numeric, optional Temperature is an optional input to the function, but is required for the Variable Deposition Model. Temperature is a scalar or vector value with the same number of Elements as Time and must be in degrees @@ -135,39 +133,36 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, """ assert isinstance(Time, datetime.datetime), \ - "Time variable is not datetime instance" + "Time variable is not datetime instance" assert np.char.isnumeric(str(Rain)) and (Rain >= 0), \ - "Error with the Rain value" + "Error with the Rain value" assert np.char.isnumeric(str(RainThresh)) and np.isscalar(RainThresh) and \ - (RainThresh >= 0), "Error with the RainThresh value" + (RainThresh >= 0), "Error with the RainThresh value" assert np.char.isnumeric(str(Tilt)), "Error with the Tilt value" assert np.char.isnumeric(str(PM2_5)) and (PM2_5 >= 0), \ - "Error with the PM2_5 value" + "Error with the PM2_5 value" assert np.char.isnumeric(str(PM10)) and (PM10 >= 0), \ - "Error with the PM10 value" + "Error with the PM10 value" # optional variables assert np.isscalar(ModelType), "Error with the ModelType value" assert np.char.isnumeric(str(RainAccPeriod)) and \ - np.isscalar(RainAccPeriod) and (RainAccPeriod >= 1), \ - "Error with the RainAccPeriod value" + np.isscalar(RainAccPeriod) and (RainAccPeriod >= 1), \ + "Error with the RainAccPeriod value" assert np.isscalar(LUC), "Error with the LUC value" assert np.char.isnumeric(str(WindSpeed)) and (WindSpeed >= 0), \ - "Error with the WindSpeed value" + "Error with the WindSpeed value" assert np.char.isnumeric(str(Temperature)) and (Temperature >= 0), \ - "Error with the Temperature value" + "Error with the Temperature value" - # Time is datetime structure + # Time is datetime structure TimeAsDatenum = time.mktime(Time.timetuple()) RainAccAsDatenum = np.floor(TimeAsDatenum * 24 / RainAccPeriod) # Doubt - [RainAccTimes, UnqRainAccFrstVal, UnqRainAccIndx] = np.unique( - RainAccAsDatenum, - return_index=True, - return_inverse=True - ) + [RainAccTimes, UnqRainAccFrstVal, UnqRainAccIndx] = \ + np.unique(RainAccAsDatenum, return_index=True, return_inverse=True) RainAtAccTimes = accumarray(UnqRainAccIndx, Rain) # Doubt @@ -177,30 +172,22 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, AccumRain[-1] = RainAtAccTimes[-1] vd_switch = { - - 1 : depo_veloc(Temperature, WindSpeed, LUC), + 1: depo_velocity(Temperature, WindSpeed, LUC), # case 1 Variable Deposition Velocity - - 2 : np.array([0.0009,0.004]), + 2: np.array([0.0009, 0.004]), # case 2 % Fixed Settling Velocity in m/s - - 3 : np.array([0.0015,0.0917]) + 3: np.array([0.0015, 0.0917]) # case 3 % Fixed Deposition Velcoity in m/s - } try: vd = vd_switch[ModelType] except Exception as e: - warnings.warn("Unknown ModelType, assuming MoodelType to 2.") + warnings.warn("Unknown ModelType, assuming ModelType to 2."+str(e)) ModelType = 2 vd = vd_switch[ModelType] - - PMConcentration = np.zeros(len(np.ravel(TimeAsDatenum)), 2) - - PMConcentration[:, 0] = PM2_5 # fill PM2.5 data in column 1 PMConcentration[:, 1] = PM10 - PM2_5 # fill in PM2.5-PM10 in column 2 @@ -211,7 +198,7 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, F = PMConcentration * vd # g * m^-2 * s^-1, by particulate size HorizontalTotalMassRate = F[:, 0] + F[:, 2] # g * m^-2 * s^-1, total - TiltedMassRate = HorizontalTotalMassRate * np.cosd(mp.pi * Tilt / 180) + TiltedMassRate = HorizontalTotalMassRate * np.cosd(np.pi * Tilt / 180) TiltedMassNoRain = integrate.cumtrapz(TimeAsDatenum*86400, TiltedMassRate) @@ -219,10 +206,9 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, for cntr1 in range(0, len(RainAtAccTimes)): if (RainAtAccTimes[cntr1] >= RainThresh): - TiltedMass[UnqRainAccFrstVal[cntr1 + 1] : end] = \ - TiltedMass[UnqRainAccFrstVal[cntr1 + 1] : end] - \ - TiltedMass[UnqRainAccFrstVal[cntr1 + 1] - 1 ] - + TiltedMass[UnqRainAccFrstVal[cntr1 + 1]:] = \ + TiltedMass[UnqRainAccFrstVal[cntr1 + 1]:] - \ + TiltedMass[UnqRainAccFrstVal[cntr1 + 1] - 1] SoilingRate = 34.37 * special.erf(0.17*TiltedMass**0.8473) @@ -232,7 +218,7 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, def depo_velocity(T, WindSpeed, LUC): - # convert temperature into Kelvin + # convert temperature into Kelvin T = T + 273.15 # save wind data @@ -242,8 +228,8 @@ def depo_velocity(T, WindSpeed, LUC): u = WindSpeed g = 9.81 # gravity in m/s^2 - Na = 6.022 * 10**23 # avagadros number - R = 8.314 # Universal gas consant in m3Pa/Kmol + # Na = 6.022 * 10**23 # avagadros number + R = 8.314 # Universal gas consant in m3Pa/Kmol k = 1.38 * 10**-23 # Boltzmann's constant in m^2kg/sK P = 101300 # pressure in Pa rhoair = 1.2041 # density of air in kg/m3 @@ -251,21 +237,20 @@ def depo_velocity(T, WindSpeed, LUC): rhop = 1500 # Assume density of particle in kg/m^3 switcher = { - 1 : 0.56, - 4 : 0.56, - 6 : 0.54, - 8 : 0.54, - 10 : 0.54, + 1: 0.56, + 4: 0.56, + 6: 0.54, + 8: 0.54, + 10: 0.54, } try: gamma = switcher[LUC] except Exception as e: - warnings.warn("Unknown Land Use Category (LUC), assuming LUC 8.") + warnings.warn("Unknown Land Use Category, assuming LUC 8. "+str(e)) LUC = 8 gamma = switcher[LUC] - # Diameter of particle in um Dpum = np.array([2.5, 10]) Dpm = Dpum*10**-6 # Diameter of particle in m @@ -275,7 +260,7 @@ def depo_velocity(T, WindSpeed, LUC): nu = mu/rhoair lambda1 = 2*mu/(P*(8.*0.0288/(np.pi*R*T))**(0.5)) # mean free path ll = np.array([lambda1, lambda1]) - Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) + Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) # slip correction coefficient # Calculate vs @@ -293,7 +278,7 @@ def depo_velocity(T, WindSpeed, LUC): # gamma=0.56 # for urban # alpha=1.5 # for urban EB = Sc**(-1 * gamma) - St = vs*(ustar ** 2)/ (g*nu) + St = vs*(ustar**2)/(g*nu) EIM = 10.0**(-3.0/St) # For smooth surfaces # EIM =((St)./(0.82+St)).^2 @@ -310,8 +295,8 @@ def depo_velocity(T, WindSpeed, LUC): # For wind speeds >3 and <=5, use a = -.002, b = 0.018 # For wind speeds > 5, use a = 0, b = 0 avals = a[1]*np.ones_like(u, dtype=float) - avals[u>3] = a[2] - avals[u>5] = a[3] + avals[u > 3] = a[2] + avals[u > 5] = a[3] bvals = b[1]*np.ones_like(u, dtype=float) bvals[u > 3] = b[2] @@ -325,17 +310,16 @@ def depo_velocity(T, WindSpeed, LUC): eta0 = ((1-15*zeta0)**(0.25)) ra = np.zeros_like(zeta, dtype=float) # Preallocate memory - ra[zeta == 0] = (1/ (0.4* ustar[zeta == 0]))* np.log(10.0/z0) - ra[zeta > 0] = (1/(0.4*ustar[zeta > 0]))*(np.log(10.0/z0) + 4.7*(zeta[zeta > 0]-\ - zeta0[zeta > 0])) - ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0]))* (np.log(10.0/ z0) +\ - np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 /\ - ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + 2 * \ - (np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) + ra[zeta == 0] = (1 / (0.4 * ustar[zeta == 0])) * np.log(10.0 / z0) + ra[zeta > 0] = (1 / (0.4 * ustar[zeta > 0]))*(np.log(10.0/z0) + + 4.7*(zeta[zeta > 0] - zeta0[zeta > 0])) + ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0])) * (np.log(10.0 / z0) + + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 / + ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + + 2*(np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) # Calculate vd and mass flux vd = 1/(ra+rb)+vs return vd - From 0677b6c504fc78836837466edf785a7b6b9b3409 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Sat, 11 Jan 2020 02:29:39 +0530 Subject: [PATCH 04/22] E128_Error --- pvlib/pvl_soiling_hsu.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pvlib/pvl_soiling_hsu.py b/pvlib/pvl_soiling_hsu.py index 0dca1f6853..2337cb21e6 100644 --- a/pvlib/pvl_soiling_hsu.py +++ b/pvlib/pvl_soiling_hsu.py @@ -172,12 +172,12 @@ def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, AccumRain[-1] = RainAtAccTimes[-1] vd_switch = { - 1: depo_velocity(Temperature, WindSpeed, LUC), - # case 1 Variable Deposition Velocity - 2: np.array([0.0009, 0.004]), - # case 2 % Fixed Settling Velocity in m/s - 3: np.array([0.0015, 0.0917]) - # case 3 % Fixed Deposition Velcoity in m/s + 1: depo_velocity(Temperature, WindSpeed, LUC), + # case 1 Variable Deposition Velocity + 2: np.array([0.0009, 0.004]), + # case 2 % Fixed Settling Velocity in m/s + 3: np.array([0.0015, 0.0917]) + # case 3 % Fixed Deposition Velcoity in m/s } try: @@ -311,12 +311,12 @@ def depo_velocity(T, WindSpeed, LUC): ra = np.zeros_like(zeta, dtype=float) # Preallocate memory ra[zeta == 0] = (1 / (0.4 * ustar[zeta == 0])) * np.log(10.0 / z0) - ra[zeta > 0] = (1 / (0.4 * ustar[zeta > 0]))*(np.log(10.0/z0) + - 4.7*(zeta[zeta > 0] - zeta0[zeta > 0])) - ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0])) * (np.log(10.0 / z0) + - np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 / - ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + - 2*(np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) + ra[zeta > 0] = (1 / (0.4 * ustar[zeta > 0]))*(np.log(10.0/z0) + + 4.7*(zeta[zeta > 0] - zeta0[zeta > 0])) + ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0])) * (np.log(10.0 / z0) + + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 + / ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + + 2*(np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) # Calculate vd and mass flux From 586b6b9c893e266b9442e258146055a8553efa5f Mon Sep 17 00:00:00 2001 From: nappaillav Date: Sat, 11 Jan 2020 03:16:10 +0530 Subject: [PATCH 05/22] E128_Error --- pvlib/pvl_soiling_hsu.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pvlib/pvl_soiling_hsu.py b/pvlib/pvl_soiling_hsu.py index 2337cb21e6..3ddbac384b 100644 --- a/pvlib/pvl_soiling_hsu.py +++ b/pvlib/pvl_soiling_hsu.py @@ -311,12 +311,12 @@ def depo_velocity(T, WindSpeed, LUC): ra = np.zeros_like(zeta, dtype=float) # Preallocate memory ra[zeta == 0] = (1 / (0.4 * ustar[zeta == 0])) * np.log(10.0 / z0) - ra[zeta > 0] = (1 / (0.4 * ustar[zeta > 0]))*(np.log(10.0/z0) - + 4.7*(zeta[zeta > 0] - zeta0[zeta > 0])) + ra[zeta > 0] = (1 / (0.4 * ustar[zeta > 0])) * (np.log(10.0 / z0) + + 4.7 * (zeta[zeta > 0] - zeta0[zeta > 0])) ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0])) * (np.log(10.0 / z0) - + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 - / ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) - + 2*(np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) + + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 + / ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) + + 2*(np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) # Calculate vd and mass flux From 114148d1e9a9ce2ed08b707275dc684c4a892230 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Sun, 12 Jan 2020 00:30:35 +0530 Subject: [PATCH 06/22] format_corrections --- pvlib/pvl_soiling_hsu.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pvlib/pvl_soiling_hsu.py b/pvlib/pvl_soiling_hsu.py index 3ddbac384b..480010c69e 100644 --- a/pvlib/pvl_soiling_hsu.py +++ b/pvlib/pvl_soiling_hsu.py @@ -311,12 +311,24 @@ def depo_velocity(T, WindSpeed, LUC): ra = np.zeros_like(zeta, dtype=float) # Preallocate memory ra[zeta == 0] = (1 / (0.4 * ustar[zeta == 0])) * np.log(10.0 / z0) - ra[zeta > 0] = (1 / (0.4 * ustar[zeta > 0])) * (np.log(10.0 / z0) - + 4.7 * (zeta[zeta > 0] - zeta0[zeta > 0])) - ra[zeta < 0] = (1 / (0.4 * ustar[zeta < 0])) * (np.log(10.0 / z0) - + np.log((eta0[zeta < 0]**2 + 1) * (eta0[zeta < 0]+1)**2 - / ((eta[zeta < 0]**2 + 1) * (eta[zeta < 0]+1)**2)) - + 2*(np.arctan(eta[zeta < 0])-np.arctan(eta0[zeta < 0]))) + + sub_a = 1 / (0.4 * ustar[zeta > 0]) + sub_b = np.log(10.0 / z0) + sub_c = zeta[zeta > 0] - zeta0[zeta > 0] + + ra[zeta > 0] = sub_a * (sub_b + 4.7 * sub_c) + + sub_a = (1 / (0.4 * ustar[zeta < 0])) + sub_b = np.log(10.0 / z0) + sub_ba = eta0[zeta < 0]**2 + 1 + sub_bb = (eta0[zeta < 0]+1)**2 + sub_bc = eta[zeta < 0]**2 + 1 + sub_bd = (eta[zeta < 0]+1)**2 + sub_d = np.arctan(eta[zeta < 0]) + sub_e = np.arctan(eta0[zeta < 0]) + + sub_c = np.log(sub_ba * sub_bb / (sub_bc * sub_bd)) + ra[zeta < 0] = sub_a * (sub_b + sub_c + 2*(sub_d-sub_e)) # Calculate vd and mass flux From 23e9042b0ec030e6cd43e012e0610cd2f4e39958 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Wed, 15 Jan 2020 23:44:44 +0530 Subject: [PATCH 07/22] updated soiling_hsu --- pvlib/losses.py | 205 ++++++++++++++++++++++++ pvlib/pvl_soiling_hsu.py | 337 --------------------------------------- 2 files changed, 205 insertions(+), 337 deletions(-) create mode 100644 pvlib/losses.py delete mode 100644 pvlib/pvl_soiling_hsu.py diff --git a/pvlib/losses.py b/pvlib/losses.py new file mode 100644 index 0000000000..0a13dc4d61 --- /dev/null +++ b/pvlib/losses.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +""" +This module contains functions for losses of various types: soiling, mismatch, +snow cover, etc. +""" + +import numpy as np +import pandas as pd +from pvlib.tools import cosd + + +def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, + depo_veloc={'2_5': 0.004, '10': 0.0009}, + rain_accum_period=pd.Timedelta('1h')): + """ + Calculates soiling ratio given particulate and rain data using the model + from Humboldt State University [1]_. + + Parameters + ---------- + + rainfall : Series + Rain accumulated in each time period. [mm] + + cleaning_threshold : float + Amount of rain in an accumulation period needed to clean the PV + modules. [mm] + + tilt : float + Tilt of the PV panels from horizontal. [degree] + + pm2_5 : numeric + Concentration of airborne particulate matter (PM) with + aerodynamic diameter less than 2.5 microns. [g/m^3] + + pm10 : numeric + Concentration of airborne particulate matter (PM) with + aerodynamicdiameter less than 10 microns. [g/m^3] + + depo_veloc : dict, default {'2_5': 0.4, '10': 0.09} + Deposition or settling velocity of particulates. [m/s] + + rain_accum_period : Timedelta, default 1 hour + Period for accumulating rainfall to check against `cleaning_threshold` + It is recommended that `rain_accum_period` be between 1 hour and + 24 hours. + + Returns + ------- + soiling_ratio : Series + Values between 0 and 1. Equal to 1 - transmission loss. + + References + ----------- + .. [1] M. Coello and L. Boyle, "Simple Model For Predicting Time Series + Soiling of Photovoltaic Panels," in IEEE Journal of Photovoltaics. + doi: 10.1109/JPHOTOV.2019.2919628 + .. [2] Atmospheric Chemistry and Physics: From Air Pollution to Climate + Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. + + """ + + # accumulate rainfall into periods for comparison with threshold + accum_rain = rainfall.rolling(rain_accum_period, closed='right').sum() + # cleaning is True for intervals with rainfall greater than threshold + cleaning_times = accum_rain.index[accum_rain >= cleaning_threshold] + + horiz_mass_rate = pm2_5 * depo_veloc['2_5']\ + + np.maximum(pm10 - pm2_5, 0.) * depo_veloc['10'] + tilted_mass_rate = horiz_mass_rate * cosd(tilt) # assuming no rain + + # tms -> tilt_mass_rate + tms_cumsum = np.cumsum(tilted_mass_rate * np.ones(rainfall.shape)) + + mass_no_cleaning = pd.Series(index=rainfall.index, data=tms_cumsum) + mass_removed = pd.Series(index=rainfall.index) + mass_removed[0] = 0. + mass_removed[cleaning_times] = mass_no_cleaning[cleaning_times] + accum_mass = mass_no_cleaning - mass_removed.ffill() + + soiling_ratio = 1 - 0.3437 * np.erf(0.17 * accum_mass**0.8473) + + return soiling_ratio + + +def depo_velocity(T, WindSpeed, LUC): + + # convert temperature into Kelvin + T = T + 273.15 + + # save wind data + if(np.isscalar(WindSpeed)): + u = np.array([WindSpeed]) + else: + u = WindSpeed + + g = 9.81 # gravity in m/s^2 + # Na = 6.022 * 10**23 # avagadros number + R = 8.314 # Universal gas consant in m3Pa/Kmol + k = 1.38 * 10**-23 # Boltzmann's constant in m^2kg/sK + P = 101300 # pressure in Pa + rhoair = 1.2041 # density of air in kg/m3 + z0 = 1 + rhop = 1500 # Assume density of particle in kg/m^3 + + switcher = { + 1: 0.56, + 4: 0.56, + 6: 0.54, + 8: 0.54, + 10: 0.54, + } + + try: + gamma = switcher[LUC] + except Exception as e: + warnings.warn("Unknown Land Use Category, assuming LUC 8. "+str(e)) + LUC = 8 + gamma = switcher[LUC] + + # Diameter of particle in um + Dpum = np.array([2.5, 10]) + Dpm = Dpum*10**-6 # Diameter of particle in m + + # Calculations + mu = 1.8*10**-5*(T/298)**0.85 # viscosity of air in kg/m s + nu = mu/rhoair + lambda1 = 2*mu/(P*(8.*0.0288/(np.pi*R*T))**(0.5)) # mean free path + ll = np.array([lambda1, lambda1]) + Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) + # slip correction coefficient + + # Calculate vs + vs = rhop*Dpm**2*(g*Cc/(mu*18)) # particle settling velocity + + # Calculate rb + ustar = np.zeros_like(u, dtype=float) # pre-allocate ustar + # Equation 11.66 in Ramaswami (and 16.67 and Sienfeld &Pandis) + ustar[u > 0] = 0.4 * u[u > 0]/np.log(10/z0) + ustar[u <= 0] = 0.001 + + D = k*T*(Cc/(3*np.pi*mu*Dpm)) + + Sc = nu/D + # gamma=0.56 # for urban + # alpha=1.5 # for urban + EB = Sc**(-1 * gamma) + St = vs*(ustar**2)/(g*nu) + + EIM = 10.0**(-3.0/St) # For smooth surfaces + # EIM =((St)./(0.82+St)).^2 + + R1 = np.exp(-St**(0.5)) # percentage of particles that stick + + rb = 1/(3*(EB+EIM)*ustar*R1) + + # Calculate ra + a = np.array([-0.096, -0.037, -0.002, 0, 0.004, 0.035]) + b = np.array([0.029, 0.029, 0.018, 0, -0.018, -0.036]) + + # For wind speeds <= 3, use a = -0.037 and b = 0.029 + # For wind speeds >3 and <=5, use a = -.002, b = 0.018 + # For wind speeds > 5, use a = 0, b = 0 + avals = a[1]*np.ones_like(u, dtype=float) + avals[u > 3] = a[2] + avals[u > 5] = a[3] + + bvals = b[1]*np.ones_like(u, dtype=float) + bvals[u > 3] = b[2] + bvals[u > 5] = b[3] + + L = 1/(avals + bvals*np.log(z0)) + + zeta0 = z0/L + zeta = 10.0/L + eta = ((1-15*zeta)**(0.25)) + eta0 = ((1-15*zeta0)**(0.25)) + + ra = np.zeros_like(zeta, dtype=float) # Preallocate memory + ra[zeta == 0] = (1 / (0.4 * ustar[zeta == 0])) * np.log(10.0 / z0) + + sub_a = 1 / (0.4 * ustar[zeta > 0]) + sub_b = np.log(10.0 / z0) + sub_c = zeta[zeta > 0] - zeta0[zeta > 0] + + ra[zeta > 0] = sub_a * (sub_b + 4.7 * sub_c) + + sub_a = (1 / (0.4 * ustar[zeta < 0])) + sub_b = np.log(10.0 / z0) + sub_ba = eta0[zeta < 0]**2 + 1 + sub_bb = (eta0[zeta < 0]+1)**2 + sub_bc = eta[zeta < 0]**2 + 1 + sub_bd = (eta[zeta < 0]+1)**2 + sub_d = np.arctan(eta[zeta < 0]) + sub_e = np.arctan(eta0[zeta < 0]) + + sub_c = np.log(sub_ba * sub_bb / (sub_bc * sub_bd)) + ra[zeta < 0] = sub_a * (sub_b + sub_c + 2*(sub_d-sub_e)) + + # Calculate vd and mass flux + + vd = 1/(ra+rb)+vs + + + return vd diff --git a/pvlib/pvl_soiling_hsu.py b/pvlib/pvl_soiling_hsu.py deleted file mode 100644 index 480010c69e..0000000000 --- a/pvlib/pvl_soiling_hsu.py +++ /dev/null @@ -1,337 +0,0 @@ -import time -import datetime -import numpy as np -import warnings -from scipy import integrate, special - - -def accumarray(Indx, value): - - n = np.max(Indx)+1 - if(np.isscalar(value)): - value = np.repeat(value, len(Indx)) - - A = np.zeros((n,)) - for i in range(n): - A[i] = np.sum(value[Indx[:] == i]) - return A - - -def pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, - ModelType=2, RainAccPeriod=1, LUC=8, - WindSpeed=2, Temperature=12 - ): - - """ - PVL_SOILING_HSU Calculates soiling rate over time given particulate and - rain data - - Parameters - ---------- - - Time : Time_Structure - Time values for the soiling function do not need to be - regularly spaced, although large gaps in timing are - discouraged. (datetime) - - Rain : numeric - Rainfall values should be in mm of rainfall. Programmatically, rain - is accumulated over a given time period, and cleaning is applied - immediately after a time period where the cleaning threshold is - reached. (mm) - - RainThresh : numeric - RainThresh is a scalar for the amount of rain, in mm, in an - accumulation period needed to clean the PV modules. (mm) - - Tilt : numeric - Tilt is a scalar or vector for the tilt of the PV panels. (degree) - - PM2_5 : numeric - PM2_5 is the concentration of airborne particulate matter (PM) with - diameter less than 2.5 microns. (g/m^3) - - PM10 : numeric - PM10 is the concentration of airborne particulate matter (PM) with - diameter less than 10 microns. (g/m^3) - - ModelType : numeric, optional - ModelType is an optional input to the function to determine the - the model type to be used in the soiling model, see [1]. A - value of "1" indicates that the Variable Deposition Velocity model - shall be used, a value of "2" indicates that the Fixed Settling - Velocity model shall be used, and a value of "3" indicates that the - Fixed Deposition Velocity model shall be used. [1] indicates that the - Fixed Settling Velocity model performs best under a wide range of - conditions, and thus "2" is the default ModelType if ModelType - is omitted. Validation efforts by Sandia National Laboratories - confirm these findings. If an incorrect ModelType is provided, the - Fixed Settling Velocity (type 2) will be used (with a warning). - - RainAccPeriod : numeric, optional - RainAccPeriod is an optional input that specifies the period, - in hours, over which to accumulate rainfall totals before checking - against the rain cleaning threshold. For example, if the rain - threshold is 0.5 mm per hour, then RainThresh should be 0.5 and - RainAccPeriod should be 1. If the threshold is 1 mm per hour, then - the values should be 1 and 1, respectively. The minimum RainAccPeriod - is 1hour. The default value is 1, indicating hourly rain accumulation. - Accumulation periods exceeding 24 (daily accumulation) are not - recommended. (mm per hour) - - LUC : numeric, optional - LUC is an optional input to the function, but it is required for the - Variable Deposition Model. LUC is the Land Use Category as specified - in Table 19.2 of [2]. LUC must be a numeric scalar with value 1, 4, - 6, 8, or 10, corresponding to land with evergreen trees, deciduous - trees, grass, desert, or shrubs with interrupted woodlands. If - omitted, the default value of 8 (desert) is used. - - WindSpeed : numeric, optional - WindSpeed is an optional input to the function, but is required for - the Variable Deposition Model. WindSpeed is a scalar or vector value - with the same number of elements as Time, and must be in meters per - second. If WindSpeed is omitted, the value of 2 m/s is used as - default. (m/s) - - Temperature : numeric, optional - Temperature is an optional input to the function, but is required for - the Variable Deposition Model. Temperature is a scalar or vector - value with the same number of Elements as Time and must be in degrees - C. By Default, the value of 12 C is used as default. (Celcius) - - Returns - ------- - SR : numeric - The soiling ratio (SR) of a tilted PV panel, this is a number - between 0 and 1. SR is a time series where each element of SR - correlates with the accumulated soiling and rain cleaning at the times - specified in Time. - - Notes - ------ - The following are default values - - ============================ ================ - Parameter Value - ============================ ================ - ModelType 2 - Temperature at zero altitude 288.15 K - RainAccPeriod 1 mm per hour - LUC 2 - WindSpeed 2 m/s - Temperature 12 C - ============================ ================ - - References - ----------- - .. [1] M. Coello and L. Boyle, "Simple Model For Predicting Time Series - Soiling of Photovoltaic Panels," in IEEE Journal of Photovoltaics. - doi: 10.1109/JPHOTOV.2019.2919628 - .. [2] Atmospheric Chemistry and Physics: From Air Pollution to Climate - Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. - - """ - assert isinstance(Time, datetime.datetime), \ - "Time variable is not datetime instance" - assert np.char.isnumeric(str(Rain)) and (Rain >= 0), \ - "Error with the Rain value" - assert np.char.isnumeric(str(RainThresh)) and np.isscalar(RainThresh) and \ - (RainThresh >= 0), "Error with the RainThresh value" - assert np.char.isnumeric(str(Tilt)), "Error with the Tilt value" - assert np.char.isnumeric(str(PM2_5)) and (PM2_5 >= 0), \ - "Error with the PM2_5 value" - assert np.char.isnumeric(str(PM10)) and (PM10 >= 0), \ - "Error with the PM10 value" - # optional variables - assert np.isscalar(ModelType), "Error with the ModelType value" - assert np.char.isnumeric(str(RainAccPeriod)) and \ - np.isscalar(RainAccPeriod) and (RainAccPeriod >= 1), \ - "Error with the RainAccPeriod value" - assert np.isscalar(LUC), "Error with the LUC value" - assert np.char.isnumeric(str(WindSpeed)) and (WindSpeed >= 0), \ - "Error with the WindSpeed value" - assert np.char.isnumeric(str(Temperature)) and (Temperature >= 0), \ - "Error with the Temperature value" - - # Time is datetime structure - TimeAsDatenum = time.mktime(Time.timetuple()) - - RainAccAsDatenum = np.floor(TimeAsDatenum * 24 / RainAccPeriod) - - # Doubt - - [RainAccTimes, UnqRainAccFrstVal, UnqRainAccIndx] = \ - np.unique(RainAccAsDatenum, return_index=True, return_inverse=True) - - RainAtAccTimes = accumarray(UnqRainAccIndx, Rain) - # Doubt - - AccumRain = np.zeros_like(Rain) - AccumRain[UnqRainAccFrstVal[1:]-1] = RainAtAccTimes[1:-1] - AccumRain[-1] = RainAtAccTimes[-1] - - vd_switch = { - 1: depo_velocity(Temperature, WindSpeed, LUC), - # case 1 Variable Deposition Velocity - 2: np.array([0.0009, 0.004]), - # case 2 % Fixed Settling Velocity in m/s - 3: np.array([0.0015, 0.0917]) - # case 3 % Fixed Deposition Velcoity in m/s - } - - try: - vd = vd_switch[ModelType] - except Exception as e: - warnings.warn("Unknown ModelType, assuming ModelType to 2."+str(e)) - ModelType = 2 - vd = vd_switch[ModelType] - - PMConcentration = np.zeros(len(np.ravel(TimeAsDatenum)), 2) - PMConcentration[:, 0] = PM2_5 # fill PM2.5 data in column 1 - PMConcentration[:, 1] = PM10 - PM2_5 # fill in PM2.5-PM10 in column 2 - - PMConcentration[PM10 - PM2_5 < 0, 1] = 0 - - PMConcentration = PMConcentration * 10**-6 - - F = PMConcentration * vd # g * m^-2 * s^-1, by particulate size - HorizontalTotalMassRate = F[:, 0] + F[:, 2] # g * m^-2 * s^-1, total - - TiltedMassRate = HorizontalTotalMassRate * np.cosd(np.pi * Tilt / 180) - - TiltedMassNoRain = integrate.cumtrapz(TimeAsDatenum*86400, TiltedMassRate) - - TiltedMass = TiltedMassNoRain - - for cntr1 in range(0, len(RainAtAccTimes)): - if (RainAtAccTimes[cntr1] >= RainThresh): - TiltedMass[UnqRainAccFrstVal[cntr1 + 1]:] = \ - TiltedMass[UnqRainAccFrstVal[cntr1 + 1]:] - \ - TiltedMass[UnqRainAccFrstVal[cntr1 + 1] - 1] - - SoilingRate = 34.37 * special.erf(0.17*TiltedMass**0.8473) - - SR = (100 - SoilingRate)/100 - return SR - - -def depo_velocity(T, WindSpeed, LUC): - - # convert temperature into Kelvin - T = T + 273.15 - - # save wind data - if(np.isscalar(WindSpeed)): - u = np.array([WindSpeed]) - else: - u = WindSpeed - - g = 9.81 # gravity in m/s^2 - # Na = 6.022 * 10**23 # avagadros number - R = 8.314 # Universal gas consant in m3Pa/Kmol - k = 1.38 * 10**-23 # Boltzmann's constant in m^2kg/sK - P = 101300 # pressure in Pa - rhoair = 1.2041 # density of air in kg/m3 - z0 = 1 - rhop = 1500 # Assume density of particle in kg/m^3 - - switcher = { - 1: 0.56, - 4: 0.56, - 6: 0.54, - 8: 0.54, - 10: 0.54, - } - - try: - gamma = switcher[LUC] - except Exception as e: - warnings.warn("Unknown Land Use Category, assuming LUC 8. "+str(e)) - LUC = 8 - gamma = switcher[LUC] - - # Diameter of particle in um - Dpum = np.array([2.5, 10]) - Dpm = Dpum*10**-6 # Diameter of particle in m - - # Calculations - mu = 1.8*10**-5*(T/298)**0.85 # viscosity of air in kg/m s - nu = mu/rhoair - lambda1 = 2*mu/(P*(8.*0.0288/(np.pi*R*T))**(0.5)) # mean free path - ll = np.array([lambda1, lambda1]) - Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) - # slip correction coefficient - - # Calculate vs - vs = rhop*Dpm**2*(g*Cc/(mu*18)) # particle settling velocity - - # Calculate rb - ustar = np.zeros_like(u, dtype=float) # pre-allocate ustar - # Equation 11.66 in Ramaswami (and 16.67 and Sienfeld &Pandis) - ustar[u > 0] = 0.4 * u[u > 0]/np.log(10/z0) - ustar[u <= 0] = 0.001 - - D = k*T*(Cc/(3*np.pi*mu*Dpm)) - - Sc = nu/D - # gamma=0.56 # for urban - # alpha=1.5 # for urban - EB = Sc**(-1 * gamma) - St = vs*(ustar**2)/(g*nu) - - EIM = 10.0**(-3.0/St) # For smooth surfaces - # EIM =((St)./(0.82+St)).^2 - - R1 = np.exp(-St**(0.5)) # percentage of particles that stick - - rb = 1/(3*(EB+EIM)*ustar*R1) - - # Calculate ra - a = np.array([-0.096, -0.037, -0.002, 0, 0.004, 0.035]) - b = np.array([0.029, 0.029, 0.018, 0, -0.018, -0.036]) - - # For wind speeds <= 3, use a = -0.037 and b = 0.029 - # For wind speeds >3 and <=5, use a = -.002, b = 0.018 - # For wind speeds > 5, use a = 0, b = 0 - avals = a[1]*np.ones_like(u, dtype=float) - avals[u > 3] = a[2] - avals[u > 5] = a[3] - - bvals = b[1]*np.ones_like(u, dtype=float) - bvals[u > 3] = b[2] - bvals[u > 5] = b[3] - - L = 1/(avals + bvals*np.log(z0)) - - zeta0 = z0/L - zeta = 10.0/L - eta = ((1-15*zeta)**(0.25)) - eta0 = ((1-15*zeta0)**(0.25)) - - ra = np.zeros_like(zeta, dtype=float) # Preallocate memory - ra[zeta == 0] = (1 / (0.4 * ustar[zeta == 0])) * np.log(10.0 / z0) - - sub_a = 1 / (0.4 * ustar[zeta > 0]) - sub_b = np.log(10.0 / z0) - sub_c = zeta[zeta > 0] - zeta0[zeta > 0] - - ra[zeta > 0] = sub_a * (sub_b + 4.7 * sub_c) - - sub_a = (1 / (0.4 * ustar[zeta < 0])) - sub_b = np.log(10.0 / z0) - sub_ba = eta0[zeta < 0]**2 + 1 - sub_bb = (eta0[zeta < 0]+1)**2 - sub_bc = eta[zeta < 0]**2 + 1 - sub_bd = (eta[zeta < 0]+1)**2 - sub_d = np.arctan(eta[zeta < 0]) - sub_e = np.arctan(eta0[zeta < 0]) - - sub_c = np.log(sub_ba * sub_bb / (sub_bc * sub_bd)) - ra[zeta < 0] = sub_a * (sub_b + sub_c + 2*(sub_d-sub_e)) - - # Calculate vd and mass flux - - vd = 1/(ra+rb)+vs - - return vd From 619b1a4d5bbae540e09666e6c6fbd9c1d5bd3190 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Wed, 15 Jan 2020 23:50:34 +0530 Subject: [PATCH 08/22] updated soiling_hsu --- pvlib/losses.py | 122 ------------------------------------------------ 1 file changed, 122 deletions(-) diff --git a/pvlib/losses.py b/pvlib/losses.py index 0a13dc4d61..750cf029c3 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -81,125 +81,3 @@ def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, soiling_ratio = 1 - 0.3437 * np.erf(0.17 * accum_mass**0.8473) return soiling_ratio - - -def depo_velocity(T, WindSpeed, LUC): - - # convert temperature into Kelvin - T = T + 273.15 - - # save wind data - if(np.isscalar(WindSpeed)): - u = np.array([WindSpeed]) - else: - u = WindSpeed - - g = 9.81 # gravity in m/s^2 - # Na = 6.022 * 10**23 # avagadros number - R = 8.314 # Universal gas consant in m3Pa/Kmol - k = 1.38 * 10**-23 # Boltzmann's constant in m^2kg/sK - P = 101300 # pressure in Pa - rhoair = 1.2041 # density of air in kg/m3 - z0 = 1 - rhop = 1500 # Assume density of particle in kg/m^3 - - switcher = { - 1: 0.56, - 4: 0.56, - 6: 0.54, - 8: 0.54, - 10: 0.54, - } - - try: - gamma = switcher[LUC] - except Exception as e: - warnings.warn("Unknown Land Use Category, assuming LUC 8. "+str(e)) - LUC = 8 - gamma = switcher[LUC] - - # Diameter of particle in um - Dpum = np.array([2.5, 10]) - Dpm = Dpum*10**-6 # Diameter of particle in m - - # Calculations - mu = 1.8*10**-5*(T/298)**0.85 # viscosity of air in kg/m s - nu = mu/rhoair - lambda1 = 2*mu/(P*(8.*0.0288/(np.pi*R*T))**(0.5)) # mean free path - ll = np.array([lambda1, lambda1]) - Cc = 1+2*ll/Dpm*(1.257+0.4*np.exp(-1.1*Dpm/(ll*2))) - # slip correction coefficient - - # Calculate vs - vs = rhop*Dpm**2*(g*Cc/(mu*18)) # particle settling velocity - - # Calculate rb - ustar = np.zeros_like(u, dtype=float) # pre-allocate ustar - # Equation 11.66 in Ramaswami (and 16.67 and Sienfeld &Pandis) - ustar[u > 0] = 0.4 * u[u > 0]/np.log(10/z0) - ustar[u <= 0] = 0.001 - - D = k*T*(Cc/(3*np.pi*mu*Dpm)) - - Sc = nu/D - # gamma=0.56 # for urban - # alpha=1.5 # for urban - EB = Sc**(-1 * gamma) - St = vs*(ustar**2)/(g*nu) - - EIM = 10.0**(-3.0/St) # For smooth surfaces - # EIM =((St)./(0.82+St)).^2 - - R1 = np.exp(-St**(0.5)) # percentage of particles that stick - - rb = 1/(3*(EB+EIM)*ustar*R1) - - # Calculate ra - a = np.array([-0.096, -0.037, -0.002, 0, 0.004, 0.035]) - b = np.array([0.029, 0.029, 0.018, 0, -0.018, -0.036]) - - # For wind speeds <= 3, use a = -0.037 and b = 0.029 - # For wind speeds >3 and <=5, use a = -.002, b = 0.018 - # For wind speeds > 5, use a = 0, b = 0 - avals = a[1]*np.ones_like(u, dtype=float) - avals[u > 3] = a[2] - avals[u > 5] = a[3] - - bvals = b[1]*np.ones_like(u, dtype=float) - bvals[u > 3] = b[2] - bvals[u > 5] = b[3] - - L = 1/(avals + bvals*np.log(z0)) - - zeta0 = z0/L - zeta = 10.0/L - eta = ((1-15*zeta)**(0.25)) - eta0 = ((1-15*zeta0)**(0.25)) - - ra = np.zeros_like(zeta, dtype=float) # Preallocate memory - ra[zeta == 0] = (1 / (0.4 * ustar[zeta == 0])) * np.log(10.0 / z0) - - sub_a = 1 / (0.4 * ustar[zeta > 0]) - sub_b = np.log(10.0 / z0) - sub_c = zeta[zeta > 0] - zeta0[zeta > 0] - - ra[zeta > 0] = sub_a * (sub_b + 4.7 * sub_c) - - sub_a = (1 / (0.4 * ustar[zeta < 0])) - sub_b = np.log(10.0 / z0) - sub_ba = eta0[zeta < 0]**2 + 1 - sub_bb = (eta0[zeta < 0]+1)**2 - sub_bc = eta[zeta < 0]**2 + 1 - sub_bd = (eta[zeta < 0]+1)**2 - sub_d = np.arctan(eta[zeta < 0]) - sub_e = np.arctan(eta0[zeta < 0]) - - sub_c = np.log(sub_ba * sub_bb / (sub_bc * sub_bd)) - ra[zeta < 0] = sub_a * (sub_b + sub_c + 2*(sub_d-sub_e)) - - # Calculate vd and mass flux - - vd = 1/(ra+rb)+vs - - - return vd From cd8f7d6b5c3d2aabb1641a8e061e52e0515f19b4 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Thu, 16 Jan 2020 02:56:26 +0530 Subject: [PATCH 09/22] added unit test --- pvlib/losses.py | 3 +- pvlib/test/test_losses.py | 73 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 pvlib/test/test_losses.py diff --git a/pvlib/losses.py b/pvlib/losses.py index 750cf029c3..873b16337b 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -6,6 +6,7 @@ import numpy as np import pandas as pd +from scipy.special import erf from pvlib.tools import cosd @@ -78,6 +79,6 @@ def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, mass_removed[cleaning_times] = mass_no_cleaning[cleaning_times] accum_mass = mass_no_cleaning - mass_removed.ffill() - soiling_ratio = 1 - 0.3437 * np.erf(0.17 * accum_mass**0.8473) + soiling_ratio = 1 - 0.3437 * erf(0.17 * accum_mass**0.8473) return soiling_ratio diff --git a/pvlib/test/test_losses.py b/pvlib/test/test_losses.py new file mode 100644 index 0000000000..4d1597c367 --- /dev/null +++ b/pvlib/test/test_losses.py @@ -0,0 +1,73 @@ +import pandas as pd +from pandas.util.testing import assert_series_equal +from pvlib.losses import soiling_hsu + + +def test_soiling_hsu(): + """Test Soiling HSU function""" + dt = pd.date_range(start=pd.datetime(2019, 1, 1, 0, 0, 0), + end=pd.datetime(2019, 1, 1, 23, 59, 0), freq='1h') + + rainfall = pd.Series( + data=[0., 0., 0., 0., 1., 0., 0., 0., 0.5, 0.5, 0., 0., 0., 0., 0., + 0., 0.3, 0.3, 0.3, 0.3, 0., 0., 0., 0.], index=dt) + + pm2_5 = 1.0 + pm10 = 2.0 + depo_veloc = {'2_5': 1.0, '10': 1.0} + tilt = 0. + + expected_no_cleaning = pd.Series( + data=[0.884980357535360, 0.806308930084762, 0.749974647038078, + 0.711804155175089, 0.687489866078621, 0.672927554408964, + 0.664714899337491, 0.660345851212099, 0.658149551658860, + 0.657104593968981, 0.656633344364056, 0.656431630729954, + 0.656349579062171, 0.656317825078228, 0.656306121502393, + 0.656302009396500, 0.656300630853678, 0.656300189543417, + 0.656300054532516, 0.656300015031680, 0.656300003971846, + 0.656300001006533, 0.656300000244750, 0.656300000057132], + index=dt) + + result1 = soiling_hsu(rainfall=rainfall, cleaning_threshold=10., tilt=tilt, + pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, + rain_accum_period=pd.Timedelta('1h')) + assert_series_equal(result1, expected_no_cleaning) + + # one cleaning event at 4:00 + result2 = soiling_hsu(rainfall=rainfall, cleaning_threshold=1., tilt=tilt, + pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, + rain_accum_period=pd.Timedelta('1h')) + + expected2 = pd.Series(index=dt) + expected2[dt[:4]] = expected_no_cleaning[dt[:4]] + expected2[dt[4]] = 1. + expected2[dt[5:]] = expected_no_cleaning[dt[:19]] + assert_series_equal(result2, expected2) + + # two cleaning events at 4:00-5:00 and 9:00 + result3 = soiling_hsu(rainfall=rainfall, cleaning_threshold=1., tilt=tilt, + pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, + rain_accum_period=pd.Timedelta('2h')) + + expected3 = pd.Series(index=dt) + expected3[dt[:4]] = expected_no_cleaning[dt[:4]] + expected3[dt[4:6]] = 1. + expected3[dt[6:9]] = expected_no_cleaning[dt[:3]] + expected3[dt[9]] = 1. + expected3[dt[10:]] = expected_no_cleaning[dt[:14]] + assert_series_equal(result3, expected3) + + # three cleaning events at 4:00-6:00, 8:00-11:00, and 17:00-20:00 + result4 = soiling_hsu(rainfall=rainfall, cleaning_threshold=0.5, tilt=tilt, + pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, + rain_accum_period=pd.Timedelta('3h')) + + expected4 = pd.Series(index=dt) + expected4[dt[:4]] = expected_no_cleaning[dt[:4]] + expected4[dt[4:7]] = 1. + expected4[dt[7]] = expected_no_cleaning[dt[0]] + expected4[dt[8:12]] = 1. + expected4[dt[12:17]] = expected_no_cleaning[dt[:5]] + expected4[dt[17:21]] = 1. + expected4[dt[21:]] = expected_no_cleaning[:3] + assert_series_equal(result4, expected4) From 49d29eff838b42ca0ef98ddc32c869866ae13688 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Thu, 16 Jan 2020 09:16:05 +0530 Subject: [PATCH 10/22] added unit test --- pvlib/losses.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pvlib/losses.py b/pvlib/losses.py index 873b16337b..750cf029c3 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -6,7 +6,6 @@ import numpy as np import pandas as pd -from scipy.special import erf from pvlib.tools import cosd @@ -79,6 +78,6 @@ def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, mass_removed[cleaning_times] = mass_no_cleaning[cleaning_times] accum_mass = mass_no_cleaning - mass_removed.ffill() - soiling_ratio = 1 - 0.3437 * erf(0.17 * accum_mass**0.8473) + soiling_ratio = 1 - 0.3437 * np.erf(0.17 * accum_mass**0.8473) return soiling_ratio From b75a690d2e4e245680e0dadcb604b70c2b1de36c Mon Sep 17 00:00:00 2001 From: nappaillav Date: Thu, 16 Jan 2020 09:29:59 +0530 Subject: [PATCH 11/22] added unit test --- pvlib/losses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pvlib/losses.py b/pvlib/losses.py index 750cf029c3..ae894d7614 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -6,6 +6,7 @@ import numpy as np import pandas as pd +import math from pvlib.tools import cosd @@ -78,6 +79,6 @@ def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, mass_removed[cleaning_times] = mass_no_cleaning[cleaning_times] accum_mass = mass_no_cleaning - mass_removed.ffill() - soiling_ratio = 1 - 0.3437 * np.erf(0.17 * accum_mass**0.8473) + soiling_ratio = 1 - 0.3437 * math.erf(0.17 * accum_mass**0.8473) return soiling_ratio From 57524797fd53c8acd1976b12dea00dcf32dd40d9 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Thu, 16 Jan 2020 09:45:03 +0530 Subject: [PATCH 12/22] added unit test --- pvlib/losses.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/pvlib/losses.py b/pvlib/losses.py index ae894d7614..0c1e27f4ec 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -6,9 +6,44 @@ import numpy as np import pandas as pd -import math from pvlib.tools import cosd +def erf(x): + """ + Calculates the ERF function + + Parameters + ---------- + x : numeric + Input value/array + + Returns + ------- + erf : numeric + The values of the error function at the given points x. + + """ + + + # constants + a1 = 0.254829592 + a2 = -0.284496736 + a3 = 1.421413741 + a4 = -1.453152027 + a5 = 1.061405429 + p = 0.3275911 + + # Save the sign of x + sign = 1 + if x < 0: + sign = -1 + x = abs(x) + + # A&S formula 7.1.26 + t = 1.0/(1.0 + p*x) + y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*np.exp(-x*x) + + return sign*y def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, depo_veloc={'2_5': 0.004, '10': 0.0009}, From c0a245b46796ba0dea52e7acb473ced98ee28fad Mon Sep 17 00:00:00 2001 From: nappaillav Date: Thu, 16 Jan 2020 09:48:54 +0530 Subject: [PATCH 13/22] added unit test --- pvlib/losses.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pvlib/losses.py b/pvlib/losses.py index 0c1e27f4ec..41cf53e06f 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -8,30 +8,30 @@ import pandas as pd from pvlib.tools import cosd + def erf(x): """ - Calculates the ERF function + Calculates the ERF function Parameters ---------- x : numeric - Input value/array + Input value/array Returns ------- erf : numeric - The values of the error function at the given points x. + The values of the error function at the given points x. """ - # constants - a1 = 0.254829592 + a1 = 0.254829592 a2 = -0.284496736 - a3 = 1.421413741 + a3 = 1.421413741 a4 = -1.453152027 - a5 = 1.061405429 - p = 0.3275911 + a5 = 1.061405429 + p = 0.3275911 # Save the sign of x sign = 1 @@ -45,6 +45,7 @@ def erf(x): return sign*y + def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, depo_veloc={'2_5': 0.004, '10': 0.0009}, rain_accum_period=pd.Timedelta('1h')): @@ -114,6 +115,6 @@ def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, mass_removed[cleaning_times] = mass_no_cleaning[cleaning_times] accum_mass = mass_no_cleaning - mass_removed.ffill() - soiling_ratio = 1 - 0.3437 * math.erf(0.17 * accum_mass**0.8473) + soiling_ratio = 1 - 0.3437 * erf(0.17 * accum_mass**0.8473) return soiling_ratio From ed375f354d3c6539fdae587ec487220201b7fc86 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Thu, 16 Jan 2020 21:54:49 +0530 Subject: [PATCH 14/22] corrections_to_test_losses --- pvlib/losses.py | 38 +------------------------------------- pvlib/test/test_losses.py | 2 ++ 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/pvlib/losses.py b/pvlib/losses.py index 41cf53e06f..873b16337b 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -6,46 +6,10 @@ import numpy as np import pandas as pd +from scipy.special import erf from pvlib.tools import cosd -def erf(x): - """ - Calculates the ERF function - - Parameters - ---------- - x : numeric - Input value/array - - Returns - ------- - erf : numeric - The values of the error function at the given points x. - - """ - - # constants - a1 = 0.254829592 - a2 = -0.284496736 - a3 = 1.421413741 - a4 = -1.453152027 - a5 = 1.061405429 - p = 0.3275911 - - # Save the sign of x - sign = 1 - if x < 0: - sign = -1 - x = abs(x) - - # A&S formula 7.1.26 - t = 1.0/(1.0 + p*x) - y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*np.exp(-x*x) - - return sign*y - - def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, depo_veloc={'2_5': 0.004, '10': 0.0009}, rain_accum_period=pd.Timedelta('1h')): diff --git a/pvlib/test/test_losses.py b/pvlib/test/test_losses.py index 4d1597c367..b6234ba25f 100644 --- a/pvlib/test/test_losses.py +++ b/pvlib/test/test_losses.py @@ -1,8 +1,10 @@ import pandas as pd from pandas.util.testing import assert_series_equal from pvlib.losses import soiling_hsu +from pvlib.test.conftest import requires_scipy +@requires_scipy def test_soiling_hsu(): """Test Soiling HSU function""" dt = pd.date_range(start=pd.datetime(2019, 1, 1, 0, 0, 0), From 0e5a529fbe59e3d8a8e24e2b46c01a4dc75c4405 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Thu, 16 Jan 2020 22:12:12 +0530 Subject: [PATCH 15/22] corrections_to_test_losses --- pvlib/losses.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pvlib/losses.py b/pvlib/losses.py index 873b16337b..cd93c0980f 100644 --- a/pvlib/losses.py +++ b/pvlib/losses.py @@ -6,7 +6,6 @@ import numpy as np import pandas as pd -from scipy.special import erf from pvlib.tools import cosd @@ -60,6 +59,10 @@ def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. """ + try: + from scipy.special import erf + except ImportError: + raise ImportError("The soiling_hsu function requires scipy.") # accumulate rainfall into periods for comparison with threshold accum_rain = rainfall.rolling(rain_accum_period, closed='right').sum() From b3e2e61c95ec000b794cba5d73f4bbe8dfce98b0 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Fri, 17 Jan 2020 02:15:24 +0530 Subject: [PATCH 16/22] cleaning Test function --- pvlib/test/test_losses.py | 128 +++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 51 deletions(-) diff --git a/pvlib/test/test_losses.py b/pvlib/test/test_losses.py index b6234ba25f..4479a1b783 100644 --- a/pvlib/test/test_losses.py +++ b/pvlib/test/test_losses.py @@ -2,22 +2,34 @@ from pandas.util.testing import assert_series_equal from pvlib.losses import soiling_hsu from pvlib.test.conftest import requires_scipy +import pytest -@requires_scipy -def test_soiling_hsu(): - """Test Soiling HSU function""" +@pytest.fixture +def expected_output(): + # Sample output (calculated manually) dt = pd.date_range(start=pd.datetime(2019, 1, 1, 0, 0, 0), end=pd.datetime(2019, 1, 1, 23, 59, 0), freq='1h') - rainfall = pd.Series( - data=[0., 0., 0., 0., 1., 0., 0., 0., 0.5, 0.5, 0., 0., 0., 0., 0., - 0., 0.3, 0.3, 0.3, 0.3, 0., 0., 0., 0.], index=dt) + expected_no_cleaning = pd.Series( + data=[0.884980357535360, 0.806308930084762, 0.749974647038078, + 0.711804155175089, 0.687489866078621, 0.672927554408964, + 0.664714899337491, 0.660345851212099, 0.658149551658860, + 0.657104593968981, 0.656633344364056, 0.656431630729954, + 0.656349579062171, 0.656317825078228, 0.656306121502393, + 0.656302009396500, 0.656300630853678, 0.656300189543417, + 0.656300054532516, 0.656300015031680, 0.656300003971846, + 0.656300001006533, 0.656300000244750, 0.656300000057132], + index=dt) + + return expected_no_cleaning - pm2_5 = 1.0 - pm10 = 2.0 - depo_veloc = {'2_5': 1.0, '10': 1.0} - tilt = 0. + +@pytest.fixture +def expected_output_2(): + # Sample output (calculated manually) + dt = pd.date_range(start=pd.datetime(2019, 1, 1, 0, 0, 0), + end=pd.datetime(2019, 1, 1, 23, 59, 0), freq='1h') expected_no_cleaning = pd.Series( data=[0.884980357535360, 0.806308930084762, 0.749974647038078, @@ -30,46 +42,60 @@ def test_soiling_hsu(): 0.656300001006533, 0.656300000244750, 0.656300000057132], index=dt) - result1 = soiling_hsu(rainfall=rainfall, cleaning_threshold=10., tilt=tilt, - pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, - rain_accum_period=pd.Timedelta('1h')) - assert_series_equal(result1, expected_no_cleaning) - - # one cleaning event at 4:00 - result2 = soiling_hsu(rainfall=rainfall, cleaning_threshold=1., tilt=tilt, - pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, - rain_accum_period=pd.Timedelta('1h')) - - expected2 = pd.Series(index=dt) - expected2[dt[:4]] = expected_no_cleaning[dt[:4]] - expected2[dt[4]] = 1. - expected2[dt[5:]] = expected_no_cleaning[dt[:19]] - assert_series_equal(result2, expected2) - - # two cleaning events at 4:00-5:00 and 9:00 - result3 = soiling_hsu(rainfall=rainfall, cleaning_threshold=1., tilt=tilt, - pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, - rain_accum_period=pd.Timedelta('2h')) - - expected3 = pd.Series(index=dt) - expected3[dt[:4]] = expected_no_cleaning[dt[:4]] - expected3[dt[4:6]] = 1. - expected3[dt[6:9]] = expected_no_cleaning[dt[:3]] - expected3[dt[9]] = 1. - expected3[dt[10:]] = expected_no_cleaning[dt[:14]] - assert_series_equal(result3, expected3) + expected = pd.Series(index=dt) + expected[dt[:4]] = expected_no_cleaning[dt[:4]] + expected[dt[4:7]] = 1. + expected[dt[7]] = expected_no_cleaning[dt[0]] + expected[dt[8:12]] = 1. + expected[dt[12:17]] = expected_no_cleaning[dt[:5]] + expected[dt[17:21]] = 1. + expected[dt[21:]] = expected_no_cleaning[:3] + + return expected + + +@pytest.fixture +def rainfall_input(): + + dt = pd.date_range(start=pd.datetime(2019, 1, 1, 0, 0, 0), + end=pd.datetime(2019, 1, 1, 23, 59, 0), freq='1h') + rainfall = pd.Series( + data=[0., 0., 0., 0., 1., 0., 0., 0., 0.5, 0.5, 0., 0., 0., 0., 0., + 0., 0.3, 0.3, 0.3, 0.3, 0., 0., 0., 0.], index=dt) + return rainfall + + +@requires_scipy +def test_soiling_hsu_no_cleaning(rainfall_input, expected_output): + """Test Soiling HSU function""" + + rainfall = rainfall_input + pm2_5 = 1.0 + pm10 = 2.0 + depo_veloc = {'2_5': 1.0, '10': 1.0} + tilt = 0. + expected_no_cleaning = expected_output + + result = soiling_hsu(rainfall=rainfall, cleaning_threshold=10., tilt=tilt, + pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, + rain_accum_period=pd.Timedelta('1h')) + assert_series_equal(result, expected_no_cleaning) + + +@requires_scipy +def test_soiling_hsu(rainfall_input, expected_output_2): + """Test Soiling HSU function""" + + rainfall = rainfall_input + pm2_5 = 1.0 + pm10 = 2.0 + depo_veloc = {'2_5': 1.0, '10': 1.0} + tilt = 0. + expected = expected_output_2 # three cleaning events at 4:00-6:00, 8:00-11:00, and 17:00-20:00 - result4 = soiling_hsu(rainfall=rainfall, cleaning_threshold=0.5, tilt=tilt, - pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, - rain_accum_period=pd.Timedelta('3h')) - - expected4 = pd.Series(index=dt) - expected4[dt[:4]] = expected_no_cleaning[dt[:4]] - expected4[dt[4:7]] = 1. - expected4[dt[7]] = expected_no_cleaning[dt[0]] - expected4[dt[8:12]] = 1. - expected4[dt[12:17]] = expected_no_cleaning[dt[:5]] - expected4[dt[17:21]] = 1. - expected4[dt[21:]] = expected_no_cleaning[:3] - assert_series_equal(result4, expected4) + result = soiling_hsu(rainfall=rainfall, cleaning_threshold=0.5, tilt=tilt, + pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, + rain_accum_period=pd.Timedelta('3h')) + + assert_series_equal(result, expected) From 45c792040b5bed08b785d83ce793ee5302b11d28 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Fri, 17 Jan 2020 02:24:29 +0530 Subject: [PATCH 17/22] cleaning Test function --- pvlib/test/test_losses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/test/test_losses.py b/pvlib/test/test_losses.py index 4479a1b783..8fc52b974c 100644 --- a/pvlib/test/test_losses.py +++ b/pvlib/test/test_losses.py @@ -1,7 +1,7 @@ import pandas as pd from pandas.util.testing import assert_series_equal from pvlib.losses import soiling_hsu -from pvlib.test.conftest import requires_scipy +from conftest import requires_scipy import pytest From e83f568862eb0ad61d30f6e1ca9d37c20fd0256e Mon Sep 17 00:00:00 2001 From: nappaillav Date: Fri, 17 Jan 2020 10:01:53 +0530 Subject: [PATCH 18/22] cleaning Test function --- pvlib/test/test_losses.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pvlib/test/test_losses.py b/pvlib/test/test_losses.py index 8fc52b974c..c6081c7cfd 100644 --- a/pvlib/test/test_losses.py +++ b/pvlib/test/test_losses.py @@ -26,21 +26,12 @@ def expected_output(): @pytest.fixture -def expected_output_2(): +def expected_output_2(expected_output): # Sample output (calculated manually) dt = pd.date_range(start=pd.datetime(2019, 1, 1, 0, 0, 0), end=pd.datetime(2019, 1, 1, 23, 59, 0), freq='1h') - expected_no_cleaning = pd.Series( - data=[0.884980357535360, 0.806308930084762, 0.749974647038078, - 0.711804155175089, 0.687489866078621, 0.672927554408964, - 0.664714899337491, 0.660345851212099, 0.658149551658860, - 0.657104593968981, 0.656633344364056, 0.656431630729954, - 0.656349579062171, 0.656317825078228, 0.656306121502393, - 0.656302009396500, 0.656300630853678, 0.656300189543417, - 0.656300054532516, 0.656300015031680, 0.656300003971846, - 0.656300001006533, 0.656300000244750, 0.656300000057132], - index=dt) + expected_no_cleaning = expected_output expected = pd.Series(index=dt) expected[dt[:4]] = expected_no_cleaning[dt[:4]] From 2efe2d1e75ed64550824f19228338e6680ff3e5f Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 17 Jan 2020 10:04:49 -0700 Subject: [PATCH 19/22] update api.rst, whatsnew --- docs/sphinx/source/api.rst | 10 +++++++++- docs/sphinx/source/whatsnew/v0.7.1.rst | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index ed6140af38..486598d7a0 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -298,7 +298,7 @@ PVWatts model pvsystem.pvwatts_ac pvsystem.pvwatts_losses -Functions for fitting PV models +Functions for fitting diode models ------------------------------- .. autosummary:: :toctree: generated/ @@ -307,6 +307,14 @@ Functions for fitting PV models ivtools.fit_sdm_cec_sam ivtools.fit_sdm_desoto +Losses +------ +.. autosummary:: + :toctree: generated/ + + losses.soiling_hsu + + Other ----- diff --git a/docs/sphinx/source/whatsnew/v0.7.1.rst b/docs/sphinx/source/whatsnew/v0.7.1.rst index 53dace9cfe..c80808df96 100644 --- a/docs/sphinx/source/whatsnew/v0.7.1.rst +++ b/docs/sphinx/source/whatsnew/v0.7.1.rst @@ -14,7 +14,9 @@ Enhancements objects. (:issue:`841`) * Added `leap_day` parameter to `iotools.get_psm3` instead of hardcoding it as False. - +* Added a new module `pvlib.losses` for various loss models. +* Added the Humboldt State University soiling model + :py:func:`~pvlib.losses.soiling_hsu`. (:issue:`739`) Bug fixes ~~~~~~~~~ @@ -32,3 +34,4 @@ Documentation Contributors ~~~~~~~~~~~~ * Kevin Anderson (:ghuser:`kanderso-nrel`) +* (:ghuser:`nappaillav`) From 920d77b42f3fdbfe08ae4ee527e5637a38ef2339 Mon Sep 17 00:00:00 2001 From: nappaillav Date: Fri, 17 Jan 2020 23:37:06 +0530 Subject: [PATCH 20/22] merge_correction --- pvlib/{test => tests}/test_losses.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pvlib/{test => tests}/test_losses.py (100%) diff --git a/pvlib/test/test_losses.py b/pvlib/tests/test_losses.py similarity index 100% rename from pvlib/test/test_losses.py rename to pvlib/tests/test_losses.py From e331f19b2c02f254e546357267b1a0833016bc0f Mon Sep 17 00:00:00 2001 From: nappaillav Date: Fri, 17 Jan 2020 23:42:34 +0530 Subject: [PATCH 21/22] merge_corrections --- docs/sphinx/source/whatsnew/v0.7.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.1.rst b/docs/sphinx/source/whatsnew/v0.7.1.rst index 6a53596356..c949c36675 100644 --- a/docs/sphinx/source/whatsnew/v0.7.1.rst +++ b/docs/sphinx/source/whatsnew/v0.7.1.rst @@ -42,4 +42,4 @@ Contributors ~~~~~~~~~~~~ * Kevin Anderson (:ghuser:`kanderso-nrel`) * Mark Mikofski (:ghuser:`mikofski`) -* (:ghuser:`nappaillav`) +* Valliappan CA (:ghuser:`nappaillav`) From cfa5a0f830c197e27cd894a53aae44b53e99690f Mon Sep 17 00:00:00 2001 From: nappaillav Date: Sat, 18 Jan 2020 01:11:27 +0530 Subject: [PATCH 22/22] updated_init --- pvlib/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pvlib/__init__.py b/pvlib/__init__.py index c99d2e9fd7..098202c819 100644 --- a/pvlib/__init__.py +++ b/pvlib/__init__.py @@ -14,3 +14,4 @@ from pvlib import modelchain # noqa: F401 from pvlib import singlediode # noqa: F401 from pvlib import bifacial # noqa: F401 +from pvlib import losses # noqa: F401