-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathpvl_soiling_hsu.m
355 lines (311 loc) · 15.9 KB
/
pvl_soiling_hsu.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
function SR = pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, varargin)
% PVL_SOILING_HSU Calculates soiling rate over time given particulate and rain data
%
% Syntax
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10)
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType)
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType, RainAccPeriod)
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType, RainAccPeriod, LUC, WindSpeed, Temperature)
%
% Description
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10)
% Uses the time, rainfall amounts, tilt angle, particulate matter
% concentrations, and rain cleaning threshold to predict soiling rate
% using the fixed settling model. Uses hourly rain accumulation to
% determine cleaning.
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType)
% Uses the time, rainfall amounts, tilt angle, particulate matter
% concentrations, and rain cleaning threshold to predict soiling rate
% using the model type selected by ModelType. Uses hourly rain
% accumulation to determine cleaning.
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType, RainAccPeriod)
% Uses the time, rainfall amounts, tilt angle, particulate matter
% concentrations, and rain cleaning threshold to predict soiling rate
% using the model type selected by ModelType. The user specifies the
% rain accumulation period (in hours) over which to determine whether
% sufficient rain has fallen to clean the module.
% [SR]=pvl_soiling_hsu(Time, Rain, RainThresh, Tilt, PM2_5, PM10, ModelType, RainAccPeriod, LUC, WindSpeed, Temperature)
% Uses the parameters of the fixed models along with Land Use Category
% (LUC), wind speed, and ambient temperature to predict soiling ratio
% using the variable deposition velocity model.
%
% Input Parameters:
% Time is a struct with elements as described in pvl_maketimestruct.
% Time values for the soiling function do not need to be
% regularly spaced, although large gaps in timing are discouraged.
%
% Rain is a vector of rainfall in mm of the same length as Time, where
% each element of Rain correlates with the element of Time.
% 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.
%
% 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.
%
% 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.
%
% PM2_5 is the concentration of airborne particulate matter (PM) with diameter less
% than 2.5 microns, in g/m^3. Historical US concentration data sets may be
% found at https://aws.epa.gov/aqsweb/airdata/download_files.html. With
% file descriptions (for hourly data sets) at
% https://aqs.epa.gov/aqsweb/airdata/FileFormats.html#_hourly_data_files
%
% PM10 is the concentration of airborne particulate matter (PM) with diameter less than
% 10 microns, in g/m^3. Historical US PM data sets found as described
% above.
%
% 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 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.
%
% 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 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.
%
% 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.
%
%
% Output Parameters:
% SR = 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.
%
% Background and recognition:
% This soiling model was developed by Liza Boyle and Merissa Coello at
% Humboldt State University (HSU). Many thanks to Liza and Merissa for
% model development, initial code, and subsequent code and performance
% reviews.
%
% 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.
%
% See also PVL_MAKETIMESTRUCT
p=inputParser;
p.addRequired('Time', @isstruct);
p.addRequired('Rain', @(x) all(isnumeric(x) & all(x>=0) & isvector(x)));
p.addRequired('RainThresh', @(x) all(isnumeric(x) & isscalar(x) & all(x>=0)));
p.addRequired('Tilt', @(x) all(isnumeric(x) & isvector(x)));
p.addRequired('PM2_5', @(x) all(isnumeric(x) & all(x>=0) & isvector(x)))
p.addRequired('PM10', @(x) all(isnumeric(x) & all(x>=0) & isvector(x)))
p.addOptional('ModelType', 2, @(x) all(isscalar(x)));
p.addOptional('RainAccPeriod', 1, @(x) all(isscalar(x) & x>=1 & isnumeric(x)));
p.addOptional('LUC', 8, @(x) all(isscalar(x)))
p.addOptional('WindSpeed', 2, @(x) all(isnumeric(x) & all(x>=0) & isvector(x)));
p.addOptional('Temperature', 12, @(x) all(isnumeric(x) & all(x>=0) & isvector(x)));
p.parse(Time, Rain, RainThresh, Tilt, PM2_5, PM10, varargin{:});
RainAccPeriod = p.Results.RainAccPeriod;
ModelType = p.Results.ModelType;
LUC = p.Results.LUC;
WindSpeed = p.Results.WindSpeed;
Temperature = p.Results.Temperature;
% Create a datenum from the time structure to handle duration easily
TimeAsDatenum = datenum(Time.year, Time.month, Time.day, Time.hour, ...
Time.minute, Time.second);
% Following section accumulates the rain in each accumulation period
% To change this to a more frequent accumulation rate, create a
% new array based on TimeAsDatenum where the unit is the given unit of
% accumulation (e.g. for hourly accumulation you might use
% TimeAsDatenum*24)
RainAccAsDatenum = floor(TimeAsDatenum * 24 ./ RainAccPeriod);
% Determine the number of unique days, the first instance of that day, and
% create an index of which entries are part of that day
[RainAccTimes, UnqRainAccFrstVal, UnqRainAccIndx] = unique(RainAccAsDatenum);
% Accumulate rain readings according to the unique index of days
RainAtAccTimes = accumarray(UnqRainAccIndx, Rain);
% Create an accumulated rain vector, then populate the last entry of each
% date with the rain that fell on that day
AccumRain = zeros(size(Rain));
AccumRain(UnqRainAccFrstVal(2:end)-1) = RainAtAccTimes(1:end-1);
% Rain accumulated on the last day is placed at the last time (does not
% affect soiling calculation)
AccumRain(end) = RainAtAccTimes(end);
switch ModelType
case 1 % Variable Deposition Velocity
vd = depo_veloc(Temperature, WindSpeed, LUC);
case 2 % Fixed Settling Velocity in m/s
vd(1,2) = 0.004;
vd(1,1) = 0.0009;
case 3 % Fixed Deposition Velcoity in m/s
vd(1,2) = 0.0917;
vd(1,1) = 0.0015;
otherwise
warning('Unknown Model Type specified, using Fixed Setting Velocity model')
vd(1,2) = 0.004;
vd(1,1) = 0.0009;
end
% Corrects PM10 measurement since PM2.5 is included in measurement
PMConcentration=nan(numel(TimeAsDatenum),2); % pre-allocate with NAN
PMConcentration(:,1) = PM2_5; % fill PM2.5 data in column 1
% Since PM2.5 particulate is counted in PM10 measurements, and we need only
% the particular from 2.5 micron to 10 micron, subtract the 2.5 micron
% particulate from the 10 micron particulate
PMConcentration(:,2) = PM10 - PM2_5; % fill in PM2.5-PM10 data in column 2
% For any instances where 2.5 micron particulate was more thatn 10 micron,
% set the amount of 2.5 to 10 micron particulate to 0
PMConcentration(PM10 - PM2_5 < 0 , 2) = 0;
% EPA concentrations reported in micrograms/m^3, we need it in g/m^3
PMConcentration = PMConcentration .* 1e-6;
% Calculate the mass deposition rate of each particulate size
F=PMConcentration .* vd; % g * m^-2 * s^-1, by particulate size
HorizontalTotalMassRate = F(:,1) + F(:,2); % g * m^-2 * s^-1, total
% Mass depositition rate on a tilted surface. In the event of a changing
% tilt (e.g. tracking system) the mass rate is adjusted by the tilt angle
% at each time step, and thus a linear interpolation of changing tilt
% angles are used (when using trapezoidal integration).
TiltedMassRate = HorizontalTotalMassRate .* cosd(Tilt);
% Mass on the tilted surface if no rain occurs. Uses trapezoidal
% integration and determines the amount of mass deposited on a tilted
% module.
TiltedMassNoRain = cumtrapz(TimeAsDatenum*86400, TiltedMassRate);
% Create a Tilted soiling mass that will account for rain cleaning
TiltedMass = TiltedMassNoRain;
% For each rain accumulation period, check to see if enough rain fell to meet or
% exceed the cleaning threshold. If the threshold was met or exceeded,
% then subtract from each subsuequent time step the mass of future time
% steps. This essentially "resets" the cumulative soiling accumulation when
% the rain meets or exceeds the stated thresshold. Note that this means
% that the mass will never be reported as zero because the cleaning is
% applied immediately after a time step, and new mass accumulates between
% the cleaning and the first time reported after the cleaning.
% Note that this could be done more clearly by going through each time step
% and accumulating or clearing based on daily rainfall, but this is much
% faster, and can utilize trapezoidal soiling integration.
for cntr1 = 1:numel(RainAtAccTimes)-1
if RainAtAccTimes(cntr1) >= RainThresh
TiltedMass(UnqRainAccFrstVal(cntr1+1):end) = TiltedMass(UnqRainAccFrstVal(cntr1+1):end)-TiltedMass(UnqRainAccFrstVal(cntr1+1)-1);
end
end
% Soiling rate is the % transmittance reduction using Heagzy equation
SoilingRate = 34.37 * erf(0.17 .* (TiltedMass.^0.8473));
% Soiling Ratio is the % soil transmittance (0.95 = 5% soiling loss)
SR = (100 - SoilingRate) ./ 100;
end
% This function creates deposition velocities for the variable deposition
% model.
function vd = depo_veloc(T, WindSpeed, LUC)
% convert temperature into Kelvin
T = T + 273.15;
% save wind data
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
switch LUC
case {1, 4}
gamma = 0.56;
case {6, 8, 10}
gamma = 0.54;
otherwise
warning('Unknown Land Use Category (LUC), assuming LUC 8.');
gamma = 0.54;
end
%Diameter of particle in um
Dpum=[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./(pi.*R.*T)).^(1/2)); %mean free path
Cc = 1+2.*[lambda1 lambda1]./Dpm.*(1.257+0.4.*exp(-1.1.*Dpm./([lambda1 lambda1].*2))); %slip correction coefficient
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Calculate vs
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
vs = rhop.*Dpm.^2 .* (g .* Cc ./(mu.*18)); %particle settling velocity
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Calculate rb
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ustar = NaN(size(u)); % pre-allocate ustar
%Equation 11.66 in Ramaswami (and 16.67 and Sienfeld &Pandis)
ustar(u > 0) = 0.4 * u(u > 0) ./ log(10/z0);
ustar(u<=0) = 0.001;
D=k*T.*(Cc./(3*pi*mu*Dpm));
Sc=nu./D;
%gamma=.56; %for urban
%alpha=1.5; %for urban
EB=Sc.^(-1 * gamma);
St = vs .* (ustar .^ 2) ./ (g .* nu);
EIM=10.^(-3./St); %For smooth surfaces
%EIM=((St)./(0.82+St)).^2;
R1=exp(-St.^(1/2)); %percentage of particles that stick
rb=1./(3*(EB+EIM).*ustar.*R1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Calculate ra
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
a=[-0.096,-0.037,-0.002,0,0.004,0.035];
b=[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(2) .* ones(size(u));
avals(u>3) = a(3);
avals(u>5) = a(4);
bvals = b(2) .* ones(size(u));
bvals(u>3) = b(3);
bvals(u>5) = b(4);
L = 1 ./ (avals + bvals .* log(z0));
zeta0=z0./L;
zeta=10./L;
eta = ((1-15.*zeta).^(1./4));
eta0 = ((1-15.*zeta0).^(1./4));
ra = NaN(size(zeta)); % Preallocate memory
ra(zeta == 0) = (1 ./ (0.4 .* ustar(zeta == 0))) .* log(10 ./ z0);
ra(zeta > 0) = (1./(0.4.*ustar(zeta > 0))).*(log(10./z0) + ...
4.7.*(zeta(zeta > 0)-zeta0(zeta > 0)));
ra(zeta < 0) = (1 ./ (0.4 .* ustar(zeta < 0))) .* (log(10 ./ z0) + ...
log((eta0(zeta < 0).^2 + 1) .* (eta0(zeta < 0)+1).^2 ./ ...
((eta(zeta < 0).^2 + 1) .* (eta(zeta < 0)+1).^2)) + ...
2 .* (atan(eta(zeta < 0))-atan(eta0(zeta < 0))));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Calculate vd and mass flux
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
vd=1./(ra+rb)+vs;
end