
/****************************************************************************
 *
 * MODULE:       r.ros
 * AUTHOR(S):    Jianping Xu, Rutgers University, 1993
 *               Markus Neteler <neteler itc.it>
 *               Roberto Flor <flor itc.it>, Brad Douglas <rez touchofmadness.com>,
 *               Glynn Clements <glynn gclements.plus.com>, Jachym Cepicky <jachym les-ejk.cz>
 * PURPOSE:
 *
 * This raster module creates three raster map layers:
 *	1. Base (perpendicular) rate of spread (ROS);
 *	2. Maximum (forward) ROS; and
 * 	3. Direction of the Maximum ROS.
 * The calculation of the two ROS values for each raster
 * cell is based on the Fortran code by Pat Andrews (1983)
 * of the Northern Forest Fire Laboratory, USDA Forest
 * Service. These three raster map layers are expected to
 * be the inputs for a seperate GRASS raster module 
 * 'r.spread'.
 * 
 * 'r.ros' can be run in two standard GRASS modes:
 * interactive and command line. For an interactive run,
 * type in 
 *	r.ros 
 * and follow the prompts; for a command line mode, 
 * type in 
 * 	r.ros [-v] model=name [moisture_1h=name] 
 *			[moisture_10h=name] 
 *			[moisture_100h=name] 
 *			moisture_live=name [velocity=name]
 * 			[direction=name] [slope=name] 
 *			[aspect=name] output=name
 * where:
 *   Flag:
 *   Raster Maps:
 *      model   	1-13: the standard fuel models,
 *			all other numbers: same as barriers;
 *  	moisture_1h   	100*moisture_content;
 *	moisture_10h  	100*moisture_content;
 *	moisture_100h   100*moisture_content;
 *	moisture_live   100*moisture_content;
 *	velocity   	ft/minute;
 *	direction   	degree;
 *	slope   	degree;
 * 	aspect   	degree starting from East, anti-clockwise;
 *	output 
 *	  for Base ROS	cm/min (technically not ft/min);
 *  	  for Max ROS	cm/min (technically not ft/min);
 *	  for Direction	degree.
 *
 * Note that the name given as output will be used as 
 * PREFIX for several actual raster maps. For example, if 
 * my_ros is given as the output name, 'r.ros' will 
 * actually produce three raster maps named my_ros.base, 
 * my_ros.max, and my_ros.maxdir, or even my_ros.spotdist
 * respectively. 
 *   
 * COPYRIGHT:    (C) 2000-2009 by the GRASS Development Team
 *
 *               This program is free software under the GNU General Public
 *               License (>=v2). Read the file COPYING that comes with GRASS
 *               for details.
 *
 *****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/glocale.h>
#include "local_proto.h"


#define DATA(map, r, c)         (map)[(r) * ncols + (c)]

/*measurements of the 13 fuel models, input of Rothermel equation (1972) */
float WO[4][14] =
    { {0, 0.034, 0.092, 0.138, 0.230, 0.046, 0.069, 0.052, 0.069, 0.134,
       0.138, 0.069, 0.184, 0.322},
{0, 0, 0.046, 0, 0.184, 0.023, 0.115, 0.086, 0.046, 0.019, 0.092, 0.207,
 0.644, 1.058},
{0, 0, 0.023, 0, 0.092, 0, 0.092, 0.069, 0.115, 0.007, 0.230, 0.253, 0.759,
 1.288},
{0, 0, 0.023, 0, 0.230, 0.092, 0, 0.017, 0, 0, 0.092, 0, 0}
};

					    /*ovendry fuel loading, lb./ft.^2 */
float DELTA[] = { 0, 1.0, 1.0, 2.5, 6.0, 2.0, 2.5, 2.5,
    0.2, 0.2, 1.0, 1.0, 2.3, 3.0
};				/*fuel depth, ft. */
float SIGMA[4][14] =
    { {0, 3500, 3000, 1500, 2000, 2000, 1750, 1750, 2000, 2500, 2000, 1500,
       1500, 1500},
{0, 0, 109, 0, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109},
{0, 0, 30, 0, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30},
{0, 0, 1500, 0, 1500, 1500, 0, 1500, 0, 0, 1500, 0, 0, 0}
};

			 /*fuel particale surface-area-to-volume ratio, 1/ft. */
float MX[] = { 0, 0.12, 0.15, 0.25, 0.20, 0.20, 0.25, 0.40,
    0.30, 0.25, 0.25, 0.15, 0.20, 0.25
};				/*moisture content of extinction */

