Esempio n. 1
0
// If a parameter is given, we try to connect to the given server.
// This enables us to test the remote capabilites of ScrollView.
int main(int argc, char** argv) {
	const char* server_name;
	if (argc > 1) { server_name = argv[1]; } else { server_name = "localhost"; }
	SVPaint svp(server_name);
}
Esempio n. 2
0
/*****************************************************************************
  Function name: IceEnergyBalance()

  Purpose      : Calculate the surface energy balance for the snow pack

  Required     :
    double TSurf           - new estimate of effective surface temperature

  Returns      :
    double RestTerm        - Rest term in the energy balance

  Modifies     : 
    double *RefreezeEnergy - Refreeze energy (W/m2) 
    double *VaporMassFlux  - Mass flux of water vapor to or from the
                            intercepted snow (m/s)

  Comments     :
    Reference:  Bras, R. A., Hydrology, an introduction to hydrologic
                science, Addisson Wesley, Inc., Reading, etc., 1990.

  Modifications:
  16-Jul-04 Renamed VaporMassFlux to vapor_flux to denote fact that its
	    units are m/timestep rather than kg/m2s.  Created new variable
	    VaporMassFlux with units of kg/m2s.  After VaporMassFlux is
	    computed, vapor_flux is derived from it by unit conversion.
	    vapor_flux is the variable that is passed in/out of this
	    function.							TJB
  24-Aug-04 Added logic to handle blowing_flux and vapor_flux.		TJB
  28-Sep-04 Added Ra_used to store the aerodynamic resistance used in flux
	    calculations.						TJB
  04-Oct-04 Merged with Laura Bowling's updated lake model code.  Now
	    blowing snow sublimation is calculated for lakes.		TJB
  2006-Sep-23 Replaced redundant STEFAN constant with STEFAN_B.  TJB
  2006-Nov-07 Removed LAKE_MODEL option. TJB

*****************************************************************************/
double IceEnergyBalance::calculate(double TSurf)
{

  const char *Routine = "IceEnergyBalance";

  double Density;                /* Density of water/ice at TMean (kg/m3) */
  double EsSnow;                 /* saturated vapor pressure in the snow pack
                                   (Pa)  */  
  
  double Ls;                     /* Latent heat of sublimation (J/kg) */
  double NetRad;			/* Net radiation exchange at surface (W/m2) */
  double RestTerm;		/* Rest term in surface energy balance
				   (W/m2) */
  double  TMean;                /* Average temperature for time step (C) */
  double qnull;
  double VaporMassFlux;          /* Total mass flux of water vapor to or from
                                    snow (kg/m2s) */
  double BlowingMassFlux;        /* Mass flux of water vapor to or from
                                    blowing snow (kg/m2s) */
  double SurfaceMassFlux;        /* Mass flux of water vapor to or from
                                    snow pack (kg/m2s) */
  
  /* Calculate active temp for energy balance as average of old and new  */
  
/*   TMean = 0.5 * (OldTSurf + TSurf); */
  TMean = TSurf;
  Density = RHO_W;
  
  /* Correct aerodynamic conductance for stable conditions
     Note: If air temp >> snow temp then aero_cond -> 0 (i.e. very stable)
     velocity (vel_2m) is expected to be in m/sec */                        
  
  /* Apply the stability correction to the aerodynamic resistance 
     NOTE: In the old code 2m was passed instead of Z-Displacement.  I (bart)
     think that it is more correct to calculate ALL fluxes at the same
     reference level */


  if (Wind > 0.0)
    *Ra_used = Ra / StabilityCorrection(Z, 0.f, TMean, Tair, Wind, Z0);
    /* *Ra_used = Ra / StabilityCorrection(2.f, 0.f, TMean, Tair, Wind, Z0);*/
  else
    *Ra_used = HUGE_RESIST;

  /* Calculate longwave exchange and net radiation */
 
  *LongRadOut = LongRadIn - STEFAN_B * (TMean+273.15) * (TMean+273.15) 
    * (TMean+273.15) * (TMean+273.15);
  NetRad = ShortRad + *LongRadOut;
  
  /* Calculate the sensible heat flux */

  *SensibleHeat = AirDens * CP_PM * (Tair - TMean) / *Ra_used;

  /* Calculate the mass flux of ice to or from the surface layer */
 
  /* Calculate the saturated vapor pressure in the snow pack, 
     (Equation 3.32, Bras 1990) */

  EsSnow = svp(TMean) /* * 1000. */;

/*   EsSnow = 610.78 * exp((double)((17.269 * TMean) / (237.3 + TMean))); */

/*   if (TMean < 0.0) */
/*     EsSnow *= 1.0 + .00972 * TMean + .000042 * pow((double)TMean,(double)2.0); */
  
  /* Calculate sublimation terms and latent heat flux */

  /* blowing_flux was calculated outside of the root_brent iteration */
  BlowingMassFlux = *blowing_flux * Density / (Dt * SECPHOUR);

  latent_heat_from_snow(AirDens, EactAir, Lv, Press, Ra, TMean, Vpd,
                        LatentHeat, LatentHeatSub, &VaporMassFlux, &BlowingMassFlux,
                        &SurfaceMassFlux);

  /* Convert sublimation terms from kg/m2s to m/timestep */
  *vapor_flux = VaporMassFlux * Dt * SECPHOUR / Density;
  *surface_flux = SurfaceMassFlux * Dt * SECPHOUR / Density;

  /* Calculate advected heat flux from rain 
     WORK IN PROGRESS:  Should the following read (Tair - Tsurf) ?? */
  
  // Temporary fix for lake model.
  *AdvectedEnergy = (CH_WATER * Tair * Rain) / (Dt*SECPHOUR);
  //*AdvectedEnergy = 0.0;
  
  /* Calculate change in cold content */
  /* No change in cold content in lake model */

  /* Changes for lake model start here. Actually, equals qo-Io (P&H eq. 7)*/
    /* because Io (SWnet) is included in NetRad below. */
  qnull = (1/AvgCond)*(Tfreeze - TMean + SWconducted);
  *qf   = qnull;

  /* Changes for lake model end here. */
  
  /* Calculate net energy exchange at the snow surface */
  
  RestTerm = ( NetRad + *SensibleHeat + *LatentHeat + *LatentHeatSub + *AdvectedEnergy 
	       + qnull );

  *RefreezeEnergy = (SurfaceLiquidWater * Lf * Density)/(Dt * SECPHOUR);

  /* Melting, or partially refreeze surface water. */
  if (TSurf == 0.0 && RestTerm > -(*RefreezeEnergy)) {
    *RefreezeEnergy = -RestTerm;  /* available energy input over cold content
                                    used to melt, i.e. Qrf is negative value
                                    (energy out of pack)*/ 
    RestTerm = 0.0;
  }
  else {                         /* Pack is getting colder. */
    RestTerm += *RefreezeEnergy; /* add this positive value to the pack */
  }
  
  return RestTerm;
}
void latent_heat_from_snow(double  AirDens,
			   double  EactAir,
			   double  Lv,
			   double  Press,
			   double  Ra,
			   double  TMean, 
			   double  Vpd,
			   double *LatentHeat,
			   double *LatentHeatSublimation,
			   double *VaporMassFlux,
			   double *BlowingMassFlux,
			   double *SurfaceMassFlux) {
/**********************************************************************
  latent_heat_from_snow.c       Laura Bowling           

  Split out of the snowpack energy balance, this subroutine computes
  the latent heat from the snowpack.

  Modifications:
  11-18-02 Modified to handle the effects of blowing snow.       LCB
  16-Jul-04 Moved the calculation of BlowingMassFlux back into this
	    function.  Added "month" to the parameter list and added
	    declaration of *veg_lib, to enable this calculation to
	    occur.  Modified calculations of all sublimation terms
	    to ensure that VaporMassFlux, BlowingMassFlux, and
	    SurfaceMassFlux consistently have units of kg/m2s.	TJB
  05-Aug-04 Moved the calculation of BlowingMassFlux back out of this
	    function into surface_fluxes(), as part of merge with
	    Laura Bowling's latest code.			TJB

***********************************************************************/

  double EsSnow;
  double Ls;

  EsSnow = svp(TMean);

  // SurfaceMassFlux and BlowingMassFlux in kg/m2s

  *SurfaceMassFlux = AirDens * ( EPS / Press ) * ( EactAir - EsSnow ) / Ra;
  
  if ( Vpd == 0.0 && *SurfaceMassFlux < 0.0 ) 
    *SurfaceMassFlux = 0.0;

  /* Calculate total latent heat flux */

  *VaporMassFlux = *SurfaceMassFlux + *BlowingMassFlux;

  if ( TMean >= 0.0 ) {
    /* Melt conditions: use latent heat of vaporization */
    *LatentHeat = Lv * (*VaporMassFlux);
    *LatentHeatSublimation = 0;
  }
  else {
    /* Accumulation: use latent heat of sublimation (Eq. 3.19, Bras 1990 */
    Ls = (677. - 0.07 * TMean) * JOULESPCAL * GRAMSPKG;
    *LatentHeatSublimation = Ls * (*VaporMassFlux);
    *LatentHeat = 0;
  }

}  
Esempio n. 4
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);
}
Esempio n. 5
0
/******************************************************************************
 * @brief    Initialize atmospheric variables for the model and snow time steps.
 *****************************************************************************/
