rsunlib.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. /****************************************************************************
  2. r.sun: rsunlib.c. This program was writen by Jaro Hofierka in Summer 1993
  3. and re-engineered in 1996-1999. In cooperation with Marcel Suri and
  4. Thomas Huld from JRC in Ispra a new version of r.sun was prepared using
  5. ESRA solar radiation formulas. See the manual page for details.
  6. (C) 2002 Copyright Jaro Hofierka, Gresaka 22, 085 01 Bardejov, Slovakia,
  7. and GeoModel, s.r.o., Bratislava, Slovakia
  8. email: hofierka at geomodel.sk, marcel.suri at jrc.it, suri at geomodel.sk
  9. (C) 2011 by Hamish Bowman, and the GRASS Development Team
  10. ****************************************************************************/
  11. /*
  12. * This program is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU General Public License
  14. * as published by the Free Software Foundation; either version 2
  15. * of the License, or (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program; if not, write to the
  24. * Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  26. */
  27. /*v. 2.0 July 2002, NULL data handling, JH */
  28. /*v. 2.1 January 2003, code optimization by Thomas Huld, JH */
  29. #include <stdio.h>
  30. #include <math.h>
  31. #include <grass/gis.h>
  32. #include <grass/glocale.h>
  33. #include "sunradstruct.h"
  34. #include "local_proto.h"
  35. #include "rsunglobals.h"
  36. int civilTimeFlag;
  37. int useCivilTime()
  38. {
  39. return civilTimeFlag;
  40. }
  41. void setUseCivilTime(int val)
  42. {
  43. civilTimeFlag = val;
  44. }
  45. double angular_loss_denom;
  46. void setAngularLossDenominator()
  47. {
  48. angular_loss_denom = 1. / (1 - exp(-1. / a_r));
  49. }
  50. int useShadowFlag;
  51. int useShadow()
  52. {
  53. return useShadowFlag;
  54. }
  55. void setUseShadow(int val)
  56. {
  57. useShadowFlag = val;
  58. }
  59. int useHorizonDataFlag;
  60. int useHorizonData()
  61. {
  62. return useHorizonDataFlag;
  63. }
  64. void setUseHorizonData(int val)
  65. {
  66. useHorizonDataFlag = val;
  67. }
  68. double timeOffset;
  69. double getTimeOffset()
  70. {
  71. return timeOffset;
  72. }
  73. void setTimeOffset(double val)
  74. {
  75. timeOffset = val;
  76. }
  77. double horizonInterval;
  78. double getHorizonInterval()
  79. {
  80. return horizonInterval;
  81. }
  82. void setHorizonInterval(double val)
  83. {
  84. horizonInterval = val;
  85. }
  86. /* com_sol_const(): compute the Solar Constant corrected for the day of the
  87. year. The Earth is closest to the Sun (Perigee) on about January 3rd,
  88. it is furthest from the sun (Apogee) about July 6th. The 1367 W/m^2 solar
  89. constant is at the average 1AU distance, but on Jan 3 it gets up to
  90. around 1412.71 W/m^2 and on July 6 it gets down to around 1321 W/m^2.
  91. This value is for what hits the top of the atmosphere before any energy
  92. is attenuated. */
  93. double com_sol_const(int no_of_day)
  94. {
  95. double I0, d1;
  96. /* Solar constant: 1367.0 W/m^2.
  97. Perigee offset: here we call Jan 2 at 8:18pm the Perigee, so day
  98. number 2.8408. In angular units that's (2*pi * 2.8408 / 365.25) = 0.048869.
  99. Orbital eccentricity: For Earth this is currently about 0.01672,
  100. and so the distance to the sun varies by +/- 0.01672 from the
  101. mean distance (1AU), so over the year the amplitude of the
  102. function is 2*ecc = 0.03344.
  103. And 365.25 is of course the number of days in a year.
  104. */
  105. /* v W/(m*m) */
  106. d1 = pi2 * no_of_day / 365.25;
  107. I0 = 1367. * (1 + 0.03344 * cos(d1 - 0.048869));
  108. return I0;
  109. }
  110. void com_par_const(double longitTime, struct SunGeometryConstDay *sungeom,
  111. struct GridGeometry *gridGeom)
  112. {
  113. double pom;
  114. double totOffsetTime;
  115. sungeom->lum_C11 = gridGeom->sinlat * sungeom->cosdecl;
  116. sungeom->lum_C13 = -gridGeom->coslat * sungeom->sindecl;
  117. sungeom->lum_C22 = sungeom->cosdecl;
  118. sungeom->lum_C31 = gridGeom->coslat * sungeom->cosdecl;
  119. sungeom->lum_C33 = gridGeom->sinlat * sungeom->sindecl;
  120. if (fabs(sungeom->lum_C31) >= EPS) {
  121. totOffsetTime = timeOffset + longitTime;
  122. if (useCivilTime()) {
  123. sungeom->timeAngle -= totOffsetTime * HOURANGLE;
  124. }
  125. pom = -sungeom->lum_C33 / sungeom->lum_C31;
  126. if (fabs(pom) <= 1) {
  127. pom = acos(pom);
  128. pom = (pom * 180) / M_PI;
  129. sungeom->sunrise_time = (90 - pom) / 15 + 6;
  130. sungeom->sunset_time = (pom - 90) / 15 + 18;
  131. }
  132. else {
  133. if (pom < 0) {
  134. /* G_debug(3,"\n Sun is ABOVE the surface during the whole day"); */
  135. sungeom->sunrise_time = 0;
  136. sungeom->sunset_time = 24;
  137. }
  138. else {
  139. /* G_debug(3,"\n The sun is BELOW the surface during the whole day"); */
  140. if (fabs(pom) - 1 <= EPS) {
  141. sungeom->sunrise_time = 12;
  142. sungeom->sunset_time = 12;
  143. }
  144. }
  145. }
  146. }
  147. }
  148. void com_par(struct SunGeometryConstDay *sungeom,
  149. struct SunGeometryVarDay *sunVarGeom,
  150. struct GridGeometry *gridGeom, double latitude, double longitude)
  151. {
  152. double pom, xpom, ypom;
  153. double costimeAngle;
  154. double lum_Lx, lum_Ly;
  155. double inputAngle;
  156. double delt_lat, delt_lon;
  157. double delt_lat_m, delt_lon_m;
  158. double delt_dist;
  159. costimeAngle = cos(sungeom->timeAngle);
  160. lum_Lx = -sungeom->lum_C22 * sin(sungeom->timeAngle);
  161. lum_Ly = sungeom->lum_C11 * costimeAngle + sungeom->lum_C13;
  162. sunVarGeom->sinSolarAltitude =
  163. sungeom->lum_C31 * costimeAngle + sungeom->lum_C33;
  164. if (fabs(sungeom->lum_C31) < EPS) {
  165. if (fabs(sunVarGeom->sinSolarAltitude) >= EPS) {
  166. if (sunVarGeom->sinSolarAltitude > 0) {
  167. /* G_debug(3,"\tSun is ABOVE area during the whole day"); */
  168. sungeom->sunrise_time = 0;
  169. sungeom->sunset_time = 24;
  170. }
  171. else {
  172. sunVarGeom->solarAltitude = 0.;
  173. sunVarGeom->solarAzimuth = UNDEF;
  174. return;
  175. }
  176. }
  177. else {
  178. /* G_debug(3,"\tThe Sun is ON HORIZON during the whole day"); */
  179. sungeom->sunrise_time = 0;
  180. sungeom->sunset_time = 24;
  181. }
  182. }
  183. sunVarGeom->solarAltitude = asin(sunVarGeom->sinSolarAltitude); /* vertical angle of the sun */
  184. /* sinSolarAltitude is sin(solarAltitude) */
  185. xpom = lum_Lx * lum_Lx;
  186. ypom = lum_Ly * lum_Ly;
  187. pom = sqrt(xpom + ypom);
  188. if (fabs(pom) > EPS) {
  189. sunVarGeom->solarAzimuth = lum_Ly / pom;
  190. sunVarGeom->solarAzimuth = acos(sunVarGeom->solarAzimuth); /* horiz. angle of the Sun */
  191. /* solarAzimuth *= RAD; */
  192. if (lum_Lx < 0)
  193. sunVarGeom->solarAzimuth = pi2 - sunVarGeom->solarAzimuth;
  194. }
  195. else {
  196. sunVarGeom->solarAzimuth = UNDEF;
  197. }
  198. if (sunVarGeom->solarAzimuth < 0.5 * M_PI)
  199. sunVarGeom->sunAzimuthAngle = 0.5 * M_PI - sunVarGeom->solarAzimuth;
  200. else
  201. sunVarGeom->sunAzimuthAngle = 2.5 * M_PI - sunVarGeom->solarAzimuth;
  202. inputAngle = sunVarGeom->sunAzimuthAngle + pihalf;
  203. inputAngle = (inputAngle >= pi2) ? inputAngle - pi2 : inputAngle;
  204. /* 1852m * 60 * 0.0001rad * 180/pi= 636.67m */
  205. delt_lat = -0.0001 * cos(inputAngle); /* Arbitrary small distance in latitude */
  206. delt_lon = 0.0001 * sin(inputAngle) / cos(latitude);
  207. delt_lat_m = delt_lat * (180/M_PI) * 1852*60;
  208. delt_lon_m = delt_lon * (180/M_PI) * 1852*60 * cos(latitude);
  209. delt_dist = sqrt(delt_lat_m * delt_lat_m + delt_lon_m * delt_lon_m);
  210. /*
  211. sunVarGeom->stepsinangle = gridGeom->stepxy * sin(sunVarGeom->sunAzimuthAngle);
  212. sunVarGeom->stepcosangle = gridGeom->stepxy * cos(sunVarGeom->sunAzimuthAngle);
  213. */
  214. sunVarGeom->stepsinangle = gridGeom->stepxy * delt_lat_m / delt_dist;
  215. sunVarGeom->stepcosangle = gridGeom->stepxy * delt_lon_m / delt_dist;
  216. sunVarGeom->tanSolarAltitude = tan(sunVarGeom->solarAltitude);
  217. return;
  218. }
  219. int searching(double *length, struct SunGeometryVarDay *sunVarGeom,
  220. struct GridGeometry *gridGeom)
  221. {
  222. double z2;
  223. double curvature_diff;
  224. int succes = 0;
  225. if (sunVarGeom->zp == UNDEFZ)
  226. return 0;
  227. gridGeom->yy0 += sunVarGeom->stepsinangle;
  228. gridGeom->xx0 += sunVarGeom->stepcosangle;
  229. if (((gridGeom->xx0 + (0.5 * gridGeom->stepx)) < 0)
  230. || ((gridGeom->xx0 + (0.5 * gridGeom->stepx)) > gridGeom->deltx)
  231. || ((gridGeom->yy0 + (0.5 * gridGeom->stepy)) < 0)
  232. || ((gridGeom->yy0 + (0.5 * gridGeom->stepy)) > gridGeom->delty))
  233. succes = 3;
  234. else
  235. succes = 1;
  236. if (succes == 1) {
  237. where_is_point(length, sunVarGeom, gridGeom);
  238. if (func == NULL) {
  239. gridGeom->xx0 = gridGeom->xg0;
  240. gridGeom->yy0 = gridGeom->yg0;
  241. return (3);
  242. }
  243. curvature_diff = EARTHRADIUS * (1. - cos(*length / EARTHRADIUS));
  244. z2 = sunVarGeom->z_orig + curvature_diff +
  245. *length * sunVarGeom->tanSolarAltitude;
  246. if (z2 < sunVarGeom->zp)
  247. succes = 2; /* shadow */
  248. if (z2 > sunVarGeom->zmax)
  249. succes = 3; /* no test needed all visible */
  250. }
  251. if (succes != 1) {
  252. gridGeom->xx0 = gridGeom->xg0;
  253. gridGeom->yy0 = gridGeom->yg0;
  254. }
  255. return (succes);
  256. }
  257. double lumcline2(struct SunGeometryConstDay *sungeom,
  258. struct SunGeometryVarDay *sunVarGeom,
  259. struct SunGeometryVarSlope *sunSlopeGeom,
  260. struct GridGeometry *gridGeom, unsigned char *horizonpointer)
  261. {
  262. double s = 0;
  263. double length;
  264. int r = 0;
  265. int lowPos, highPos;
  266. double timeoffset, horizPos;
  267. double horizonHeight;
  268. func = cube;
  269. sunVarGeom->isShadow = 0;
  270. if (useShadow()) {
  271. length = 0;
  272. if (useHorizonData()) {
  273. /* Start is due east, sungeom->timeangle = -pi/2 */
  274. /* timeoffset = sungeom->timeAngle+pihalf; */
  275. timeoffset = sunVarGeom->sunAzimuthAngle;
  276. /*
  277. if(timeoffset<0.)
  278. timeoffset+=pi2;
  279. else if(timeoffset>pi2)
  280. timeoffset-=pi2;
  281. horizPos = arrayNumInt - timeoffset/horizonInterval;
  282. */
  283. horizPos = timeoffset / getHorizonInterval();
  284. lowPos = (int)horizPos;
  285. highPos = lowPos + 1;
  286. if (highPos == arrayNumInt) {
  287. highPos = 0;
  288. }
  289. horizonHeight = invScale * ((1. -
  290. (horizPos -
  291. lowPos)) * horizonpointer[lowPos]
  292. + (horizPos - lowPos)
  293. * horizonpointer[highPos]);
  294. sunVarGeom->isShadow =
  295. (horizonHeight > sunVarGeom->solarAltitude);
  296. if (!sunVarGeom->isShadow) {
  297. /* if (z_orig != UNDEFZ) {
  298. s = sunSlopeGeom->lum_C31_l
  299. * cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
  300. + sunSlopeGeom->lum_C33_l;
  301. } else {
  302. s = sunVarGeom->sinSolarAltitude;
  303. }
  304. */
  305. s = sunSlopeGeom->lum_C31_l
  306. * cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
  307. + sunSlopeGeom->lum_C33_l; /* Jenco */
  308. }
  309. } /* End if useHorizonData() */
  310. else {
  311. while ((r = searching(&length, sunVarGeom, gridGeom)) == 1) {
  312. if (r == 3)
  313. break; /* no test is needed */
  314. }
  315. if (r == 2) {
  316. sunVarGeom->isShadow = 1; /* shadow */
  317. }
  318. else {
  319. /* if (z_orig != UNDEFZ) {
  320. s = sunSlopeGeom->lum_C31_l
  321. * cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
  322. + sunSlopeGeom->lum_C33_l;
  323. } else {
  324. s = sunVarGeom->sinSolarAltitude;
  325. }
  326. */
  327. s = sunSlopeGeom->lum_C31_l
  328. * cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
  329. + sunSlopeGeom->lum_C33_l; /* Jenco */
  330. }
  331. }
  332. }
  333. else {
  334. /* if (z_orig != UNDEFZ) {
  335. s = sunSlopeGeom->lum_C31_l
  336. * cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
  337. + sunSlopeGeom->lum_C33_l;
  338. } else {
  339. s = sunVarGeom->sinSolarAltitude;
  340. }
  341. */
  342. s = sunSlopeGeom->lum_C31_l
  343. * cos(-sungeom->timeAngle - sunSlopeGeom->longit_l)
  344. + sunSlopeGeom->lum_C33_l; /* Jenco */
  345. }
  346. /* if (s <= 0) return UNDEFZ; ?? */
  347. if (s < 0)
  348. return 0.;
  349. return (s);
  350. }
  351. double brad(double sh, double *bh, struct SunGeometryVarDay *sunVarGeom,
  352. struct SunGeometryVarSlope *sunSlopeGeom,
  353. struct SolarRadVar *sunRadVar)
  354. {
  355. double opticalAirMass, airMass2Linke, rayl, br;
  356. double drefract, temp1, temp2, h0refract;
  357. double elevationCorr;
  358. double locSolarAltitude;
  359. locSolarAltitude = sunVarGeom->solarAltitude;
  360. /* FIXME: please document all coefficients */
  361. elevationCorr = exp(-sunVarGeom->z_orig / 8434.5);
  362. temp1 = 0.1594 + locSolarAltitude * (1.123 + 0.065656 * locSolarAltitude);
  363. temp2 = 1. + locSolarAltitude * (28.9344 + 277.3971 * locSolarAltitude);
  364. drefract = 0.061359 * temp1 / temp2; /* in radians */
  365. h0refract = locSolarAltitude + drefract;
  366. opticalAirMass = elevationCorr / (sin(h0refract) +
  367. 0.50572 * pow(h0refract * rad2deg +
  368. 6.07995, -1.6364));
  369. airMass2Linke = 0.8662 * sunRadVar->linke;
  370. if (opticalAirMass <= 20.) {
  371. rayl = 1. / (6.6296 +
  372. opticalAirMass * (1.7513 +
  373. opticalAirMass * (-0.1202 +
  374. opticalAirMass *
  375. (0.0065 -
  376. opticalAirMass *
  377. 0.00013))));
  378. }
  379. else {
  380. rayl = 1. / (10.4 + 0.718 * opticalAirMass);
  381. }
  382. *bh =
  383. sunRadVar->cbh * sunRadVar->G_norm_extra *
  384. sunVarGeom->sinSolarAltitude * exp(-rayl * opticalAirMass *
  385. airMass2Linke);
  386. if (sunSlopeGeom->aspect != UNDEF && sunSlopeGeom->slope != 0.)
  387. br = *bh * sh / sunVarGeom->sinSolarAltitude;
  388. else
  389. br = *bh;
  390. return (br);
  391. }
  392. double brad_angle_loss(double sh, double *bh,
  393. struct SunGeometryVarDay *sunVarGeom,
  394. struct SunGeometryVarSlope *sunSlopeGeom,
  395. struct SolarRadVar *sunRadVar)
  396. {
  397. double p, opticalAirMass, airMass2Linke, rayl, br;
  398. double drefract, temp1, temp2, h0refract;
  399. double locSolarAltitude;
  400. locSolarAltitude = sunVarGeom->solarAltitude;
  401. /* FIXME: please document all coefficients */
  402. p = exp(-sunVarGeom->z_orig / 8434.5);
  403. temp1 = 0.1594 + locSolarAltitude * (1.123 + 0.065656 * locSolarAltitude);
  404. temp2 = 1. + locSolarAltitude * (28.9344 + 277.3971 * locSolarAltitude);
  405. drefract = 0.061359 * temp1 / temp2; /* in radians */
  406. h0refract = locSolarAltitude + drefract;
  407. opticalAirMass = p / (sin(h0refract) +
  408. 0.50572 * pow(h0refract * rad2deg + 6.07995,
  409. -1.6364));
  410. airMass2Linke = 0.8662 * sunRadVar->linke;
  411. if (opticalAirMass <= 20.)
  412. rayl =
  413. 1. / (6.6296 +
  414. opticalAirMass *
  415. (1.7513 + opticalAirMass *
  416. (-0.1202 + opticalAirMass *
  417. (0.0065 - opticalAirMass * 0.00013))));
  418. else
  419. rayl = 1. / (10.4 + 0.718 * opticalAirMass);
  420. *bh =
  421. sunRadVar->cbh * sunRadVar->G_norm_extra *
  422. sunVarGeom->sinSolarAltitude * exp(-rayl * opticalAirMass *
  423. airMass2Linke);
  424. if (sunSlopeGeom->aspect != UNDEF && sunSlopeGeom->slope != 0.)
  425. br = *bh * sh / sunVarGeom->sinSolarAltitude;
  426. else
  427. br = *bh;
  428. br *= (1 - exp(-sh / a_r)) * angular_loss_denom;
  429. return (br);
  430. }
  431. double drad(double sh, double bh, double *rr,
  432. struct SunGeometryVarDay *sunVarGeom,
  433. struct SunGeometryVarSlope *sunSlopeGeom,
  434. struct SolarRadVar *sunRadVar)
  435. {
  436. double tn, fd, fx = 0., A1, A2, A3, A1b;
  437. double r_sky, kb, dr, gh, a_ln, ln, fg;
  438. double dh;
  439. double cosslope, sinslope;
  440. double locSinSolarAltitude;
  441. double locLinke;
  442. locLinke = sunRadVar->linke;
  443. locSinSolarAltitude = sunVarGeom->sinSolarAltitude;
  444. cosslope = cos(sunSlopeGeom->slope);
  445. sinslope = sin(sunSlopeGeom->slope);
  446. /* FIXME: please document all coefficients */
  447. tn = -0.015843 + locLinke * (0.030543 + 0.0003797 * locLinke);
  448. A1b = 0.26463 + locLinke * (-0.061581 + 0.0031408 * locLinke);
  449. if (A1b * tn < 0.0022)
  450. A1 = 0.0022 / tn;
  451. else
  452. A1 = A1b;
  453. A2 = 2.04020 + locLinke * (0.018945 - 0.011161 * locLinke);
  454. A3 = -1.3025 + locLinke * (0.039231 + 0.0085079 * locLinke);
  455. fd = A1 + A2 * locSinSolarAltitude +
  456. A3 * locSinSolarAltitude * locSinSolarAltitude;
  457. dh = sunRadVar->cdh * sunRadVar->G_norm_extra * fd * tn;
  458. gh = bh + dh;
  459. if (sunSlopeGeom->aspect != UNDEF && sunSlopeGeom->slope != 0.) {
  460. kb = bh / (sunRadVar->G_norm_extra * locSinSolarAltitude);
  461. r_sky = (1. + cosslope) / 2.;
  462. a_ln = sunVarGeom->solarAzimuth - sunSlopeGeom->aspect;
  463. ln = a_ln;
  464. if (a_ln > M_PI)
  465. ln = a_ln - pi2;
  466. else if (a_ln < -M_PI)
  467. ln = a_ln + pi2;
  468. a_ln = ln;
  469. fg = sinslope - sunSlopeGeom->slope * cosslope -
  470. M_PI * sin(0.5 * sunSlopeGeom->slope) * sin(0.5 *
  471. sunSlopeGeom->slope);
  472. if ((sunVarGeom->isShadow == 1) || sh <= 0.)
  473. fx = r_sky + fg * 0.252271;
  474. else if (sunVarGeom->solarAltitude >= 0.1) {
  475. fx = ((0.00263 - kb * (0.712 + 0.6883 * kb)) * fg + r_sky) *
  476. (1. - kb) + kb * sh / locSinSolarAltitude;
  477. }
  478. else if (sunVarGeom->solarAltitude < 0.1)
  479. fx = ((0.00263 - 0.712 * kb - 0.6883 * kb * kb) * fg +
  480. r_sky) * (1. - kb) + kb *
  481. sinslope * cos(a_ln) /
  482. (0.1 - 0.008 * sunVarGeom->solarAltitude);
  483. dr = dh * fx;
  484. /* refl. rad */
  485. *rr = sunRadVar->alb * gh * (1 - cosslope) / 2.;
  486. }
  487. else { /* plane */
  488. dr = dh;
  489. *rr = 0.;
  490. }
  491. return (dr);
  492. }
  493. #define c2 -0.074
  494. double drad_angle_loss(double sh, double bh, double *rr,
  495. struct SunGeometryVarDay *sunVarGeom,
  496. struct SunGeometryVarSlope *sunSlopeGeom,
  497. struct SolarRadVar *sunRadVar)
  498. {
  499. double dh;
  500. double tn, fd, fx = 0., A1, A2, A3, A1b;
  501. double r_sky, kb, dr, gh, a_ln, ln, fg;
  502. double cosslope, sinslope;
  503. double diff_coeff_angleloss;
  504. double refl_coeff_angleloss;
  505. double c1;
  506. double diff_loss_factor, refl_loss_factor;
  507. double locSinSolarAltitude;
  508. double locLinke;
  509. locSinSolarAltitude = sunVarGeom->sinSolarAltitude;
  510. locLinke = sunRadVar->linke;
  511. cosslope = cos(sunSlopeGeom->slope);
  512. sinslope = sin(sunSlopeGeom->slope);
  513. /* FIXME: please document all coefficients */
  514. tn = -0.015843 + locLinke * (0.030543 + 0.0003797 * locLinke);
  515. A1b = 0.26463 + locLinke * (-0.061581 + 0.0031408 * locLinke);
  516. if (A1b * tn < 0.0022)
  517. A1 = 0.0022 / tn;
  518. else
  519. A1 = A1b;
  520. A2 = 2.04020 + locLinke * (0.018945 - 0.011161 * locLinke);
  521. A3 = -1.3025 + locLinke * (0.039231 + 0.0085079 * locLinke);
  522. fd = A1 + A2 * locSinSolarAltitude +
  523. A3 * locSinSolarAltitude * locSinSolarAltitude;
  524. dh = sunRadVar->cdh * sunRadVar->G_norm_extra * fd * tn;
  525. gh = bh + dh;
  526. if (sunSlopeGeom->aspect != UNDEF && sunSlopeGeom->slope != 0.) {
  527. kb = bh / (sunRadVar->G_norm_extra * locSinSolarAltitude);
  528. r_sky = (1. + cosslope) / 2.;
  529. a_ln = sunVarGeom->solarAzimuth - sunSlopeGeom->aspect;
  530. ln = a_ln;
  531. if (a_ln > M_PI)
  532. ln = a_ln - pi2;
  533. else if (a_ln < -M_PI)
  534. ln = a_ln + pi2;
  535. a_ln = ln;
  536. fg = sinslope - sunSlopeGeom->slope * cosslope -
  537. M_PI * sin(sunSlopeGeom->slope / 2.) * sin(sunSlopeGeom->slope /
  538. 2.);
  539. if ((sunVarGeom->isShadow) || (sh <= 0.))
  540. fx = r_sky + fg * 0.252271;
  541. else if (sunVarGeom->solarAltitude >= 0.1) {
  542. fx = ((0.00263 - kb * (0.712 + 0.6883 * kb)) * fg + r_sky) * (1. -
  543. kb)
  544. + kb * sh / locSinSolarAltitude;
  545. }
  546. else if (sunVarGeom->solarAltitude < 0.1)
  547. fx = ((0.00263 - 0.712 * kb - 0.6883 * kb * kb) * fg +
  548. r_sky) * (1. - kb) + kb * sinslope * cos(a_ln) /
  549. (0.1 - 0.008 * sunVarGeom->solarAltitude);
  550. dr = dh * fx;
  551. /* refl. rad */
  552. *rr = sunRadVar->alb * gh * (1 - cosslope) / 2.;
  553. }
  554. else { /* plane */
  555. dr = dh;
  556. *rr = 0.;
  557. }
  558. c1 = 4. / (3. * M_PI);
  559. diff_coeff_angleloss = sinslope
  560. + (M_PI - sunSlopeGeom->slope - sinslope) / (1 + cosslope);
  561. refl_coeff_angleloss = sinslope
  562. + (sunSlopeGeom->slope - sinslope) / (1 - cosslope);
  563. diff_loss_factor
  564. = 1. - exp(-(c1 * diff_coeff_angleloss
  565. + c2 * diff_coeff_angleloss * diff_coeff_angleloss)
  566. / a_r);
  567. refl_loss_factor
  568. = 1. - exp(-(c1 * refl_coeff_angleloss
  569. + c2 * refl_coeff_angleloss * refl_coeff_angleloss)
  570. / a_r);
  571. dr *= diff_loss_factor;
  572. *rr *= refl_loss_factor;
  573. return (dr);
  574. }