Ejemplo n.º 1
0
/******************************************************************************
 * @brief    Interpolate a set of N points by fitting a polynomial of degree N-1
 *****************************************************************************/
void
polint(double  xa[],
       double  ya[],
       int     n,
       double  x,
       double *y,
       double *dy)
{
    int     i, m, ns;
    double  den, dif, dift, ho, hp, w;
    double *c = NULL;
    double *d = NULL;

    ns = 1;
    dif = fabs(x - xa[1]);
    c = (double *)malloc((size_t) ((n + 1) * sizeof(double)));
    check_alloc_status(c, "Memory allocation error.");
    d = (double *)malloc((size_t) ((n + 1) * sizeof(double)));
    check_alloc_status(d, "Memory allocation error.");


    for (i = 1; i <= n; i++) {
        if ((dift = fabs(x - xa[i])) < dif) {
            ns = i;
            dif = dift;
        }
        c[i] = ya[i];
        d[i] = ya[i];
    }
    *y = ya[ns--];
    for (m = 1; m < n; m++) {
        for (i = 1; i <= n - m; i++) {
            ho = xa[i] - x;
            hp = xa[i + m] - x;
            w = c[i + 1] - d[i];
            if ((den = ho - hp) == 0.0) {
                log_err("interpolation error");
            }
            den = w / den;
            d[i] = hp * den;
            c[i] = ho * den;
        }
        *y += (*dy = (2 * ns < (n - m) ? c[ns + 1] : d[ns--]));
    }
    free(d);
    free(c);
}
Ejemplo n.º 2
0
/******************************************************************************
 * @brief    Get netCDF dimension.
 *****************************************************************************/
void
get_nc_var_attr(char  *nc_name,
                char  *var_name,
                char  *attr_name,
                char **attr)
{
    int    nc_id;
    int    var_id;
    int    status;
    size_t attr_len;

    // open the netcdf file
    status = nc_open(nc_name, NC_NOWRITE, &nc_id);
    check_nc_status(status, "Error opening %s", nc_name);

    // get variable id
    status = nc_inq_varid(nc_id, var_name, &var_id);
    check_nc_status(status, "Error getting variable id %s in %s", var_name, nc_name);

    // get size of the attribute
    status = nc_inq_attlen(nc_id, var_id, attr_name, &attr_len);
    check_nc_status(status, "Error getting attribute length for %s:%s in %s", var_name,
                attr_name, nc_name);

    // allocate memory for attribute
    *attr = malloc((attr_len + 1) * sizeof(**attr));
    check_alloc_status(*attr, "Memory allocation error.");

    // read attribute text
    status = nc_get_att_text(nc_id, var_id, attr_name, *attr);
    check_nc_status(status, "Error getting netCDF attribute %s for var %s in %s", attr_name,
                var_name, nc_name);

    // we need to null terminate the string ourselves according to NetCDF docs
    (*attr)[attr_len] = '\0';

    // close the netcdf file
    status = nc_close(nc_id);
    check_nc_status(status, "Error closing %s", nc_name);
}
Ejemplo n.º 3
0
/******************************************************************************
 * @brief    Calculate the transpiration from the canopy.
 *****************************************************************************/
void
transpiration(layer_data_struct *layer,
              veg_var_struct    *veg_var,
              unsigned short     veg_class,
              double             rad,
              double             vpd,
              double             net_short,
              double             air_temp,
              double             ra,
              double             dryFrac,
              double             delta_t,
              double             elevation,
              double            *Wmax,
              double            *Wcr,
              double            *Wpwp,
              double            *layerevap,
              double            *frost_fract,
              double            *root,
              double             shortwave,
              double             Catm,
              double            *CanopLayerBnd)
{
    extern veg_lib_struct   *vic_run_veg_lib;
    extern option_struct     options;
    extern parameters_struct param;

    size_t                   i;
    size_t                   frost_area;
    double                   gsm_inv;   /* soil moisture stress factor */
    double                   moist1, moist2; /* tmp holding of moisture */
    double                   evap;      /* tmp holding for evap total */
    double                   Wcr1;      /* tmp holding of critical water for upper layers */
    double                   root_sum;  /* proportion of roots in moist>Wcr zones */
    double                   spare_evap; /* evap for 2nd distribution */
    double                   avail_moist[MAX_LAYERS]; /* moisture available for trans */
    double                   ice[MAX_LAYERS];
    double                   gc;
    double                  *gsLayer = NULL;
    size_t                   cidx;

    /**********************************************************************
       EVAPOTRANSPIRATION

       Calculation of the evapotranspirations
       2.18

       First part: Soil moistures and root fractions of both layers
       influence each other

       Re-written to allow for multi-layers.
    **********************************************************************/

    /**************************************************
       Set ice content in all individual layers
    **************************************************/
    for (i = 0; i < options.Nlayer; i++) {
        ice[i] = 0;
        for (frost_area = 0; frost_area < options.Nfrost; frost_area++) {
            ice[i] += layer[i].ice[frost_area] * frost_fract[frost_area];
        }
    }

    /**************************************************
       Compute moisture content in combined upper layers
    **************************************************/
    moist1 = 0.0;
    Wcr1 = 0.0;
    for (i = 0; i < options.Nlayer - 1; i++) {
        if (root[i] > 0.) {
            avail_moist[i] = 0;
            for (frost_area = 0; frost_area < options.Nfrost; frost_area++) {
                avail_moist[i] +=
                    ((layer[i].moist -
                      layer[i].ice[frost_area]) * frost_fract[frost_area]);
            }
            moist1 += avail_moist[i];
            Wcr1 += Wcr[i];
        }
        else {
            avail_moist[i] = 0.;
        }
    }

    /*****************************************
       Compute moisture content in lowest layer
    *****************************************/
    i = options.Nlayer - 1;
    moist2 = 0;
    for (frost_area = 0; frost_area < options.Nfrost; frost_area++) {
        moist2 +=
            ((layer[i].moist -
              layer[i].ice[frost_area]) * frost_fract[frost_area]);
    }
    avail_moist[i] = moist2;

    /** Set photosynthesis inhibition factor **/
    if (layer[0].moist > vic_run_veg_lib[veg_class].Wnpp_inhib * Wmax[0]) {
        veg_var->NPPfactor = vic_run_veg_lib[veg_class].NPPfactor_sat +
                             (1 - vic_run_veg_lib[veg_class].NPPfactor_sat) *
                             (Wmax[0] - layer[0].moist) / (Wmax[0] -
                                                           vic_run_veg_lib[
                                                               veg_class].
                                                           Wnpp_inhib *
                                                           Wmax[0]);
    }
    else {
        veg_var->NPPfactor = 1.0;
    }

    /******************************************************************
       CASE 1: Moisture in both layers exceeds Wcr, or Moisture in
       layer with more than half of the roots exceeds Wcr.

       Potential evapotranspiration not hindered by soil dryness.  If
       layer with less than half the roots is dryer than Wcr, extra
       evaporation is taken from the wetter layer.  Otherwise layers
       contribute to evapotransipration based on root fraction.
    ******************************************************************/

    if (options.SHARE_LAYER_MOIST &&
        ((moist1 >= Wcr1 && moist2 >= Wcr[options.Nlayer - 1] && Wcr1 > 0.) ||
         (moist1 >= Wcr1 && (1 - root[options.Nlayer - 1]) >= 0.5) ||
         (moist2 >= Wcr[options.Nlayer - 1] && root[options.Nlayer - 1] >=
          0.5))) {
        gsm_inv = 1.0;

        /* compute whole-canopy stomatal resistance */
        if (!options.CARBON || options.RC_MODE == RC_JARVIS) {
            /* Jarvis scheme, using resistance factors from Wigmosta et al., 1994 */
            veg_var->rc = calc_rc(vic_run_veg_lib[veg_class].rmin, net_short,
                                  vic_run_veg_lib[veg_class].RGL, air_temp, vpd,
                                  veg_var->LAI, gsm_inv, false);
            if (options.CARBON) {
                for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                    if (veg_var->LAI > 0) {
                        veg_var->rsLayer[cidx] = veg_var->rc / veg_var->LAI;
                    }
                    else {
                        veg_var->rsLayer[cidx] = param.HUGE_RESIST;
                    }
                    if (veg_var->rsLayer[cidx] > param.CANOPY_RSMAX) {
                        veg_var->rsLayer[cidx] = param.CANOPY_RSMAX;
                    }
                }
            }
        }
        else {
            /* Compute rc based on photosynthetic demand from Knorr 1997 */
            calc_rc_ps(vic_run_veg_lib[veg_class].Ctype,
                       vic_run_veg_lib[veg_class].MaxCarboxRate,
                       vic_run_veg_lib[veg_class].MaxETransport,
                       vic_run_veg_lib[veg_class].CO2Specificity,
                       veg_var->NscaleFactor, air_temp, shortwave,
                       veg_var->aPARLayer, elevation, Catm,
                       CanopLayerBnd, veg_var->LAI, gsm_inv, vpd,
                       veg_var->rsLayer, &(veg_var->rc));
        }

        /* compute transpiration */
        evap = penman(air_temp, elevation, rad, vpd, ra, veg_var->rc,
                      vic_run_veg_lib[veg_class].rarc) *
               delta_t / CONST_CDAY * dryFrac;

        /** divide up evap based on root distribution **/
        /** Note the indexing of the roots **/
        root_sum = 1.0;
        spare_evap = 0.0;
        for (i = 0; i < options.Nlayer; i++) {
            if (avail_moist[i] >= Wcr[i]) {
                layerevap[i] = evap * (double) root[i];
            }
            else {
                if (avail_moist[i] >= Wpwp[i]) {
                    gsm_inv = (avail_moist[i] - Wpwp[i]) /
                              (Wcr[i] - Wpwp[i]);
                }
                else {
                    gsm_inv = 0.0;
                }

                layerevap[i] = evap * gsm_inv * (double) root[i];
                root_sum -= root[i];
                spare_evap = evap * (double) root[i] * (1.0 - gsm_inv);
            }
        }

        /** Assign excess evaporation to wetter layer **/
        if (spare_evap > 0.0) {
            for (i = 0; i < options.Nlayer; i++) {
                if (avail_moist[i] >= Wcr[i]) {
                    layerevap[i] += (double) root[i] * spare_evap / root_sum;
                }
            }
        }
    }

    /*********************************************************************
       CASE 2: Independent evapotranspirations

       Evapotranspiration is restricted by low soil moisture. Evaporation
       is computed independantly from each soil layer.
    *********************************************************************/

    else {
        /* Initialize conductances for aggregation over soil layers */
        gc = 0;
        if (options.CARBON) {
            gsLayer = calloc(options.Ncanopy, sizeof(*gsLayer));
            check_alloc_status(gsLayer, "Memory allocation error.");
            for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                gsLayer[cidx] = 0;
            }
        }

        for (i = 0; i < options.Nlayer; i++) {
            /** Set evaporation restriction factor **/
            if (avail_moist[i] >= Wcr[i]) {
                gsm_inv = 1.0;
            }
            else if (avail_moist[i] >= Wpwp[i] && avail_moist[i] < Wcr[i]) {
                gsm_inv = (avail_moist[i] - Wpwp[i]) / (Wcr[i] - Wpwp[i]);
            }
            else {
                gsm_inv = 0.0;
            }

            if (gsm_inv > 0.0) {
                /* compute whole-canopy stomatal resistance */
                if (!options.CARBON || options.RC_MODE == RC_JARVIS) {
                    /* Jarvis scheme, using resistance factors from Wigmosta et al., 1994 */
                    veg_var->rc = calc_rc(vic_run_veg_lib[veg_class].rmin,
                                          net_short,
                                          vic_run_veg_lib[veg_class].RGL,
                                          air_temp, vpd,
                                          veg_var->LAI, gsm_inv, false);
                    if (options.CARBON) {
                        for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                            if (veg_var->LAI > 0) {
                                veg_var->rsLayer[cidx] = veg_var->rc /
                                                         veg_var->LAI;
                            }
                            else {
                                veg_var->rsLayer[cidx] = param.HUGE_RESIST;
                            }
                            if (veg_var->rsLayer[cidx] > param.CANOPY_RSMAX) {
                                veg_var->rsLayer[cidx] = param.CANOPY_RSMAX;
                            }
                        }
                    }
                }
                else {
                    /* Compute rc based on photosynthetic demand from Knorr 1997 */
                    calc_rc_ps(vic_run_veg_lib[veg_class].Ctype,
                               vic_run_veg_lib[veg_class].MaxCarboxRate,
                               vic_run_veg_lib[veg_class].MaxETransport,
                               vic_run_veg_lib[veg_class].CO2Specificity,
                               veg_var->NscaleFactor, air_temp, shortwave,
                               veg_var->aPARLayer, elevation, Catm,
                               CanopLayerBnd, veg_var->LAI, gsm_inv, vpd,
                               veg_var->rsLayer, &(veg_var->rc));
                }

                /* compute transpiration */
                layerevap[i] = penman(air_temp, elevation, rad, vpd, ra,
                                      veg_var->rc,
                                      vic_run_veg_lib[veg_class].rarc) *
                               delta_t / CONST_CDAY * dryFrac *
                               (double) root[i];

                if (veg_var->rc > 0) {
                    gc += 1 / (veg_var->rc);
                }
                else {
                    gc += param.HUGE_RESIST;
                }

                if (options.CARBON) {
                    for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                        if (veg_var->rsLayer[cidx] > 0) {
                            gsLayer[cidx] += 1 / (veg_var->rsLayer[cidx]);
                        }
                        else {
                            gsLayer[cidx] += param.HUGE_RESIST;
                        }
                    }
                }
            }
            else {
                layerevap[i] = 0.0;
                gc += 0;
                if (options.CARBON) {
                    for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                        gsLayer[cidx] += 0;
                    }
                }
            }
        } // end loop over layers

        /* Now, take the inverse of the conductance */
        if (gc > 0) {
            veg_var->rc = 1 / gc;
        }
        else {
            veg_var->rc = param.HUGE_RESIST;
        }
        if (veg_var->rc > param.CANOPY_RSMAX) {
            veg_var->rc = param.CANOPY_RSMAX;
        }

        if (options.CARBON) {
            for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                if (gsLayer[cidx] > 0) {
                    veg_var->rsLayer[cidx] = 1 / gsLayer[cidx];
                }
                else {
                    veg_var->rsLayer[cidx] = param.HUGE_RESIST;
                }
                if (veg_var->rsLayer[cidx] > param.CANOPY_RSMAX) {
                    veg_var->rsLayer[cidx] = param.CANOPY_RSMAX;
                }
            }
        }

        if (options.CARBON) {
            free((char *) gsLayer);
        }
    }

    /****************************************************************
       Check that evapotransipration does not cause soil moisture to
       fall below wilting point.
    ****************************************************************/
    for (i = 0; i < options.Nlayer; i++) {
        if (ice[i] > 0) {
            if (ice[i] >= Wpwp[i]) {
                // ice content greater than wilting point can use all unfrozen moist
                if (layerevap[i] > avail_moist[i]) {
                    layerevap[i] = avail_moist[i];
                }
            }
            else {
                // ice content less than wilting point restrict loss of unfrozen moist
                if (layerevap[i] > layer[i].moist - Wpwp[i]) {
                    layerevap[i] = layer[i].moist - Wpwp[i];
                }
            }
        }
        else {
            // No ice restrict loss of unfrozen moist
            if (layerevap[i] > layer[i].moist - Wpwp[i]) {
                layerevap[i] = layer[i].moist - Wpwp[i];
            }
        }
        if (layerevap[i] < 0.0) {
            layerevap[i] = 0.0;
        }
    }
}
Ejemplo n.º 4
0
/******************************************************************************
 * @brief    Allocate memory for the atmos data structure.
 *****************************************************************************/
