main.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. /****************************************************************************
  2. *
  3. * MODULE: r.ros
  4. * AUTHOR(S): Jianping Xu, Rutgers University, 1993
  5. * Markus Neteler <neteler itc.it>
  6. * Roberto Flor <flor itc.it>, Brad Douglas <rez touchofmadness.com>,
  7. * Glynn Clements <glynn gclements.plus.com>, Jachym Cepicky <jachym les-ejk.cz>
  8. * PURPOSE:
  9. *
  10. * This raster module creates three raster map layers:
  11. * 1. Base (perpendicular) rate of spread (ROS);
  12. * 2. Maximum (forward) ROS; and
  13. * 3. Direction of the Maximum ROS.
  14. * The calculation of the two ROS values for each raster
  15. * cell is based on the Fortran code by Pat Andrews (1983)
  16. * of the Northern Forest Fire Laboratory, USDA Forest
  17. * Service. These three raster map layers are expected to
  18. * be the inputs for a seperate GRASS raster module
  19. * 'r.spread'.
  20. *
  21. * 'r.ros' can be run in two standard GRASS modes:
  22. * interactive and command line. For an interactive run,
  23. * type in
  24. * r.ros
  25. * and follow the prompts; for a command line mode,
  26. * type in
  27. * r.ros [-v] model=name [moisture_1h=name]
  28. * [moisture_10h=name]
  29. * [moisture_100h=name]
  30. * moisture_live=name [velocity=name]
  31. * [direction=name] [slope=name]
  32. * [aspect=name] output=name
  33. * where:
  34. * Flag:
  35. * Raster Maps:
  36. * model 1-13: the standard fuel models,
  37. * all other numbers: same as barriers;
  38. * moisture_1h 100*moisture_content;
  39. * moisture_10h 100*moisture_content;
  40. * moisture_100h 100*moisture_content;
  41. * moisture_live 100*moisture_content;
  42. * velocity ft/minute;
  43. * direction degree;
  44. * slope degree;
  45. * aspect degree starting from East, anti-clockwise;
  46. * output
  47. * for Base ROS cm/min (technically not ft/min);
  48. * for Max ROS cm/min (technically not ft/min);
  49. * for Direction degree.
  50. *
  51. * Note that the name given as output will be used as
  52. * PREFIX for several actual raster maps. For example, if
  53. * my_ros is given as the output name, 'r.ros' will
  54. * actually produce three raster maps named my_ros.base,
  55. * my_ros.max, and my_ros.maxdir, or even my_ros.spotdist
  56. * respectively.
  57. *
  58. * COPYRIGHT: (C) 2000-2009 by the GRASS Development Team
  59. *
  60. * This program is free software under the GNU General Public
  61. * License (>=v2). Read the file COPYING that comes with GRASS
  62. * for details.
  63. *
  64. *****************************************************************************/
  65. #include <stdlib.h>
  66. #include <stdio.h>
  67. #include <math.h>
  68. #include <grass/gis.h>
  69. #include <grass/raster.h>
  70. #include <grass/glocale.h>
  71. #include "local_proto.h"
  72. #define DATA(map, r, c) (map)[(r) * ncols + (c)]
  73. /*measurements of the 13 fuel models, input of Rothermel equation (1972) */
  74. float WO[4][14] =
  75. { {0, 0.034, 0.092, 0.138, 0.230, 0.046, 0.069, 0.052, 0.069, 0.134,
  76. 0.138, 0.069, 0.184, 0.322},
  77. {0, 0, 0.046, 0, 0.184, 0.023, 0.115, 0.086, 0.046, 0.019, 0.092, 0.207,
  78. 0.644, 1.058},
  79. {0, 0, 0.023, 0, 0.092, 0, 0.092, 0.069, 0.115, 0.007, 0.230, 0.253, 0.759,
  80. 1.288},
  81. {0, 0, 0.023, 0, 0.230, 0.092, 0, 0.017, 0, 0, 0.092, 0, 0}
  82. };
  83. /*ovendry fuel loading, lb./ft.^2 */
  84. float DELTA[] = { 0, 1.0, 1.0, 2.5, 6.0, 2.0, 2.5, 2.5,
  85. 0.2, 0.2, 1.0, 1.0, 2.3, 3.0
  86. }; /*fuel depth, ft. */
  87. float SIGMA[4][14] =
  88. { {0, 3500, 3000, 1500, 2000, 2000, 1750, 1750, 2000, 2500, 2000, 1500,
  89. 1500, 1500},
  90. {0, 0, 109, 0, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109},
  91. {0, 0, 30, 0, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30},
  92. {0, 0, 1500, 0, 1500, 1500, 0, 1500, 0, 0, 1500, 0, 0, 0}
  93. };
  94. /*fuel particale surface-area-to-volume ratio, 1/ft. */
  95. float MX[] = { 0, 0.12, 0.15, 0.25, 0.20, 0.20, 0.25, 0.40,
  96. 0.30, 0.25, 0.25, 0.15, 0.20, 0.25
  97. }; /*moisture content of extinction */
  98. CELL *map_elev; /*full array for elevation map layer (for spotting) */
  99. int nrows, ncols;
  100. struct Cell_head window;
  101. int main(int argc, char *argv[])
  102. {
  103. /***input of Rothermel equation (1972)***/
  104. float h = 8000.0, /*heat of combustion, BTU/lb. */
  105. rhop = 32, /*ovendry fuel density, lb./ft.^3 */
  106. ST = 0.0555; /*fuel total mineral content, lb. minerals/lb. ovendry */
  107. float sigma[14];
  108. /***derived parameters of Rothermel equation (1972)***/
  109. float R, /*rate of spread, ft./min.
  110. R = IR*xi*(1+phiw+phis)/(rhob*epsilon*Qig) */
  111. IR, /*reaction intensity, BTU/ft.^2/min.
  112. IR = gamma*wn*h*etaM*etas */
  113. gamma, /*optimum reation velosity, 1/min.
  114. gamma = gammamax*(beta/betaop)^A*
  115. exp(A(1-beta/betaop)) */
  116. gammamax, /*maximum reation velosity, 1/min.
  117. gammamax = sigma^1.5/(495+0.0594*sigma^1.5) */
  118. betaop, /*optimum packing ratio
  119. betaop = 3.348/(sigma^0.8189) */
  120. A, /*A = 1/(4.77*sigma^0.1-7.27) */
  121. etas = 0.174 / pow(0.01, 0.19), /*mineral damping coefficient */
  122. xi, /*propagating flux ratio,
  123. xi = exp((0.792+0.681*sigma^0.5)(beta+0.1))/
  124. (192+0.2595*sigma) */
  125. phiw, /*wind coefficient, phiw = C*U^B/(beta/betaop)^E */
  126. C, /*C = 7.47/exp(0.133*sigma^0.55) */
  127. B, /*B = 0.02526*sigma^.054 */
  128. E, /*E = 0.715/exp(0.000359*sigma) */
  129. phis, /*slope coefficient,phis = 5.275/beta^0.3*(tan(theta)^2) */
  130. rhob, /*ovendry bulk density, lb./ft.^3, rohb = wo/delta */
  131. epsilon[4][14], /*effective heating number, epsilon = 1/exp(138/sigma) */
  132. Qig[14], /*heat of preignition, BTU/lb. Qig = 250+1116*Mf */
  133. beta; /*packing ratio, beta = rhob/rhop */
  134. /***intermediate variables***/
  135. float R0, /*base ROS (w/out wind/slope) */
  136. Rdir, sin_fac, cos_fac, Ffactor_all[4][14], /*in all fuel subclasses by sigma/WO */
  137. Ffactor_in_dead[3][14], /*in dead fuel subclasses by sigma/WO */
  138. Ffactor_dead[14], /*dead fuel weight by sigma/WO */
  139. Ffactor_live[14], /*live fuel weight by sigma/WO */
  140. Gfactor_in_dead[3][14], /*in dead fuel by the 6 classes */
  141. G1, G2, G3, G4, G5, wo_dead[14], /*dead fuel total load */
  142. wn_dead, /*net dead fuel total load */
  143. wn_live, /*net live fuel (total) load */
  144. class_sum, moisture[4], /*moistures of 1-h,10-h,100-h,live fuels */
  145. Mf_dead, /*total moisture of dead fuels */
  146. etaM_dead, /*dead fuel misture damping coefficent */
  147. etaM_live, /*live fuel misture damping coefficent */
  148. xmext, /*live fuel moisture of extinction */
  149. phi_ws, /*wind and slope conbined coefficient */
  150. wmfd, fdmois, fined, finel;
  151. /*other local variables */
  152. int col, row,
  153. spotting,
  154. model, class,
  155. fuel_fd = 0,
  156. mois_1h_fd = 0, mois_10h_fd = 0, mois_100h_fd = 0, mois_live_fd = 0,
  157. vel_fd = 0, dir_fd = 0,
  158. elev_fd = 0, slope_fd = 0, aspect_fd = 0,
  159. base_fd = 0, max_fd = 0, maxdir_fd = 0, spotdist_fd = 0;
  160. char name_base[60], name_max[60], name_maxdir[60], name_spotdist[60];
  161. CELL *fuel, /*cell buffer for fuel model map layer */
  162. *mois_1h, /*cell buffer for 1-hour fuel moisture map layer */
  163. *mois_10h, /*cell buffer for 10-hour fuel moisture map layer */
  164. *mois_100h, /*cell buffer for 100-hour fuel moisture map layer */
  165. *mois_live, /*cell buffer for live fuel moisture map layer */
  166. *vel, /*cell buffer for wind velocity map layer */
  167. *dir, /*cell buffer for wind direction map layer */
  168. *elev = NULL, /*cell buffer for elevation map layer (for spotting) */
  169. *slope, /*cell buffer for slope map layer */
  170. *aspect, /*cell buffer for aspect map layer */
  171. *base, /*cell buffer for base ROS map layer */
  172. *max, /*cell buffer for max ROS map layer */
  173. *maxdir, /*cell buffer for max ROS direction map layer */
  174. *spotdist = NULL; /*cell buffer for max spotting distance map layer */
  175. extern struct Cell_head window;
  176. struct
  177. {
  178. struct Option *model,
  179. *mois_1h, *mois_10h, *mois_100h, *mois_live,
  180. *vel, *dir, *elev, *slope, *aspect, *output;
  181. } parm;
  182. /* please, remove before GRASS 7 released */
  183. struct Flag *flag_s;
  184. struct GModule *module;
  185. /* initialize access to database and create temporary files */
  186. G_gisinit(argv[0]);
  187. /* Set description */
  188. module = G_define_module();
  189. G_add_keyword(_("raster"));
  190. G_add_keyword(_("fire"));
  191. module->label = _("Generates rate of spread raster map layers.");
  192. module->description =
  193. _("Generates three, or four raster map layers showing 1) the base "
  194. "(perpendicular) rate of spread (ROS), 2) the maximum (forward) ROS, "
  195. "3) the direction of the maximum ROS, and optionally 4) the "
  196. "maximum potential spotting distance.");
  197. parm.model = G_define_standard_option(G_OPT_R_INPUT);
  198. parm.model->key = "model";
  199. parm.model->description = _("Name of raster map containing fuel MODELs");
  200. parm.mois_1h = G_define_standard_option(G_OPT_R_INPUT);
  201. parm.mois_1h->key = "moisture_1h";
  202. parm.mois_1h->required = NO;
  203. parm.mois_1h->description =
  204. _("Name of raster map containing the 1-HOUR fuel MOISTURE (%)");
  205. parm.mois_10h = G_define_standard_option(G_OPT_R_INPUT);
  206. parm.mois_10h->key = "moisture_10h";
  207. parm.mois_10h->required = NO;
  208. parm.mois_10h->description =
  209. _("Name of raster map containing the 10-HOUR fuel MOISTURE (%)");
  210. parm.mois_100h = G_define_standard_option(G_OPT_R_INPUT);
  211. parm.mois_100h->key = "moisture_100h";
  212. parm.mois_100h->required = NO;
  213. parm.mois_100h->description =
  214. _("Name of raster map containing the 100-HOUR fuel MOISTURE (%)");
  215. parm.mois_live = G_define_standard_option(G_OPT_R_INPUT);
  216. parm.mois_live->key = "moisture_live";
  217. parm.mois_live->description =
  218. _("Name of raster map containing LIVE fuel MOISTURE (%)");
  219. parm.vel = G_define_standard_option(G_OPT_R_INPUT);
  220. parm.vel->key = "velocity";
  221. parm.vel->required = NO;
  222. parm.vel->description =
  223. _("Name of raster map containing midflame wind VELOCITYs (ft/min)");
  224. parm.dir = G_define_standard_option(G_OPT_R_INPUT);
  225. parm.dir->key = "direction";
  226. parm.dir->required = NO;
  227. parm.dir->description =
  228. _("Name of raster map containing wind DIRECTIONs (degree)");
  229. parm.slope = G_define_standard_option(G_OPT_R_INPUT);
  230. parm.slope->key = "slope";
  231. parm.slope->required = NO;
  232. parm.slope->description =
  233. _("Name of raster map containing SLOPE (degree)");
  234. parm.aspect = G_define_standard_option(G_OPT_R_INPUT);
  235. parm.aspect->key = "aspect";
  236. parm.aspect->required = NO;
  237. parm.aspect->description =
  238. _("Name of raster map containing ASPECT (degree, anti-clockwise from E)");
  239. parm.elev = G_define_standard_option(G_OPT_R_ELEV);
  240. parm.elev->required = NO;
  241. parm.elev->description =
  242. _("Name of raster map containing ELEVATION (m) (required w/ -s)");
  243. parm.output = G_define_standard_option(G_OPT_R_OUTPUT);
  244. parm.output->description = _("Name prefix for output raster maps (.base, .max, .maxdir)");
  245. flag_s = G_define_flag();
  246. flag_s->key = 's';
  247. flag_s->description = _("Also produce maximum SPOTTING distance");
  248. /* Parse command line */
  249. if (G_parser(argc, argv))
  250. exit(EXIT_FAILURE);
  251. spotting = flag_s->answer;
  252. /* Check if input layers exists in data base */
  253. if (G_find_raster2(parm.model->answer, "") == NULL)
  254. G_fatal_error(_("Raster map <%s> not found"), parm.model->answer);
  255. if (!
  256. (parm.mois_1h->answer || parm.mois_10h->answer ||
  257. parm.mois_100h->answer)) {
  258. G_fatal_error(_("No dead fuel moisture is given. "
  259. "At least one of the 1-h, 10-h, 100-h moisture layers is required."));
  260. }
  261. if (parm.mois_1h->answer) {
  262. if (G_find_raster2(parm.mois_1h->answer, "") == NULL)
  263. G_fatal_error(_("Raster map <%s> not found"),
  264. parm.mois_1h->answer);
  265. }
  266. if (parm.mois_10h->answer) {
  267. if (G_find_raster2(parm.mois_10h->answer, "") == NULL)
  268. G_fatal_error(_("Raster map <%s> not found"),
  269. parm.mois_10h->answer);
  270. }
  271. if (parm.mois_100h->answer) {
  272. if (G_find_raster2(parm.mois_100h->answer, "") == NULL)
  273. G_fatal_error(_("Raster map <%s> not found"),
  274. parm.mois_100h->answer);
  275. }
  276. if (G_find_raster2(parm.mois_live->answer, "") == NULL)
  277. G_fatal_error(_("Raster map <%s> not found"), parm.mois_live->answer);
  278. if (parm.vel->answer && !(parm.dir->answer)) {
  279. G_fatal_error(_("A wind direction layer should be "
  280. "given if the wind velocity layer <%s> has been given"),
  281. parm.vel->answer);
  282. }
  283. if (!(parm.vel->answer) && parm.dir->answer) {
  284. G_fatal_error(_("A wind velocity layer should be given "
  285. "if the wind direction layer <%s> has been given"),
  286. parm.dir->answer);
  287. }
  288. if (parm.vel->answer) {
  289. if (G_find_raster2(parm.vel->answer, "") == NULL)
  290. G_fatal_error(_("Raster map <%s> not found"), parm.vel->answer);
  291. }
  292. if (parm.dir->answer) {
  293. if (G_find_raster2(parm.dir->answer, "") == NULL)
  294. G_fatal_error(_("Raster map <%s> not found"), parm.dir->answer);
  295. }
  296. if (parm.slope->answer && !(parm.aspect->answer)) {
  297. G_fatal_error(_("An aspect layer should be given "
  298. "if the slope layer <%s> has been given"),
  299. parm.slope->answer);
  300. }
  301. if (!(parm.slope->answer) && parm.aspect->answer) {
  302. G_fatal_error(_("A slope layer should be given "
  303. "if the aspect layer <%s> has been given"),
  304. parm.aspect->answer);
  305. }
  306. if (parm.slope->answer) {
  307. if (G_find_raster2(parm.slope->answer, "") == NULL)
  308. G_fatal_error(_("Raster map <%s> not found"), parm.slope->answer);
  309. }
  310. if (parm.aspect->answer) {
  311. if (G_find_raster2(parm.aspect->answer, "") == NULL)
  312. G_fatal_error(_("Raster map <%s> not found"),
  313. parm.aspect->answer);
  314. }
  315. if (spotting) {
  316. if (!(parm.elev->answer)) {
  317. G_fatal_error(_("An elevation layer should be given "
  318. "if considering spotting"));
  319. }
  320. else {
  321. if (G_find_raster2(parm.elev->answer, "") == NULL)
  322. G_fatal_error(_("Raster map <%s> not found"),
  323. parm.elev->answer);
  324. }
  325. }
  326. /*assign names of the three output ROS layers */
  327. sprintf(name_base, "%s.base", parm.output->answer);
  328. sprintf(name_max, "%s.max", parm.output->answer);
  329. sprintf(name_maxdir, "%s.maxdir", parm.output->answer);
  330. /*check if the output layer names EXIST */
  331. if (G_check_overwrite(argc, argv) == 0) {
  332. if (G_find_raster2(name_base, G_mapset()))
  333. G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
  334. name_base, G_mapset());
  335. if (G_find_raster2(name_max, G_mapset()))
  336. G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
  337. name_max, G_mapset());
  338. if (G_find_raster2(name_maxdir, G_mapset()))
  339. G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
  340. name_maxdir, G_mapset());
  341. /*assign a name to output SPOTTING distance layer */
  342. if (spotting) {
  343. sprintf(name_spotdist, "%s.spotdist", parm.output->answer);
  344. if (G_find_raster2(name_spotdist, G_mapset()))
  345. G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
  346. name_spotdist, G_mapset());
  347. }
  348. }
  349. /* Get database window parameters */
  350. G_get_window(&window);
  351. /* find number of rows and columns in window */
  352. nrows = Rast_window_rows();
  353. ncols = Rast_window_cols();
  354. fuel = Rast_allocate_c_buf();
  355. mois_1h = Rast_allocate_c_buf();
  356. mois_10h = Rast_allocate_c_buf();
  357. mois_100h = Rast_allocate_c_buf();
  358. mois_live = Rast_allocate_c_buf();
  359. vel = Rast_allocate_c_buf();
  360. dir = Rast_allocate_c_buf();
  361. slope = Rast_allocate_c_buf();
  362. aspect = Rast_allocate_c_buf();
  363. base = Rast_allocate_c_buf();
  364. max = Rast_allocate_c_buf();
  365. maxdir = Rast_allocate_c_buf();
  366. if (spotting) {
  367. spotdist = Rast_allocate_c_buf();
  368. elev = Rast_allocate_c_buf();
  369. map_elev = (CELL *) G_calloc(nrows * ncols, sizeof(CELL));
  370. }
  371. /* Open input cell layers for reading */
  372. fuel_fd = Rast_open_old(parm.model->answer, "");
  373. if (parm.mois_1h->answer)
  374. mois_1h_fd = Rast_open_old(parm.mois_1h->answer, "");
  375. if (parm.mois_10h->answer)
  376. mois_10h_fd = Rast_open_old(parm.mois_10h->answer,"");
  377. if (parm.mois_100h->answer)
  378. mois_100h_fd = Rast_open_old(parm.mois_100h->answer, "");
  379. mois_live_fd = Rast_open_old(parm.mois_live->answer, "");
  380. if (parm.vel->answer)
  381. vel_fd = Rast_open_old(parm.vel->answer, "");
  382. if (parm.dir->answer)
  383. dir_fd = Rast_open_old(parm.dir->answer, "");
  384. if (parm.slope->answer)
  385. slope_fd = Rast_open_old(parm.slope->answer, "");
  386. if (parm.aspect->answer)
  387. aspect_fd = Rast_open_old(parm.aspect->answer, "");
  388. if (spotting)
  389. elev_fd = Rast_open_old(parm.elev->answer, "");
  390. base_fd = Rast_open_c_new(name_base);
  391. max_fd = Rast_open_c_new(name_max);
  392. maxdir_fd = Rast_open_c_new(name_maxdir);
  393. if (spotting)
  394. spotdist_fd = Rast_open_c_new(name_spotdist);
  395. /*compute weights, combined wo, and combined sigma */
  396. /*wo[model] -- simple sum of WO[class][model] by all fuel subCLASS */
  397. /*sigma[model] -- weighted sum of SIGMA[class][model] by all fuel subCLASS *epsilon[class][model] */
  398. for (model = 1; model <= 13; model++) {
  399. class_sum = 0.0;
  400. wo_dead[model] = 0.0;
  401. sigma[model] = 0.0;
  402. for (class = 0; class <= 3; class++) {
  403. class_sum = class_sum + WO[class][model] * SIGMA[class][model];
  404. if (SIGMA[class][model] > 0.0) {
  405. epsilon[class][model] = exp(-138.0 / SIGMA[class][model]);
  406. }
  407. else {
  408. epsilon[class][model] = 0.0;
  409. }
  410. }
  411. for (class = 0; class <= 3; class++) {
  412. Ffactor_all[class][model] =
  413. WO[class][model] * SIGMA[class][model] / class_sum;
  414. sigma[model] =
  415. sigma[model] +
  416. SIGMA[class][model] * Ffactor_all[class][model];
  417. }
  418. class_sum = 0.0;
  419. for (class = 0; class <= 2; class++) {
  420. wo_dead[model] = wo_dead[model] + WO[class][model];
  421. class_sum = class_sum + WO[class][model] * SIGMA[class][model];
  422. }
  423. for (class = 0; class <= 2; class++) {
  424. Ffactor_in_dead[class][model] =
  425. WO[class][model] * SIGMA[class][model] / class_sum;
  426. }
  427. /* compute G factor for each of the 6 subclasses */
  428. G1 = 0.0;
  429. G2 = 0.0;
  430. G3 = 0.0;
  431. G4 = 0.0;
  432. G5 = 0.0;
  433. for (class = 0; class <= 2; class++) {
  434. if (SIGMA[class][model] >= 1200)
  435. G1 = G1 + Ffactor_in_dead[class][model];
  436. if (SIGMA[class][model] < 1200 && SIGMA[class][model] >= 192)
  437. G2 = G2 + Ffactor_in_dead[class][model];
  438. if (SIGMA[class][model] < 192 && SIGMA[class][model] >= 96)
  439. G3 = G3 + Ffactor_in_dead[class][model];
  440. if (SIGMA[class][model] < 96 && SIGMA[class][model] >= 48)
  441. G4 = G4 + Ffactor_in_dead[class][model];
  442. if (SIGMA[class][model] < 48 && SIGMA[class][model] >= 16)
  443. G5 = G5 + Ffactor_in_dead[class][model];
  444. }
  445. for (class = 0; class <= 2; class++) {
  446. if (SIGMA[class][model] >= 1200)
  447. Gfactor_in_dead[class][model] = G1;
  448. if (SIGMA[class][model] < 1200 && SIGMA[class][model] >= 192)
  449. Gfactor_in_dead[class][model] = G2;
  450. if (SIGMA[class][model] < 192 && SIGMA[class][model] >= 96)
  451. Gfactor_in_dead[class][model] = G3;
  452. if (SIGMA[class][model] < 96 && SIGMA[class][model] >= 48)
  453. Gfactor_in_dead[class][model] = G4;
  454. if (SIGMA[class][model] < 48 && SIGMA[class][model] >= 16)
  455. Gfactor_in_dead[class][model] = G5;
  456. if (SIGMA[class][model] < 16)
  457. Gfactor_in_dead[class][model] = 0.0;
  458. }
  459. Ffactor_dead[model] =
  460. class_sum / (class_sum + WO[3][model] * SIGMA[3][model]);
  461. Ffactor_live[model] = 1 - Ffactor_dead[model];
  462. }
  463. /*if considering spotting, read elevation map into an array */
  464. if (spotting)
  465. for (row = 0; row < nrows; row++) {
  466. Rast_get_c_row(elev_fd, elev, row);
  467. for (col = 0; col < ncols; col++)
  468. DATA(map_elev, row, col) = elev[col];
  469. }
  470. /*major computation: compute ROSs one cell a time */
  471. for (row = 0; row < nrows; row++) {
  472. G_percent(row, nrows, 2);
  473. Rast_get_c_row(fuel_fd, fuel, row);
  474. if (parm.mois_1h->answer)
  475. Rast_get_c_row(mois_1h_fd, mois_1h, row);
  476. if (parm.mois_10h->answer)
  477. Rast_get_c_row(mois_10h_fd, mois_10h, row);
  478. if (parm.mois_100h->answer)
  479. Rast_get_c_row(mois_100h_fd, mois_100h, row);
  480. Rast_get_c_row(mois_live_fd, mois_live, row);
  481. if (parm.vel->answer)
  482. Rast_get_c_row(vel_fd, vel, row);
  483. if (parm.dir->answer)
  484. Rast_get_c_row(dir_fd, dir, row);
  485. if (parm.slope->answer)
  486. Rast_get_c_row(slope_fd, slope, row);
  487. if (parm.aspect->answer)
  488. Rast_get_c_row(aspect_fd, aspect, row);
  489. /*initialize cell buffers for output map layers */
  490. for (col = 0; col < ncols; col++) {
  491. base[col] = max[col] = maxdir[col] = 0;
  492. if (spotting)
  493. spotdist[col] = 0;
  494. }
  495. for (col = 0; col < ncols; col++) {
  496. /*check if a fuel is within the 13 models,
  497. *if not, no processing; useful when no data presents*/
  498. if (fuel[col] < 1 || fuel[col] > 13)
  499. continue;
  500. if (parm.mois_1h->answer)
  501. moisture[0] = 0.01 * mois_1h[col];
  502. if (parm.mois_10h->answer)
  503. moisture[1] = 0.01 * mois_10h[col];
  504. if (parm.mois_100h->answer)
  505. moisture[2] = 0.01 * mois_100h[col];
  506. moisture[3] = 0.01 * mois_live[col];
  507. if (parm.aspect->answer)
  508. aspect[col] = (630 - aspect[col]) % 360;
  509. /* assign some dead fuel moisture if not completely
  510. * given based on emperical relationship*/
  511. if (!(parm.mois_10h->answer || parm.mois_100h->answer)) {
  512. moisture[1] = moisture[0] + 0.01;
  513. moisture[2] = moisture[0] + 0.02;
  514. }
  515. if (!(parm.mois_1h->answer || parm.mois_100h->answer)) {
  516. moisture[0] = moisture[1] - 0.01;
  517. moisture[2] = moisture[1] + 0.01;
  518. }
  519. if (!(parm.mois_1h->answer || parm.mois_10h->answer)) {
  520. moisture[0] = moisture[2] - 0.02;
  521. moisture[1] = moisture[2] - 0.01;
  522. }
  523. if (!(parm.mois_1h->answer) && parm.mois_10h->answer &&
  524. parm.mois_100h->answer)
  525. moisture[0] = moisture[1] - 0.01;
  526. if (!(parm.mois_10h->answer) && parm.mois_1h->answer &&
  527. parm.mois_100h->answer)
  528. moisture[1] = moisture[0] + 0.01;
  529. if (!(parm.mois_100h->answer) && parm.mois_1h->answer &&
  530. parm.mois_10h->answer)
  531. moisture[2] = moisture[1] + 0.01;
  532. /*compute xmext, moisture of extinction of live fuels */
  533. wmfd = 0.0;
  534. fined = 0.0;
  535. if (SIGMA[3][fuel[col]] > 0.0) {
  536. for (class = 0; class <= 2; class++) {
  537. if (SIGMA[class][fuel[col]] == 0.0)
  538. continue;
  539. fined =
  540. fined +
  541. WO[class][fuel[col]] * exp(-138.0 /
  542. SIGMA[class][fuel[col]]);
  543. wmfd =
  544. wmfd +
  545. WO[class][fuel[col]] * exp(-138.0 /
  546. SIGMA[class][fuel[col]]) *
  547. moisture[class];
  548. }
  549. fdmois = wmfd / fined;
  550. finel = WO[3][fuel[col]] * exp(-500.0 / SIGMA[3][fuel[col]]);
  551. xmext =
  552. 2.9 * (fined / finel) * (1 - fdmois / MX[fuel[col]]) -
  553. 0.226;
  554. }
  555. else
  556. xmext = MX[fuel[col]];
  557. if (xmext < MX[fuel[col]])
  558. xmext = MX[fuel[col]];
  559. /*compute other intermediate values */
  560. Mf_dead = 0.0;
  561. wn_dead = 0.0;
  562. class_sum = 0.0;
  563. for (class = 0; class <= 2; class++) {
  564. Mf_dead =
  565. Mf_dead +
  566. moisture[class] * Ffactor_in_dead[class][fuel[col]];
  567. wn_dead =
  568. wn_dead +
  569. WO[class][fuel[col]] * Gfactor_in_dead[class][fuel[col]] *
  570. (1 - ST);
  571. Qig[class] = 250 + 1116 * moisture[class];
  572. class_sum =
  573. class_sum +
  574. Ffactor_all[class][fuel[col]] *
  575. epsilon[class][fuel[col]] * Qig[class];
  576. }
  577. etaM_dead =
  578. 1.0 - 2.59 * (Mf_dead / MX[fuel[col]]) +
  579. 5.11 * (Mf_dead / MX[fuel[col]]) * (Mf_dead / MX[fuel[col]]) -
  580. 3.52 * (Mf_dead / MX[fuel[col]]) * (Mf_dead / MX[fuel[col]]) *
  581. (Mf_dead / MX[fuel[col]]);
  582. if (Mf_dead >= MX[fuel[col]])
  583. etaM_dead = 0.0;
  584. etaM_live =
  585. 1.0 - 2.59 * (moisture[3] / xmext) +
  586. 5.11 * (moisture[3] / xmext) * (moisture[3] / xmext) -
  587. 3.52 * (moisture[3] / xmext) * (moisture[3] / xmext) *
  588. (moisture[3] / xmext);
  589. if (moisture[3] >= xmext)
  590. etaM_live = 0.0;
  591. wn_live = WO[3][fuel[col]] * (1 - ST);
  592. Qig[3] = 250 + 1116 * moisture[3];
  593. class_sum =
  594. class_sum +
  595. Ffactor_all[3][fuel[col]] * epsilon[3][fuel[col]] * Qig[3];
  596. /*final computations */
  597. rhob = (wo_dead[fuel[col]] + WO[3][fuel[col]]) / DELTA[fuel[col]];
  598. beta = rhob / rhop;
  599. betaop = 3.348 / pow(sigma[fuel[col]], 0.8189);
  600. A = 133 / pow(sigma[fuel[col]], 0.7913);
  601. gammamax =
  602. pow(sigma[fuel[col]],
  603. 1.5) / (495 + 0.0594 * pow(sigma[fuel[col]], 1.5));
  604. gamma =
  605. gammamax * pow(beta / betaop,
  606. A) * exp(A * (1 - beta / betaop));
  607. xi = exp((0.792 + 0.681 * pow(sigma[fuel[col]], 0.5)) * (beta +
  608. 0.1)) /
  609. (192 + 0.2595 * sigma[fuel[col]]);
  610. IR = gamma * h * (wn_dead * etaM_dead +
  611. wn_live * etaM_live) * etas;
  612. R0 = IR * xi / (rhob * class_sum);
  613. if (parm.vel->answer && parm.dir->answer) {
  614. C = 7.47 * exp(-0.133 * pow(sigma[fuel[col]], 0.55));
  615. B = 0.02526 * pow(sigma[fuel[col]], 0.54);
  616. E = 0.715 * exp(-0.000359 * sigma[fuel[col]]);
  617. phiw = C * pow((double)vel[col], B) / pow(beta / betaop, E);
  618. }
  619. else
  620. phiw = 0.0;
  621. if (parm.slope->answer && parm.aspect->answer) {
  622. phis =
  623. 5.275 * pow(beta,
  624. -0.3) * tan(0.01745 * slope[col]) *
  625. tan(0.01745 * slope[col]);
  626. }
  627. else
  628. phis = 0.0;
  629. /*compute the maximum ROS R and its direction,
  630. *vector adding for the effect of wind and slope*/
  631. if (parm.dir->answer && parm.aspect->answer) {
  632. sin_fac =
  633. phiw * sin(0.01745 * dir[col]) +
  634. phis * sin(0.01745 * aspect[col]);
  635. cos_fac =
  636. phiw * cos(0.01745 * dir[col]) +
  637. phis * cos(0.01745 * aspect[col]);
  638. phi_ws = sqrt(sin_fac * sin_fac + cos_fac * cos_fac);
  639. Rdir = atan2(sin_fac, cos_fac) / 0.01745;
  640. }
  641. else if (parm.dir->answer && !(parm.aspect->answer)) {
  642. phi_ws = phiw;
  643. Rdir = dir[col];
  644. }
  645. else if (!(parm.dir->answer) && parm.aspect->answer) {
  646. phi_ws = phis;
  647. Rdir = (float)aspect[col];
  648. }
  649. else {
  650. phi_ws = 0.0;
  651. Rdir = 0.0;
  652. }
  653. R = R0 * (1 + phi_ws);
  654. if (Rdir < 0.0)
  655. Rdir = Rdir + 360;
  656. /*printf("\nparm.dir->aanswer=%s, parm.aspect->aanswer=%s, phis=%f, phi_ws=%f, aspect[col]=%d,Rdir=%f",parm.dir->answer,parm.aspect->answer,phis,phi_ws,aspect[col],Rdir); */
  657. /*maximum spotting distance */
  658. if (spotting)
  659. spotdist[col] =
  660. spot_dist(fuel[col], R, vel[col], Rdir, row, col);
  661. /*to avoid 0 R, convert ft./min to cm/min */
  662. R0 = 30.5 * R0;
  663. R = 30.5 * R;
  664. /*4debugging R0 = R0/30.5/1.1; R = R/30.5/1.1; */
  665. base[col] = (int)R0;
  666. max[col] = (int)R;
  667. maxdir[col] = (int)Rdir;
  668. /*printf("(%d, %d)\nR0=%.2f, vel=%d, dir=%d, phiw=%.2f, s=%d, as=%d, phis=%.2f, R=%.1f, Rdir=%.0f\n", row, col, R0, vel[col], dir[col], phiw, slope[col], aspect[col], phis, R, Rdir); */
  669. }
  670. Rast_put_row(base_fd, base, CELL_TYPE);
  671. Rast_put_row(max_fd, max, CELL_TYPE);
  672. Rast_put_row(maxdir_fd, maxdir, CELL_TYPE);
  673. if (spotting)
  674. Rast_put_row(spotdist_fd, spotdist, CELL_TYPE);
  675. }
  676. G_percent(row, nrows, 2);
  677. Rast_close(fuel_fd);
  678. if (parm.mois_1h->answer)
  679. Rast_close(mois_1h_fd);
  680. if (parm.mois_10h->answer)
  681. Rast_close(mois_10h_fd);
  682. if (parm.mois_100h->answer)
  683. Rast_close(mois_100h_fd);
  684. Rast_close(mois_live_fd);
  685. if (parm.vel->answer)
  686. Rast_close(vel_fd);
  687. if (parm.dir->answer)
  688. Rast_close(dir_fd);
  689. if (parm.slope->answer)
  690. Rast_close(slope_fd);
  691. if (parm.aspect->answer)
  692. Rast_close(aspect_fd);
  693. Rast_close(base_fd);
  694. Rast_close(max_fd);
  695. Rast_close(maxdir_fd);
  696. if (spotting) {
  697. Rast_close(spotdist_fd);
  698. Rast_close(spotdist_fd);
  699. G_free(map_elev);
  700. }
  701. /*
  702. for (model = 1; model <= 13; model++) {
  703. if (model == 1)
  704. printf("\n Grass and Grass-dominated\n");
  705. else if (model == 4)
  706. printf(" Chaparral and Shrubfields\n");
  707. else if (model == 8)
  708. printf(" Timber Litter\n");
  709. else if (model == 11)
  710. printf(" Logging Slash\n");
  711. printf("Model %2d ", model);
  712. for (class = 0; class <= 3; class++)
  713. printf("%4.0f/%.3f ", SIGMA[class][model], WO[class][model]);
  714. printf(" %.1f %.2f\n", DELTA[model], MX[model]);
  715. }
  716. printf("\nWeight in All Fuel Subclasses:\n");
  717. for (model = 1; model <= 13; model++) {
  718. printf("model %2d ", model);
  719. for (class = 0; class <= 3; class++)
  720. printf("%.2f ", Ffactor_all[class][model]);
  721. printf("%4.0f/%.3f=%6.0f model %2d\n", sigma[model], wo_dead[model]+WO[3][model], sigma[model]/(wo_dead[model]+WO[3][model]), model);
  722. }
  723. printf("\nWeight in Dead Fuel Subclasses, Dead Weight/Live Weight:\n");for (model = 1; model <= 13; model++) {
  724. printf("model %2d ", model);
  725. for (class = 0; class <= 2; class++)
  726. printf("%.2f ", Ffactor_in_dead[class][model]);
  727. printf("%.2f/%.2f model %2d\n", Ffactor_dead[model], Ffactor_live[model], model);
  728. }
  729. */
  730. G_done_msg(_("Raster maps <%s>, <%s> and <%s> created."), name_base, name_max, name_maxdir);
  731. exit(EXIT_SUCCESS);
  732. }