void
vic_force(atmos_data_struct *atmos,
          dmy_struct        *dmy,
          FILE             **infile,
          veg_con_struct    *veg_con,
          veg_hist_struct  **veg_hist,
          soil_con_struct   *soil_con)
{
    extern option_struct       options;
    extern param_set_struct    param_set;
    extern global_param_struct global_param;
    extern parameters_struct   param;
    extern size_t              NR, NF;

    size_t                     i;
    size_t                     j;
    size_t                     v;
    size_t                     rec;
    size_t                     uidx;
    double                     t_offset;
    double                   **forcing_data;
    double                  ***veg_hist_data;
    double                     avgJulyAirTemp;
    double                    *Tfactor;
    bool                      *AboveTreeLine;

    /*******************************
       Check that required inputs were supplied
    *******************************/

    if (!param_set.TYPE[AIR_TEMP].SUPPLIED) {
        log_err("Air temperature must be supplied as a forcing");
    }
    if (!param_set.TYPE[PREC].SUPPLIED) {
        log_err("Precipitation must be supplied as a forcing");
    }
    if (!param_set.TYPE[SWDOWN].SUPPLIED) {
        log_err("Downward shortwave radiation must be supplied as a forcing");
    }
    if (!param_set.TYPE[LWDOWN].SUPPLIED) {
        log_err("Downward longwave radiation must be supplied as a forcing");
    }
    if (!param_set.TYPE[PRESSURE].SUPPLIED) {
        log_err("Atmospheric pressure must be supplied as a forcing");
    }
    if (!param_set.TYPE[VP].SUPPLIED) {
        log_err("Vapor ressure must be supplied as a forcing");
    }
    if (!param_set.TYPE[WIND].SUPPLIED) {
        log_err("Wind speed must be supplied as a forcing");
    }

    /*******************************
       Miscellaneous initialization
    *******************************/

    /* Assign local copies of some variables */
    avgJulyAirTemp = soil_con->avgJulyAirTemp;
    Tfactor = soil_con->Tfactor;
    AboveTreeLine = soil_con->AboveTreeLine;

    /* Assign N_ELEM for veg-dependent forcings */
    if (param_set.TYPE[ALBEDO].SUPPLIED) {
        param_set.TYPE[ALBEDO].N_ELEM = veg_con[0].vegetat_type_num;
    }
    if (param_set.TYPE[LAI_IN].SUPPLIED) {
        param_set.TYPE[LAI_IN].N_ELEM = veg_con[0].vegetat_type_num;
    }
    if (param_set.TYPE[FCANOPY].SUPPLIED) {
        param_set.TYPE[FCANOPY].N_ELEM = veg_con[0].vegetat_type_num;
    }

    /*******************************
       read in meteorological data
    *******************************/

    forcing_data = read_forcing_data(infile, global_param, &veg_hist_data);

    log_info("Read meteorological forcing file");

    /****************************************************
       Variables in the atmos_data structure
    ****************************************************/

    t_offset = Tfactor[0];
    for (i = 1; i < options.SNOW_BAND; i++) {
        if (Tfactor[i] < t_offset) {
            t_offset = Tfactor[i];
        }
    }

    for (rec = 0; rec < global_param.nrecs; rec++) {
        for (i = 0; i < NF; i++) {
            uidx = rec * NF + i;
            // temperature in Celsius
            atmos[rec].air_temp[i] = forcing_data[AIR_TEMP][uidx];
            // precipitation in mm/period
            atmos[rec].prec[i] = forcing_data[PREC][uidx];
            // downward shortwave in W/m2
            atmos[rec].shortwave[i] = forcing_data[SWDOWN][uidx];
            // downward longwave in W/m2
            atmos[rec].longwave[i] = forcing_data[LWDOWN][uidx];
            // pressure in Pa
            atmos[rec].pressure[i] = forcing_data[PRESSURE][uidx] * PA_PER_KPA;
            // vapor pressure in Pa
            atmos[rec].vp[i] = forcing_data[VP][uidx] * PA_PER_KPA;
            // vapor pressure deficit in Pa
            atmos[rec].vpd[i] = svp(atmos[rec].air_temp[i]) - atmos[rec].vp[i];
            // air density in kg/m3
            atmos[rec].density[i] = air_density(atmos[rec].air_temp[i],
                                                atmos[rec].pressure[i]);
            // wind speed in m/s
            atmos[rec].wind[i] = forcing_data[WIND][uidx];
            // snow flag
            atmos[rec].snowflag[i] = will_it_snow(&(atmos[rec].air_temp[i]),
                                                  t_offset,
                                                  param.SNOW_MAX_SNOW_TEMP,
                                                  &(atmos[rec].prec[i]), 1);
            // Optional inputs
            if (options.LAKES) {
                // Channel inflow from upstream (into lake)
                if (param_set.TYPE[CHANNEL_IN].SUPPLIED) {
                    atmos[rec].channel_in[i] = forcing_data[CHANNEL_IN][uidx];
                }
                else {
                    atmos[rec].channel_in[i] = 0;
                }
            }
            if (options.CARBON) {
                // Atmospheric CO2 concentration
                atmos[rec].Catm[i] = forcing_data[CATM][uidx];
                // Fraction of shortwave that is direct
                atmos[rec].fdir[i] = forcing_data[FDIR][uidx];
                // photosynthetically active radiation
                atmos[rec].par[i] = forcing_data[PAR][uidx];
                // Cosine of solar zenith angle
                atmos[rec].coszen[i] = compute_coszen(soil_con->lat,
                                                      soil_con->lng,
                                                      soil_con->time_zone_lng,
                                                      dmy[rec].day_in_year,
                                                      dmy[rec].dayseconds);
            }
        }
        if (NF > 1) {
            atmos[rec].air_temp[NR] = average(atmos[rec].air_temp, NF);
            // For precipitation put total
            atmos[rec].prec[NR] = average(atmos[rec].prec, NF) * NF;
            atmos[rec].shortwave[NR] = average(atmos[rec].shortwave, NF);
            atmos[rec].longwave[NR] = average(atmos[rec].longwave, NF);
            atmos[rec].pressure[NR] = average(atmos[rec].pressure, NF);
            atmos[rec].vp[NR] = average(atmos[rec].vp, NF);
            atmos[rec].vpd[NR] = average(atmos[rec].vpd, NF);
            atmos[rec].density[NR] = average(atmos[rec].density, NF);
            atmos[rec].wind[NR] = average(atmos[rec].wind, NF);
            atmos[rec].snowflag[NR] = false;
            for (i = 0; i < NF; i++) {
                if (atmos[rec].snowflag[i] == true) {
                    atmos[rec].snowflag[NR] = true;
                }
            }
            if (options.LAKES) {
                atmos[rec].channel_in[NR] =
                    average(atmos[rec].channel_in, NF) * NF;
            }
            if (options.CARBON) {
                atmos[rec].Catm[NR] = average(atmos[rec].Catm, NF);
                atmos[rec].fdir[NR] = average(atmos[rec].fdir, NF);
                atmos[rec].par[NR] = average(atmos[rec].par, NF);
                // for coszen, use value at noon
                atmos[rec].coszen[NR] = compute_coszen(soil_con->lat,
                                                       soil_con->lng,
                                                       soil_con->time_zone_lng,
                                                       dmy[rec].day_in_year,
                                                       SEC_PER_DAY / 2);
            }
        }
    }

    /****************************************************
       Variables in the veg_hist structure
    ****************************************************/

    /* First, assign default climatology */
    for (rec = 0; rec < global_param.nrecs; rec++) {
        for (v = 0; v <= veg_con[0].vegetat_type_num; v++) {
            for (i = 0; i < NF; i++) {
                veg_hist[rec][v].albedo[i] =
                    veg_con[v].albedo[dmy[rec].month - 1];
                veg_hist[rec][v].displacement[i] =
                    veg_con[v].displacement[dmy[rec].month - 1];
                veg_hist[rec][v].fcanopy[i] =
                    veg_con[v].fcanopy[dmy[rec].month - 1];
                veg_hist[rec][v].LAI[i] =
                    veg_con[v].LAI[dmy[rec].month - 1];
                veg_hist[rec][v].roughness[i] =
                    veg_con[v].roughness[dmy[rec].month - 1];
            }
        }
    }

    /* Next, overwrite with veg_hist values, validate, and average */
    for (rec = 0; rec < global_param.nrecs; rec++) {
        for (v = 0; v <= veg_con[0].vegetat_type_num; v++) {
            for (i = 0; i < NF; i++) {
                uidx = rec * NF + i;
                if (param_set.TYPE[ALBEDO].SUPPLIED &&
                    options.ALB_SRC == FROM_VEGHIST) {
                    if (veg_hist_data[ALBEDO][v][uidx] != NODATA_VH) {
                        veg_hist[rec][v].albedo[i] =
                            veg_hist_data[ALBEDO][v][uidx];
                    }
                }
                if (param_set.TYPE[LAI_IN].SUPPLIED &&
                    options.LAI_SRC == FROM_VEGHIST) {
                    if (veg_hist_data[LAI_IN][v][uidx] != NODATA_VH) {
                        veg_hist[rec][v].LAI[i] =
                            veg_hist_data[LAI_IN][v][uidx];
                    }
                }
                if (param_set.TYPE[FCANOPY].SUPPLIED &&
                    options.FCAN_SRC == FROM_VEGHIST) {
                    if (veg_hist_data[FCANOPY][v][uidx] != NODATA_VH) {
                        veg_hist[rec][v].fcanopy[i] =
                            veg_hist_data[FCANOPY][v][uidx];
                    }
                }
                // Check on fcanopy
                if (veg_hist[rec][v].fcanopy[i] < MIN_FCANOPY) {
                    log_warn(
                        "rec %zu, veg %zu substep %zu fcanopy %f < minimum of %f; setting = %f\n", rec, v, i,
                        veg_hist[rec][v].fcanopy[i], MIN_FCANOPY,
                        MIN_FCANOPY);
                    veg_hist[rec][v].fcanopy[i] = MIN_FCANOPY;
                }
            }
            if (NF > 1) {
                veg_hist[rec][v].albedo[NR] = average(veg_hist[rec][v].albedo,
                                                      NF);
                veg_hist[rec][v].displacement[NR] = average(
                    veg_hist[rec][v].displacement, NF);
                veg_hist[rec][v].fcanopy[NR] = average(
                    veg_hist[rec][v].fcanopy, NF);
                veg_hist[rec][v].LAI[NR] = average(veg_hist[rec][v].LAI, NF);
                veg_hist[rec][v].roughness[NR] = average(
                    veg_hist[rec][v].roughness, NF);
            }
        }
    }

    /****************************************************
       Free forcing_data and veg_hist_data structures
    ****************************************************/

    for (i = 0; i < N_FORCING_TYPES; i++) {
        if (param_set.TYPE[i].SUPPLIED) {
            if (i != ALBEDO && i != LAI_IN && i != FCANOPY) {
                free(forcing_data[i]);
            }
            else {
                for (j = 0; j < param_set.TYPE[i].N_ELEM; j++) {
                    free(veg_hist_data[i][j]);
                }
                free(veg_hist_data[i]);
            }
        }
    }
    free(forcing_data);
    free(veg_hist_data);

    /****************************************************
       Compute treeline based on July average temperature
    ****************************************************/

    if (options.COMPUTE_TREELINE) {
        if (!(options.JULY_TAVG_SUPPLIED && avgJulyAirTemp == -999)) {
            compute_treeline(atmos, dmy, avgJulyAirTemp, Tfactor,
                             AboveTreeLine);
        }
    }
}
/*****************************************************************************
  Function name: SnowInterception()

  Purpose      : Calculate snow interception and release by the canopy

  Required     :
    int Dt                 - Model timestep (hours)
    double F                - Fractional coverage
    double LAI              - Leaf Area Index
    double MaxInt           - Maximum rainfall interception storage (m)
    double Ra               - Aerodynamic resistance (uncorrected for
                             stability) (s/m)
    double *Ra_used         - Aerodynamic resistance after adjustments (s/m)
    double AirDens          - Density of air (kg/m3)
    double EactAir          - Actual vapor pressure of air (Pa) 
    double Lv               - Latent heat of vaporization (J/kg3)
    PIXRAD *LocalRad       - Components of radiation balance for current pixel
                             (W/m2) 
    double Press            - Air pressure (Pa)
    double Tair             - Air temperature (C) 
    double Vpd	           - Vapor pressure deficit (Pa) 
    double Wind             - Wind speed (m/s)
    double *RainFall        - Amount of rain (m)
    double *Snowfall        - Amount of snow (m)
    double *IntRain         - Intercepted rain (m) 
    double *IntSnow         - Snow water equivalent of intercepted snow (m)
    double *TempIntStorage  - Temporary storage for snowmelt and rainfall
                             involved in mass release calculations (m)
    double *VaporMassFlux   - Vapor mass flux to/from intercepted snow
                             (m/timestep)
    double *Tcanopy         - Canopy temperature (C)
    double *MeltEnergy      - Energy used in heating and melting of the snow 
                             (W/m2)

  Returns      : none

  Modifies     :
    double *Ra_used         - Aerodynamic resistance after adjustments (s/m)
    double *RainFall        - Amount of rain (m)
    double *Snowfall        - Amount of snow (m)
    double *IntRain         - Intercepted rain (m) 
    double *IntSnow         - Snow water equivalent of intercepted snow (m)
    double *TempIntStorage  - Temporary storage for snowmelt and rainfall
                             involved in mass release calculations (m)
    double *VaporMassFlux   - Vapor mass flux to/from intercepted snow
                             (m/timestep)  
    double *Tcanopy         - Canopy temperature (C)

  Comments     : Only the top canopy layer is taken into account for snow
                 interception.  Snow interception by lower canopy is
                 disregarded.  Rain water CAN be intercepted by lower canopy
                 layers (similar to InterceptionStorage()).
                 Of course:  NO vegetation -> NO interception

  Modifications:
  06-98 included maximum structural loading to prevent the model
        from loading the canopy with more snow than it can handle
        structurally.                                             PXS
  09-98 aerodynamic resistances in the canopy when snow has been  
        intercepted is increased by a factor of 10:  include REF
        Journal of Hydrology, 1998                            KAC, GO'D
  28-Sep-04 Added Ra_used to store the aerodynamic resistance actually
	    used in flux calculations.				TJB

*****************************************************************************/
void snow_intercept(double Dt, 
		    double F,  
		    double LAI, 
		    double MaxInt, 
		    double Ra, 
		    double *Ra_used, 
		    double AirDens,
		    double EactAir, 
		    double Lv, 
		    double Shortwave,
		    double Longwave, 
		    double Press, 
		    double Tair, 
		    double Vpd, 
		    double Wind,  
		    double *RainFall,
		    double *SnowFall, 
		    double *IntRain, 
		    double *IntSnow,
		    double *TempIntStorage, 
		    double *VaporMassFlux,
		    double *Tcanopy, 
		    double *MeltEnergy, 
		    int month, 
		    int rec,
		    int hour)
{
  double AdvectedEnergy;         /* Energy advected by the rain (W/m2) */
  double BlownSnow;              /* Depth of snow blown of the canopy (m) */
  double DeltaSnowInt;           /* Change in the physical swe of snow
				    interceped on the branches. (m) */
  double Drip;                   /* Amount of drip from intercepted snow as a
				    result of snowmelt (m) */
  double ExcessSnowMelt;         /* Snowmelt in excess of the water holding
				    capacity of the tree (m) */
  double EsSnow;                 /* saturated vapor pressure in the snow pack
                                   (Pa)  */
  double InitialSnowInt;         /* Initial intercepted snow (m) */ 
  double InitialWaterInt;        /* Initial intercepted water (snow and rain)
                                    (m) */ 
  double LatentHeat;             /* Latent heat flux (W/m2) */
  double LongOut;                /* Longwave radiation emitted by canopy 
                                   (W/m2) */
  double Ls;                     /* Latent heat of sublimation (J/(kg K) */
  double MassBalanceError;       /* Mass blalnce to make sure no water is
				    being destroyed/created (m) */
  double MaxWaterInt;            /* Water interception capacity (m) */  
  double MaxSnowInt;             /* Snow interception capacity (m) */
  double NetRadiation;
  double PotSnowMelt;            /* Potential snow melt (m) */
  double RainThroughFall;        /* Amount of rain reaching to the ground (m)
				  */ 
  double RefreezeEnergy;         /* Energy available for refreezing or melt */
  double ReleasedMass;           /* Amount of mass release of intercepted snow
				    (m) */ 
  double SensibleHeat;           /* Sensible heat flux (W/m2) */
  double SnowThroughFall;        /* Amount of snow reaching to the ground (m)
				  */ 
  double Tmp;                    /* Temporary variable */

  double Imax1;                  /* maxium water intecept regardless of temp */
  double IntRainFract;           /* Fraction of intercpeted water which is 
				    liquid */
  double IntSnowFract;           /* Fraction of intercepted water which is 
				    solid */
  double Overload;               /* temp variable to calculated structural 
				    overloading */

  /* Convert Units from VIC (mm -> m) */
  *RainFall /= 1000.;
  *SnowFall /= 1000.;
  *IntRain  /= 1000.;
  MaxInt    /= 1000.;

  /* Initialize Drip, H2O balance, and mass release variables. */
  
  InitialWaterInt = *IntSnow + *IntRain;
  
  *IntSnow /= F;
  *IntRain /= F;
  
  InitialSnowInt = *IntSnow;
  
  Drip = 0.0;
  ReleasedMass = 0.0;
  
  /* Determine the maximum snow interception water equivalent.           
     Kobayashi, D., 1986, Snow Accumulation on a Narrow Board,           
     Cold Regions Science and Technology, (13), pp. 239-245.           
     Figure 4. */  
  
  Imax1 = 4.0* LAI_SNOW_MULTIPLIER * LAI;

  if (Tair < -1.0 && Tair > -3.0)
    MaxSnowInt = (Tair*3.0/2.0) + (11.0/2.0);
  else if (Tair > -1.0) 
    MaxSnowInt = 4.0;  
  else
    MaxSnowInt = 1.0;
  
  /* therefore LAI_ratio decreases as temp decreases */
  
  MaxSnowInt *= LAI_SNOW_MULTIPLIER * LAI;
  
  /* Calculate snow interception. */  
  
  DeltaSnowInt = (1-*IntSnow/MaxSnowInt) * *SnowFall; 
  if (DeltaSnowInt + *IntSnow > MaxSnowInt) 
    DeltaSnowInt = MaxSnowInt - *IntSnow;
  if (DeltaSnowInt < 0.0)  
    DeltaSnowInt = 0.0;
  
  /* Reduce the amount of intercepted snow if windy and cold.         
     Ringyo Shikenjo Tokyo, #54, 1952.                                
     Bulletin of the Govt. Forest Exp. Station,                       
     Govt. Forest Exp. Station, Meguro, Tokyo, Japan.                 
     FORSTX 634.9072 R475r #54.                                       
     Page 146, Figure 10.                                               
     
     Reduce the amount of intercepted snow if snowing, windy, and     
     cold (< -3 to -5 C).                                             
     Schmidt and Troendle 1992 western snow conference paper. */  
  
  if (Tair < -3.0 && DeltaSnowInt > 0.0 && Wind > 1.0) {
    BlownSnow = (0.2 * Wind - 0.2) * DeltaSnowInt;
    if (BlownSnow >= DeltaSnowInt) 
      BlownSnow = DeltaSnowInt;
    DeltaSnowInt -= BlownSnow;
  }
  
  /* now update snowfall and total accumulated intercepted snow amounts */

  if (*IntSnow +  DeltaSnowInt > Imax1) DeltaSnowInt =0.0; 
  
  /* pixel depth    */ 
  SnowThroughFall = (*SnowFall - DeltaSnowInt) * F + (*SnowFall) * (1 - F);
  
  /* physical depth */
  *IntSnow += DeltaSnowInt;
  
  /* Calculate amount of rain intercepted on branches and stored in
     intercepted snow. */  
  
  /* physical depth */
  MaxWaterInt = LIQUID_WATER_CAPACITY * (*IntSnow) + MaxInt;
  
  if ((*IntRain + *RainFall) <= MaxWaterInt) {
    /* physical depth */
    *IntRain += *RainFall;
    /* pixel depth */
    RainThroughFall = *RainFall * (1 - F);      
  }
  else {
    /* pixel depth */
    RainThroughFall = (*IntRain + *RainFall - MaxWaterInt) * F + 
      (*RainFall * (1 - F));
    /* physical depth */
    *IntRain = MaxWaterInt;
  }

  /* at this point we have calculated the amount of snowfall intercepted and
     the amount of rainfall intercepted.  These values have been 
     appropriately subtracted from SnowFall and RainFall to determine 
     SnowThroughfall and RainThroughfall.  However, we can end up with the 
     condition that the total intercepted rain plus intercepted snow is 
     greater than the maximum bearing capacity of the tree regardless of air 
     temp (Imax1).  The following routine will adjust *IntRain and *IntSnow 
     by triggering mass release due to overloading.  Of course since *IntRain
     and *IntSnow are mixed, we need to slough them of as fixed fractions  */

  if (*IntRain + *IntSnow > Imax1) { /*then trigger structural unloading*/
    Overload = (*IntSnow + *IntRain) - Imax1;
    IntRainFract= *IntRain/(*IntRain + *IntSnow);
    IntSnowFract = *IntSnow/(*IntRain + *IntSnow);
    *IntRain = *IntRain - Overload*IntRainFract;
    *IntSnow = *IntSnow - Overload*IntSnowFract;
    RainThroughFall = RainThroughFall + (Overload*IntRainFract)*F;
    SnowThroughFall = SnowThroughFall + (Overload*IntSnowFract)*F;
  }
  
  /* The canopy temperature is assumed to be equal to the air temperature if 
     the air temperature is below 0C, otherwise the canopy temperature is 
     equal to 0C */
  
  if (Tair > 0.)
    *Tcanopy = 0.;
  else
    *Tcanopy = Tair;

  /* Calculate the net radiation at the canopy surface, using the canopy 
     temperature.  The outgoing longwave is subtracted twice, because the 
     canopy radiates in two directions */

  Tmp = *Tcanopy + 273.15;
  LongOut = STEFAN_B * (Tmp * Tmp * Tmp * Tmp);
  NetRadiation = (1.-NEW_SNOW_ALB)*Shortwave + Longwave - 2 * F * LongOut;
  NetRadiation /= F;

  /* Calculate the vapor mass flux between the canopy and the surrounding 
     air mass */
  
  EsSnow = svp(*Tcanopy); 

  *Ra_used = Ra;

  /** Added division by 10 to incorporate change in canopy resistance due
      to smoothing by intercepted snow **/
  *VaporMassFlux = AirDens * (0.622/Press) * (EactAir - EsSnow) / *Ra_used / 10.0;
  *VaporMassFlux /= RHO_W; 

  if (Vpd == 0.0 && *VaporMassFlux < 0.0)
    *VaporMassFlux = 0.0;
  
  /* Calculate the latent heat flux */

  Ls = (677. - 0.07 * *Tcanopy) * 4.1868 * 1000.;
  LatentHeat = Ls * *VaporMassFlux * RHO_W;

  /* Calculate the sensible heat flux */

  SensibleHeat = AirDens * Cp * (Tair - *Tcanopy) / *Ra_used;
  
  /** Multiply Ra by 10 to reflect the value used in calculating VaporMassFlux **/
  *Ra_used *= 10.0;

  /* Calculate the advected energy */

  AdvectedEnergy = (4186.8 * Tair * *RainFall)/(Dt * SECPHOUR);

  /* Calculate the amount of energy available for refreezing */

  RefreezeEnergy = SensibleHeat + LatentHeat + NetRadiation + AdvectedEnergy;

  RefreezeEnergy *= Dt * SECPHOUR;

  /* if RefreezeEnergy is positive it means energy is available to melt the
     intercepted snow in the canopy.  If it is negative, it means that 
     intercepted water will be refrozen */
  
  /* Update maximum water interception storage */
  
  MaxWaterInt = LIQUID_WATER_CAPACITY * (*IntSnow) + MaxInt;

  /* Convert the vapor mass flux from a flux to a depth per interval */
  *VaporMassFlux *= Dt * SECPHOUR;
  
  if (RefreezeEnergy > 0.0) {

    if (-(*VaporMassFlux) > *IntRain) {
      *VaporMassFlux = -(*IntRain);
      *IntRain = 0.;
    }
    else
      *IntRain += *VaporMassFlux;

    PotSnowMelt = min((RefreezeEnergy/Lf/RHO_W), *IntSnow);

    *MeltEnergy -= (Lf * PotSnowMelt * RHO_W) / (Dt *SECPHOUR);
    
    if ((*IntRain + PotSnowMelt) <= MaxWaterInt) {

      *IntSnow -= PotSnowMelt;
      *IntRain += PotSnowMelt;
      PotSnowMelt = 0.0;

    }
    
    else {

      ExcessSnowMelt = PotSnowMelt + *IntRain - MaxWaterInt;
      
      *IntSnow -= MaxWaterInt - (*IntRain);
      *IntRain = MaxWaterInt;
      if (*IntSnow < 0.0) 
        *IntSnow = 0.0;
      
      if (SnowThroughFall > 0.0 && 
	  InitialSnowInt <= MIN_INTERCEPTION_STORAGE) {
        /* Water in excess of MaxWaterInt has been generated.  If it is 
           snowing and there was little intercepted snow at the beginning 
	   of the time step ( <= MIN_INTERCEPTION_STORAGE), then allow the 
	   snow to melt as it is intercepted */
        Drip += ExcessSnowMelt; 
        *IntSnow -= ExcessSnowMelt;
        if (*IntSnow < 0.0) 
          *IntSnow = 0.0;
      }
      else 
      /* Else, SnowThroughFall = 0.0 or SnowThroughFall > 0.0 and there is a 
         substantial amount of intercepted snow at the beginning of the time 
         step ( > MIN_INTERCEPTION_STORAGE).  Snow melt may generate mass 
         release. */
        *TempIntStorage += ExcessSnowMelt;
      
      MassRelease(IntSnow, TempIntStorage, &ReleasedMass, &Drip);
    }
    
    /* If intercepted snow has melted, add the water it held to drip */
    
    MaxWaterInt = LIQUID_WATER_CAPACITY * (*IntSnow) + MaxInt;
    if (*IntRain > MaxWaterInt) {
      Drip += *IntRain - MaxWaterInt;
      *IntRain = MaxWaterInt;
    }
  }  
  
  else /* else (RefreezeEnergy <= 0.0) */ {
    
    /* Reset *TempIntStorage to 0.0 when energy balance is negative */
    
    *TempIntStorage = 0.0;
    
    /* Refreeze as much surface water as you can */
    
    if (RefreezeEnergy > - (*IntRain) * Lf) {
      *IntSnow += fabs(RefreezeEnergy) / Lf;
      *IntRain -= fabs(RefreezeEnergy) / Lf;

      *MeltEnergy += (fabs(RefreezeEnergy) * RHO_W) / (Dt *SECPHOUR);

      RefreezeEnergy = 0.0;
    }

    else {
      
      /* All of the water in the surface layer has been frozen. */
      
      *IntSnow += *IntRain;
     
      /* Added on April 8 as a test */
      /*       RefreezeEnergy += *IntRain*Lf; */
      /*       *VaporMassFlux = MAX(*VaporMassFlux,  */
      /*                            RefreezeEnergy/(Ls * RHO_W)); */
      
      /* Energy released by freezing of intercepted water is added to the 
         MeltEnergy */

      *MeltEnergy += (Lf * *IntRain * RHO_W) / (Dt *SECPHOUR);
      *IntRain = 0.0;
      
    } 
    
    if (-(*VaporMassFlux) > *IntSnow) {
      *VaporMassFlux = -(*IntSnow);
      *IntSnow = 0.0;
    }
    else
      *IntSnow += *VaporMassFlux;
  } 
  
  *IntSnow *= F;
  *IntRain *= F;
  *MeltEnergy *= F;
  *VaporMassFlux *= F;
  Drip           *= F;
  ReleasedMass   *= F;
  
  /* Calculate intercepted H2O balance. */  
  
  MassBalanceError = (InitialWaterInt - (*IntSnow + *IntRain))
                   + (*SnowFall + *RainFall) - (SnowThroughFall
                   + RainThroughFall + Drip + ReleasedMass)
                   + *VaporMassFlux;

  *RainFall = RainThroughFall + Drip;
  *SnowFall = SnowThroughFall + ReleasedMass;

  /* Convert Units to VIC (m -> mm) */
  *VaporMassFlux *= -1.;
  *RainFall *= 1000.;
  *SnowFall *= 1000.;
  *IntRain  *= 1000.;

}
Esempio n. 7
0
/******************************************************************************
 * @brief    Calculate sublimation from blowing snow
 *****************************************************************************/