void
alloc_atmos(atmos_data_struct *atmos)
{
    extern option_struct options;

    atmos->air_temp = calloc(NR + 1, sizeof(*(atmos->air_temp)));
    check_alloc_status(atmos->air_temp, "Memory allocation error.");

    atmos->density = calloc(NR + 1, sizeof(*(atmos->density)));
    check_alloc_status(atmos->density, "Memory allocation error.");

    atmos->longwave = calloc(NR + 1, sizeof(*(atmos->longwave)));
    check_alloc_status(atmos->longwave, "Memory allocation error.");

    atmos->prec = calloc(NR + 1, sizeof(*(atmos->prec)));
    check_alloc_status(atmos->prec, "Memory allocation error.");

    atmos->pressure = calloc(NR + 1, sizeof(*(atmos->pressure)));
    check_alloc_status(atmos->pressure, "Memory allocation error.");

    atmos->shortwave = calloc(NR + 1, sizeof(*(atmos->shortwave)));
    check_alloc_status(atmos->shortwave, "Memory allocation error.");

    atmos->snowflag = calloc(NR + 1, sizeof(*(atmos->snowflag)));
    check_alloc_status(atmos->snowflag, "Memory allocation error.");

    atmos->vp = calloc(NR + 1, sizeof(*(atmos->vp)));
    check_alloc_status(atmos->vp, "Memory allocation error.");

    atmos->vpd = calloc(NR + 1, sizeof(*(atmos->vpd)));
    check_alloc_status(atmos->vpd, "Memory allocation error.");

    atmos->wind = calloc(NR + 1, sizeof(*(atmos->wind)));
    check_alloc_status(atmos->wind, "Memory allocation error.");

    if (options.LAKES) {
        atmos->channel_in = calloc(NR + 1, sizeof(*(atmos->channel_in)));
        check_alloc_status(atmos->channel_in, "Memory allocation error.");

    }
    if (options.CARBON) {
        atmos->Catm = calloc(NR + 1, sizeof(*(atmos->Catm)));
        check_alloc_status(atmos->Catm, "Memory allocation error.");

        atmos->coszen = calloc(NR + 1, sizeof(*(atmos->coszen)));
        check_alloc_status(atmos->coszen, "Memory allocation error.");

        atmos->fdir = calloc(NR + 1, sizeof(*(atmos->fdir)));
        check_alloc_status(atmos->fdir, "Memory allocation error.");

        atmos->par = calloc(NR + 1, sizeof(*(atmos->par)));
        check_alloc_status(atmos->par, "Memory allocation error.");

    }
}
Ejemplo n.º 5
0
/******************************************************************************
 * @brief    Read atmospheric forcing data.
 *****************************************************************************/
