Skip to content

Instantly share code, notes, and snippets.

@grandadmiral-thrawn
Created September 6, 2014 20:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grandadmiral-thrawn/9c16f7e08282f8ff28b6 to your computer and use it in GitHub Desktop.
Save grandadmiral-thrawn/9c16f7e08282f8ff28b6 to your computer and use it in GitHub Desktop.
Sun Algorithm, REVISED
this is the revised version of the solar algorithm for HJ andrews data! It now takes advantage of the suncycle.m function which can be found in this gist as well (NOAA). it now knows when night is and turns radiation to 0. previously there was some extraneous radiation which apparently is problematic in this type of equation due to the way in which clouds are incorporated- false "backscattering" takes effect. output will be included later.
function [maxwattstable, minwattstable] = solarhj;
%%% SOLARHJ is a function to compute the minimum and maximum solar radiation possible in W/m2 from the stations
% at the HJ Andrews.
% the outputs are 2 "tables" containing the array of values representing the max and minimum W/m2 that are available
% "instantaneously" in the order of 'PRIMET','CS2MET','CENMET','VANMET','H15MET', and 'UPLMET'.
% this function can be used within another QAQC script; call
% [max_table, min_table] = solarhj;
% this will enable you to have the look up tables in another file for reference.
% Created by Fox Peterson
% o7-31-2014
% SUN_POSITION is a BIF from MATLAB
% the algorithm used to compute irradiance is from the help manual for ARC GIS 10.2, solar insolation tool
% however, the Transmissivity Parameter comes from the construction for Air Mass presented in the
% online lectures from deAnza College at www.deanza.edu/faculty/hamidiridha. The algorithm has been
% altered by Fox
% the cloud cover constructs come from the national solar radiation db handbook 1961-1990,
% which is available online at rredc.nrel.gov
% the calculation of incidence angle comes from notes taken by Fox Peterson while working with Chris and Sean
% Carigg, which I have confirmed against the python code in the google earth engine, see code.google.com.
SCONST = 1367;
TIMEINT = 1; % hours (1 hour intervals here...)
cloudcover_h = 0;
cloudcover_m = 0;
cloudcover_l = 0;
utc_offset = -8;
M_theta = 1; % Relative optical length, representative of "air mass"; should be between 0 and 2.5.
% a small table containing the values of the stations to put into sun pos
stations = {{'PRIMET',{[44.21189300, -122.25594100], 436, 4.80, 158}};
{'CS2MET',{[44.21473500, -122.24862900], 482, 10.26, 19}};
{'CENMET',{[44.24348200, -122.14109300], 1028, 12.17, 224}};
{'VANMET',{[44.27161800,-122.14936800], 1268,5.48, 183}};
{'H15MET',{[44.2645069, -122.173778247], 909, 29.60, 215}};
{'UPLMET',{[44.20709700, -122.11976300], 1298, 8.31, 72}}
};
% a time series ranging from 2010-01-01 00:00:00 to 2010-12-31 23:59:59 by 1 hour breaks
timeseries1 = [734139:0.0416666667442769:734503.958333333];
Qstar_stations = zeros(length(timeseries1),6);
Qstar = zeros(length(timeseries1),1);
%%% set very lenient night criteria
%isNight = ones(length(timeseries1),1);
%[~,~,~,h,~,~] = datevec(timeseries1);
%isNight(h>22 | h <5) = 0;
sunrises_stations = zeros(length(timeseries1),6);
sunsets_stations = zeros(length(timeseries1),6);
nights_binary = zeros(length(timeseries1),6);
sunrises = zeros(length(timeseries1),1);
sunsets = zeros(length(timeseries1),1);
nights = zeros(length(timeseries1),1);
for i = 1:len(stations)
stationname = stations{i}{1}
location.latitude = stations{i}{2}{1}(1);
location.longitude = stations{i}{2}{1}(2);
location.altitude = stations{i}{2}{2};
stationslope = stations{i}{2}{3};
stationaspect = stations{i}{2}{4};
for j = 1:length(timeseries1)
datetime = datevec(timeseries1(j));
time.year = datetime(1,1); % Valid for [-2000, 6000]
time.month = datetime(1,2); % month [1-12]
time.day = datetime(1,3); % calendar day [1-31]
time.hour = datetime(1,4); % local hour [0-23]
time.min = datetime(1,5); % minute [0-59]
time.sec = datetime(1,6); % second [0-59]
time.UTC = utc_offset;
sun = sun_position(time,location); % sun.zenith, sun.azimuth
solarazimuth = sun.azimuth;
solarzenith = sun.zenith;
% compute the parameters needed for solar insolation
%%% compute the sunrise and sunset using the NOAA function
%%% convert from GMT and truncate
suntimes = suncycle(location.latitude, location.longitude, datetime);
sunrises(j) = floor(suntimes(1)-8);
sunsets(j) = floor(suntimes(2)-8);
%%% if the sunset is less than 0, add 24 (GMT conversion)
if sunsets(j)<0
sunsets(j) = sunsets(j) + 24;
end
%% if it is after sunrise or sunset, a zero is put out and stored in "nights"
isDay = (time.hour>sunrises(j) & time.hour<sunsets(j));
nights(j) = isDay;
% new 09-06-2014, suncycle for night time check- other one left night rads due to clouds
%[sunrise(j,1), sunset(j,1)] = suncycle(location.latitude, location.longitude,timeseries1(j))
%%% function here assumes we have a perfectly clear atmosphere but that earth is spherical
if solarzenith > 0 && solarzenith < 180;
Transmissivity = 1/(4*pi()^2)*sind(solarzenith)*(1-0.7*cloudcover_h)*(1-0.4*cloudcover_m)*(1-0.4*cloudcover_l);
else
Transmissivity == 0.1;
end
AngleIncidence = cosd(solarazimuth - stationaspect)*sind(stationslope)*sind(solarzenith) - cosd(solarzenith)*cosd(stationslope);
%HillEFX = @(solarazimuth, solarzenith, stationaspect, stationslope) cos(rad2deg(solarazimuth) - rad2deg(stationaspect)) * sin(rad2deg(stationslope))*sin(rad2deg(solarzenith)) + cos(rad2deg(solarzenith))*cos(rad2deg(stationslope));
Qstar(j,1) = SCONST * Transmissivity *24* cosd(AngleIncidence);
end
%clearvars stationname location stationslope station aspect
Qstar_stations(:,i) = Qstar;
sunrises_stations(:,i) = sunrises;
sunsets_stations(:,i) = sunsets;
nights_binary(:,i) = nights;
end
maxwattstable = Qstar_stations;
filename = 'Maximum_solar_downwelling_Wm2.csv';
fid = fopen(filename,'w');
fprintf(fid,'%s,%s,%s,%s,%s,%s,%s\n','DATETIME','PRIMET','CS2MET','CENMET','VANMET','H15MET','UPLMET');
[rows,cols] = size(Qstar_stations)
for i = 1:rows
fprintf(fid,'%s,%7.3f,%7.3f,%7.3f,%7.3f,%7.3f,%7.3f',datestr(timeseries1(i)),maxwattstable(i,1:cols-1));
fprintf(fid,'%7.3f\n',Qstar_stations(i,cols));
end
fclose(fid);
% assume cloudcover_h = 1, cloudcover_l = 1, and cloudcover_m = 1-- fully clouds
% now assume that this means the diffuse removal is then (0.3)*(0.6)*(0.6) = 0.1080
% NEW 09-06-2014, night time check - night most definitely exists after 10 pm and before 5 am
for i = 1:size(Qstar_stations,2);
Qstar_stations_n(:,i) = Qstar_stations(:,i).*nights_binary(:,i);
end
minwattstable = Qstar_stations_n.*0.1080;
filename = 'Miniumum_solar_downwelling_Wm2.csv';
fid = fopen(filename,'w');
fprintf(fid,'%s,%s,%s,%s,%s,%s,%s\n','DATETIME','PRIMET','CS2MET','CENMET','VANMET','H15MET','UPLMET');
[rows,cols] = size(Qstar_stations)
for i = 1:rows
fprintf(fid,'%s,%7.3f,%7.3f,%7.3f,%7.3f,%7.3f,%7.3f',datestr(timeseries1(i)), minwattstable(i,1:cols-1));
fprintf(fid,'%7.3f\n',minwattstable(i,cols));
end
fclose(fid);
end
function [rs,t,d,z,a,r] = suncycle(lat,lon,date,n)
% SUNCYCLE returns the Time of SunRise, SunSet, Solar Altitude and Radiation
%
% [SunRiseSet,Day,Dec,Alt,Azm,Rad] = SUNCYCLE( Lat , Lon , Date , N );
%
%----------------------------------------------------------------------------
% Inputs:
%
% Lat , Lon = geographical Position, scalars
%
% Date = [ YYYY MM DD ] or DateNum, multiple Rows allowed
% default: actual Date
%
% N = Resolution for Estimation, optional,
% default: 2880 (30 sec Intervall)
%
%----------------------------------------------------------------------------
% OutPut:
%
% SunRiseSet = [ SunRiseTime SunSetTime ], decimal hour, refers to GMT
% special Values: [ 0 24 ] Polar Day
% [ 24 0 ] Polar Night
%
% Day = Decimal Day since [ YYYY 01 01 ], N Columns
%
% Dec = Solar Declination, N Columns
% Alt = Solar Altitude, N Columns
% Azm = Solar Azimuth, N Columns
% Rad = Solar Radiation (no-sky), N Columns, [W/m²]
% perpendicular to Earth Surface, i.e. normalized with sin(Alt)
%
%----------------------------------------------------------------------------
%
% Solar Radiation outside Athmosphere - Solar Constant: SC = 1370 W/m^2
%
% Variation of Solar Radiation due to the ellipticity of the Earth's orbit:
%
% SC * ( 1 - 0.0335 sin( 2*pi * ( Day - 94 )/365 ) )
%
% Absorbtion Factor trough Athmosphere via Altitude:
%
% Fabs = (1/1.35) .^ ( 1./cos( 2*pi * (90-Alt)/360 ) )
%
%----------------------------------------------------------------------------
%
% see also: SUNZENIT, SUNCOVER
%
%----------------------------------------------------------------------------
% Code adapted from: AIR_SEA TOOLBOX (version 2.0: 8/9/99)
% Rich Pawlowicz
%
% It is put together from expressions taken from Appendix E in the
% 1978 edition of Almanac for Computers, Nautical Almanac Office, U.S.
% Naval Observatory. They are reduced accuracy expressions valid for the
% years 1800-2100. Solar declination computed from these expressions is
% accurate to at least 1'.
%
rs = [];
z = [];
r = [];
t = [];
d = [];
%********************************************************
Nin = nargin;
if Nin < 3
date = clock;
date = date(1:3);
elseif isempty(date)
return
elseif size(date,2) == 1
date = datevec(date);
elseif ~( size(date,2) >= 3 )
date = cat(2,date,ones(size(date,1),3));
end
if Nin < 4
int = 30; % Intervall [sec]
n = ceil( 24*3600 / int );
end
%********************************************************
lon = lon - 360 * floor( ( lon + 180 ) / 360 ); % [ -180 .. 180 )
t = datenum(date(:,1),date(:,2),date(:,3)) - datenum(date(:,1),01,01);
m = size(t,1);
dt = 1/n;
t = t(:,ones(1,n)) + dt * ones(m,1) * ( 0 : n-1 ) - lon/360;
[d,z,a,r] = soradna(lat,lon,t,date(:,1));
%********************************************************
% SunRise/SunSet
rs = NaN * ones(m,2);
zz = double( abs(r) <= 1e-10 );
sz = sum(zz,2);
ok = ( ( 0 < sz ) & ( sz < n ) );
if any(ok)
nn = sum(ok);
ok = find(ok);
rs(ok,1) = sum(cumprod(zz(ok,:),2),2);
rs(ok,2) = n - sum(cumprod(zz(ok,n:-1:1),2),2) + 1;
rs(ok,:) = min(max(rs(ok,:),1),n);
rs(ok,:) = t( ok(:,[1 1]) + ( rs(ok,:) - 1 ) * m );
rs(ok,:) = 24 * ( rs(ok,:) - floor(rs(ok,:)) );
rs(ok,:) = rs(ok,:) + 24 * dt/2 * ( ones(nn,1) * [ 1 -1 ] );
end
ok = ( sz == n ); % Night
if any(ok)
nn = sum(ok);
ok = find(ok);
rs(ok,:) = ( ones(nn,1) * [ 24 0 ] );
end
ok = ( sz == 0 ); % Day
if any(ok)
nn = sum(ok);
ok = find(ok);
rs(ok,:) = ( ones(nn,1) * [ 0 24 ] );
end
%**************************************************************************
%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
function [DEC,JD,AZM,RAD] = soradna(LAT,LON,JD,y);
% SORADNA computes no-sky solar radiation and solar altitude.
%
% [DEC,AZM,ALT,RAD] = SORADNA1(lat,lon,day,Year)
%
% computes instantaneous values of solar declination, radiation and altitude
% from Position, yearday and Year.
%
%%Assumes yd is either a column or row vector, the other input variables are
%%scalars, OR yd is a scalar, the other inputs matrices.
%
% It is put together from expressions taken from Appendix E in the
% 1978 edition of Almanac for Computers, Nautical Almanac Office, U.S.
% Naval Observatory. They are reduced accuracy expressions valid for the
% years 1800-2100. Solar declination computed from these expressions is
% accurate to at least 1'.
%
% The solar constant (1368.0 W/m^2) represents a mean of satellite measurements
% made over the last sunspot cycle (1979-1995) taken from
% Coffey et al (1995), Earth System Monitor, 6, 6-10.
%
SC = 1368.0; % Solar Constant
d2r = pi/180; % deg --> rad
[m,n] = size(JD);
y = datenum(y,01,01);
JD = JD + y(:,ones(1,n));
JD = datevec(JD(:));
% compute Universal Time in hours
UT = JD(:,4) + JD(:,5) / 60 + JD(:,6) / 3600;
% compute Julian ephemeris date in days (Day 1 is 1 Jan 4713 B.C.=-4712 Jan 1)
JD = 367 * JD(:,1) - fix( 7 * ( JD(:,1) + fix( (JD(:,2)+9) / 12 ) ) / 4 ) + ...
fix( 275 * JD(:,2) / 9 ) + JD(:,3) + 1721013 + UT/24;
% compute interval in Julian centuries since 1900
JD = ( JD - 2415020 ) / 36525;
% compute mean anomaly of the sun
G = 358.475833 + 35999.049750 * JD - 0.000150 * JD.^2;
% compute mean longitude of sun
L = 279.696678 + 36000.768920 * JD + 0.000303 * JD.^2;
% compute mean anomaly of Jupiter: 225.444651 + 2880 * JD + 154.906654 * JD;
JP = 225.444651 + 3034.906654 * JD;
% compute mean anomaly of Venus
VN = 212.603219 + 58517.803875 * JD + 0.001286 * JD.^2;
% compute longitude of the ascending node of the moon's orbit
NM = 259.183275 - 1934.142008 * JD + 0.002078 * JD.^2;
G = ( G - 360 * fix( G / 360 ) ) * d2r;
L = ( L - 360 * fix( L / 360 ) ) * d2r;
JP = ( JP - 360 * fix( JP / 360 ) ) * d2r;
VN = ( VN - 360 * fix( VN / 360 ) ) * d2r;
NM = ( NM - 360 * fix( NM / 360 ) + 360 ) * d2r;
% compute sun theta (THETA)
DEC = +0.397930 * sin(L) - 0.000040 * cos(L) ...
+0.009999 * sin(G-L) + 0.003334 * sin(G+L) ...
+0.000042 * sin(2*G+L) - 0.000014 * sin(2*G-L) ...
-0.000030 * JD.*sin(G-L) - 0.000010 * JD.*sin(G+L) ...
-0.000208 * JD.*sin(L) - 0.000039 * sin(NM-L) ...
-0.000010 * cos(G-L-JP);
% compute sun rho
RHO = 1.000421 - 0.033503 * cos(G) - 0.000140 * cos(2*G) + ...
0.000084 * JD.*cos(G) - 0.000033 * sin(G-JP) + 0.000027 * sin(2*G-2*VN);
%%% RHO = 1 - 0.0335*sin( 2 * pi * (DayOfYear - 94)/365 )
% compute declination: DEC = asin( THETA ./ sqrt(RHO) );
DEC = DEC ./ sqrt(RHO);
% compute equation of time (in seconds of time)
JD = 276.697 + (0.98564734*36525) * JD; % [deg]
JD = ( JD - 360 * fix( JD / 360 ) ) * d2r;
JD = -97.8 * sin( JD) - 431.3 * cos( JD) ...
+596.6 * sin(2*JD) - 1.9 * cos(2*JD) ...
+4.0 * sin(3*JD) + 19.3 * cos(3*JD) - 12.7 * sin(4*JD);
JD = JD / 3600;
% compute local hour angle (LHA)
JD = JD + UT - 12;
JD = 15 * JD + LON;
JD = JD * d2r;
LAT = LAT * d2r;
AZM = JD; % Here: Local Hour Angle LHA
% compute radius vector: RV = sqrt(RHO);
% compute solar altitude: sin(ALT) = sin(LAT)*sin(DEC) + ...
% cos(LAT)*cos(DEC)*cos(LHA)
JD = sin(LAT) * DEC + cos(LAT) * sqrt(1-DEC.^2) .* cos(JD);
% compute solar radiation outside atmosphere
RAD = ( SC ./ RHO ) .* JD .* ( JD > 0 ); % here: JD == sin(ALT)
DEC = asin(DEC);
JD = asin(JD);
int = mod( floor( AZM / pi ) , 2 ); % 0: [ 0 .. 180 ); 1: [ 180 .. 360 )
AZM = atan( sin(AZM) ./ ( sin(LAT) .* cos(AZM) - cos(LAT) .* tan(DEC) ) );
DEC = DEC / d2r;
AZM = AZM / d2r;
JD = JD / d2r;
AZM = AZM + 180 * (1-int) + 180 * ( AZM < 0 ); % Correction to [ 0 .. 360 ]
DEC = reshape(DEC,m,n);
AZM = reshape(AZM,m,n);
JD = reshape( JD,m,n);
RAD = reshape(RAD,m,n);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment