Rsunlib
Rsunlib
r.sun: rsunlib.c. This program was written by Jaro Hofierka in Summer 1993
and re-engineered in 1996-1999. In cooperation with Marcel Suri and
Thomas Huld from JRC in Ispra a new version of r.sun was prepared using
ESRA solar radiation formulas. See the manual page for details.
(C) 2002 Copyright Jaro Hofierka, Gresaka 22, 085 01 Bardejov, Slovakia,
and GeoModel, s.r.o., Bratislava, Slovakia
email: hofierka at geomodel.sk, marcel.suri at jrc.it, suri at geomodel.sk
#include <stdio.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/glocale.h>
#include "sunradstruct.h"
#include "local_proto.h"
#include "rsunglobals.h"
int civilTimeFlag;
int useCivilTime(void)
{
return civilTimeFlag;
}
double angular_loss_denom;
void setAngularLossDenominator(void)
{
angular_loss_denom = 1. / (1 - exp(-1. / a_r));
}
int useShadowFlag;
int useShadow(void)
{
return useShadowFlag;
}
void setUseShadow(int val)
{
useShadowFlag = val;
}
int useHorizonDataFlag;
int useHorizonData(void)
{
return useHorizonDataFlag;
}
double timeOffset;
double getTimeOffset(void)
{
return timeOffset;
}
double horizonInterval;
double getHorizonInterval(void)
{
return horizonInterval;
}
/* com_sol_const(): compute the Solar Constant corrected for the day of the
year. The Earth is closest to the Sun (Perigee) on about January 3rd,
it is furthest from the sun (Apogee) about July 6th. The 1367 W/m^2 solar
constant is at the average 1AU distance, but on Jan 3 it gets up to
around 1412.71 W/m^2 and on July 6 it gets down to around 1321 W/m^2.
This value is for what hits the top of the atmosphere before any energy
is attenuated. */
double com_sol_const(int no_of_day)
{
double I0, d1;
/* v W/(m*m) */
d1 = pi2 * no_of_day / 365.25;
I0 = solar_constant * (1 + 0.03344 * cos(d1 - 0.048869));
return I0;
}
if (useCivilTime()) {
sungeom->timeAngle -= totOffsetTime * HOURANGLE;
}
pom = -sungeom->lum_C33 / sungeom->lum_C31;
if (fabs(pom) <= 1) {
pom = acos(pom);
pom = (pom * 180) / M_PI;
sungeom->sunrise_time = (90 - pom) / 15 + 6;
sungeom->sunset_time = (pom - 90) / 15 + 18;
}
else {
if (pom < 0) {
/* G_debug(3,"\n Sun is ABOVE the surface during the whole
* day"); */
sungeom->sunrise_time = 0;
sungeom->sunset_time = 24;
}
else {
/* G_debug(3,"\n The sun is BELOW the surface during the whole
* day"); */
if (fabs(pom) - 1 <= EPS) {
sungeom->sunrise_time = 12;
sungeom->sunset_time = 12;
}
}
}
}
}
costimeAngle = cos(sungeom->timeAngle);
lum_Lx = -sungeom->lum_C22 * sin(sungeom->timeAngle);
lum_Ly = sungeom->lum_C11 * costimeAngle + sungeom->lum_C13;
sunVarGeom->sinSolarAltitude =
sungeom->lum_C31 * costimeAngle + sungeom->lum_C33;
sunVarGeom->solarAltitude =
asin(sunVarGeom->sinSolarAltitude); /* vertical angle of the sun */
/* sinSolarAltitude is sin(solarAltitude) */
/*
sunVarGeom->stepsinangle = gridGeom->stepxy *
sin(sunVarGeom->sunAzimuthAngle); sunVarGeom->stepcosangle =
gridGeom->stepxy * cos(sunVarGeom->sunAzimuthAngle);
*/
sunVarGeom->stepsinangle = gridGeom->stepxy * delt_lat_m / delt_dist;
sunVarGeom->stepcosangle = gridGeom->stepxy * delt_lon_m / delt_dist;
sunVarGeom->tanSolarAltitude = tan(sunVarGeom->solarAltitude);
return;
}
if (sunVarGeom->zp == UNDEFZ)
return 0;
gridGeom->yy0 += sunVarGeom->stepsinangle;
gridGeom->xx0 += sunVarGeom->stepcosangle;
if (((gridGeom->xx0 + (0.5 * gridGeom->stepx)) < 0) ||
((gridGeom->xx0 + (0.5 * gridGeom->stepx)) > gridGeom->deltx) ||
((gridGeom->yy0 + (0.5 * gridGeom->stepy)) < 0) ||
((gridGeom->yy0 + (0.5 * gridGeom->stepy)) > gridGeom->delty))
succes = 3;
else
succes = 1;
if (succes == 1) {
where_is_point(length, sunVarGeom, gridGeom);
/*if (func == NULL) {
gridGeom->xx0 = gridGeom->xg0;
gridGeom->yy0 = gridGeom->yg0;
return (3);
} */
curvature_diff = EARTHRADIUS * (1. - cos(*length / EARTHRADIUS));
z2 = sunVarGeom->z_orig + curvature_diff +
*length * sunVarGeom->tanSolarAltitude;
if (z2 < sunVarGeom->zp)
succes = 2; /* shadow */
if (z2 > sunVarGeom->zmax)
succes = 3; /* no test needed all visible */
}
if (succes != 1) {
gridGeom->xx0 = gridGeom->xg0;
gridGeom->yy0 = gridGeom->yg0;
}
return (succes);
}
/* func = cube; */
sunVarGeom->isShadow = 0;
double timeOffset = 0;
if (sunSlopeGeom->shift12hrs)
timeOffset = M_PI;
if (useShadow()) {
length = 0;
if (useHorizonData()) {
/* Start is due east, sungeom->timeangle = -pi/2 */
/* timeoffset = sungeom->timeAngle+pihalf; */
timeoffset = sunVarGeom->sunAzimuthAngle;
/*
if(timeoffset<0.)
timeoffset+=pi2;
else if(timeoffset>pi2)
timeoffset-=pi2;
horizPos = arrayNumInt - timeoffset/horizonInterval;
*/
lowPos = (int)horizPos;
highPos = lowPos + 1;
if (highPos == arrayNumInt) {
highPos = 0;
}
horizonHeight =
invScale *
((1. - (horizPos - lowPos)) * horizonpointer[lowPos] +
(horizPos - lowPos) * horizonpointer[highPos]);
sunVarGeom->isShadow = (horizonHeight > sunVarGeom->solarAltitude);
if (!sunVarGeom->isShadow) {
/* if (z_orig != UNDEFZ) {
s = sunSlopeGeom->lum_C31_l
* cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
+ sunSlopeGeom->lum_C33_l;
} else {
s = sunVarGeom->sinSolarAltitude;
}
*/
s = sunSlopeGeom->lum_C31_l *
cos(-sungeom->timeAngle - sunSlopeGeom->longit_l +
timeOffset) +
sunSlopeGeom->lum_C33_l; /* Jenco */
}
} /* End if useHorizonData() */
else {
while ((r = searching(&length, sunVarGeom, gridGeom)) == 1) {
if (r == 3)
break; /* no test is needed */
}
if (r == 2) {
sunVarGeom->isShadow = 1; /* shadow */
}
else {
/* if (z_orig != UNDEFZ) {
s = sunSlopeGeom->lum_C31_l
* cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
+ sunSlopeGeom->lum_C33_l;
} else {
s = sunVarGeom->sinSolarAltitude;
}
*/
s = sunSlopeGeom->lum_C31_l *
cos(-sungeom->timeAngle - sunSlopeGeom->longit_l +
timeOffset) +
sunSlopeGeom->lum_C33_l; /* Jenco */
}
}
}
else {
/* if (z_orig != UNDEFZ) {
s = sunSlopeGeom->lum_C31_l
* cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
+ sunSlopeGeom->lum_C33_l;
} else {
s = sunVarGeom->sinSolarAltitude;
}
*/
s = sunSlopeGeom->lum_C31_l *
cos(-sungeom->timeAngle - sunSlopeGeom->longit_l + timeOffset) +
sunSlopeGeom->lum_C33_l; /* Jenco */
}
return (s);
}
double locSolarAltitude;
locSolarAltitude = sunVarGeom->solarAltitude;
return (br);
}
double locSolarAltitude;
locSolarAltitude = sunVarGeom->solarAltitude;
return (br);
}
double drad(double sh, double bh, double *rr,
struct SunGeometryVarDay *sunVarGeom,
struct SunGeometryVarSlope *sunSlopeGeom,
struct SolarRadVar *sunRadVar)
{
double tn, fd, fx = 0., A1, A2, A3, A1b;
double r_sky, kb, dr, gh, a_ln, ln, fg;
double dh;
double cosslope, sinslope;
double locSinSolarAltitude;
double locLinke;
locLinke = sunRadVar->linke;
locSinSolarAltitude = sunVarGeom->sinSolarAltitude;
cosslope = cos(sunSlopeGeom->slope);
sinslope = sin(sunSlopeGeom->slope);
fd = A1 + A2 * locSinSolarAltitude +
A3 * locSinSolarAltitude * locSinSolarAltitude;
dh = sunRadVar->cdh * sunRadVar->G_norm_extra * fd * tn;
gh = bh + dh;
if (sunSlopeGeom->aspect != UNDEF && sunSlopeGeom->slope != 0.) {
kb = bh / (sunRadVar->G_norm_extra * locSinSolarAltitude);
r_sky = (1. + cosslope) / 2.;
a_ln = sunVarGeom->solarAzimuth - sunSlopeGeom->aspect;
ln = a_ln;
if (a_ln > M_PI)
ln = a_ln - pi2;
else if (a_ln < -M_PI)
ln = a_ln + pi2;
a_ln = ln;
fg = sinslope - sunSlopeGeom->slope * cosslope -
M_PI * sin(0.5 * sunSlopeGeom->slope) *
sin(0.5 * sunSlopeGeom->slope);
if ((sunVarGeom->isShadow == 1) || sh <= 0.)
fx = r_sky + fg * 0.252271;
else if (sunVarGeom->solarAltitude >= 0.1) {
fx = ((0.00263 - kb * (0.712 + 0.6883 * kb)) * fg + r_sky) *
(1. - kb) +
kb * sh / locSinSolarAltitude;
}
else if (sunVarGeom->solarAltitude < 0.1)
fx = ((0.00263 - 0.712 * kb - 0.6883 * kb * kb) * fg + r_sky) *
(1. - kb) +
kb * sinslope * cos(a_ln) /
(0.1 - 0.008 * sunVarGeom->solarAltitude);
dr = dh * fx;
/* refl. rad */
*rr = sunRadVar->alb * gh * (1 - cosslope) / 2.;
}
else { /* plane */
dr = dh;
*rr = 0.;
}
return (dr);
}
#define c2 -0.074
double diff_coeff_angleloss;
double refl_coeff_angleloss;
double c1;
double diff_loss_factor, refl_loss_factor;
double locSinSolarAltitude;
double locLinke;
locSinSolarAltitude = sunVarGeom->sinSolarAltitude;
locLinke = sunRadVar->linke;
cosslope = cos(sunSlopeGeom->slope);
sinslope = sin(sunSlopeGeom->slope);
fd = A1 + A2 * locSinSolarAltitude +
A3 * locSinSolarAltitude * locSinSolarAltitude;
dh = sunRadVar->cdh * sunRadVar->G_norm_extra * fd * tn;
gh = bh + dh;
kb = bh / (sunRadVar->G_norm_extra * locSinSolarAltitude);
r_sky = (1. + cosslope) / 2.;
a_ln = sunVarGeom->solarAzimuth - sunSlopeGeom->aspect;
ln = a_ln;
dr = dh * fx;
/* refl. rad */
*rr = sunRadVar->alb * gh * (1 - cosslope) / 2.;
}
else { /* plane */
dr = dh;
*rr = 0.;
}
c1 = 4. / (3. * M_PI);
diff_coeff_angleloss =
sinslope + (M_PI - sunSlopeGeom->slope - sinslope) / (1 + cosslope);
refl_coeff_angleloss =
sinslope + (sunSlopeGeom->slope - sinslope) / (1 - cosslope);
diff_loss_factor =
1. - exp(-(c1 * diff_coeff_angleloss +
c2 * diff_coeff_angleloss * diff_coeff_angleloss) /
a_r);
refl_loss_factor =
1. - exp(-(c1 * refl_coeff_angleloss +
c2 * refl_coeff_angleloss * refl_coeff_angleloss) /
a_r);
dr *= diff_loss_factor;
*rr *= refl_loss_factor;
return (dr);
}