void
vic_force(void)
{
    extern size_t              NF;
    extern size_t              NR;
    extern size_t              current;
    extern force_data_struct  *force;
    extern dmy_struct         *dmy;
    extern domain_struct       global_domain;
    extern domain_struct       local_domain;
    extern filenames_struct    filenames;
    extern global_param_struct global_param;
    extern option_struct       options;
    extern soil_con_struct    *soil_con;
    extern veg_con_map_struct *veg_con_map;
    extern veg_con_struct    **veg_con;
    extern veg_hist_struct   **veg_hist;
    extern parameters_struct   param;
    extern param_set_struct    param_set;

    double                    *t_offset = NULL;
    double                    *dvar = NULL;
    size_t                     i;
    size_t                     j;
    size_t                     v;
    size_t                     band;
    int                        vidx;
    size_t                     d3count[3];
    size_t                     d3start[3];
    size_t                     d4count[4];
    size_t                     d4start[4];
    double                    *Tfactor;

    // allocate memory for variables to be read
    dvar = malloc(local_domain.ncells_active * sizeof(*dvar));
    check_alloc_status(dvar, "Memory allocation error.");

    // for now forcing file is determined by the year
    sprintf(filenames.forcing[0], "%s%4d.nc", filenames.f_path_pfx[0],
            dmy[current].year);

    // global_param.forceoffset[0] resets every year since the met file restarts
    // every year
    if (current > 1 && (dmy[current].year != dmy[current - 1].year)) {
        global_param.forceoffset[0] = 0;
    }

    // only the time slice changes for the met file reads. The rest is constant
    d3start[1] = 0;
    d3start[2] = 0;
    d3count[0] = 1;
    d3count[1] = global_domain.n_ny;
    d3count[2] = global_domain.n_nx;

    // Air temperature: tas
    for (j = 0; j < NF; j++) {
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[AIR_TEMP].varname,
                                    d3start, d3count, dvar);
        for (i = 0; i < local_domain.ncells_active; i++) {
            force[i].air_temp[j] = (double) dvar[i];
        }
    }

    // Precipitation: prcp
    for (j = 0; j < NF; j++) {
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[PREC].varname,
                                    d3start, d3count, dvar);
        for (i = 0; i < local_domain.ncells_active; i++) {
            force[i].prec[j] = (double) dvar[i];
        }
    }

    // Downward solar radiation: dswrf
    for (j = 0; j < NF; j++) {
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[SWDOWN].varname,
                                    d3start, d3count, dvar);
        for (i = 0; i < local_domain.ncells_active; i++) {
            force[i].shortwave[j] = (double) dvar[i];
        }
    }

    // Downward longwave radiation: dlwrf
    for (j = 0; j < NF; j++) {
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[LWDOWN].varname,
                                    d3start, d3count, dvar);
        for (i = 0; i < local_domain.ncells_active; i++) {
            force[i].longwave[j] = (double) dvar[i];
        }
    }

    // Wind speed: wind
    for (j = 0; j < NF; j++) {
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[WIND].varname,
                                    d3start, d3count, dvar);
        for (i = 0; i < local_domain.ncells_active; i++) {
            force[i].wind[j] = (double) dvar[i];
        }
    }

    // vapor pressure: vp
    for (j = 0; j < NF; j++) {
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[VP].varname,
                                    d3start, d3count, dvar);
        for (i = 0; i < local_domain.ncells_active; i++) {
            force[i].vp[j] = (double) dvar[i];
        }
    }

    // Pressure: pressure
    for (j = 0; j < NF; j++) {
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[PRESSURE].varname,
                                    d3start, d3count, dvar);
        for (i = 0; i < local_domain.ncells_active; i++) {
            force[i].pressure[j] = (double) dvar[i];
        }
    }
    // Optional inputs
    if (options.LAKES) {
        // Channel inflow to lake
        d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] +
                     j;
        get_scatter_nc_field_double(filenames.forcing[0],
                                    param_set.TYPE[CHANNEL_IN].varname,
                                    d3start, d3count, dvar);
        for (j = 0; j < NF; j++) {
            for (i = 0; i < local_domain.ncells_active; i++) {
                force[i].channel_in[j] = (double) dvar[i];
            }
        }
    }
    if (options.CARBON) {
        // Atmospheric CO2 mixing ratio
        for (j = 0; j < NF; j++) {
            d3start[0] = global_param.forceskip[0] +
                         global_param.forceoffset[0] + j;
            get_scatter_nc_field_double(filenames.forcing[0],
                                        param_set.TYPE[CATM].varname,
                                        d3start, d3count, dvar);
            for (i = 0; i < local_domain.ncells_active; i++) {
                force[i].Catm[j] = (double) dvar[i];
            }
        }
        // Cosine of solar zenith angle
        for (j = 0; j < NF; j++) {
            for (i = 0; i < local_domain.ncells_active; i++) {
                force[i].coszen[j] = compute_coszen(
                    local_domain.locations[i].latitude,
                    local_domain.locations[i].longitude,
                    soil_con[i].time_zone_lng, dmy[current].day_in_year,
                    dmy[current].dayseconds);
            }
        }
        // Fraction of shortwave that is direct
        for (j = 0; j < NF; j++) {
            d3start[0] = global_param.forceskip[0] +
                         global_param.forceoffset[0] + j;
            get_scatter_nc_field_double(filenames.forcing[0],
                                        param_set.TYPE[FDIR].varname,
                                        d3start, d3count, dvar);
            for (i = 0; i < local_domain.ncells_active; i++) {
                force[i].fdir[j] = (double) dvar[i];
            }
        }
        // Photosynthetically active radiation
        for (j = 0; j < NF; j++) {
            d3start[0] = global_param.forceskip[0] +
                         global_param.forceoffset[0] + j;
            get_scatter_nc_field_double(filenames.forcing[0],
                                        param_set.TYPE[PAR].varname,
                                        d3start, d3count, dvar);
            for (i = 0; i < local_domain.ncells_active; i++) {
                force[i].par[j] = (double) dvar[i];
            }
        }
    }

    // Update the offset counter
    global_param.forceoffset[0] += NF;

    // Initialize the veg_hist structure with the current climatological
    // vegetation parameters.  This may be overwritten with the historical
    // forcing time series.
    for (i = 0; i < local_domain.ncells_active; i++) {
        for (v = 0; v < options.NVEGTYPES; v++) {
            vidx = veg_con_map[i].vidx[v];
            if (vidx != NODATA_VEG) {
                for (j = 0; j < NF; j++) {
                    veg_hist[i][vidx].albedo[j] =
                        veg_con[i][vidx].albedo[dmy[current].month - 1];
                    veg_hist[i][vidx].displacement[j] =
                        veg_con[i][vidx].displacement[dmy[current].month - 1];
                    veg_hist[i][vidx].fcanopy[j] =
                        veg_con[i][vidx].fcanopy[dmy[current].month - 1];
                    veg_hist[i][vidx].LAI[j] =
                        veg_con[i][vidx].LAI[dmy[current].month - 1];
                    veg_hist[i][vidx].roughness[j] =
                        veg_con[i][vidx].roughness[dmy[current].month - 1];
                }
            }
        }
    }

    // Read veg_hist file
    if (options.LAI_SRC == FROM_VEGHIST ||
        options.FCAN_SRC == FROM_VEGHIST ||
        options.ALB_SRC == FROM_VEGHIST) {
        // for now forcing file is determined by the year
        sprintf(filenames.forcing[1], "%s%4d.nc", filenames.f_path_pfx[1],
                dmy[current].year);

        // global_param.forceoffset[1] resets every year since the met file restarts
        // every year
        if (current > 1 && (dmy[current].year != dmy[current - 1].year)) {
            global_param.forceoffset[1] = 0;
        }

        // only the time slice changes for the met file reads. The rest is constant
        d4start[2] = 0;
        d4start[3] = 0;
        d4count[0] = 1;
        d4count[1] = 1;
        d4count[2] = global_domain.n_ny;
        d4count[3] = global_domain.n_nx;

        // Leaf Area Index: lai
        if (options.LAI_SRC == FROM_VEGHIST) {
            for (j = 0; j < NF; j++) {
                d4start[0] = global_param.forceskip[1] +
                             global_param.forceoffset[1] + j;
                for (v = 0; v < options.NVEGTYPES; v++) {
                    d4start[1] = v;
                    get_scatter_nc_field_double(filenames.forcing[1], "lai",
                                                d4start, d4count, dvar);
                    for (i = 0; i < local_domain.ncells_active; i++) {
                        vidx = veg_con_map[i].vidx[v];
                        if (vidx != NODATA_VEG) {
                            veg_hist[i][vidx].LAI[j] = (double) dvar[i];
                        }
                    }
                }
            }
        }

        // Partial veg cover fraction: fcov
        if (options.FCAN_SRC == FROM_VEGHIST) {
            for (j = 0; j < NF; j++) {
                d4start[0] = global_param.forceskip[1] +
                             global_param.forceoffset[1] + j;
                for (v = 0; v < options.NVEGTYPES; v++) {
                    d4start[1] = v;
                    get_scatter_nc_field_double(filenames.forcing[1], "fcov",
                                                d4start, d4count, dvar);
                    for (i = 0; i < local_domain.ncells_active; i++) {
                        vidx = veg_con_map[i].vidx[v];
                        if (vidx != NODATA_VEG) {
                            veg_hist[i][vidx].fcanopy[j] = (double) dvar[i];
                        }
                    }
                }
            }
        }

        // Albedo: alb
        if (options.ALB_SRC == FROM_VEGHIST) {
            for (j = 0; j < NF; j++) {
                d4start[0] = global_param.forceskip[1] +
                             global_param.forceoffset[1] + j;
                for (v = 0; v < options.NVEGTYPES; v++) {
                    d4start[1] = v;
                    get_scatter_nc_field_double(filenames.forcing[1], "alb",
                                                d4start, d4count, dvar);
                    for (i = 0; i < local_domain.ncells_active; i++) {
                        vidx = veg_con_map[i].vidx[v];
                        if (vidx != NODATA_VEG) {
                            veg_hist[i][vidx].albedo[j] = (double) dvar[i];
                        }
                    }
                }
            }
        }

        // Update the offset counter
        global_param.forceoffset[1] += NF;
    }


    // allocate memory for t_offset
    t_offset = malloc(local_domain.ncells_active * sizeof(*t_offset));
    check_alloc_status(t_offset, "Memory allocation error.");

    for (i = 0; i < local_domain.ncells_active; i++) {
        if (options.SNOW_BAND > 1) {
            Tfactor = soil_con[i].Tfactor;
            t_offset[i] = Tfactor[0];
            for (band = 1; band < options.SNOW_BAND; band++) {
                if (Tfactor[band] < t_offset[i]) {
                    t_offset[i] = Tfactor[band];
                }
            }
        }
        else {
            t_offset[i] = 0;
        }
    }
    // Convert forcings into what we need and calculate missing ones
    for (i = 0; i < local_domain.ncells_active; i++) {
        for (j = 0; j < NF; j++) {
            // pressure in Pa
            force[i].pressure[j] *= PA_PER_KPA;
            // vapor pressure in Pa
            force[i].vp[j] *= PA_PER_KPA;
            // vapor pressure deficit in Pa
            force[i].vpd[j] = svp(force[i].air_temp[j]) - force[i].vp[j];
            if (force[i].vpd[j] < 0) {
                force[i].vpd[j] = 0;
                force[i].vp[j] = svp(force[i].air_temp[j]);
            }
            // air density in kg/m3
            force[i].density[j] = air_density(force[i].air_temp[j],
                                              force[i].pressure[j]);
            // snow flag
            force[i].snowflag[j] = will_it_snow(&(force[i].air_temp[j]),
                                                t_offset[i],
                                                param.SNOW_MAX_SNOW_TEMP,
                                                &(force[i].prec[j]), 1);
        }
        // Check on fcanopy
        for (v = 0; v < options.NVEGTYPES; v++) {
            vidx = veg_con_map[i].vidx[v];
            if (vidx != NODATA_VEG) {
                for (j = 0; j < NF; j++) {
                    if ((veg_hist[i][vidx].fcanopy[j] < MIN_FCANOPY) &&
                        ((current == 0) ||
                         (options.FCAN_SRC == FROM_VEGHIST))) {
                        // Only issue this warning once if not using veg hist fractions
                        log_warn(
                            "cell %zu, veg` %d substep %zu fcanopy %f < minimum of %f; setting = %f", i, vidx, j,
                            veg_hist[i][vidx].fcanopy[j], MIN_FCANOPY,
                            MIN_FCANOPY);
                        veg_hist[i][vidx].fcanopy[j] = MIN_FCANOPY;
                    }
                }
            }
        }
    }


    // Put average value in NR field
    for (i = 0; i < local_domain.ncells_active; i++) {
        force[i].air_temp[NR] = average(force[i].air_temp, NF);
        // For precipitation put total
        force[i].prec[NR] = average(force[i].prec, NF) * NF;
        force[i].shortwave[NR] = average(force[i].shortwave, NF);
        force[i].longwave[NR] = average(force[i].longwave, NF);
        force[i].pressure[NR] = average(force[i].pressure, NF);
        force[i].wind[NR] = average(force[i].wind, NF);
        force[i].vp[NR] = average(force[i].vp, NF);
        force[i].vpd[NR] = (svp(force[i].air_temp[NR]) - force[i].vp[NR]);
        force[i].density[NR] = air_density(force[i].air_temp[NR],
                                           force[i].pressure[NR]);
        force[i].snowflag[NR] = will_it_snow(force[i].air_temp, t_offset[i],
                                             param.SNOW_MAX_SNOW_TEMP,
                                             force[i].prec, NF);

        for (v = 0; v < options.NVEGTYPES; v++) {
            vidx = veg_con_map[i].vidx[v];
            if (vidx != NODATA_VEG) {
                // not the correct way to calculate average albedo in general,
                // but leave for now (it's correct if albedo is constant over
                // the model step)
                veg_hist[i][vidx].albedo[NR] = average(veg_hist[i][vidx].albedo,
                                                       NF);
                veg_hist[i][vidx].displacement[NR] = average(
                    veg_hist[i][vidx].displacement, NF);
                veg_hist[i][vidx].fcanopy[NR] = average(
                    veg_hist[i][vidx].fcanopy, NF);
                veg_hist[i][vidx].LAI[NR] = average(veg_hist[i][vidx].LAI, NF);
                veg_hist[i][vidx].roughness[NR] = average(
                    veg_hist[i][vidx].roughness, NF);
            }
        }

        // Optional inputs
        if (options.LAKES) {
            force[i].channel_in[NR] = average(force[i].channel_in, NF) * NF;
        }
        if (options.CARBON) {
            force[i].Catm[NR] = average(force[i].Catm, NF);
            force[i].fdir[NR] = average(force[i].fdir, NF);
            force[i].par[NR] = average(force[i].par, NF);
            // for coszen, use value at noon
            force[i].coszen[NR] = compute_coszen(
                local_domain.locations[i].latitude,
                local_domain.locations[i].longitude, soil_con[i].time_zone_lng,
                dmy[current].day_in_year, SEC_PER_DAY / 2);
        }
    }


    // cleanup
    free(dvar);
}
Ejemplo n.º 6
0
/******************************************************************************
* @brief        This routine computes all surface fluxes
******************************************************************************/
int
surface_fluxes(bool                 overstory,
               double               BareAlbedo,
               double               ice0,
               double               moist0,
               double               surf_atten,
               double              *Melt,
               double              *Le,
               double              *aero_resist,
               double              *displacement,
               double              *gauge_correction,
               double              *out_prec,
               double              *out_rain,
               double              *out_snow,
               double              *ref_height,
               double              *roughness,
               double              *snow_inflow,
               double              *wind,
               double              *root,
               size_t               Nlayers,
               size_t               Nveg,
               unsigned short       band,
               double               dp,
               unsigned short       iveg,
               unsigned short       veg_class,
               force_data_struct   *force,
               dmy_struct          *dmy,
               energy_bal_struct   *energy,
               global_param_struct *gp,
               cell_data_struct    *cell,
               snow_data_struct    *snow,
               soil_con_struct     *soil_con,
               veg_var_struct      *veg_var,
               double               lag_one,
               double               sigma_slope,
               double               fetch,
               double              *CanopLayerBnd)
{
    extern veg_lib_struct   *vic_run_veg_lib;
    extern option_struct     options;
    extern parameters_struct param;

    int                      MAX_ITER_GRND_CANOPY;
    int                      ErrorFlag;
    int                      INCLUDE_SNOW = false;
    int                      UNSTABLE_CNT;
    int                      UNSTABLE_SNOW = false;
    int                      N_steps;
    int                      UnderStory;
    size_t                   hidx; // index of initial element of atmos array
    size_t                   step_inc; // number of atmos array elements to skip per surface fluxes step
    size_t                   endhidx; // index of final element of atmos array
    double                   step_dt; // time length of surface fluxes step (in seconds)
    size_t                   lidx;
    int                      over_iter;
    int                      under_iter;
    int                      q;
    double                   Ls;
    double                   LongUnderIn; // inmoing LW to ground surface
    double                   LongUnderOut; // outgoing LW from ground surface
    double                   NetLongSnow; // net LW over snowpack
    double                   NetShortSnow; // net SW over understory
    double                   NetShortGrnd; // net SW over snow-free surface
    double                   OldTSurf; // previous snow surface temperature
    double                   ShortUnderIn; // incoming SW to understory
    double                   Tair; // air temperature
    double                   Tcanopy; // canopy air temperature
    double                   Tgrnd; // soil surface temperature
    double                   Tsurf; // ground surface temperature
    double                   VPDcanopy; // vapor pressure deficit in canopy/atmos
    double                   VPcanopy; // vapor pressure in canopy/atmos
    double                   coverage; // mid-step snow cover fraction
    double                   delta_coverage; // change in snow cover fraction
    double                   delta_snow_heat; // change in snowpack heat storage
    double                   last_Tcanopy;
    double                   last_snow_coverage; // previous snow covered area
    double                   last_snow_flux;
    double                   last_tol_under; // previous surface iteration tol
    double                   last_tol_over; // previous overstory iteration tol
    double                   ppt; // precipitation/melt reaching soil surface
    double                   rainfall; // rainfall
    double                   snowfall; // snowfall
    double                   snow_flux; // heat flux through snowpack
    double                   snow_grnd_flux; // ground heat flux into snowpack
    double                   tol_under;
    double                   tol_over;
    double                  *aero_resist_used;
    double                  *inflow;
    layer_data_struct       *layer;

    // Step-specific quantities
    double                   step_Wdew;
    double                   step_melt;
    double                   step_melt_energy; /* energy used to reduce snow coverage */
    double                   step_out_prec;
    double                   step_out_rain;
    double                   step_out_snow;
    double                   step_ppt;
    double                   step_prec;

    // Quantities that need to be summed or averaged over multiple snow steps
    // energy structure
    double            store_AlbedoOver;
    double            store_AlbedoUnder;
    double            store_AtmosLatent;
    double            store_AtmosLatentSub;
    double            store_AtmosSensible;
    double            store_LongOverIn;
    double            store_LongUnderIn;
    double            store_LongUnderOut;
    double            store_NetLongAtmos;
    double            store_NetLongOver;
    double            store_NetLongUnder;
    double            store_NetShortAtmos;
    double            store_NetShortGrnd;
    double            store_NetShortOver;
    double            store_NetShortUnder;
    double            store_ShortOverIn;
    double            store_ShortUnderIn;
    double            store_advected_sensible;
    double            store_advection;
    double            store_canopy_advection;
    double            store_canopy_latent;
    double            store_canopy_latent_sub;
    double            store_canopy_sensible;
    double            store_canopy_refreeze;
    double            store_deltaCC;
    double            store_deltaH;
    double            store_fusion;
    double            store_grnd_flux;
    double            store_latent;
    double            store_latent_sub;
    double            store_melt_energy;
    double            store_refreeze_energy;
    double            store_sensible;
    double            store_snow_flux;
    // snow structure
    double            store_canopy_vapor_flux;
    double            store_melt;
    double            store_vapor_flux;
    double            store_blowing_flux;
    double            store_surface_flux;
    // veg_var structure
    double            store_canopyevap;
    double            store_throughfall;
    // cell structure
    double            store_layerevap[MAX_LAYERS];
    double            store_ppt;
    double            store_aero_cond_used[2];
    double            store_pot_evap;

    // Structures holding values for current snow step
    energy_bal_struct snow_energy;    // energy fluxes at snowpack surface
    energy_bal_struct soil_energy;    // energy fluxes at soil surface
    veg_var_struct    snow_veg_var;    // veg fluxes/storages in presence of snow
    veg_var_struct    soil_veg_var;    // veg fluxes/storages in soil energy balance
    snow_data_struct  step_snow;
    layer_data_struct step_layer[MAX_LAYERS];

    // Structures holding values for current iteration
    energy_bal_struct iter_snow_energy;    // energy fluxes at snowpack surface
    energy_bal_struct iter_soil_energy;    // energy fluxes at soil surface
    veg_var_struct    iter_snow_veg_var;    // veg fluxes/storages in presence of snow
    veg_var_struct    iter_soil_veg_var;    // veg fluxes/storages in soil energy balance
    snow_data_struct  iter_snow;
    layer_data_struct iter_layer[MAX_LAYERS];
    double            iter_aero_resist[3];
    double            iter_aero_resist_veg[3];
    double            iter_aero_resist_used[2];
    double            iter_pot_evap;
#ifdef VCS_V5
    double            iter_pot_evap_veg;
    double            iter_pot_evap_soil;
#endif

    // handle bisection of understory solution
    double            store_tol_under;
    double            A_tol_under;

    // handle bisection of overstory solution
    double            store_tol_over;

    // Carbon cycling
    double            dryFrac;
    double           *LAIlayer = NULL;
    double           *faPAR = NULL;
    size_t            cidx;
    double            store_gc;
    double           *store_gsLayer = NULL;
    double            store_Ci;
    double            store_GPP;
    double            store_Rdark;
    double            store_Rphoto;
    double            store_Rmaint;
    double            store_Rgrowth;
    double            store_Raut;
    double            store_NPP;

    if (options.CLOSE_ENERGY) {
        MAX_ITER_GRND_CANOPY = 10;
    }
    else {
        MAX_ITER_GRND_CANOPY = 0;
    }

    if (options.CARBON) {
        store_gsLayer = calloc(options.Ncanopy, sizeof(*store_gsLayer));
        check_alloc_status(store_gsLayer, "Memory allocation error.");
    }

    /***********************************************************************
       Set temporary variables for convenience
    ***********************************************************************/
    aero_resist_used = cell->aero_resist;
    inflow = &(cell->inflow);
    layer = cell->layer;

    /***********************************************************************
       Set temporary variables - preserves original values until iterations
       are completed
    ***********************************************************************/

    energy->advection = 0;
    energy->deltaCC = 0;
    if (snow->swq > 0) {
        snow_flux = energy->snow_flux;
    }
    else {
        snow_flux = -(energy->grnd_flux + energy->deltaH + energy->fusion);
    }
    energy->refreeze_energy = 0;
    coverage = snow->coverage;
    snow_energy = (*energy);
    soil_energy = (*energy);
    iter_soil_energy = (*energy);
    snow_veg_var = (*veg_var);
    soil_veg_var = (*veg_var);
    step_snow = (*snow);
    for (lidx = 0; lidx < Nlayers; lidx++) {
        step_layer[lidx] = layer[lidx];
    }
    for (lidx = 0; lidx < Nlayers; lidx++) {
        step_layer[lidx].evap = 0;
    }
    soil_veg_var.canopyevap = 0;
    snow_veg_var.canopyevap = 0;
    soil_veg_var.throughfall = 0;
    snow_veg_var.throughfall = 0;

    /********************************
       Set-up sub-time step controls
       (May eventually want to set this up so that it is also true
       if frozen soils are present)
    ********************************/

    if (snow->swq > 0 || snow->snow_canopy > 0 || force->snowflag[NR]) {
        hidx = 0;
        step_inc = 1;
        endhidx = hidx + NF;
        step_dt = gp->snow_dt;
    }
    else {
        hidx = NR;
        step_inc = 1;
        endhidx = hidx + step_inc;
        step_dt = gp->dt;
    }

    /*******************************************
       Initialize sub-model time step variables
    *******************************************/

    // energy structure
    store_AlbedoOver = 0;
    store_AlbedoUnder = 0;
    store_AtmosLatent = 0;
    store_AtmosLatentSub = 0;
    store_AtmosSensible = 0;
    store_LongOverIn = 0;
    store_LongUnderIn = 0;
    store_LongUnderOut = 0;
    store_NetLongAtmos = 0;
    store_NetLongOver = 0;
    store_NetLongUnder = 0;
    store_NetShortAtmos = 0;
    store_NetShortGrnd = 0;
    store_NetShortOver = 0;
    store_NetShortUnder = 0;
    store_ShortOverIn = 0;
    store_ShortUnderIn = 0;
    store_advected_sensible = 0;
    store_advection = 0;
    store_canopy_advection = 0;
    store_canopy_latent = 0;
    store_canopy_latent_sub = 0;
    store_canopy_sensible = 0;
    store_canopy_refreeze = 0;
    store_deltaCC = 0;
    store_deltaH = 0;
    store_fusion = 0;
    store_grnd_flux = 0;
    store_latent = 0;
    store_latent_sub = 0;
    store_melt_energy = 0;
    store_refreeze_energy = 0;
    store_sensible = 0;
    store_snow_flux = 0;
    // snow structure
    last_snow_coverage = snow->coverage;
    store_canopy_vapor_flux = 0;
    store_melt = 0;
    store_vapor_flux = 0;
    store_surface_flux = 0;
    store_blowing_flux = 0;
    // veg_var and cell structures
    store_throughfall = 0.;
    store_canopyevap = 0.;
    for (lidx = 0; lidx < options.Nlayer; lidx++) {
        store_layerevap[lidx] = 0.;
    }
    step_Wdew = veg_var->Wdew;
    // misc
    store_ppt = 0;
    store_aero_cond_used[0] = 0;
    store_aero_cond_used[1] = 0;
    (*snow_inflow) = 0;
    store_pot_evap = 0;
    N_steps = 0;

    // Carbon cycling
    if (options.CARBON) {
        store_gc = 0;
        for (cidx = 0; cidx < options.Ncanopy; cidx++) {
            store_gsLayer[cidx] = 0;
        }
        store_Ci = 0;
        store_GPP = 0;
        store_Rdark = 0;
        store_Rphoto = 0;
        store_Rmaint = 0;
        store_Rgrowth = 0;
        store_Raut = 0;
        store_NPP = 0;
    }

    /*************************
       Compute surface fluxes
    *************************/

    do
    {
        /** Solve energy balance for all sub-model time steps **/


        /* set air temperature and precipitation for this snow band */
        Tair = force->air_temp[hidx] + soil_con->Tfactor[band];
        step_prec = force->prec[hidx] * soil_con->Pfactor[band];

        // initialize ground surface temperaure
        Tgrnd = energy->T[0];

        // initialize canopy terms
        Tcanopy = Tair;
        VPcanopy = force->vp[hidx];
        VPDcanopy = force->vpd[hidx];

        over_iter = 0;
        tol_over = 999;

        last_Tcanopy = 999;
        last_snow_flux = 999;

        // compute LAI and absorbed PAR per canopy layer
        if (options.CARBON && iveg < Nveg) {
            LAIlayer = calloc(options.Ncanopy, sizeof(*LAIlayer));
            check_alloc_status(LAIlayer, "Memory allocation error.");
            faPAR = calloc(options.Ncanopy, sizeof(*faPAR));
            check_alloc_status(faPAR, "Memory allocation error.");

            /* Compute absorbed PAR per ground area per canopy layer (W/m2)
               normalized to PAR = 1 W, i.e. the canopy albedo in the PAR
               range (alb_total ~ 0.45*alb_par + 0.55*alb_other) */
            faparl(CanopLayerBnd,
                   veg_var->LAI,
                   soil_con->AlbedoPar,
                   force->coszen[hidx],
                   force->fdir[hidx],
                   LAIlayer,
                   faPAR);

            /* Convert to absolute (unnormalized) absorbed PAR per leaf area per canopy layer
               (umol(photons)/m2 leaf area / s); dividing by Epar converts PAR from W to umol(photons)/s */
            veg_var->aPAR = 0;
            for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                if (LAIlayer[cidx] > 1e-10) {
                    veg_var->aPARLayer[cidx] =
                        (force->par[hidx] /
                         param.PHOTO_EPAR) * faPAR[cidx] / LAIlayer[cidx];
                    veg_var->aPAR += force->par[hidx] * faPAR[cidx] /
                                     LAIlayer[cidx];
                }
                else {
                    veg_var->aPARLayer[cidx] = force->par[hidx] /
                                               param.PHOTO_EPAR *
                                               faPAR[cidx] / 1e-10;
                    veg_var->aPAR += force->par[hidx] * faPAR[cidx] / 1e-10;
                }
            }
            free((char*) LAIlayer);
            free((char*) faPAR);
        }

        // Compute mass flux of blowing snow
        if (!overstory && options.BLOWING && step_snow.swq > 0.) {
            Ls = calc_latent_heat_of_sublimation(step_snow.surf_temp);
            step_snow.blowing_flux = CalcBlowingSnow(step_dt, Tair,
                                                     step_snow.last_snow,
                                                     step_snow.surf_water,
                                                     wind[2], Ls,
                                                     force->density[hidx],
                                                     force->vp[hidx],
                                                     roughness[2],
                                                     ref_height[2],
                                                     step_snow.depth,
                                                     lag_one, sigma_slope,
                                                     step_snow.surf_temp, iveg,
                                                     Nveg, fetch,
                                                     displacement[1],
                                                     roughness[1],
                                                     &step_snow.transport);
            if ((int) step_snow.blowing_flux == ERROR) {
                return (ERROR);
            }
            step_snow.blowing_flux *= step_dt / CONST_RHOFW; /* m/time step */
        }
        else {
            step_snow.blowing_flux = 0.0;
        }

        do
        {
            /** Iterate for overstory solution **/

            over_iter++;
            last_tol_over = tol_over;

            under_iter = 0;
            tol_under = 999;
            UnderStory = 999;

            UNSTABLE_CNT = 0;

            // bisect understory
            A_tol_under = 999;
            store_tol_under = 999;

            store_tol_over = 999;

            do
            {
                /** Iterate for understory solution - itererates to find snow flux **/

                under_iter++;
                last_tol_under = tol_under;

                if (last_Tcanopy != 999) {
                    Tcanopy = (last_Tcanopy + Tcanopy) / 2.;
                }
                last_Tcanopy = Tcanopy;

                // update understory energy balance terms for iteration
                if (last_snow_flux != 999) {
                    if ((fabs(store_tol_under) > fabs(A_tol_under) &&
                         A_tol_under != 999 &&
                         fabs(store_tol_under - A_tol_under) > 1.) ||
                        tol_under < 0) { // stepped the correct way
                        UNSTABLE_CNT++;
                        if (UNSTABLE_CNT > 3 || tol_under < 0) {
                            UNSTABLE_SNOW = true;
                        }
                    }
                    else if (!INCLUDE_SNOW) { // stepped the wrong way
                        snow_flux =
                            (last_snow_flux + iter_soil_energy.snow_flux) / 2.;
                    }
                }
                last_snow_flux = snow_flux;
                A_tol_under = store_tol_under;

                snow_grnd_flux = -snow_flux;

                // Initialize structures for new iteration
                iter_snow_energy = snow_energy;
                iter_soil_energy = soil_energy;
                iter_snow_veg_var = snow_veg_var;
                iter_soil_veg_var = soil_veg_var;
                iter_snow = step_snow;
                for (lidx = 0; lidx < Nlayers; lidx++) {
                    iter_layer[lidx] = step_layer[lidx];
                }
                iter_snow_veg_var.Wdew = step_Wdew;
                iter_soil_veg_var.Wdew = step_Wdew;
                iter_snow_veg_var.canopyevap = 0;
                iter_soil_veg_var.canopyevap = 0;
                for (lidx = 0; lidx < Nlayers; lidx++) {
                    iter_layer[lidx].evap = 0;
                }
                for (q = 0; q < 3; q++) {
                    iter_aero_resist[q] = aero_resist[q];
                    iter_aero_resist_veg[q] = aero_resist[q];
                }
                iter_aero_resist_used[0] = aero_resist_used[0];
                iter_aero_resist_used[1] = aero_resist_used[1];
                iter_snow.canopy_vapor_flux = 0;
                iter_snow.vapor_flux = 0;
                iter_snow.surface_flux = 0;
                /* iter_snow.blowing_flux has already been reset to step_snow.blowing_flux */
                LongUnderOut = iter_soil_energy.LongUnderOut;
                dryFrac = -1;

                /** Solve snow accumulation, ablation and interception **/
                step_melt = solve_snow(overstory, BareAlbedo, LongUnderOut,
                                       param.SNOW_MIN_RAIN_TEMP,
                                       param.SNOW_MAX_SNOW_TEMP,
                                       Tcanopy, Tgrnd, Tair,
                                       step_prec, snow_grnd_flux,
                                       &energy->AlbedoUnder, Le,
                                       &LongUnderIn, &NetLongSnow,
                                       &NetShortGrnd,
                                       &NetShortSnow, &ShortUnderIn, &OldTSurf,
                                       iter_aero_resist, iter_aero_resist_used,
                                       &coverage, &delta_coverage,
                                       &delta_snow_heat, displacement,
                                       gauge_correction, &step_melt_energy,
                                       &step_out_prec, &step_out_rain,
                                       &step_out_snow,
                                       &step_ppt, &rainfall, ref_height,
                                       roughness, snow_inflow, &snowfall,
                                       &surf_atten,
                                       wind, root, UNSTABLE_SNOW,
                                       Nveg, iveg, band, step_dt, hidx,
                                       veg_class,
                                       &UnderStory, CanopLayerBnd, &dryFrac,
                                       dmy, force, &(iter_snow_energy),
                                       iter_layer, &(iter_snow),
                                       soil_con,
                                       &(iter_snow_veg_var));

                if (step_melt == ERROR) {
                    return (ERROR);
                }

                /* Check that the snow surface temperature was estimated, if not
                   prepare to include thin snowpack in the estimation of the
                   snow-free surface energy balance */
                if ((iter_snow.surf_temp == 999 || UNSTABLE_SNOW) &&
                    iter_snow.swq > 0) {
                    INCLUDE_SNOW = UnderStory + 1;
                    iter_soil_energy.advection = iter_snow_energy.advection;
                    iter_snow.surf_temp = step_snow.surf_temp;
                    step_melt_energy = 0;
                }
                else {
                    INCLUDE_SNOW = false;
                }

                if (iter_snow.snow) {
                    iter_aero_resist_veg[0] = iter_aero_resist_used[0];
                    iter_aero_resist_veg[1] = iter_aero_resist_used[1];
                }

                /**************************************************
                   Solve Energy Balance Components at Soil Surface
                **************************************************/

                Tsurf = calc_surf_energy_bal((*Le), LongUnderIn, NetLongSnow,
                                             NetShortGrnd, NetShortSnow,
                                             OldTSurf,
                                             ShortUnderIn, iter_snow.albedo,
                                             iter_snow_energy.latent,
                                             iter_snow_energy.latent_sub,
                                             iter_snow_energy.sensible,
                                             Tcanopy, VPDcanopy,
                                             VPcanopy,
                                             delta_coverage, dp,
                                             ice0, step_melt_energy, moist0,
                                             iter_snow.coverage,
                                             (step_snow.depth + iter_snow.depth) / 2.,
                                             BareAlbedo, surf_atten,
                                             iter_aero_resist, iter_aero_resist_veg, iter_aero_resist_used,
                                             displacement, &step_melt, &step_ppt,
                                             rainfall, ref_height, roughness,
                                             snowfall, wind, root, INCLUDE_SNOW,
                                             UnderStory, options.Nnode, Nveg,
                                             step_dt, hidx, iveg,
                                             (int) overstory, veg_class,
                                             CanopLayerBnd, &dryFrac, force,
                                             dmy, &iter_soil_energy,
                                             iter_layer,
                                             &(iter_snow), soil_con,
                                             &iter_soil_veg_var);

                if ((int) Tsurf == ERROR) {
                    // Return error flag to skip rest of grid cell
                    return (ERROR);
                }

                if (INCLUDE_SNOW) {
                    /* store melt from thin snowpack */
                    step_ppt += step_melt;
                }

                /*****************************************
                   Compute energy balance with atmosphere
                *****************************************/
                if (iter_snow.snow && overstory) {
                    // do this if overstory is active and energy balance is closed
                    Tcanopy = calc_atmos_energy_bal(
                        iter_snow_energy.canopy_sensible,
                        iter_soil_energy.sensible,
                        iter_snow_energy.canopy_latent,
                        iter_soil_energy.latent,
                        iter_snow_energy.canopy_latent_sub,
                        iter_soil_energy.latent_sub,
                        iter_snow_energy.NetLongOver,
                        iter_soil_energy.NetLongUnder,
                        iter_snow_energy.NetShortOver,
                        iter_soil_energy.NetShortUnder,
                        iter_aero_resist_veg[1], Tair,
                        force->density[hidx],
                        &iter_soil_energy.AtmosError,
                        &iter_soil_energy.AtmosLatent,
                        &iter_soil_energy.AtmosLatentSub,
                        &iter_soil_energy.NetLongAtmos,
                        &iter_soil_energy.NetShortAtmos,
                        &iter_soil_energy.AtmosSensible,
                        &iter_soil_energy.Tcanopy_fbflag,
                        &iter_soil_energy.Tcanopy_fbcount);

                    /* iterate to find Tcanopy which will solve the atmospheric energy
                       balance.  Since I do not know vp in the canopy, use the
                       sum of latent heats from the ground and foliage, and iterate
                       on the temperature used for the sensible heat flux from the
                       canopy air to the mixing level */
                    if ((int) Tcanopy == ERROR) {
                        // Return error flag to skip rest of grid cell
                        return (ERROR);
                    }
                }
                else {
                    // else put surface fluxes into atmospheric flux storage so that
                    // the model will continue to function
                    iter_soil_energy.AtmosLatent = iter_soil_energy.latent;
                    iter_soil_energy.AtmosLatentSub =
                        iter_soil_energy.latent_sub;
                    iter_soil_energy.AtmosSensible = iter_soil_energy.sensible;
                    iter_soil_energy.NetLongAtmos =
                        iter_soil_energy.NetLongUnder;
                    iter_soil_energy.NetShortAtmos =
                        iter_soil_energy.NetShortUnder;
                }
                iter_soil_energy.Tcanopy = Tcanopy;
                iter_snow_energy.Tcanopy = Tcanopy;

                /*****************************************
                   Compute iteration tolerance statistics
                *****************************************/

                // compute understory tolerance
                if (INCLUDE_SNOW ||
                    (iter_snow.swq == 0 && delta_coverage == 0)) {
                    store_tol_under = 0;
                    tol_under = 0;
                }
                else {
                    store_tol_under = snow_flux - iter_soil_energy.snow_flux;
                    tol_under = fabs(store_tol_under);
                }
                if (fabs(tol_under - last_tol_under) < param.TOL_GRND &&
                    tol_under >
                    1.) {
                    tol_under = -999;
                }

                // compute overstory tolerance
                if (overstory && iter_snow.snow) {
                    store_tol_over = Tcanopy - last_Tcanopy;
                    tol_over = fabs(store_tol_over);
                }
                else {
                    store_tol_over = 0;
                    tol_over = 0;
                }
            }
            while ((fabs(tol_under - last_tol_under) > param.TOL_GRND) &&
                   (tol_under != 0) && (under_iter < MAX_ITER_GRND_CANOPY));
        }
        while ((fabs(tol_over - last_tol_over) > param.TOL_OVER &&
                overstory) && (tol_over != 0) &&
               (over_iter < MAX_ITER_GRND_CANOPY));

        /**************************************
           Compute GPP, Raut, and NPP
        **************************************/
        if (options.CARBON) {
            if (iveg < Nveg && !step_snow.snow && dryFrac > 0) {
                canopy_assimilation(vic_run_veg_lib[veg_class].Ctype,
                                    vic_run_veg_lib[veg_class].MaxCarboxRate,
                                    vic_run_veg_lib[veg_class].MaxETransport,
                                    vic_run_veg_lib[veg_class].CO2Specificity,
                                    iter_soil_veg_var.NscaleFactor,
                                    Tair,
                                    force->shortwave[hidx],
                                    iter_soil_veg_var.aPARLayer,
                                    soil_con->elevation,
                                    force->Catm[hidx],
                                    CanopLayerBnd,
                                    veg_var->LAI,
                                    "rs",
                                    iter_soil_veg_var.rsLayer,
                                    &(iter_soil_veg_var.rc),
                                    &(iter_soil_veg_var.Ci),
                                    &(iter_soil_veg_var.GPP),
                                    &(iter_soil_veg_var.Rdark),
                                    &(iter_soil_veg_var.Rphoto),
                                    &(iter_soil_veg_var.Rmaint),
                                    &(iter_soil_veg_var.Rgrowth),
                                    &(iter_soil_veg_var.Raut),
                                    &(iter_soil_veg_var.NPP));
                /* Adjust by fraction of canopy that was dry and account for any other inhibition`*/
                dryFrac *= iter_soil_veg_var.NPPfactor;
                iter_soil_veg_var.GPP *= dryFrac;
                iter_soil_veg_var.Rdark *= dryFrac;
                iter_soil_veg_var.Rphoto *= dryFrac;
                iter_soil_veg_var.Rmaint *= dryFrac;
                iter_soil_veg_var.Rgrowth *= dryFrac;
                iter_soil_veg_var.Raut *= dryFrac;
                iter_soil_veg_var.NPP *= dryFrac;
                /* Adjust by veg cover fraction */
                iter_soil_veg_var.GPP *= iter_soil_veg_var.fcanopy;
                iter_soil_veg_var.Rdark *= iter_soil_veg_var.fcanopy;
                iter_soil_veg_var.Rphoto *= iter_soil_veg_var.fcanopy;
                iter_soil_veg_var.Rmaint *= iter_soil_veg_var.fcanopy;
                iter_soil_veg_var.Rgrowth *= iter_soil_veg_var.fcanopy;
                iter_soil_veg_var.Raut *= iter_soil_veg_var.fcanopy;
                iter_soil_veg_var.NPP *= iter_soil_veg_var.fcanopy;
            }
            else {
                iter_soil_veg_var.rc = param.HUGE_RESIST;
                for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                    iter_soil_veg_var.rsLayer[cidx] = param.HUGE_RESIST;
                }
                iter_soil_veg_var.Ci = 0;
                iter_soil_veg_var.GPP = 0;
                iter_soil_veg_var.Rdark = 0;
                iter_soil_veg_var.Rphoto = 0;
                iter_soil_veg_var.Rmaint = 0;
                iter_soil_veg_var.Rgrowth = 0;
                iter_soil_veg_var.Raut = 0;
                iter_soil_veg_var.NPP = 0;
            }
        }

        /**************************************
           Compute Potential Evap
        **************************************/

        compute_pot_evap(gp->model_steps_per_day,
                         vic_run_veg_lib[veg_class].rmin,
                         iter_soil_veg_var.albedo, force->shortwave[hidx],
                         iter_soil_energy.NetLongAtmos,
                         vic_run_veg_lib[veg_class].RGL, Tair, VPDcanopy,
                         iter_soil_veg_var.LAI, soil_con->elevation,
                         iter_aero_resist_veg,
                         vic_run_veg_lib[veg_class].overstory,
                         vic_run_veg_lib[veg_class].rarc,
                         iter_soil_veg_var.fcanopy, iter_aero_resist_used[0],
                         &iter_pot_evap
#ifdef VCS_V5
                         ,
                         &iter_pot_evap_veg,
                         &iter_pot_evap_soil
#endif
                         );