CELL *map_elev;			/*full array for elevation map layer (for spotting) */
int nrows, ncols;
struct Cell_head window;


int main(int argc, char *argv[])
{

	/***input of Rothermel equation (1972)***/
    float h = 8000.0,		/*heat of combustion, BTU/lb. */
	rhop = 32,		/*ovendry fuel density, lb./ft.^3 */
	ST = 0.0555;		/*fuel total mineral content, lb. minerals/lb. ovendry */

    float sigma[14];

	/***derived parameters of Rothermel equation (1972)***/
    float R,			/*rate of spread, ft./min.
				   R = IR*xi*(1+phiw+phis)/(rhob*epsilon*Qig)  */
      IR,			/*reaction intensity, BTU/ft.^2/min.
				   IR = gamma*wn*h*etaM*etas  */
      gamma,			/*optimum reation velosity, 1/min.
				   gamma = gammamax*(beta/betaop)^A*
				   exp(A(1-beta/betaop))  */
      gammamax,			/*maximum reation velosity, 1/min.
				   gammamax = sigma^1.5/(495+0.0594*sigma^1.5)  */
      betaop,			/*optimum packing ratio 
				   betaop = 3.348/(sigma^0.8189)  */
      A,			/*A = 1/(4.77*sigma^0.1-7.27)  */
      etas = 0.174 / pow(0.01, 0.19),	/*mineral damping coefficient */
	xi,			/*propagating flux ratio,
				   xi = exp((0.792+0.681*sigma^0.5)(beta+0.1))/
				   (192+0.2595*sigma)  */
	phiw,			/*wind coefficient,  phiw = C*U^B/(beta/betaop)^E */
	C,			/*C = 7.47/exp(0.133*sigma^0.55)  */
	B,			/*B = 0.02526*sigma^.054   */
	E,			/*E = 0.715/exp(0.000359*sigma)  */
	phis,			/*slope coefficient,phis = 5.275/beta^0.3*(tan(theta)^2) */
	rhob,			/*ovendry bulk density, lb./ft.^3, rohb = wo/delta */
	epsilon[4][14],		/*effective heating number, epsilon = 1/exp(138/sigma) */
	Qig[14],		/*heat of preignition, BTU/lb.  Qig = 250+1116*Mf */
	beta;			/*packing ratio,  beta = rhob/rhop  */

	/***intermediate variables***/
    float R0,			/*base ROS (w/out wind/slope) */
      Rdir, sin_fac, cos_fac, Ffactor_all[4][14],	/*in all fuel subclasses by sigma/WO */
      Ffactor_in_dead[3][14],	/*in dead fuel subclasses by sigma/WO */
      Ffactor_dead[14],		/*dead fuel weight by sigma/WO */
      Ffactor_live[14],		/*live fuel weight by sigma/WO */
      Gfactor_in_dead[3][14],	/*in dead fuel by the 6 classes */
      G1, G2, G3, G4, G5, wo_dead[14],	/*dead fuel total load */
      wn_dead,			/*net dead fuel total load */
      wn_live,			/*net live fuel (total) load */
      class_sum, moisture[4],	/*moistures of 1-h,10-h,100-h,live fuels */
      Mf_dead,			/*total moisture of dead fuels */
      etaM_dead,		/*dead fuel misture damping coefficent */
      etaM_live,		/*live fuel misture damping coefficent */
      xmext,			/*live fuel moisture of extinction */
      phi_ws,			/*wind and slope conbined coefficient */
      wmfd, fdmois, fined, finel;

    /*other local variables */
    int col, row,
	spotting,
	model, class,
	fuel_fd = 0,
	mois_1h_fd = 0, mois_10h_fd = 0, mois_100h_fd = 0, mois_live_fd = 0,
	vel_fd = 0, dir_fd = 0,
	elev_fd = 0, slope_fd = 0, aspect_fd = 0,
	base_fd = 0, max_fd = 0, maxdir_fd = 0, spotdist_fd = 0;

    char name_base[60], name_max[60], name_maxdir[60], name_spotdist[60];

    CELL *fuel,			/*cell buffer for fuel model map layer */
     *mois_1h,			/*cell buffer for 1-hour fuel moisture map layer */
     *mois_10h,			/*cell buffer for 10-hour fuel moisture map layer */
     *mois_100h,		/*cell buffer for 100-hour fuel moisture map layer */
     *mois_live,		/*cell buffer for live fuel moisture map layer */
     *vel,			/*cell buffer for wind velocity map layer */
     *dir,			/*cell buffer for wind direction map layer */
     *elev = NULL,		/*cell buffer for elevation map layer (for spotting) */
	*slope,			/*cell buffer for slope map layer */
	*aspect,		/*cell buffer for aspect map layer */
	*base,			/*cell buffer for base ROS map layer */
	*max,			/*cell buffer for max ROS map layer */
	*maxdir,		/*cell buffer for max ROS direction map layer */
	*spotdist = NULL;	/*cell buffer for max spotting distance map layer */

    extern struct Cell_head window;

    struct
    {
	struct Option *model,
	    *mois_1h, *mois_10h, *mois_100h, *mois_live,
	    *vel, *dir, *elev, *slope, *aspect, *output;
    } parm;

    /* please, remove before GRASS 7 released */
    struct Flag *flag_s;
    struct GModule *module;

    /* initialize access to database and create temporary files */
    G_gisinit(argv[0]);

    /* Set description */
    module = G_define_module();
    G_add_keyword(_("raster"));
    G_add_keyword(_("fire"));
    module->label = _("Generates rate of spread raster map layers.");
    module->description =
	_("Generates three, or four raster map layers showing 1) the base "
	  "(perpendicular) rate of spread (ROS), 2) the maximum (forward) ROS, "
	  "3) the direction of the maximum ROS, and optionally 4) the "
	  "maximum potential spotting distance.");

    parm.model = G_define_standard_option(G_OPT_R_INPUT);
    parm.model->key = "model";
    parm.model->description = _("Name of raster map containing fuel MODELs");

    parm.mois_1h = G_define_standard_option(G_OPT_R_INPUT);
    parm.mois_1h->key = "moisture_1h";
    parm.mois_1h->required = NO;
    parm.mois_1h->description =
	_("Name of raster map containing the 1-HOUR fuel MOISTURE (%)");

    parm.mois_10h = G_define_standard_option(G_OPT_R_INPUT);
    parm.mois_10h->key = "moisture_10h";
    parm.mois_10h->required = NO;
    parm.mois_10h->description =
	_("Name of raster map containing the 10-HOUR fuel MOISTURE (%)");

    parm.mois_100h = G_define_standard_option(G_OPT_R_INPUT);
    parm.mois_100h->key = "moisture_100h";
    parm.mois_100h->required = NO;
    parm.mois_100h->description =
	_("Name of raster map containing the 100-HOUR fuel MOISTURE (%)");

    parm.mois_live = G_define_standard_option(G_OPT_R_INPUT);
    parm.mois_live->key = "moisture_live";
    parm.mois_live->description =
	_("Name of raster map containing LIVE fuel MOISTURE (%)");

    parm.vel = G_define_standard_option(G_OPT_R_INPUT);
    parm.vel->key = "velocity";
    parm.vel->required = NO;
    parm.vel->description =
	_("Name of raster map containing midflame wind VELOCITYs (ft/min)");

    parm.dir = G_define_standard_option(G_OPT_R_INPUT);
    parm.dir->key = "direction";
    parm.dir->required = NO;
    parm.dir->description =
	_("Name of raster map containing wind DIRECTIONs (degree)");

    parm.slope = G_define_standard_option(G_OPT_R_INPUT);
    parm.slope->key = "slope";
    parm.slope->required = NO;
    parm.slope->description =
	_("Name of raster map containing SLOPE (degree)");

    parm.aspect = G_define_standard_option(G_OPT_R_INPUT);
    parm.aspect->key = "aspect";
    parm.aspect->required = NO;
    parm.aspect->description =
	_("Name of raster map containing ASPECT (degree, anti-clockwise from E)");

    parm.elev = G_define_standard_option(G_OPT_R_ELEV);
    parm.elev->required = NO;
    parm.elev->description =
	_("Name of raster map containing ELEVATION (m) (required w/ -s)");

    parm.output = G_define_standard_option(G_OPT_R_OUTPUT);
    parm.output->description = _("Name prefix for output raster maps (.base, .max, .maxdir)");

    flag_s = G_define_flag();
    flag_s->key = 's';
    flag_s->description = _("Also produce maximum SPOTTING distance");

    /*   Parse command line */
    if (G_parser(argc, argv))
	exit(EXIT_FAILURE);
    
    spotting = flag_s->answer;

    /*  Check if input layers exists in data base  */
    if (G_find_raster2(parm.model->answer, "") == NULL)
	G_fatal_error(_("Raster map <%s> not found"), parm.model->answer);

    if (!
	(parm.mois_1h->answer || parm.mois_10h->answer ||
	 parm.mois_100h->answer)) {
	G_fatal_error(_("No dead fuel moisture is given. "
			"At least one of the 1-h, 10-h, 100-h moisture layers is required."));
    }

    if (parm.mois_1h->answer) {
	if (G_find_raster2(parm.mois_1h->answer, "") == NULL)
	    G_fatal_error(_("Raster map <%s> not found"),
			  parm.mois_1h->answer);
    }
    if (parm.mois_10h->answer) {
	if (G_find_raster2(parm.mois_10h->answer, "") == NULL)
	    G_fatal_error(_("Raster map <%s> not found"),
			  parm.mois_10h->answer);
    }
    if (parm.mois_100h->answer) {
	if (G_find_raster2(parm.mois_100h->answer, "") == NULL)
	    G_fatal_error(_("Raster map <%s> not found"),
			  parm.mois_100h->answer);
    }

    if (G_find_raster2(parm.mois_live->answer, "") == NULL)
	G_fatal_error(_("Raster map <%s> not found"), parm.mois_live->answer);

    if (parm.vel->answer && !(parm.dir->answer)) {
	G_fatal_error(_("A wind direction layer should be "
			"given if the wind velocity layer <%s> has been given"),
		      parm.vel->answer);
    }
    if (!(parm.vel->answer) && parm.dir->answer) {
	G_fatal_error(_("A wind velocity layer should be given "
			"if the wind direction layer <%s> has been given"),
		      parm.dir->answer);
    }
    if (parm.vel->answer) {
	if (G_find_raster2(parm.vel->answer, "") == NULL)
	    G_fatal_error(_("Raster map <%s> not found"), parm.vel->answer);
    }
    if (parm.dir->answer) {
	if (G_find_raster2(parm.dir->answer, "") == NULL)
	    G_fatal_error(_("Raster map <%s> not found"), parm.dir->answer);
    }

    if (parm.slope->answer && !(parm.aspect->answer)) {
	G_fatal_error(_("An aspect layer should be given "
			"if the slope layer <%s> has been given"),
		      parm.slope->answer);
    }
    if (!(parm.slope->answer) && parm.aspect->answer) {
	G_fatal_error(_("A slope layer should be given "
			"if the aspect layer <%s> has been given"),
		      parm.aspect->answer);
    }
    if (parm.slope->answer) {
	if (G_find_raster2(parm.slope->answer, "") == NULL)
	    G_fatal_error(_("Raster map <%s> not found"), parm.slope->answer);
    }
    if (parm.aspect->answer) {
	if (G_find_raster2(parm.aspect->answer, "") == NULL)
	    G_fatal_error(_("Raster map <%s> not found"),
			  parm.aspect->answer);
    }

    if (spotting) {
	if (!(parm.elev->answer)) {
	    G_fatal_error(_("An elevation layer should be given "
			    "if considering spotting"));
	}
	else {
	    if (G_find_raster2(parm.elev->answer, "") == NULL)
		G_fatal_error(_("Raster map <%s> not found"),
			      parm.elev->answer);
	}
    }

    /*assign names of the three output ROS layers */
    sprintf(name_base, "%s.base", parm.output->answer);
    sprintf(name_max, "%s.max", parm.output->answer);
    sprintf(name_maxdir, "%s.maxdir", parm.output->answer);

    /*check if the output layer names EXIST */
    if (G_check_overwrite(argc, argv) == 0) {
	if (G_find_raster2(name_base, G_mapset()))
	    G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
			  name_base, G_mapset());
	
	if (G_find_raster2(name_max, G_mapset()))
	    G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
			  name_max, G_mapset());
	
	if (G_find_raster2(name_maxdir, G_mapset()))
	    G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
			  name_maxdir, G_mapset());
	
	/*assign a name to output SPOTTING distance layer */
	if (spotting) {
	    sprintf(name_spotdist, "%s.spotdist", parm.output->answer);
	    if (G_find_raster2(name_spotdist, G_mapset()))
		G_fatal_error(_("Raster map <%s> already exists in mapset <%s>"),
			      name_spotdist, G_mapset());
	}
    }

    /*  Get database window parameters  */
    G_get_window(&window);

    /*  find number of rows and columns in window    */
    nrows = Rast_window_rows();
    ncols = Rast_window_cols();

    fuel = Rast_allocate_c_buf();
    mois_1h = Rast_allocate_c_buf();
    mois_10h = Rast_allocate_c_buf();
    mois_100h = Rast_allocate_c_buf();
    mois_live = Rast_allocate_c_buf();
    vel = Rast_allocate_c_buf();
    dir = Rast_allocate_c_buf();
    slope = Rast_allocate_c_buf();
    aspect = Rast_allocate_c_buf();
    base = Rast_allocate_c_buf();
    max = Rast_allocate_c_buf();
    maxdir = Rast_allocate_c_buf();
    if (spotting) {
	spotdist = Rast_allocate_c_buf();
	elev = Rast_allocate_c_buf();
	map_elev = (CELL *) G_calloc(nrows * ncols, sizeof(CELL));
    }

    /*  Open input cell layers for reading  */

    fuel_fd = Rast_open_old(parm.model->answer, "");

    if (parm.mois_1h->answer)
	mois_1h_fd = Rast_open_old(parm.mois_1h->answer, "");

    if (parm.mois_10h->answer)
	mois_10h_fd = Rast_open_old(parm.mois_10h->answer,"");

    if (parm.mois_100h->answer)
	mois_100h_fd = Rast_open_old(parm.mois_100h->answer, "");

    mois_live_fd = Rast_open_old(parm.mois_live->answer, "");

    if (parm.vel->answer)
	vel_fd = Rast_open_old(parm.vel->answer, "");

    if (parm.dir->answer)
	dir_fd = Rast_open_old(parm.dir->answer, "");

    if (parm.slope->answer)
	slope_fd = Rast_open_old(parm.slope->answer, "");

    if (parm.aspect->answer)
	aspect_fd = Rast_open_old(parm.aspect->answer, "");

    if (spotting)
	elev_fd = Rast_open_old(parm.elev->answer, "");

    base_fd = Rast_open_c_new(name_base);
    max_fd = Rast_open_c_new(name_max);
    maxdir_fd = Rast_open_c_new(name_maxdir);
    if (spotting)
	spotdist_fd = Rast_open_c_new(name_spotdist);

    /*compute weights, combined wo, and combined sigma */
    /*wo[model] -- simple sum of WO[class][model] by all fuel subCLASS */
    /*sigma[model] -- weighted sum of SIGMA[class][model] by all fuel subCLASS       *epsilon[class][model] */
    for (model = 1; model <= 13; model++) {
	class_sum = 0.0;
	wo_dead[model] = 0.0;
	sigma[model] = 0.0;
	for (class = 0; class <= 3; class++) {
	    class_sum = class_sum + WO[class][model] * SIGMA[class][model];
	    if (SIGMA[class][model] > 0.0) {
		epsilon[class][model] = exp(-138.0 / SIGMA[class][model]);
	    }
	    else {
		epsilon[class][model] = 0.0;
	    }
	}
	for (class = 0; class <= 3; class++) {
	    Ffactor_all[class][model] =
		WO[class][model] * SIGMA[class][model] / class_sum;
	    sigma[model] =
		sigma[model] +
		SIGMA[class][model] * Ffactor_all[class][model];
	}
	class_sum = 0.0;
	for (class = 0; class <= 2; class++) {
	    wo_dead[model] = wo_dead[model] + WO[class][model];
	    class_sum = class_sum + WO[class][model] * SIGMA[class][model];
	}
	for (class = 0; class <= 2; class++) {
	    Ffactor_in_dead[class][model] =
		WO[class][model] * SIGMA[class][model] / class_sum;
	}

	/* compute G factor for each of the 6 subclasses */
	G1 = 0.0;
	G2 = 0.0;
	G3 = 0.0;
	G4 = 0.0;
	G5 = 0.0;
	for (class = 0; class <= 2; class++) {
	    if (SIGMA[class][model] >= 1200)
		G1 = G1 + Ffactor_in_dead[class][model];
	    if (SIGMA[class][model] < 1200 && SIGMA[class][model] >= 192)
		G2 = G2 + Ffactor_in_dead[class][model];
	    if (SIGMA[class][model] < 192 && SIGMA[class][model] >= 96)
		G3 = G3 + Ffactor_in_dead[class][model];
	    if (SIGMA[class][model] < 96 && SIGMA[class][model] >= 48)
		G4 = G4 + Ffactor_in_dead[class][model];
	    if (SIGMA[class][model] < 48 && SIGMA[class][model] >= 16)
		G5 = G5 + Ffactor_in_dead[class][model];
	}
	for (class = 0; class <= 2; class++) {
	    if (SIGMA[class][model] >= 1200)
		Gfactor_in_dead[class][model] = G1;
	    if (SIGMA[class][model] < 1200 && SIGMA[class][model] >= 192)
		Gfactor_in_dead[class][model] = G2;
	    if (SIGMA[class][model] < 192 && SIGMA[class][model] >= 96)
		Gfactor_in_dead[class][model] = G3;
	    if (SIGMA[class][model] < 96 && SIGMA[class][model] >= 48)
		Gfactor_in_dead[class][model] = G4;
	    if (SIGMA[class][model] < 48 && SIGMA[class][model] >= 16)
		Gfactor_in_dead[class][model] = G5;
	    if (SIGMA[class][model] < 16)
		Gfactor_in_dead[class][model] = 0.0;
	}

	Ffactor_dead[model] =
	    class_sum / (class_sum + WO[3][model] * SIGMA[3][model]);
	Ffactor_live[model] = 1 - Ffactor_dead[model];
    }

    /*if considering spotting, read elevation map into an array */
    if (spotting)
	for (row = 0; row < nrows; row++) {
	    Rast_get_c_row(elev_fd, elev, row);
	    for (col = 0; col < ncols; col++)
		DATA(map_elev, row, col) = elev[col];
	}

    /*major computation: compute ROSs one cell a time */
    for (row = 0; row < nrows; row++) {
	G_percent(row, nrows, 2);
	Rast_get_c_row(fuel_fd, fuel, row);
	if (parm.mois_1h->answer)
	    Rast_get_c_row(mois_1h_fd, mois_1h, row);
	if (parm.mois_10h->answer)
	    Rast_get_c_row(mois_10h_fd, mois_10h, row);
	if (parm.mois_100h->answer)
	    Rast_get_c_row(mois_100h_fd, mois_100h, row);
	Rast_get_c_row(mois_live_fd, mois_live, row);
	if (parm.vel->answer)
	    Rast_get_c_row(vel_fd, vel, row);
	if (parm.dir->answer)
	    Rast_get_c_row(dir_fd, dir, row);
	if (parm.slope->answer)
	    Rast_get_c_row(slope_fd, slope, row);
	if (parm.aspect->answer)
	    Rast_get_c_row(aspect_fd, aspect, row);

	/*initialize cell buffers for output map layers */
	for (col = 0; col < ncols; col++) {
	    base[col] = max[col] = maxdir[col] = 0;
	    if (spotting)
		spotdist[col] = 0;
	}

	for (col = 0; col < ncols; col++) {
	    /*check if a fuel is within the 13 models,
	     *if not, no processing; useful when no data presents*/
	    if (fuel[col] < 1 || fuel[col] > 13)
		continue;
	    if (parm.mois_1h->answer)
		moisture[0] = 0.01 * mois_1h[col];
	    if (parm.mois_10h->answer)
		moisture[1] = 0.01 * mois_10h[col];
	    if (parm.mois_100h->answer)
		moisture[2] = 0.01 * mois_100h[col];
	    moisture[3] = 0.01 * mois_live[col];
	    if (parm.aspect->answer)
		aspect[col] = (630 - aspect[col]) % 360;

	    /* assign some dead fuel moisture if not completely 
	     *     given based on emperical relationship*/
	    if (!(parm.mois_10h->answer || parm.mois_100h->answer)) {
		moisture[1] = moisture[0] + 0.01;
		moisture[2] = moisture[0] + 0.02;
	    }
	    if (!(parm.mois_1h->answer || parm.mois_100h->answer)) {
		moisture[0] = moisture[1] - 0.01;
		moisture[2] = moisture[1] + 0.01;
	    }
	    if (!(parm.mois_1h->answer || parm.mois_10h->answer)) {
		moisture[0] = moisture[2] - 0.02;
		moisture[1] = moisture[2] - 0.01;
	    }
	    if (!(parm.mois_1h->answer) && parm.mois_10h->answer &&
		parm.mois_100h->answer)
		moisture[0] = moisture[1] - 0.01;
	    if (!(parm.mois_10h->answer) && parm.mois_1h->answer &&
		parm.mois_100h->answer)
		moisture[1] = moisture[0] + 0.01;
	    if (!(parm.mois_100h->answer) && parm.mois_1h->answer &&
		parm.mois_10h->answer)
		moisture[2] = moisture[1] + 0.01;

	    /*compute xmext, moisture of extinction of live fuels */
	    wmfd = 0.0;
	    fined = 0.0;
	    if (SIGMA[3][fuel[col]] > 0.0) {
		for (class = 0; class <= 2; class++) {
		    if (SIGMA[class][fuel[col]] == 0.0)
			continue;
		    fined =
			fined +
			WO[class][fuel[col]] * exp(-138.0 /
						   SIGMA[class][fuel[col]]);
		    wmfd =
			wmfd +
			WO[class][fuel[col]] * exp(-138.0 /
						   SIGMA[class][fuel[col]]) *
			moisture[class];
		}
		fdmois = wmfd / fined;
		finel = WO[3][fuel[col]] * exp(-500.0 / SIGMA[3][fuel[col]]);
		xmext =
		    2.9 * (fined / finel) * (1 - fdmois / MX[fuel[col]]) -
		    0.226;
	    }
	    else
		xmext = MX[fuel[col]];
	    if (xmext < MX[fuel[col]])
		xmext = MX[fuel[col]];

	    /*compute other intermediate values */
	    Mf_dead = 0.0;
	    wn_dead = 0.0;
	    class_sum = 0.0;
	    for (class = 0; class <= 2; class++) {
		Mf_dead =
		    Mf_dead +
		    moisture[class] * Ffactor_in_dead[class][fuel[col]];
		wn_dead =
		    wn_dead +
		    WO[class][fuel[col]] * Gfactor_in_dead[class][fuel[col]] *
		    (1 - ST);
		Qig[class] = 250 + 1116 * moisture[class];
		class_sum =
		    class_sum +
		    Ffactor_all[class][fuel[col]] *
		    epsilon[class][fuel[col]] * Qig[class];
	    }
	    etaM_dead =
		1.0 - 2.59 * (Mf_dead / MX[fuel[col]]) +
		5.11 * (Mf_dead / MX[fuel[col]]) * (Mf_dead / MX[fuel[col]]) -
		3.52 * (Mf_dead / MX[fuel[col]]) * (Mf_dead / MX[fuel[col]]) *
		(Mf_dead / MX[fuel[col]]);
	    if (Mf_dead >= MX[fuel[col]])
		etaM_dead = 0.0;
	    etaM_live =
		1.0 - 2.59 * (moisture[3] / xmext) +
		5.11 * (moisture[3] / xmext) * (moisture[3] / xmext) -
		3.52 * (moisture[3] / xmext) * (moisture[3] / xmext) *
		(moisture[3] / xmext);
	    if (moisture[3] >= xmext)
		etaM_live = 0.0;
	    wn_live = WO[3][fuel[col]] * (1 - ST);
	    Qig[3] = 250 + 1116 * moisture[3];
	    class_sum =
		class_sum +
		Ffactor_all[3][fuel[col]] * epsilon[3][fuel[col]] * Qig[3];

	    /*final computations */
	    rhob = (wo_dead[fuel[col]] + WO[3][fuel[col]]) / DELTA[fuel[col]];
	    beta = rhob / rhop;
	    betaop = 3.348 / pow(sigma[fuel[col]], 0.8189);
	    A = 133 / pow(sigma[fuel[col]], 0.7913);
	    gammamax =
		pow(sigma[fuel[col]],
		    1.5) / (495 + 0.0594 * pow(sigma[fuel[col]], 1.5));
	    gamma =
		gammamax * pow(beta / betaop,
			       A) * exp(A * (1 - beta / betaop));
	    xi = exp((0.792 + 0.681 * pow(sigma[fuel[col]], 0.5)) * (beta +
								     0.1)) /
		(192 + 0.2595 * sigma[fuel[col]]);
	    IR = gamma * h * (wn_dead * etaM_dead +
			      wn_live * etaM_live) * etas;

	    R0 = IR * xi / (rhob * class_sum);

	    if (parm.vel->answer && parm.dir->answer) {
		C = 7.47 * exp(-0.133 * pow(sigma[fuel[col]], 0.55));
		B = 0.02526 * pow(sigma[fuel[col]], 0.54);
		E = 0.715 * exp(-0.000359 * sigma[fuel[col]]);
		phiw = C * pow((double)vel[col], B) / pow(beta / betaop, E);
	    }
	    else
		phiw = 0.0;

	    if (parm.slope->answer && parm.aspect->answer) {
		phis =
		    5.275 * pow(beta,
				-0.3) * tan(0.01745 * slope[col]) *
		    tan(0.01745 * slope[col]);
	    }
	    else
		phis = 0.0;

	    /*compute the maximum ROS R and its direction, 
	     *vector adding for the effect of wind and slope*/
	    if (parm.dir->answer && parm.aspect->answer) {
		sin_fac =
		    phiw * sin(0.01745 * dir[col]) +
		    phis * sin(0.01745 * aspect[col]);
		cos_fac =
		    phiw * cos(0.01745 * dir[col]) +
		    phis * cos(0.01745 * aspect[col]);
		phi_ws = sqrt(sin_fac * sin_fac + cos_fac * cos_fac);
		Rdir = atan2(sin_fac, cos_fac) / 0.01745;
	    }
	    else if (parm.dir->answer && !(parm.aspect->answer)) {
		phi_ws = phiw;
		Rdir = dir[col];
	    }
	    else if (!(parm.dir->answer) && parm.aspect->answer) {
		phi_ws = phis;
		Rdir = (float)aspect[col];
	    }
	    else {
		phi_ws = 0.0;
		Rdir = 0.0;
	    }
	    R = R0 * (1 + phi_ws);
	    if (Rdir < 0.0)
		Rdir = Rdir + 360;
	    /*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); */

	    /*maximum spotting distance */
	    if (spotting)
		spotdist[col] =
		    spot_dist(fuel[col], R, vel[col], Rdir, row, col);

	    /*to avoid 0 R, convert ft./min to cm/min */
	    R0 = 30.5 * R0;
	    R = 30.5 * R;
	    /*4debugging            R0 = R0/30.5/1.1; R = R/30.5/1.1; */

	    base[col] = (int)R0;
	    max[col] = (int)R;
	    maxdir[col] = (int)Rdir;
	    /*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); */
	}
	Rast_put_row(base_fd, base, CELL_TYPE);
	Rast_put_row(max_fd, max, CELL_TYPE);
	Rast_put_row(maxdir_fd, maxdir, CELL_TYPE);
	if (spotting)
	    Rast_put_row(spotdist_fd, spotdist, CELL_TYPE);
    }
    G_percent(row, nrows, 2);

    Rast_close(fuel_fd);
    if (parm.mois_1h->answer)
	Rast_close(mois_1h_fd);
    if (parm.mois_10h->answer)
	Rast_close(mois_10h_fd);
    if (parm.mois_100h->answer)
	Rast_close(mois_100h_fd);
    Rast_close(mois_live_fd);
    if (parm.vel->answer)
	Rast_close(vel_fd);
    if (parm.dir->answer)
	Rast_close(dir_fd);
    if (parm.slope->answer)
	Rast_close(slope_fd);
    if (parm.aspect->answer)
	Rast_close(aspect_fd);
    Rast_close(base_fd);
    Rast_close(max_fd);
    Rast_close(maxdir_fd);
    if (spotting) {
	Rast_close(spotdist_fd);
	Rast_close(spotdist_fd);
	G_free(map_elev);
    }

    /*      
       for (model = 1; model <= 13; model++) {
       if (model == 1)
       printf("\n           Grass and Grass-dominated\n");
       else if (model == 4)
       printf("             Chaparral and Shrubfields\n");
       else if (model == 8)
       printf("                   Timber Litter\n");
       else if (model == 11)
       printf("                   Logging Slash\n");
       printf("Model %2d   ", model);
       for (class = 0; class <= 3; class++) 
       printf("%4.0f/%.3f ", SIGMA[class][model], WO[class][model]);
       printf("  %.1f  %.2f\n", DELTA[model], MX[model]);
       } 

       printf("\nWeight in All Fuel Subclasses:\n");
       for (model = 1; model <= 13; model++) {
       printf("model %2d  ", model);
       for (class = 0; class <= 3; class++) 
       printf("%.2f  ", Ffactor_all[class][model]);
       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);
       } 
       printf("\nWeight in Dead Fuel Subclasses, Dead Weight/Live Weight:\n");for (model = 1; model <= 13; model++) {
       printf("model %2d  ", model);
       for (class = 0; class <= 2; class++) 
       printf("%.2f  ", Ffactor_in_dead[class][model]);
       printf("%.2f/%.2f  model %2d\n", Ffactor_dead[model], Ffactor_live[model], model);
       } 
     */

    G_done_msg(_("Raster maps <%s>, <%s> and <%s> created."), name_base, name_max, name_maxdir);
    
    exit(EXIT_SUCCESS);
}