double
CalcBlowingSnow(double   Dt,
                double   Tair,
                unsigned LastSnow,
                double   SurfaceLiquidWater,
                double   Wind,
                double   Ls,
                double   AirDens,
                double   EactAir,
                double   ZO,
                double   Zrh,
                double   snowdepth,
                double   lag_one,
                double   sigma_slope,
                double   Tsnow,
                int      iveg,
                int      Nveg,
                double   fe,
                double   displacement,
                double   roughness,
                double  *TotalTransport)
{
    extern parameters_struct param;
    extern option_struct     options;

    /* Local variables: */
    double                   Age;
    double                   U10, Uo, prob_occurence;
    double                   es, Ros, F;
    double                   SubFlux;
    double                   Diffusivity;
    double                   ushear;
    double                   Tk;
    double                   utshear;
    int                      p;
    double                   upper, lower, Total;
    double                   area;
    double                   sigma_w;
    double                   Zo_salt;
    double                   ratio, wind10;
    double                   Uveg, hv, Nd;
    double                   Transport;

    /*******************************************************************/
    /* Calculate some general variables, that don't depend on wind speed. */

    /* Age in hours */
    Age = LastSnow * Dt / SEC_PER_HOUR;

    /* Saturation density of water vapor, Liston A-8 */
    es = svp(Tair);

    Tk = Tair + CONST_TKFRZ;

    Ros = CONST_EPS * es / (CONST_RDAIR * Tk);

    /* Diffusivity in m2/s, Liston eq. A-7 */
    Diffusivity = (2.06e-5) * pow(Tk / 273., 1.75);

    // Essery et al. 1999, eq. 6 (m*s/kg)
    F = (Ls / (param.BLOWING_KA * Tk)) * (Ls * Tk / CONST_RDAIR - 1.);
    F += 1. / (Diffusivity * Ros);

    /* grid cell 10 m wind speed = 50th percentile wind */
    /* Wind speed at 2 m above snow was passed to this function. */

    wind10 = Wind * log(10. / ZO) / log((2 + ZO) / ZO);

    /* Check for bare soil case. */
    if (iveg == Nveg) {
        fe = 1500;
        sigma_slope = .0002;
    }
    // sigma_w/uo:
    ratio = (2.44 - (0.43) * lag_one) * sigma_slope;

    sigma_w = wind10 * ratio;
    Uo = wind10;

    /*********** Parameters for roughness above snow. *****************/
    hv = (3. / 2.) * displacement;
    Nd = (4. / 3.) * (roughness / displacement);

    /*******************************************************************/
    /** Begin loop through wind probability function.                  */

    Total = 0.0;
    *TotalTransport = 0.0;
    area = 1. / (double) param.BLOWING_NUMINCS;

    if (snowdepth > 0.0) {
        if (options.BLOWING_SPATIAL_WIND && sigma_w != 0.) {
            for (p = 0; p < param.BLOWING_NUMINCS; p++) {
                SubFlux = lower = upper = 0.0;
                /* Find the limits of integration. */
                if (p == 0) {
                    lower = -9999;
                    upper = Uo + sigma_w * log(2. * (p + 1) * area);
                }
                else if (p > 0 && p < param.BLOWING_NUMINCS / 2) {
                    lower = Uo + sigma_w * log(2. * (p) * area);
                    upper = Uo + sigma_w * log(2. * (p + 1) * area);
                }
                else if (p < (param.BLOWING_NUMINCS - 1) && p >=
                         (double) param.BLOWING_NUMINCS / 2) {
                    lower = Uo - sigma_w * log(2. - 2. * (p * area));
                    upper = Uo - sigma_w * log(2. - 2. * ((p + 1.) * area));
                }
                else if (p == param.BLOWING_NUMINCS - 1) {
                    lower = Uo - sigma_w * log(2. - 2. * (p * area));
                    upper = 9999;
                }

                if (lower > upper) { /* Could happen if lower > Uo*2 */
                    lower = upper;
                    log_err("Error with probability boundaries");
                }


                /* Find expected value of wind speed for the interval. */
                U10 = Uo;
                if (lower >= Uo) {
                    U10 = -0.5 *
                          ((upper +
                            sigma_w) * exp((-1. / sigma_w) * (upper - Uo)) -
                           (lower +
                            sigma_w) *
                           exp((-1. / sigma_w) * (lower - Uo))) / area;
                }
                else if (upper <= Uo) {
                    U10 = 0.5 *
                          ((upper -
                            sigma_w) * exp((1. / sigma_w) * (upper - Uo)) -
                           (lower -
                            sigma_w) *
                           exp((1. / sigma_w) * (lower - Uo))) / area;
                }
                else {
                    log_err("Problem with probability ranges: Increment = %d, "
                            "integration limits = %f - %f", p, upper, lower);
                }

                if (U10 < 0.4) {
                    U10 = .4;
                }

                if (U10 > 25.) {
                    U10 = 25.;
                }
                /*******************************************************************/
                /* Calculate parameters for probability of blowing snow occurence. */
                /* ( Li and Pomeroy 1997) */

                if (snowdepth < hv) {
                    Uveg = U10 / sqrt(1. + 170 * Nd * (hv - snowdepth));
                }
                else {
                    Uveg = U10;
                }


                prob_occurence = get_prob(Tair, Age, SurfaceLiquidWater, Uveg);

                /*******************************************************************/
                /* Calculate threshold shear stress. Send 0 for constant or  */
                /* 1 for variable threshold after Li and Pomeroy (1997)      */

                utshear =
                    get_thresh(Tair, SurfaceLiquidWater, ZO);

                /* Iterate to find actual shear stress during saltation. */

                shear_stress(U10, ZO, &ushear, &Zo_salt, utshear);

                if (ushear > utshear) {
                    SubFlux = CalcSubFlux(EactAir, es, Zrh, AirDens, utshear,
                                          ushear, fe, Tsnow,
                                          Tair, U10, Zo_salt, F, &Transport);
                }
                else {
                    SubFlux = 0.0;
                    Transport = 0.0;
                }

                Total += (1. / (double) param.BLOWING_NUMINCS) * SubFlux *
                         prob_occurence;
                *TotalTransport += (1. / (double) param.BLOWING_NUMINCS) *
                                   Transport * prob_occurence;
            }
        }
        else {
            U10 = Uo;
            /*******************************************************************/
            /* Calculate parameters for probability of blowing snow occurence. */
            /* ( Li and Pomeroy 1997) */

            if (snowdepth < hv) {
                Uveg = U10 / sqrt(1. + 170 * Nd * (hv - snowdepth));
            }
            else {
                Uveg = U10;
            }

            prob_occurence = get_prob(Tair, Age, SurfaceLiquidWater, Uveg);

            /*******************************************************************/
            /* Calculate threshold shear stress. Send 0 for constant or  */
            /* 1 for variable threshold after Li and Pomeroy (1997)      */

            utshear = get_thresh(Tair, SurfaceLiquidWater, ZO);

            /* Iterate to find actual shear stress during saltation. */

            shear_stress(Uo, ZO, &ushear, &Zo_salt, utshear);

            if (ushear > utshear) {
                SubFlux = CalcSubFlux(EactAir, es, Zrh, AirDens, utshear,
                                      ushear, fe, Tsnow,
                                      Tair, Uo, Zo_salt, F, &Transport);
            }
            else {
                SubFlux = 0.0;
                Transport = 0.0;
            }
            Total = SubFlux * prob_occurence;
            *TotalTransport = Transport * prob_occurence;
        }
    }

    if (Total < -.00005) {
        Total = -.00005;
    }

    return Total;
}
Esempio n. 8
0
/*****************************************************************************
  Function name: CalcBlowingSnow()

  Purpose      : Calculate sublimation from blowing snow

  Required     :  double Dt;                     Model time step (hours) 
  double Tair;                    Air temperature (C) 
  int LastSnow;                   Time steps since last snowfall. 
  double SurfaceLiquidWater;      Liquid water in the surface layer (m) 
  double Wind;                    Wind speed (m/s), 2 m above snow 
  double Ls;                      Latent heat of sublimation (J/kg) 
  double AirDens;                 Density of air (kg/m3) 
  double Lv;                      Latent heat of vaporization (J/kg3) 
  double Press;                   Air pressure (Pa) 
  double EactAir;                 Actual vapor pressure of air (Pa) 
  double ZO;                      Snow roughness height (m)
  Returns      : BlowingMassFlux

  Modifies     : 

  Comments     : Called from SnowPackEnergyBalance
    Reference:  

*****************************************************************************/
double CalcBlowingSnow( double Dt, 
			double Tair,
			int LastSnow,
			double SurfaceLiquidWater,
			double Wind,
			double Ls,
			double AirDens,
			double Press,
			double EactAir,
			double ZO,
			double Zrh,
			double snowdepth,
			float lag_one,
			float sigma_slope,
			double Tsnow, 
			int iveg, 
			int Nveg, 
			float fe,
			double displacement,
			double roughness,
			double *TotalTransport)