#ifdef VCS_V5
        cell->VCS.pot_evap_veg_daily    += iter_pot_evap_veg;
        cell->VCS.pot_evap_soil_daily   += iter_pot_evap_soil;
        cell->VCS.pot_evap_total_daily  += iter_pot_evap;
#endif
        /**************************************
           Store sub-model time step variables
        **************************************/

        snow_energy = iter_snow_energy;
        soil_energy = iter_soil_energy;
        snow_veg_var = iter_snow_veg_var;
        soil_veg_var = iter_soil_veg_var;
        step_snow = iter_snow;
        for (lidx = 0; lidx < options.Nlayer; lidx++) {
            step_layer[lidx] = iter_layer[lidx];
        }

        if (iveg != Nveg) {
            if (step_snow.snow) {
                store_throughfall += snow_veg_var.throughfall;
                store_canopyevap += snow_veg_var.canopyevap;
                soil_veg_var.Wdew = snow_veg_var.Wdew;
            }
            else {
                store_throughfall += soil_veg_var.throughfall;
                store_canopyevap += soil_veg_var.canopyevap;
                snow_veg_var.Wdew = soil_veg_var.Wdew;
            }
            step_Wdew = soil_veg_var.Wdew;
            if (options.CARBON) {
                store_gc += 1 / soil_veg_var.rc;
                for (cidx = 0; cidx < options.Ncanopy; cidx++) {
                    store_gsLayer[cidx] += 1 / soil_veg_var.rsLayer[cidx];
                }
                store_Ci += soil_veg_var.Ci;
                store_GPP += soil_veg_var.GPP;
                store_Rdark += soil_veg_var.Rdark;
                store_Rphoto += soil_veg_var.Rphoto;
                store_Rmaint += soil_veg_var.Rmaint;
                store_Rgrowth += soil_veg_var.Rgrowth;
                store_Raut += soil_veg_var.Raut;
                store_NPP += soil_veg_var.NPP;
            }
        }
        for (lidx = 0; lidx < options.Nlayer; lidx++) {
            store_layerevap[lidx] += step_layer[lidx].evap;
        }
        store_ppt += step_ppt;
        if (iter_aero_resist_used[0] > 0) {
            store_aero_cond_used[0] += 1 / iter_aero_resist_used[0];
        }
        else {
            store_aero_cond_used[0] += param.HUGE_RESIST;
        }
        if (iter_aero_resist_used[1] > 0) {
            store_aero_cond_used[1] += 1 / iter_aero_resist_used[1];
        }
        else {
            store_aero_cond_used[1] += param.HUGE_RESIST;
        }

        if (iveg != Nveg) {
            store_canopy_vapor_flux += step_snow.canopy_vapor_flux;
        }
        store_melt += step_melt;
        store_vapor_flux += step_snow.vapor_flux;
        store_surface_flux += step_snow.surface_flux;
        store_blowing_flux += step_snow.blowing_flux;

        out_prec[0] += step_out_prec;
        out_rain[0] += step_out_rain;
        out_snow[0] += step_out_snow;

        if (INCLUDE_SNOW) {
            /* copy needed flux terms to the snowpack */
            snow_energy.advected_sensible = soil_energy.advected_sensible;
            snow_energy.advection = soil_energy.advection;
            snow_energy.deltaCC = soil_energy.deltaCC;
            snow_energy.latent = soil_energy.latent;
            snow_energy.latent_sub = soil_energy.latent_sub;
            snow_energy.refreeze_energy = soil_energy.refreeze_energy;
            snow_energy.sensible = soil_energy.sensible;
            snow_energy.snow_flux = soil_energy.snow_flux;
        }

        store_AlbedoOver += snow_energy.AlbedoOver;
        store_AlbedoUnder += soil_energy.AlbedoUnder;
        store_AtmosLatent += soil_energy.AtmosLatent;
        store_AtmosLatentSub += soil_energy.AtmosLatentSub;
        store_AtmosSensible += soil_energy.AtmosSensible;
        store_LongOverIn += snow_energy.LongOverIn;
        store_LongUnderIn += LongUnderIn;
        store_LongUnderOut += soil_energy.LongUnderOut;
        store_NetLongAtmos += soil_energy.NetLongAtmos;
        store_NetLongOver += snow_energy.NetLongOver;
        store_NetLongUnder += soil_energy.NetLongUnder;
        store_NetShortAtmos += soil_energy.NetShortAtmos;
        store_NetShortGrnd += NetShortGrnd;
        store_NetShortOver += snow_energy.NetShortOver;
        store_NetShortUnder += soil_energy.NetShortUnder;
        store_ShortOverIn += snow_energy.ShortOverIn;
        store_ShortUnderIn += soil_energy.ShortUnderIn;
        store_canopy_advection += snow_energy.canopy_advection;
        store_canopy_latent += snow_energy.canopy_latent;
        store_canopy_latent_sub += snow_energy.canopy_latent_sub;
        store_canopy_sensible += snow_energy.canopy_sensible;
        store_canopy_refreeze += snow_energy.canopy_refreeze;
        store_deltaH += soil_energy.deltaH;
        store_fusion += soil_energy.fusion;
        store_grnd_flux += soil_energy.grnd_flux;
        store_latent += soil_energy.latent;
        store_latent_sub += soil_energy.latent_sub;
        store_melt_energy += step_melt_energy;
        store_sensible += soil_energy.sensible;
        if (step_snow.swq == 0 && INCLUDE_SNOW) {
            if (last_snow_coverage == 0 && step_prec > 0) {
                last_snow_coverage = 1;
            }
            store_advected_sensible += snow_energy.advected_sensible *
                                       last_snow_coverage;
            store_advection += snow_energy.advection * last_snow_coverage;
            store_deltaCC += snow_energy.deltaCC * last_snow_coverage;
            store_snow_flux += soil_energy.snow_flux * last_snow_coverage;
            store_refreeze_energy += snow_energy.refreeze_energy *
                                     last_snow_coverage;
        }
        else if (step_snow.snow || INCLUDE_SNOW) {
            store_advected_sensible += snow_energy.advected_sensible *
                                       (step_snow.coverage + delta_coverage);
            store_advection += snow_energy.advection *
                               (step_snow.coverage + delta_coverage);
            store_deltaCC += snow_energy.deltaCC *
                             (step_snow.coverage + delta_coverage);
            store_snow_flux += soil_energy.snow_flux *
                               (step_snow.coverage + delta_coverage);
            store_refreeze_energy += snow_energy.refreeze_energy *
                                     (step_snow.coverage + delta_coverage);
        }
        store_pot_evap += iter_pot_evap;

        /* increment time step */
        N_steps++;
        hidx += step_inc;
    }
    while (hidx < endhidx);

    /************************************************
       Store snow variables for sub-model time steps
    ************************************************/

    (*snow) = step_snow;
    snow->vapor_flux = store_vapor_flux;
    snow->blowing_flux = store_blowing_flux;
    snow->surface_flux = store_surface_flux;
    snow->canopy_vapor_flux = store_canopy_vapor_flux;
    (*Melt) = store_melt;
    snow->melt = store_melt;
    ppt = store_ppt;

    /******************************************************
       Store energy flux averages for sub-model time steps
    ******************************************************/

    (*energy) = soil_energy;
    energy->AlbedoOver = store_AlbedoOver / (double) N_steps;
    energy->AlbedoUnder = store_AlbedoUnder / (double) N_steps;
    energy->AtmosLatent = store_AtmosLatent / (double) N_steps;
    energy->AtmosLatentSub = store_AtmosLatentSub / (double) N_steps;
    energy->AtmosSensible = store_AtmosSensible / (double) N_steps;
    energy->LongOverIn = store_LongOverIn / (double) N_steps;
    energy->LongUnderIn = store_LongUnderIn / (double) N_steps;
    energy->LongUnderOut = store_LongUnderOut / (double) N_steps;
    energy->NetLongAtmos = store_NetLongAtmos / (double) N_steps;
    energy->NetLongOver = store_NetLongOver / (double) N_steps;
    energy->NetLongUnder = store_NetLongUnder / (double) N_steps;
    energy->NetShortAtmos = store_NetShortAtmos / (double) N_steps;
    energy->NetShortGrnd = store_NetShortGrnd / (double) N_steps;
    energy->NetShortOver = store_NetShortOver / (double) N_steps;
    energy->NetShortUnder = store_NetShortUnder / (double) N_steps;
    energy->ShortOverIn = store_ShortOverIn / (double) N_steps;
    energy->ShortUnderIn = store_ShortUnderIn / (double) N_steps;
    energy->advected_sensible = store_advected_sensible / (double) N_steps;
    energy->canopy_advection = store_canopy_advection / (double) N_steps;
    energy->canopy_latent = store_canopy_latent / (double) N_steps;
    energy->canopy_latent_sub = store_canopy_latent_sub / (double) N_steps;
    energy->canopy_refreeze = store_canopy_refreeze / (double) N_steps;
    energy->canopy_sensible = store_canopy_sensible / (double) N_steps;
    energy->deltaH = store_deltaH / (double) N_steps;
    energy->fusion = store_fusion / (double) N_steps;
    energy->grnd_flux = store_grnd_flux / (double) N_steps;
    energy->latent = store_latent / (double) N_steps;
    energy->latent_sub = store_latent_sub / (double) N_steps;
    energy->melt_energy = store_melt_energy / (double) N_steps;
    energy->sensible = store_sensible / (double) N_steps;
    if (snow->snow || INCLUDE_SNOW) {
        energy->advection = store_advection / (double) N_steps;
        energy->deltaCC = store_deltaCC / (double) N_steps;
        energy->refreeze_energy = store_refreeze_energy / (double) N_steps;
        energy->snow_flux = store_snow_flux / (double) N_steps;
    }
    energy->Tfoliage = snow_energy.Tfoliage;
    energy->Tfoliage_fbflag = snow_energy.Tfoliage_fbflag;
    energy->Tfoliage_fbcount = snow_energy.Tfoliage_fbcount;

    /**********************************************************
       Store vegetation variable sums for sub-model time steps
    **********************************************************/

    if (iveg != Nveg) {
        veg_var->throughfall = store_throughfall;
        veg_var->canopyevap = store_canopyevap;
        if (snow->snow) {
            veg_var->Wdew = snow_veg_var.Wdew;
        }
        else {
            veg_var->Wdew = soil_veg_var.Wdew;
        }
    }

    /**********************************************************
       Store soil layer variables for sub-model time steps
    **********************************************************/

    for (lidx = 0; lidx < Nlayers; lidx++) {
        layer[lidx] = step_layer[lidx];
        layer[lidx].evap = store_layerevap[lidx];
    }
    if (store_aero_cond_used[0] > 0 && store_aero_cond_used[0] <
        param.HUGE_RESIST) {
        aero_resist_used[0] = 1 / (store_aero_cond_used[0] / (double) N_steps);
    }
    else if (store_aero_cond_used[0] >= param.HUGE_RESIST) {
        aero_resist_used[0] = 0;
    }
    else {
        aero_resist_used[0] = param.HUGE_RESIST;
    }
    if (store_aero_cond_used[1] > 0 && store_aero_cond_used[1] <
        param.HUGE_RESIST) {
        aero_resist_used[1] = 1 / (store_aero_cond_used[1] / (double) N_steps);
    }
    else if (store_aero_cond_used[1] >= param.HUGE_RESIST) {
        aero_resist_used[1] = 0;
    }
    else {
        aero_resist_used[1] = param.HUGE_RESIST;
    }
    cell->pot_evap = store_pot_evap;

    /**********************************************************
       Store carbon cycle variable sums for sub-model time steps
    **********************************************************/

    if (options.CARBON && iveg != Nveg) {
        veg_var->rc = 1 / store_gc / (double) N_steps;
        for (cidx = 0; cidx < options.Ncanopy; cidx++) {
            veg_var->rsLayer[cidx] = 1 / store_gsLayer[cidx] / (double) N_steps;
        }
        veg_var->Ci = store_Ci / (double) N_steps;
        veg_var->GPP = store_GPP / (double) N_steps;
        veg_var->Rdark = store_Rdark / (double) N_steps;
        veg_var->Rphoto = store_Rphoto / (double) N_steps;
        veg_var->Rmaint = store_Rmaint / (double) N_steps;
        veg_var->Rgrowth = store_Rgrowth / (double) N_steps;
        veg_var->Raut = store_Raut / (double) N_steps;
        veg_var->NPP = store_NPP / (double) N_steps;

        free((char *) (store_gsLayer));

        soil_carbon_balance(soil_con, energy, cell, veg_var);

        // Update running total annual NPP
        if (veg_var->NPP > 0) {
            veg_var->AnnualNPP += veg_var->NPP * CONST_MWC / MOLE_PER_KMOLE *
                                  gp->dt;
        }
    }

    /********************************************************
       Compute Runoff, Baseflow, and Soil Moisture Transport
    ********************************************************/

    (*inflow) = ppt;

    ErrorFlag = runoff(cell, energy, soil_con, ppt, soil_con->frost_fract,
                       options.Nnode);

    return(ErrorFlag);
}