
/****************************************************************************
 *
 * MODULE:       i.eb.hSEBAL01
 * AUTHOR(S):    Yann Chemin - yann.chemin@gmail.com
 * PURPOSE:      Calculates sensible heat flux by SEBAL iteration
 *               Delta T will be reassessed in the iterations !
 *               This has been seen in Bastiaanssen (1995), 
 *               later modified by Chemin and Alexandridis (2001).
 *               This code is implemented in Alexandridis et al. (2009).
 *		 2018: added map input for e_act since i.rh can generate it
 *		 2018: Fail convergence more gracefully and still write output
 *		 from MOD05 and MOD07 data (see GRASS-ADDONS)
 *
 * COPYRIGHT:    (C) 2002-2018 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.
 *
 * CHANGELOG:	
 *
 *****************************************************************************/

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

double **G_alloc_matrix(int rows, int cols)
{
    double **m;
    int i;

    m = (double **)G_calloc(rows, sizeof(double *));
    m[0] = (double *)G_calloc(rows * cols, sizeof(double));
    for (i = 1; i < rows; i++)
	m[i] = m[i - 1] + cols;

    return m;
}



int main(int argc, char *argv[])
{
    struct Cell_head cellhd;

    /* buffer for in, tmp and out raster */
    void *inrast_Rn, *inrast_g0;
    void *inrast_z0m, *inrast_t0dem;
    void *inrast_eact;
    DCELL *outrast;
    int nrows = 0, ncols = 0;
    int row = 0, col = 0;
    double m_row_wet = 0.0, m_col_wet = 0.0;
    double m_row_dry = 0.0, m_col_dry = 0.0;
    int infd_Rn, infd_g0;
    int infd_z0m, infd_t0dem;
    int infd_eact;
    int outfd;
    char *Rn, *g0;
    char *z0m, *t0dem, *eact;
    char *h0;

    double ustar;
    struct History history;
    struct GModule *module;
    struct Option *input_Rn, *input_g0;
    struct Option *input_z0m, *input_t0dem, *input_ustar;
    struct Option *input_eact, *output;
    struct Option *input_row_wet, *input_col_wet;
    struct Option *input_row_dry, *input_col_dry;
    struct Flag *flag2, *flag3;

    /********************************/
    double xp = 0.0, yp = 0.0;
    double xmin = 0.0, ymin = 0.0;
    double xmax = 0.0, ymax = 0.0;
    double stepx = 0.0, stepy = 0.0;
    double latitude = 0.0, longitude = 0.0;
    int rowDry = 0, colDry = 0, rowWet = 0, colWet = 0;

    /********************************/
    xp = yp;
    yp = xp;
    xmin = ymin;
    ymin = xmin;
    xmax = ymax;
    ymax = xmax;
    stepx = stepy;
    stepy = stepx;
    latitude = longitude;
    longitude = latitude;
    rowDry = colDry;
    colDry = rowDry;
    rowWet = colWet;
    colWet = rowWet;

    /********************************/
    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("imagery"));
    G_add_keyword(_("energy balance"));
    G_add_keyword(_("soil moisture"));
    G_add_keyword(_("evaporative fraction"));
    G_add_keyword(_("SEBAL"));
    module->description =
	_("Computes sensible heat flux iteration SEBAL 01.");

    /* Define different options */
    input_Rn = G_define_standard_option(G_OPT_R_INPUT);
    input_Rn->key = "netradiation";
    input_Rn->description =
	_("Name of instantaneous net radiation raster map [W/m2]");

    input_g0 = G_define_standard_option(G_OPT_R_INPUT);
    input_g0->key = "soilheatflux";
    input_g0->description =
	_("Name of instantaneous soil heat flux raster map [W/m2]");

    input_z0m = G_define_standard_option(G_OPT_R_INPUT);
    input_z0m->key = "aerodynresistance";
    input_z0m->description =
	_("Name of aerodynamic resistance to heat momentum raster map [s/m]");

    input_t0dem = G_define_standard_option(G_OPT_R_INPUT);
    input_t0dem->key = "temperaturemeansealevel";
    input_t0dem->description =
	_("Name of altitude corrected surface temperature raster map [K]");

    input_eact = G_define_standard_option(G_OPT_R_INPUT);
    input_eact->key = "vapourpressureactual";
    input_eact->description =
	_("Name of the actual vapour pressure (e_act) map [KPa]");

    input_ustar = G_define_option();
    input_ustar->key = "frictionvelocitystar";
    input_ustar->type = TYPE_DOUBLE;
    input_ustar->required = YES;
    input_ustar->gisprompt = "old,value";
    input_ustar->answer = "0.32407";
    input_ustar->description =
	_("Value of the height independent friction velocity (u*) [m/s]");
    input_ustar->guisection = _("Parameters");

    input_row_wet = G_define_option();
    input_row_wet->key = "row_wet_pixel";
    input_row_wet->type = TYPE_DOUBLE;
    input_row_wet->required = NO;
    input_row_wet->description = _("Row value of the wet pixel");
    input_row_wet->guisection = _("Parameters");

    input_col_wet = G_define_option();
    input_col_wet->key = "column_wet_pixel";
    input_col_wet->type = TYPE_DOUBLE;
    input_col_wet->required = NO;
    input_col_wet->description = _("Column value of the wet pixel");
    input_col_wet->guisection = _("Parameters");

    input_row_dry = G_define_option();
    input_row_dry->key = "row_dry_pixel";
    input_row_dry->type = TYPE_DOUBLE;
    input_row_dry->required = NO;
    input_row_dry->description = _("Row value of the dry pixel");
    input_row_dry->guisection = _("Parameters");

    input_col_dry = G_define_option();
    input_col_dry->key = "column_dry_pixel";
    input_col_dry->type = TYPE_DOUBLE;
    input_col_dry->required = NO;
    input_col_dry->description = _("Column value of the dry pixel");
    input_col_dry->guisection = _("Parameters");

    output = G_define_standard_option(G_OPT_R_OUTPUT);
    output->description =
	_("Name for output sensible heat flux raster map [W/m2]");

    /* Define the different flags */
    flag2 = G_define_flag();
    flag2->key = 'a';
    flag2->description = _("Automatic wet/dry pixel (careful!)");

    flag3 = G_define_flag();
    flag3->key = 'c';
    flag3->description =
	_("Dry/Wet pixels coordinates are in image projection, not row/col");

    if (G_parser(argc, argv))
	exit(EXIT_FAILURE);

    /* get entered parameters */
    Rn = input_Rn->answer;
    g0 = input_g0->answer;
    z0m = input_z0m->answer;
    t0dem = input_t0dem->answer;
    eact = input_eact->answer;

    h0 = output->answer;

    ustar = atof(input_ustar->answer);

    /*If automatic flag, just forget the rest of options */
    if (flag2->answer)
	G_verbose_message(_("Automatic mode selected"));
    /*If not automatic & all pixels locations in col/row given */
    else if (!flag2->answer &&
	     input_row_wet->answer &&
	     input_col_wet->answer &&
	     input_row_dry->answer && 
             input_col_dry->answer) {
	m_row_wet = atof(input_row_wet->answer);
	m_col_wet = atof(input_col_wet->answer);
	m_row_dry = atof(input_row_dry->answer);
	m_col_dry = atof(input_col_dry->answer);
	/*If pixels locations are in projected coordinates */
	if (flag3->answer)
	    G_verbose_message(_("Manual wet/dry pixels in image coordinates"));
	G_verbose_message(_("Wet Pixel=> x:%f y:%f"), m_col_wet, m_row_wet);
	G_verbose_message(_("Dry Pixel=> x:%f y:%f"), m_col_dry, m_row_dry);
    }
    /*If not automatic & missing any of the pixel location, Fatal Error */
    else {
	G_fatal_error(_("Either auto-mode either wet/dry pixels coordinates should be provided!"));
    }

    /* check legal output name */
    if (G_legal_filename(h0) < 0)
	G_fatal_error(_("<%s> is an illegal name"), h0);

    infd_Rn = Rast_open_old(Rn, "");
    infd_g0 = Rast_open_old(g0, "");
    infd_z0m = Rast_open_old(z0m, "");
    infd_t0dem = Rast_open_old(t0dem, "");
    infd_eact = Rast_open_old(eact, "");

    Rast_get_cellhd(Rn, "", &cellhd);
    Rast_get_cellhd(g0, "", &cellhd);
    Rast_get_cellhd(z0m, "", &cellhd);
    Rast_get_cellhd(t0dem, "", &cellhd);
    Rast_get_cellhd(eact, "", &cellhd);

    /* Allocate input buffer */
    inrast_Rn = Rast_allocate_d_buf();
    inrast_g0 = Rast_allocate_d_buf();
    inrast_z0m = Rast_allocate_d_buf();
    inrast_t0dem = Rast_allocate_d_buf();
    inrast_eact = Rast_allocate_d_buf();

    /***************************************************/
    /* Setup pixel location variables */

    /***************************************************/
    stepx = cellhd.ew_res;
    stepy = cellhd.ns_res;

    xmin = cellhd.west;
    xmax = cellhd.east;
    ymin = cellhd.south;
    ymax = cellhd.north;

    nrows = Rast_window_rows();
    ncols = Rast_window_cols();

    /***************************************************/
    /* Allocate output buffer */

    /***************************************************/
    outrast = Rast_allocate_d_buf();
    outfd = Rast_open_new(h0, DCELL_TYPE);

    /***************************************************/
    /* Allocate memory for temporary images            */

    /***************************************************/
    double **d_Roh, **d_Rah;
    double **d_z0m, **d_t0dem, **d_eact;

    if ((d_Roh = G_alloc_matrix(nrows, ncols)) == NULL)
	G_verbose_message("Unable to allocate memory for temporary d_Roh image");
    if ((d_Rah = G_alloc_matrix(nrows, ncols)) == NULL)
	G_verbose_message("Unable to allocate memory for temporary d_Rah image");
    if ((d_z0m = G_alloc_matrix(nrows, ncols)) == NULL)
	G_verbose_message("Unable to allocate memory for temporary d_z0m image");
    if ((d_t0dem = G_alloc_matrix(nrows, ncols)) == NULL)
	G_verbose_message("Unable to allocate memory for temporary d_t0dem image");
    if ((d_eact = G_alloc_matrix(nrows, ncols)) == NULL)
	G_verbose_message("Unable to allocate memory for temporary d_eact image");

    /***************************************************/
    /* MANUAL T0DEM WET/DRY PIXELS */
    DCELL d_u5 = 0.0;
    DCELL d_roh1 = 0.0;
    DCELL d_rah1 = 0.0;
    DCELL d_Rn_dry = 0.0, d_g0_dry = 0.0;
    DCELL d_Rah_dry = 0.0, d_Roh_dry = 0.0;
    DCELL d_t0dem_dry = 0.0, d_t0dem_wet = 0.0;

    if (flag2->answer) {
	/* Process tempk min / max pixels */
	/* Internal use only */
	DCELL d_Rn_wet = 0.0, d_g0_wet = 0.0;
	DCELL d_Rn, d_g0, d_h0;
	DCELL t0dem_min = 1000.0, t0dem_max = 0.0;

	/*********************/
	for (row = 0; row < nrows; row++) {
	    G_percent(row, nrows, 2);
	    Rast_get_d_row(infd_t0dem, inrast_t0dem, row);
	    Rast_get_d_row(infd_z0m, inrast_z0m, row);
	    Rast_get_d_row(infd_Rn, inrast_Rn, row);
	    Rast_get_d_row(infd_g0, inrast_g0, row);
	    /*process the data */
	    for (col = 0; col < ncols; col++) {
		d_t0dem[row][col] = (double) ((DCELL *) inrast_t0dem)[col];
		d_z0m[row][col] = (double) ((DCELL *) inrast_z0m)[col];
		d_Rn = ((DCELL *) inrast_Rn)[col];
		d_g0 = ((DCELL *) inrast_g0)[col];
		if (Rast_is_d_null_value(&d_Rn) ||
		    Rast_is_d_null_value(&d_g0)) {
			d_Roh[row][col] = -999.9;
			d_Rah[row][col] = -999.9;
		    /* do nothing */
		}
		else {
		    if (d_t0dem[row][col] <= 250.0 || d_z0m[row][col] < 0.01) {
			d_Roh[row][col] = -999.9;
			d_Rah[row][col] = -999.9;
			/* do nothing */
		    }
		    else {
			d_h0 = d_Rn - d_g0;
			d_u5 = (ustar / 0.41) * log(5 / d_z0m[row][col]);
		        d_rah1 =(1/(d_u5*pow(0.41,2)))*log(5/d_z0m[row][col])*log(5/(d_z0m[row][col]*0.1));
		        d_roh1 =((998-d_eact[row][col])/(d_t0dem[row][col]*2.87))+(d_eact[row][col]/(d_t0dem[row][col]*4.61));
		        if (d_roh1 > 5)
		            d_roh1 = 1.0;
		        else
		        d_roh1 =((1000 - 4.65) / (d_t0dem[row][col] * 2.87))+(4.65 / (d_t0dem[row][col] * 4.61));

			d_Roh[row][col] = d_roh1;
			d_Rah[row][col] = d_rah1;

			if (d_t0dem[row][col] < t0dem_min &&
			    d_Rn > 0.0 && d_g0 > 0.0 && d_h0 > 0.0 &&
			    d_h0 < 100.0 && d_roh1 > 0.001 && d_rah1 > 0.001) {
			    t0dem_min = d_t0dem[row][col];
			    d_t0dem_wet = d_t0dem[row][col];
			    d_Rn_wet = d_Rn;
			    d_g0_wet = d_g0;
			    colWet = col;
			    rowWet = row;
			}
			if (d_t0dem[row][col] > t0dem_max &&
			    d_Rn > 0.0 && d_g0 > 0.0 && d_h0 > 100.0 &&
			    d_h0 < 500.0 && d_roh1 > 0.001 && d_rah1 > 0.001) {
			    t0dem_max = d_t0dem[row][col];
			    d_t0dem_dry = d_t0dem[row][col];
			    d_Rn_dry = d_Rn;
			    d_g0_dry = d_g0;
			    colDry = col;
			    rowDry = row;
			    d_Roh_dry = d_roh1;
			    d_Rah_dry = d_rah1;
			}
		    }
		}
	    }
	}
	G_verbose_message("row_wet=%d\tcol_wet=%d", rowWet, colWet);
	G_verbose_message("row_dry=%d\tcol_dry=%d", rowDry, colDry);
	G_verbose_message("g0_wet=%f", d_g0_wet);
	G_verbose_message("Rn_wet=%f", d_Rn_wet);
	G_verbose_message("LE_wet=%f", d_Rn_wet - d_g0_wet);
	G_verbose_message("t0dem_wet=%f", d_t0dem_wet);
	G_verbose_message("t0dem_dry=%f", d_t0dem_dry);
	G_verbose_message("rnet_dry=%f", d_Rn_dry);
	G_verbose_message("g0_dry=%f", d_g0_dry);
	G_verbose_message("h0_dry=%f", d_Rn_dry - d_g0_dry);
	G_verbose_message("Rah_dry=%f", d_Rah_dry);
	G_verbose_message("Roh_dry=%f", d_Roh_dry);
	G_verbose_message("auto config completed");
    }	/* END OF FLAG2 */

    /* MANUAL T0DEM WET/DRY PIXELS */
    /*DRY PIXEL */
    if (flag3->answer) {
	/*Calculate coordinates of row/col from projected ones */
	row = (int)((ymax - m_row_dry) / (double)stepy);
	col = (int)((m_col_dry - xmin) / (double)stepx);
	G_verbose_message("Manual: Dry Pixel | row:%i col:%i", row, col);
    	rowDry = row;
    	colDry = col;
    }
    else {
	row = rowDry;
	col = colDry;
	G_verbose_message("Dry Pixel | row:%i col:%i", row, col);
    }
    /*WET PIXEL */
    if (flag3->answer) {
	/*Calculate coordinates of row/col from projected ones */
	row = (int)((ymax - m_row_wet) / (double)stepy);
	col = (int)((m_col_wet - xmin) / (double)stepx);
	G_verbose_message("Manual: Wet Pixel | row:%i col:%i", row, col);
    	rowWet = row;
    	colWet = col;
    }
    else {
	row = rowWet;
	col = colWet;
	G_verbose_message("Wet Pixel | row:%i col:%i", row, col);
    }
    /* END OF MANUAL WET/DRY PIXELS */

    /* Extract end-members */
    Rast_get_d_row(infd_Rn, inrast_Rn, rowDry);
    Rast_get_d_row(infd_g0, inrast_g0, rowDry);
    Rast_get_d_row(infd_t0dem, inrast_t0dem, rowDry);
    d_Rn_dry = ((DCELL *) inrast_Rn)[colDry];
    d_g0_dry = ((DCELL *) inrast_g0)[colDry];
    d_t0dem_dry = ((DCELL *) inrast_t0dem)[colDry];

    Rast_get_d_row(infd_t0dem, inrast_t0dem, rowWet);
    d_t0dem_wet = ((DCELL *) inrast_t0dem)[colWet];

    double h_dry;
    h_dry = d_Rn_dry - d_g0_dry;
    G_verbose_message("h_dry = %f", h_dry);
    G_verbose_message("t0dem_dry = %f", d_t0dem_dry);
    G_verbose_message("t0dem_wet = %f", d_t0dem_wet);

    DCELL d_rah_dry = d_Rah_dry;
    DCELL d_roh_dry = d_Roh_dry;

    DCELL d_h1, d_h2, d_h3;
    DCELL d_rah2, d_rah3;
    DCELL d_L, d_x, d_psih, d_psim;

    /* INITIALIZATION */
    /******************************************************/
    /*If d_rah_dry and d_roh_dry are not auto found, then:*/
    if(d_Rah_dry==0.0 && d_Roh_dry==0.0){
    /***************************************************/
    for (row = 0; row < nrows; row++) {
	G_percent(row, nrows, 2);
	/* read every cell in the line buffers */
	for (col = 0; col < ncols; col++) {
	    d_eact[row][col] = (double) ((DCELL *) inrast_eact)[col];
	    d_z0m[row][col] = (double) ((DCELL *) inrast_z0m)[col];
	    if (Rast_is_d_null_value(&d_t0dem[row][col]) ||
		Rast_is_d_null_value(&d_eact[row][col]) ||
		Rast_is_d_null_value(&d_z0m[row][col])) {
		Rast_set_d_null_value(&outrast[col], 1);
		d_Roh[row][col] = -999.9;
		d_Rah[row][col] = -999.9;
		if (row == rowDry && col == colDry) {	/*collect dry pix info */
		    d_rah_dry = d_Rah[row][col];
		    d_roh_dry = d_Roh[row][col];
		    G_verbose_message("Init: d_rah_dry=%f d_roh_dry=%f",d_rah_dry,d_roh_dry);
		}
	    }
	    else {
		d_u5 = (ustar / 0.41) * log(5 / d_z0m[row][col]);
		d_rah1 =(1/(d_u5*pow(0.41,2)))*log(5/d_z0m[row][col])*log(5/(d_z0m[row][col]*0.1));
		d_roh1 =((998-d_eact[row][col])/(d_t0dem[row][col]*2.87))+(d_eact[row][col]/(d_t0dem[row][col]*4.61));
		if (d_roh1 > 5)
		    d_roh1 = 1.0;
		else
		    d_roh1 =((1000 - 4.65) / (d_t0dem[row][col] * 2.87))+(4.65 / (d_t0dem[row][col] * 4.61));
		if (row == rowDry && col == colDry) {	
		    /*collect dry pix info */
		    d_rah_dry = d_rah1;
		    d_roh_dry = d_roh1;
		    G_verbose_message("row=%d col=%d", row, col);
		    G_verbose_message("ustar=%f", ustar);
		    G_verbose_message("d_u5=%f", d_u5);
		    G_verbose_message("d_t0dem_dry=%f", d_t0dem[row][col]);
		    G_verbose_message("d_z0m_dry=%f", d_z0m[row][col]);
		    G_verbose_message("d_rah_dry=%f", d_rah_dry);
		    G_verbose_message("d_roh_dry=%f", d_roh_dry);
		}
		d_Roh[row][col] = d_roh1;
		d_Rah[row][col] = d_rah1;
	    }
	}
    }
    } /*END OF if !d_Rah_dry and !d_Roh_dry*/
    DCELL d_dT_dry;

    /*Calculate dT_dry */
    d_dT_dry = (h_dry * d_rah_dry) / (1004 * d_roh_dry);
    double a, b;

    /*Calculate coefficients for next dT equation */
    /*a = 1.0/ ((d_dT_dry-0.0) / (d_t0dem_dry-d_t0dem_wet)); */
    /*b = ( a * d_t0dem_wet ) * (-1.0); */
    double sumx = d_t0dem_wet + d_t0dem_dry;
    double sumy = d_dT_dry + 0.0;
    double sumx2 = pow(d_t0dem_wet, 2) + pow(d_t0dem_dry, 2);
    double sumxy = (d_t0dem_wet * 0.0) + (d_t0dem_dry * d_dT_dry);

    a = (sumxy - ((sumx * sumy) / 2.0)) / (sumx2 - (pow(sumx, 2) / 2.0));
    b = (sumy - (a * sumx)) / 2.0;
    G_verbose_message("d_dT_dry=%f", d_dT_dry);
    G_verbose_message("dT1=%f * t0dem + (%f)", a, b);
    DCELL d_h_dry = 0.0;
    if(isnan(a) || isnan(b)){
    	G_free(outrast);
    	Rast_close(outfd);
	G_fatal_error("Delta T Convergence failed, exiting prematurily, please check output");
    }

    /* ITERATION 1 */
    /***************************************************/
    /***************************************************/
    /* outfd = Rast_open_old(h0, ""); */
    /* Rast_get_cellhd(h0, "", &cellhd); */
    for (row = 0; row < nrows; row++) {
	G_percent(row, nrows, 2);
	/* read every cell in the line buffers */
	for (col = 0; col < ncols; col++) {
	    d_rah1 = d_Rah[row][col];
	    d_roh1 = d_Roh[row][col];
	    if (Rast_is_d_null_value(&d_t0dem[row][col]) ||
		Rast_is_d_null_value(&d_z0m[row][col])) {
		Rast_set_d_null_value(&outrast[col], 1);
	    }
	    else {
		if (d_rah1 < 1.0)
		    d_h1 = 0.0;
		else
		    d_h1 = (1004 * d_roh1) * (a * d_t0dem[row][col] + b) / d_rah1;
		if (d_h1 < 0 && d_h1 > -50) {
		    d_h1 = 0.0;
		}
		if (d_h1 < -50 || d_h1 > 1000) {
		    Rast_set_d_null_value(&outrast[col], 1);
		}else{
			outrast[col] = d_h1;
			d_L =-1004*d_roh1*pow(ustar,3)*d_t0dem[row][col]/(d_h1*9.81*0.41);
			d_x = pow((1 - 16 * (5 / d_L)), 0.25);
			d_psim =2 * log((1 + d_x) / 2) + log((1 + pow(d_x, 2)) / 2) -
		    		2 * atan(d_x) + 0.5 * M_PI;
			d_psih = 2 * log((1 + pow(d_x, 2)) / 2);
			d_u5 = (ustar / 0.41) * log(5 / d_z0m[row][col]);
			d_rah2 =(1 / (d_u5 * pow(0.41, 2))) * log((5 / d_z0m[row][col]) - d_psim)
		    	* log((5 / (d_z0m[row][col] * 0.1)) - d_psih);
			if (row == rowDry && col == colDry) {	/*collect dry pix info */
		    		d_h1 = (1004 * d_roh1) * (a * d_t0dem[row][col] + b) / d_rah_dry;
				d_L =-1004*d_roh1*pow(ustar,3)*d_t0dem[row][col]/(d_h1*9.81*0.41);
				d_x = pow((1 - 16 * (5 / d_L)), 0.25);
				d_psim =2 * log((1 + d_x) / 2) + log((1 + pow(d_x, 2)) / 2) -
		    			2 * atan(d_x) + 0.5 * M_PI;
				d_psih = 2 * log((1 + pow(d_x, 2)) / 2);
				d_u5 = (ustar / 0.41) * log(5 / d_z0m[row][col]);
				d_rah2 =(1 / (d_u5 * pow(0.41, 2))) * log((5 / d_z0m[row][col]) - d_psim)
		    			* log((5 / (d_z0m[row][col] * 0.1)) - d_psih);
		    		d_rah_dry = d_rah2;
		    		d_h_dry = d_h1;
				G_verbose_message("d_z0m (dry)=%f",d_z0m[row][col]);
				G_verbose_message("d_rah1 (dry)=%f",d_rah1);
				G_verbose_message("d_rah2 (dry)=%f",d_rah2);
				G_verbose_message("d_h1 (dry)=%f",d_h1);
			}
			d_Rah[row][col] = d_rah1;
		}
	    }
	}
    }

    /*Calculate dT_dry */
    G_verbose_message("d_h_dry=%f",d_h_dry);
    G_verbose_message("d_rah_dry=%f",d_rah_dry);
    G_verbose_message("d_roh_dry=%f",d_roh_dry);
    d_dT_dry = (d_h_dry * d_rah_dry) / (1004 * d_roh_dry);
    /*Calculate coefficients for next dT equation */
    /*      a = (d_dT_dry-0)/(d_t0dem_dry-d_t0dem_wet); */
    /*      b = (-1.0) * ( a * d_t0dem_wet ); */
    /*      G_verbose_message("d_dT_dry=%f",d_dT_dry); */
    /*      G_verbose_message("dT2=%f * t0dem + (%f)", a, b); */
    sumx = d_t0dem_wet + d_t0dem_dry;
    sumy = d_dT_dry + 0.0;
    sumx2 = pow(d_t0dem_wet, 2) + pow(d_t0dem_dry, 2);
    sumxy = (d_t0dem_wet * 0.0) + (d_t0dem_dry * d_dT_dry);
    a = (sumxy - ((sumx * sumy) / 2.0)) / (sumx2 - (pow(sumx, 2) / 2.0));
    b = (sumy - (a * sumx)) / 2.0;
    G_verbose_message("d_dT_dry=%f", d_dT_dry);
    G_verbose_message("dT2=%f * t0dem + (%f)", a, b);
    if(isnan(a) || isnan(b)){
    	G_free(outrast);
    	Rast_close(outfd);
    	G_fatal_error("Delta T Convergence failed, exiting prematurily, please check output");
    }

    /* ITERATION 2 */
    /***************************************************/
    /***************************************************/
    for (row = 0; row < nrows; row++) {
	G_percent(row, nrows, 2);
	/* read every cell in the line buffers */
	for (col = 0; col < ncols; col++) {
	    d_rah2 = d_Rah[row][col];
	    d_roh1 = d_Roh[row][col];
	    if (Rast_is_d_null_value(&d_t0dem[row][col]) ||
		Rast_is_d_null_value(&d_z0m[row][col])) {
		Rast_set_d_null_value(&outrast[col], 1);
	    }
	    else {
		if (d_rah2 < 1.0) {
		    d_h2 = 0.0;
		}
		else {
		    d_h2 = (1004 * d_roh1) * (a * d_t0dem[row][col] + b) / d_rah2;
		}
		if (d_h2 < 0 && d_h2 > -50) {
		    d_h2 = 0.0;
		}
		if (d_h2 < -50 || d_h2 > 1000) {
		    Rast_set_d_null_value(&outrast[col], 1);
		}
		outrast[col] = d_h2;
		d_L =
		    -1004 * d_roh1 * pow(ustar,
		    3) * d_t0dem[row][col] / (d_h2 * 9.81 * 0.41);
		d_x = pow((1 - 16 * (5 / d_L)), 0.25);
		d_psim = 2 * log((1 + d_x) / 2) + log((1 + pow(d_x, 2)) / 2) -
		    2 * atan(d_x) + 0.5 * M_PI;
		d_psih = 2 * log((1 + pow(d_x, 2)) / 2);
		d_u5 = (ustar / 0.41) * log(5 / d_z0m[row][col]);
		d_rah3 =
		    (1 / (d_u5 * pow(0.41, 2))) * log((5 / d_z0m[row][col]) -
		    d_psim) * log((5 / (d_z0m[row][col] * 0.1)) - d_psih);
		if (row == rowDry && col == colDry) {	/*collect dry pix info */
		    d_rah_dry = d_rah3;
		    d_h_dry = d_h2;
		}
		d_Rah[row][col] = d_rah2;
	    }
	}
    }

    /*Calculate dT_dry */
    d_dT_dry = (d_h_dry * d_rah_dry) / (1004 * d_roh_dry);
    /*Calculate coefficients for next dT equation */
    /*      a = (d_dT_dry-0)/(d_t0dem_dry-d_t0dem_wet); */
    /*      b = (-1.0) * ( a * d_t0dem_wet ); */
    /*      G_verbose_message("d_dT_dry=%f",d_dT_dry); */
    /*      G_verbose_message("dT3=%f * t0dem + (%f)", a, b); */
    sumx = d_t0dem_wet + d_t0dem_dry;
    sumy = d_dT_dry + 0.0;
    sumx2 = pow(d_t0dem_wet, 2) + pow(d_t0dem_dry, 2);
    sumxy = (d_t0dem_wet * 0.0) + (d_t0dem_dry * d_dT_dry);
    a = (sumxy - ((sumx * sumy) / 2.0)) / (sumx2 - (pow(sumx, 2) / 2.0));
    b = (sumy - (a * sumx)) / 2.0;
    G_verbose_message("d_dT_dry=%f", d_dT_dry);
    G_verbose_message("dT3=%f * t0dem + (%f)", a, b);
    if(isnan(a) || isnan(b)){
    	G_free(outrast);
    	Rast_close(outfd);
	G_fatal_error("Delta T Convergence failed, exiting prematurily, please check output");
    }

    /* ITERATION 3 */
    /***************************************************/
    /***************************************************/
    for (row = 0; row < nrows; row++) {
	G_percent(row, nrows, 2);
	/* read every cell in the line buffers */
	for (col = 0; col < ncols; col++) {
	    d_rah3 = d_Rah[row][col];
	    if (Rast_is_d_null_value(&d_t0dem[row][col]) ||
		Rast_is_d_null_value(&d_z0m[row][col])) {
		Rast_set_d_null_value(&outrast[col], 1);
	    }
	    else {
		if (d_rah3 < 1.0) {
		    d_h3 = 0.0;
		}
		else {
		    d_h3 = (1004 * d_Roh[row][col]) * (a * d_t0dem[row][col] + b) / d_rah3;
		}
		if (d_h3 < 0 && d_h3 > -50) {
		    d_h3 = 0.0;
		}
		if (d_h3 < -50 || d_h3 > 1000) {
		    Rast_set_d_null_value(&outrast[col], 1);
		} else {
		outrast[col] = d_h3;
		}
	    }
	}
	Rast_put_d_row(outfd, outrast);
    }
    G_free(inrast_eact);
    Rast_close(infd_eact);
    G_free(inrast_z0m);
    Rast_close(infd_z0m);
    G_free(inrast_t0dem);
    Rast_close(infd_t0dem);

    G_free(outrast);
    Rast_close(outfd);

    /* add command line incantation to history file */
    Rast_short_history(h0, "raster", &history);
    Rast_command_history(&history);
    Rast_write_history(h0, &history);

    exit(EXIT_SUCCESS);
}
