main.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  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(_("rate of spread"));
  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 = G_window_rows();
  353. ncols = G_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 =
  373. Rast_open_old(parm.model->answer,
  374. G_find_raster2(parm.model->answer, ""));
  375. if (fuel_fd < 0)
  376. G_fatal_error(_("Unable to open raster map <%s>"),
  377. parm.model->answer);
  378. if (parm.mois_1h->answer) {
  379. mois_1h_fd =
  380. Rast_open_old(parm.mois_1h->answer,
  381. G_find_raster2(parm.mois_1h->answer, ""));
  382. if (mois_1h_fd < 0)
  383. G_fatal_error(_("Unable to open raster map <%s>"),
  384. parm.mois_1h->answer);
  385. }
  386. if (parm.mois_10h->answer) {
  387. mois_10h_fd =
  388. Rast_open_old(parm.mois_10h->answer,
  389. G_find_raster2(parm.mois_10h->answer, ""));
  390. if (mois_10h_fd < 0)
  391. G_fatal_error(_("Unable to open raster map <%s>"),
  392. parm.mois_10h->answer);
  393. }
  394. if (parm.mois_100h->answer) {
  395. mois_100h_fd =
  396. Rast_open_old(parm.mois_100h->answer,
  397. G_find_raster2(parm.mois_100h->answer, ""));
  398. if (mois_100h_fd < 0)
  399. G_fatal_error(_("Unable to open raster map <%s>"),
  400. parm.mois_100h->answer);
  401. }
  402. mois_live_fd =
  403. Rast_open_old(parm.mois_live->answer,
  404. G_find_raster2(parm.mois_live->answer, ""));
  405. if (mois_live_fd < 0)
  406. G_fatal_error(_("Unable to open raster map <%s>"),
  407. parm.mois_live->answer);
  408. if (parm.vel->answer) {
  409. vel_fd =
  410. Rast_open_old(parm.vel->answer,
  411. G_find_raster2(parm.vel->answer, ""));
  412. if (vel_fd < 0)
  413. G_fatal_error(_("Unable to open raster map <%s>"),
  414. parm.vel->answer);
  415. }
  416. if (parm.dir->answer) {
  417. dir_fd =
  418. Rast_open_old(parm.dir->answer,
  419. G_find_raster2(parm.dir->answer, ""));
  420. if (dir_fd < 0)
  421. G_fatal_error(_("Unable to open raster map <%s>"),
  422. parm.dir->answer);
  423. }
  424. if (parm.slope->answer) {
  425. slope_fd =
  426. Rast_open_old(parm.slope->answer,
  427. G_find_raster2(parm.slope->answer, ""));
  428. if (slope_fd < 0)
  429. G_fatal_error(_("Unable to open raster map <%s>"),
  430. parm.slope->answer);
  431. }
  432. if (parm.aspect->answer) {
  433. aspect_fd =
  434. Rast_open_old(parm.aspect->answer,
  435. G_find_raster2(parm.aspect->answer, ""));
  436. if (aspect_fd < 0)
  437. G_fatal_error(_("Unable to open raster map <%s>"),
  438. parm.aspect->answer);
  439. }
  440. if (spotting) {
  441. elev_fd =
  442. Rast_open_old(parm.elev->answer,
  443. G_find_raster2(parm.elev->answer, ""));
  444. if (elev_fd < 0)
  445. G_fatal_error(_("Unable to open raster map <%s>"),
  446. parm.elev->answer);
  447. }
  448. base_fd = Rast_open_c_new(name_base);
  449. max_fd = Rast_open_c_new(name_max);
  450. maxdir_fd = Rast_open_c_new(name_maxdir);
  451. if (spotting)
  452. spotdist_fd = Rast_open_c_new(name_spotdist);
  453. /*compute weights, combined wo, and combined sigma */
  454. /*wo[model] -- simple sum of WO[class][model] by all fuel subCLASS */
  455. /*sigma[model] -- weighted sum of SIGMA[class][model] by all fuel subCLASS *epsilon[class][model] */
  456. for (model = 1; model <= 13; model++) {
  457. class_sum = 0.0;
  458. wo_dead[model] = 0.0;
  459. sigma[model] = 0.0;
  460. for (class = 0; class <= 3; class++) {
  461. class_sum = class_sum + WO[class][model] * SIGMA[class][model];
  462. if (SIGMA[class][model] > 0.0) {
  463. epsilon[class][model] = exp(-138.0 / SIGMA[class][model]);
  464. }
  465. else {
  466. epsilon[class][model] = 0.0;
  467. }
  468. }
  469. for (class = 0; class <= 3; class++) {
  470. Ffactor_all[class][model] =
  471. WO[class][model] * SIGMA[class][model] / class_sum;
  472. sigma[model] =
  473. sigma[model] +
  474. SIGMA[class][model] * Ffactor_all[class][model];
  475. }
  476. class_sum = 0.0;
  477. for (class = 0; class <= 2; class++) {
  478. wo_dead[model] = wo_dead[model] + WO[class][model];
  479. class_sum = class_sum + WO[class][model] * SIGMA[class][model];
  480. }
  481. for (class = 0; class <= 2; class++) {
  482. Ffactor_in_dead[class][model] =
  483. WO[class][model] * SIGMA[class][model] / class_sum;
  484. }
  485. /* compute G factor for each of the 6 subclasses */
  486. G1 = 0.0;
  487. G2 = 0.0;
  488. G3 = 0.0;
  489. G4 = 0.0;
  490. G5 = 0.0;
  491. for (class = 0; class <= 2; class++) {
  492. if (SIGMA[class][model] >= 1200)
  493. G1 = G1 + Ffactor_in_dead[class][model];
  494. if (SIGMA[class][model] < 1200 && SIGMA[class][model] >= 192)
  495. G2 = G2 + Ffactor_in_dead[class][model];
  496. if (SIGMA[class][model] < 192 && SIGMA[class][model] >= 96)
  497. G3 = G3 + Ffactor_in_dead[class][model];
  498. if (SIGMA[class][model] < 96 && SIGMA[class][model] >= 48)
  499. G4 = G4 + Ffactor_in_dead[class][model];
  500. if (SIGMA[class][model] < 48 && SIGMA[class][model] >= 16)
  501. G5 = G5 + Ffactor_in_dead[class][model];
  502. }
  503. for (class = 0; class <= 2; class++) {
  504. if (SIGMA[class][model] >= 1200)
  505. Gfactor_in_dead[class][model] = G1;
  506. if (SIGMA[class][model] < 1200 && SIGMA[class][model] >= 192)
  507. Gfactor_in_dead[class][model] = G2;
  508. if (SIGMA[class][model] < 192 && SIGMA[class][model] >= 96)
  509. Gfactor_in_dead[class][model] = G3;
  510. if (SIGMA[class][model] < 96 && SIGMA[class][model] >= 48)
  511. Gfactor_in_dead[class][model] = G4;
  512. if (SIGMA[class][model] < 48 && SIGMA[class][model] >= 16)
  513. Gfactor_in_dead[class][model] = G5;
  514. if (SIGMA[class][model] < 16)
  515. Gfactor_in_dead[class][model] = 0.0;
  516. }
  517. Ffactor_dead[model] =
  518. class_sum / (class_sum + WO[3][model] * SIGMA[3][model]);
  519. Ffactor_live[model] = 1 - Ffactor_dead[model];
  520. }
  521. /*if considering spotting, read elevation map into an array */
  522. if (spotting)
  523. for (row = 0; row < nrows; row++) {
  524. if (Rast_get_c_row(elev_fd, elev, row) < 0)
  525. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.elev->answer, row);
  526. for (col = 0; col < ncols; col++)
  527. DATA(map_elev, row, col) = elev[col];
  528. }
  529. /*major computation: compute ROSs one cell a time */
  530. for (row = 0; row < nrows; row++) {
  531. G_percent(row, nrows, 2);
  532. if (Rast_get_c_row(fuel_fd, fuel, row) < 0)
  533. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.model->answer, row);
  534. if (parm.mois_1h->answer)
  535. if (Rast_get_c_row(mois_1h_fd, mois_1h, row) < 0)
  536. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.mois_1h->answer, row);
  537. if (parm.mois_10h->answer)
  538. if (Rast_get_c_row(mois_10h_fd, mois_10h, row) < 0)
  539. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.mois_10h->answer, row);
  540. if (parm.mois_100h->answer)
  541. if (Rast_get_c_row(mois_100h_fd, mois_100h, row) < 0)
  542. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.mois_100h->answer, row);
  543. if (Rast_get_c_row(mois_live_fd, mois_live, row) < 0)
  544. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.mois_live->answer, row);
  545. if (parm.vel->answer)
  546. if (Rast_get_c_row(vel_fd, vel, row) < 0)
  547. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.vel->answer, row);
  548. if (parm.dir->answer)
  549. if (Rast_get_c_row(dir_fd, dir, row) < 0)
  550. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.dir->answer, row);
  551. if (parm.slope->answer)
  552. if (Rast_get_c_row(slope_fd, slope, row) < 0)
  553. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.slope->answer, row);
  554. if (parm.aspect->answer)
  555. if (Rast_get_c_row(aspect_fd, aspect, row) < 0)
  556. G_fatal_error(_("Unable to read raster map <%s> row %d"), parm.aspect->answer, row);
  557. /*initialize cell buffers for output map layers */
  558. for (col = 0; col < ncols; col++) {
  559. base[col] = max[col] = maxdir[col] = 0;
  560. if (spotting)
  561. spotdist[col] = 0;
  562. }
  563. for (col = 0; col < ncols; col++) {
  564. /*check if a fuel is within the 13 models,
  565. *if not, no processing; useful when no data presents*/
  566. if (fuel[col] < 1 || fuel[col] > 13)
  567. continue;
  568. if (parm.mois_1h->answer)
  569. moisture[0] = 0.01 * mois_1h[col];
  570. if (parm.mois_10h->answer)
  571. moisture[1] = 0.01 * mois_10h[col];
  572. if (parm.mois_100h->answer)
  573. moisture[2] = 0.01 * mois_100h[col];
  574. moisture[3] = 0.01 * mois_live[col];
  575. if (parm.aspect->answer)
  576. aspect[col] = (630 - aspect[col]) % 360;
  577. /* assign some dead fuel moisture if not completely
  578. * given based on emperical relationship*/
  579. if (!(parm.mois_10h->answer || parm.mois_100h->answer)) {
  580. moisture[1] = moisture[0] + 0.01;
  581. moisture[2] = moisture[0] + 0.02;
  582. }
  583. if (!(parm.mois_1h->answer || parm.mois_100h->answer)) {
  584. moisture[0] = moisture[1] - 0.01;
  585. moisture[2] = moisture[1] + 0.01;
  586. }
  587. if (!(parm.mois_1h->answer || parm.mois_10h->answer)) {
  588. moisture[0] = moisture[2] - 0.02;
  589. moisture[1] = moisture[2] - 0.01;
  590. }
  591. if (!(parm.mois_1h->answer) && parm.mois_10h->answer &&
  592. parm.mois_100h->answer)
  593. moisture[0] = moisture[1] - 0.01;
  594. if (!(parm.mois_10h->answer) && parm.mois_1h->answer &&
  595. parm.mois_100h->answer)
  596. moisture[1] = moisture[0] + 0.01;
  597. if (!(parm.mois_100h->answer) && parm.mois_1h->answer &&
  598. parm.mois_10h->answer)
  599. moisture[2] = moisture[1] + 0.01;
  600. /*compute xmext, moisture of extinction of live fuels */
  601. wmfd = 0.0;
  602. fined = 0.0;
  603. if (SIGMA[3][fuel[col]] > 0.0) {
  604. for (class = 0; class <= 2; class++) {
  605. if (SIGMA[class][fuel[col]] == 0.0)
  606. continue;
  607. fined =
  608. fined +
  609. WO[class][fuel[col]] * exp(-138.0 /
  610. SIGMA[class][fuel[col]]);
  611. wmfd =
  612. wmfd +
  613. WO[class][fuel[col]] * exp(-138.0 /
  614. SIGMA[class][fuel[col]]) *
  615. moisture[class];
  616. }
  617. fdmois = wmfd / fined;
  618. finel = WO[3][fuel[col]] * exp(-500.0 / SIGMA[3][fuel[col]]);
  619. xmext =
  620. 2.9 * (fined / finel) * (1 - fdmois / MX[fuel[col]]) -
  621. 0.226;
  622. }
  623. else
  624. xmext = MX[fuel[col]];
  625. if (xmext < MX[fuel[col]])
  626. xmext = MX[fuel[col]];
  627. /*compute other intermediate values */
  628. Mf_dead = 0.0;
  629. wn_dead = 0.0;
  630. class_sum = 0.0;
  631. for (class = 0; class <= 2; class++) {
  632. Mf_dead =
  633. Mf_dead +
  634. moisture[class] * Ffactor_in_dead[class][fuel[col]];
  635. wn_dead =
  636. wn_dead +
  637. WO[class][fuel[col]] * Gfactor_in_dead[class][fuel[col]] *
  638. (1 - ST);
  639. Qig[class] = 250 + 1116 * moisture[class];
  640. class_sum =
  641. class_sum +
  642. Ffactor_all[class][fuel[col]] *
  643. epsilon[class][fuel[col]] * Qig[class];
  644. }
  645. etaM_dead =
  646. 1.0 - 2.59 * (Mf_dead / MX[fuel[col]]) +
  647. 5.11 * (Mf_dead / MX[fuel[col]]) * (Mf_dead / MX[fuel[col]]) -
  648. 3.52 * (Mf_dead / MX[fuel[col]]) * (Mf_dead / MX[fuel[col]]) *
  649. (Mf_dead / MX[fuel[col]]);
  650. if (Mf_dead >= MX[fuel[col]])
  651. etaM_dead = 0.0;
  652. etaM_live =
  653. 1.0 - 2.59 * (moisture[3] / xmext) +
  654. 5.11 * (moisture[3] / xmext) * (moisture[3] / xmext) -
  655. 3.52 * (moisture[3] / xmext) * (moisture[3] / xmext) *
  656. (moisture[3] / xmext);
  657. if (moisture[3] >= xmext)
  658. etaM_live = 0.0;
  659. wn_live = WO[3][fuel[col]] * (1 - ST);
  660. Qig[3] = 250 + 1116 * moisture[3];
  661. class_sum =
  662. class_sum +
  663. Ffactor_all[3][fuel[col]] * epsilon[3][fuel[col]] * Qig[3];
  664. /*final computations */
  665. rhob = (wo_dead[fuel[col]] + WO[3][fuel[col]]) / DELTA[fuel[col]];
  666. beta = rhob / rhop;
  667. betaop = 3.348 / pow(sigma[fuel[col]], 0.8189);
  668. A = 133 / pow(sigma[fuel[col]], 0.7913);
  669. gammamax =
  670. pow(sigma[fuel[col]],
  671. 1.5) / (495 + 0.0594 * pow(sigma[fuel[col]], 1.5));
  672. gamma =
  673. gammamax * pow(beta / betaop,
  674. A) * exp(A * (1 - beta / betaop));
  675. xi = exp((0.792 + 0.681 * pow(sigma[fuel[col]], 0.5)) * (beta +
  676. 0.1)) /
  677. (192 + 0.2595 * sigma[fuel[col]]);
  678. IR = gamma * h * (wn_dead * etaM_dead +
  679. wn_live * etaM_live) * etas;
  680. R0 = IR * xi / (rhob * class_sum);
  681. if (parm.vel->answer && parm.dir->answer) {
  682. C = 7.47 * exp(-0.133 * pow(sigma[fuel[col]], 0.55));
  683. B = 0.02526 * pow(sigma[fuel[col]], 0.54);
  684. E = 0.715 * exp(-0.000359 * sigma[fuel[col]]);
  685. phiw = C * pow((double)vel[col], B) / pow(beta / betaop, E);
  686. }
  687. else
  688. phiw = 0.0;
  689. if (parm.slope->answer && parm.aspect->answer) {
  690. phis =
  691. 5.275 * pow(beta,
  692. -0.3) * tan(0.01745 * slope[col]) *
  693. tan(0.01745 * slope[col]);
  694. }
  695. else
  696. phis = 0.0;
  697. /*compute the maximum ROS R and its direction,
  698. *vector adding for the effect of wind and slope*/
  699. if (parm.dir->answer && parm.aspect->answer) {
  700. sin_fac =
  701. phiw * sin(0.01745 * dir[col]) +
  702. phis * sin(0.01745 * aspect[col]);
  703. cos_fac =
  704. phiw * cos(0.01745 * dir[col]) +
  705. phis * cos(0.01745 * aspect[col]);
  706. phi_ws = sqrt(sin_fac * sin_fac + cos_fac * cos_fac);
  707. Rdir = atan2(sin_fac, cos_fac) / 0.01745;
  708. }
  709. else if (parm.dir->answer && !(parm.aspect->answer)) {
  710. phi_ws = phiw;
  711. Rdir = dir[col];
  712. }
  713. else if (!(parm.dir->answer) && parm.aspect->answer) {
  714. phi_ws = phis;
  715. Rdir = (float)aspect[col];
  716. }
  717. else {
  718. phi_ws = 0.0;
  719. Rdir = 0.0;
  720. }
  721. R = R0 * (1 + phi_ws);
  722. if (Rdir < 0.0)
  723. Rdir = Rdir + 360;
  724. /*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); */
  725. /*maximum spotting distance */
  726. if (spotting)
  727. spotdist[col] =
  728. spot_dist(fuel[col], R, vel[col], Rdir, row, col);
  729. /*to avoid 0 R, convert ft./min to cm/min */
  730. R0 = 30.5 * R0;
  731. R = 30.5 * R;
  732. /*4debugging R0 = R0/30.5/1.1; R = R/30.5/1.1; */
  733. base[col] = (int)R0;
  734. max[col] = (int)R;
  735. maxdir[col] = (int)Rdir;
  736. /*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); */
  737. }
  738. Rast_put_row(base_fd, base, CELL_TYPE);
  739. Rast_put_row(max_fd, max, CELL_TYPE);
  740. Rast_put_row(maxdir_fd, maxdir, CELL_TYPE);
  741. if (spotting)
  742. Rast_put_row(spotdist_fd, spotdist, CELL_TYPE);
  743. }
  744. G_percent(row, nrows, 2);
  745. Rast_close(fuel_fd);
  746. if (parm.mois_1h->answer)
  747. Rast_close(mois_1h_fd);
  748. if (parm.mois_10h->answer)
  749. Rast_close(mois_10h_fd);
  750. if (parm.mois_100h->answer)
  751. Rast_close(mois_100h_fd);
  752. Rast_close(mois_live_fd);
  753. if (parm.vel->answer)
  754. Rast_close(vel_fd);
  755. if (parm.dir->answer)
  756. Rast_close(dir_fd);
  757. if (parm.slope->answer)
  758. Rast_close(slope_fd);
  759. if (parm.aspect->answer)
  760. Rast_close(aspect_fd);
  761. Rast_close(base_fd);
  762. Rast_close(max_fd);
  763. Rast_close(maxdir_fd);
  764. if (spotting) {
  765. Rast_close(spotdist_fd);
  766. Rast_close(spotdist_fd);
  767. G_free(map_elev);
  768. }
  769. /*
  770. for (model = 1; model <= 13; model++) {
  771. if (model == 1)
  772. printf("\n Grass and Grass-dominated\n");
  773. else if (model == 4)
  774. printf(" Chaparral and Shrubfields\n");
  775. else if (model == 8)
  776. printf(" Timber Litter\n");
  777. else if (model == 11)
  778. printf(" Logging Slash\n");
  779. printf("Model %2d ", model);
  780. for (class = 0; class <= 3; class++)
  781. printf("%4.0f/%.3f ", SIGMA[class][model], WO[class][model]);
  782. printf(" %.1f %.2f\n", DELTA[model], MX[model]);
  783. }
  784. printf("\nWeight in All Fuel Subclasses:\n");
  785. for (model = 1; model <= 13; model++) {
  786. printf("model %2d ", model);
  787. for (class = 0; class <= 3; class++)
  788. printf("%.2f ", Ffactor_all[class][model]);
  789. 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);
  790. }
  791. printf("\nWeight in Dead Fuel Subclasses, Dead Weight/Live Weight:\n");for (model = 1; model <= 13; model++) {
  792. printf("model %2d ", model);
  793. for (class = 0; class <= 2; class++)
  794. printf("%.2f ", Ffactor_in_dead[class][model]);
  795. printf("%.2f/%.2f model %2d\n", Ffactor_dead[model], Ffactor_live[model], model);
  796. }
  797. */
  798. G_done_msg(_("Raster maps <%s>, <%s> and <%s> created."), name_base, name_max, name_maxdir);
  799. exit(EXIT_SUCCESS);
  800. }