main.c 31 KB

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