{
  /* Local variables: */

  double Age;
  double U10, Uo, prob_occurence;
  double es, Ros, F;
  double SubFlux;
  double Diffusivity;
  double ushear, Qsalt, hsalt, phi_s, psi_s;
  double Tk;
  double Lv;
  double T, ztop;
  double ut10, utshear;
  int p;
  double upper, lower, Total;
  double area;
  double sigma_w;
  double undersat_2;
  double b, temp2; /* SBSM scaling parameter. */
  double temp, temp3;
  double Zo_salt;
  double ratio, wind10;
  double Uveg, hv, Nd;
  double Transport;
  int count=0;

  Lv = (2.501e6 - 0.002361e6 * Tsnow);
  /*******************************************************************/
  /* Calculate some general variables, that don't depend on wind speed. */

  /* Age in hours */
  Age = LastSnow*(Dt);
      
  /* Saturation density of water vapor, Liston A-8 */
  es = svp(Tair);

  Tk = Tair  + KELVIN;
    
  Ros = 0.622*es/(287*Tk);
  
  /* Diffusivity in m2/s, Liston eq. A-7 */
  Diffusivity = (2.06e-5) * pow(Tk/273.,1.75);

  // Essery et al. 1999, eq. 6 (m*s/kg)
  F = (Ls/(Ka*Tk))*(Ls*MW/(R*Tk) - 1.);
  F += 1./(Diffusivity*Ros);

  /* grid cell 10 m wind speed = 50th percentile wind */
  /* Wind speed at 2 m above snow was passed to this function. */

  wind10 = Wind*log(10./ZO)/log((2+ZO)/ZO);
  //  fprintf(stderr,"wind=%f, Uo=%f\n",Wind, Uo);

  /* Check for bare soil case. */
  if(iveg == Nveg) {
    fe = 1500;
    sigma_slope = .0002;
  }
  // sigma_w/uo:
  ratio = (2.44 - (0.43)*lag_one)*sigma_slope;
  //  sigma_w = wind10/(.69+(1/ratio));
  //  Uo = sigma_w/ratio;
  
  sigma_w = wind10*ratio;
  Uo = wind10;
  
  /*********** Parameters for roughness above snow. *****************/
    hv = (3./2.)*displacement;
    Nd = (4./3.)*(roughness/displacement);

  /*******************************************************************/
  /** Begin loop through wind probability function.                  */

  Total = 0.0;
  *TotalTransport = 0.0;
  area = 1./NUMINCS;
  
  if(snowdepth > 0.0) {
    if(SPATIAL_WIND && sigma_w != 0.) {
      for(p= 0; p< NUMINCS; p++) {
      
	SubFlux = lower = upper = 0.0;
      /* Find the limits of integration. */
      if(p==0) {
	lower = -9999;
	upper = Uo + sigma_w*log(2.*(p+1)*area);
      }
      else if(p > 0 && p < NUMINCS/2) {
	lower = Uo + sigma_w*log(2.*(p)*area);
	upper = Uo + sigma_w*log(2.*(p+1)*area);
      }
      else if(p < (NUMINCS-1) && p >= NUMINCS/2) {
	lower = Uo - sigma_w*log(2.-2.*(p*area));
	upper = Uo - sigma_w*log(2.-2.*((p+1.)*area));
      }
      else if(p == NUMINCS-1) {
	lower =  Uo - sigma_w*log(2.-2.*(p*area));
	upper = 9999;
      }

      if(lower > upper)  {/* Could happen if lower > Uo*2 */
	lower = upper;
	fprintf(stderr,"Warning: Error with probability boundaries in CalcBlowingSnow()\n");
      }


      /* Find expected value of wind speed for the interval. */
      U10 = Uo;
      if(lower >= Uo ) 
	U10 = -0.5*((upper+sigma_w)*exp((-1./sigma_w)*(upper - Uo))
		    - (lower+sigma_w)*exp((-1./sigma_w)*(lower - Uo)))/area;
      else if(upper <= Uo )
	U10 = 0.5*((upper-sigma_w)*exp((1./sigma_w)*(upper - Uo))
		   - (lower-sigma_w)*exp((1./sigma_w)*(lower - Uo)))/area;
      else {
	fprintf(stderr,"ERROR in CalcBlowingSnow.c: Problem with probability ranges\n");
	fprintf(stderr,"  Increment = %d, integration limits = %f - %f\n",p,upper, lower);
        return ( ERROR );
      }
   
      if(U10 < 0.4)
	U10 = .4;

      if(U10 > 25.) U10 = 25.;
      /*******************************************************************/
      /* Calculate parameters for probability of blowing snow occurence. */
      /* ( Li and Pomeroy 1997) */

      if(snowdepth < hv) {
		Uveg = U10/sqrt(1.+ 170*Nd*(hv - snowdepth));
      }
      else
	Uveg = U10;
  
      //  fprintf(stderr, "Uveg = %f, U10 = %f\n",Uveg, U10);

      prob_occurence = get_prob(Tair, Age, SurfaceLiquidWater, Uveg);

      //   printf("prob=%f\n",prob_occurence);
     
      /*******************************************************************/
      /* Calculate threshold shear stress. Send 0 for constant or  */
      /* 1 for variable threshold after Li and Pomeroy (1997)      */

      utshear = get_thresh(Tair, SurfaceLiquidWater, ZO, VAR_THRESHOLD);
   
      /* Iterate to find actual shear stress during saltation. */

      shear_stress(U10, ZO, &ushear, &Zo_salt, utshear);
      
      if(ushear > utshear) {
	
	SubFlux = CalcSubFlux(EactAir, es, Zrh, AirDens, utshear,ushear, fe, Tsnow, 
			      Tair, U10, Zo_salt, F, &Transport);
      }
      else {
	SubFlux=0.0;
	Transport = 0.0;
      }
 
      Total += (1./NUMINCS)*SubFlux*prob_occurence;
      *TotalTransport += (1./NUMINCS)*Transport*prob_occurence;

      }
   
    }
    else {
      U10=Uo;
       /*******************************************************************/
      /* Calculate parameters for probability of blowing snow occurence. */
      /* ( Li and Pomeroy 1997) */
  
      if(snowdepth < hv)
	Uveg = U10/sqrt(1.+ 170*Nd*(hv - snowdepth));
      else
	Uveg = U10;

      prob_occurence = get_prob(Tair, Age, SurfaceLiquidWater, Uveg);
    
      /*******************************************************************/
      /* Calculate threshold shear stress. Send 0 for constant or  */
      /* 1 for variable threshold after Li and Pomeroy (1997)      */

      utshear = get_thresh(Tair, SurfaceLiquidWater, ZO, VAR_THRESHOLD);

      /* Iterate to find actual shear stress during saltation. */
      
      shear_stress(Uo, ZO, &ushear, &Zo_salt, utshear);

      if(ushear > utshear) {
	SubFlux = CalcSubFlux(EactAir, es, Zrh, AirDens, utshear,ushear, fe, Tsnow, 
			      Tair, Uo, Zo_salt, F, &Transport);
      }
      else {
	SubFlux=0.0;
	Transport = 0.0;
      }
      Total = SubFlux*prob_occurence;
      *TotalTransport = Transport*prob_occurence;
    }
  }

  if(Total < -.00005)
    Total = -.00005;
 
  return Total;
  
}