void turbulent_fluxes(double rh, double rv, double P, double Ta, double T, double Qa, double Q, double dQdT, double *H, double *dHdT, double *E, double *dEdT){ double rho, cp, pot; rho=air_density(0.5*(Ta+T), Qa, P); cp=air_cp(0.5*(Ta+T)); //pot=pow((1000.0/P),(0.286*(1-0.23*Qa))); pot=1.0; //sensible heat flux [W/m2] /*a maximum value of the resistance for the sensible heat flux was introduced according to Jordan et el., 1999 (HEAT BUDGET OF SNOW-COVERED SEA ICE AT NORTH POLE 4) as Cwindless=0.5 W m^2 K^-1, rh=rho*cp/C=1300/0.5=2600*/ *H=cp*rho*pot*(T-Ta)/Fmin(rh,2.6E3); *dHdT=cp*rho*pot/Fmin(rh,2.6E3); //*H=cp*rho*pot*(T-Ta)/rh; //*dHdT=cp*rho*pot/rh; //evaporation [kg/(s*m2)] *E=rho*(Q-Qa)/rv; *dEdT=rho*dQdT/rv; }
/****************************************************************************** * @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); }
int compute_short_period_averages (machine_t machines[], sensor_t *sensor, mmdat_t *pshort_hist, struct tm start_time, struct tm end_time) { printf ("================Computing short period averages======================\n"); int i = 0; int j = 0; double temp_sum, temp_avg; double pres_sum, pres_avg; double humd_sum, humd_avg; double rho; double *type_sum = (double*) malloc (sizeof (double) * CMP_END); memset (type_sum, 0, sizeof (double) * CMP_END); double *type_avg = (double*) malloc (sizeof (double) * CMP_END); memset (type_avg, 0, sizeof (double) * CMP_END); /* Compute average energy consumption of all the machines */ for (i = 0; i < NUM_TOTAL; i++) { // Compute for this machine int tmp_sum = 0; int avg = 0; for (j = 0; j < (machines[i].phead - 1); j++) { tmp_sum += machines[i].current_periodwindow[j].current; } if (machines[i].phead - 1 > 0) avg = tmp_sum / (machines[i].phead - 1); else avg = 0; // Sum up the avg for this machine type type_sum[machines[i].type] += avg; type_sum[CMP_ALL] += avg; // Adjust machines[i].current_periodwindow[0] = machines[i].current_periodwindow[machines[i].phead]; machines[i].phead = 1; } /* Compute total average */ for (i = 0; i < CMP_END; i++) { type_avg[i] = type_sum[i] / num_machines[i]; } /* Compute average temp, humidty and pressure and air density*/ for (i = 0; i < sensor->size - 1; i++) { temp_sum += sensor->temperature[i]; humd_sum += sensor->humidity[i]; pres_sum += sensor->pressure[i]; } if (sensor->size - 1 > 0) { temp_avg = temp_sum / (sensor->size - 1); humd_avg = humd_sum / (sensor->size - 1); pres_avg = pres_sum / (sensor->size - 1); rho = air_density (temp_avg, humd_avg, pres_avg); // adjust sensor->temperature[0] = sensor->temperature[sensor->size]; sensor->humidity[0] = sensor->humidity[sensor->size]; sensor->pressure[0] = sensor->pressure[sensor->size]; sensor->size = 1; } else { temp_avg = 0; humd_avg = 0; pres_avg = 0; rho = 0; } if (pshort_hist->size == STORAGE_SHORT) { llist_t *penultimate = pshort_hist->last->prev; llist_entry_destroy (&(pshort_hist->last)); pshort_hist->last = penultimate; pshort_hist->last->next = NULL; pshort_hist->size -= 1; } /* make an entry */ llist_t *entry; llist_entry_create (&entry); entry->data->starttime = start_time; entry->data->endtime = end_time; entry->data->avg_temperature = temp_avg; entry->data->avg_humidity = humd_avg; entry->data->avg_pressure = pres_avg; entry->data->rho = rho; for (i = 0; i < CMP_END; i++) { entry->data->avg_current[i] = type_avg[i]; } if (pshort_hist->head != NULL) { entry->next = pshort_hist->head; pshort_hist->head->prev = entry; } pshort_hist->head = entry; pshort_hist->size += 1; air_density_current_ratio (entry->data->rho, entry->data->avg_current, entry->data->rho_cur_ratio); /* Free memory */ free (type_sum); free (type_avg); return 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); } } }
void canopy_fluxes(long r, long c, double Tv, double Tg, double Ta, double Qgsat, double Qa, double zmu, double zmT, double z0, double z0s, double d0, double z0r, double hveg, double v, double LR, double P, double SW, double LW, double e, double LSAI, double decaycoeff0, double *land, double Wcrn, double Wcrnmax, double Wcsn, double Wcsnmax, double *Esubl, double *Etrans, double *LWv, double *LWg, double *H, double *LE, double *h, double *dhdT, double *Ts, double *Qs, double *r_uc, double *froot, double *theta, DOUBLEVECTOR *soil_transp_layer, long chgsgn, double *Lobukhov, PAR *par, long n, double *rh, double *rv, double *rc, double *rb, double *u_top, double *decay, double *Locc, double *LWup_above_v, double psi, double **soil, double *alpha, double *beta, double *T, DOUBLEVECTOR *soil_evap_layer){ double rm, ft=0.0, fw, fwliq, fwice; double Qv, dQvdT, Hg, Lt, Lv, R, dLWvdT, dHdT, dEdT, dEsubldT, E; double Loc=1.E50, Loc0, Ts0; long cont, cont2, l; //CANOPY FRACTION SET AT THE MAX OF SNOW AND LIQUID WATER FRACTION ON CANOPY fwliq=pow(Wcrn/Wcrnmax,2./3.); fwice=pow(Wcsn/Wcsnmax,2./3.); fw=Fmax(fwliq, fwice); if(fw<0) fw=0.0; if(fw>1) fw=1.0; //LONGWAVE longwave_vegetation(LW, e, Tg, Tv, LSAI, LWv, LWg, &dLWvdT, LWup_above_v); //FIND SPECIFIC HUMIDITY IN THE VEGETATION LAYER sat_spec_humidity(&Qv, &dQvdT, 1.0, Tv, P); //UNDERCANOPY TURBULENT FLUXES //iteration for Ts cont2=0; do{ Ts0=*Ts; if(chgsgn>10){ //neglects stability corrections, if the number of iterations in Tcanopy is larger than a threshold aero_resistance2(zmu, zmT, z0, d0, z0r, hveg, v, Ta, Ts0, Qa, *Qs, P, LR, LSAI, &rm, rh, rv, u_top, Lobukhov, par->state_turb, 4, par->maxiter_Businger); }else{ //considers stability corrections aero_resistance2(zmu, zmT, z0, d0, z0r, hveg, v, Ta, Ts0, Qa, *Qs, P, LR, LSAI, &rm, rh, rv, u_top, Lobukhov, par->state_turb, par->monin_obukhov, par->maxiter_Businger); } cont2++; //iteration for Loc (within canopy Obukhov length) cont=0; do{ Loc0=Loc; //if(cont==par->maxiter_Loc) Loc=-1.E50; veg_transmittance(r, c, rm, v, Ts0, Tg, z0s, LSAI, decaycoeff0, z0, d0, hveg, *u_top, *Lobukhov, rb, r_uc, rh, decay, Loc, par->stabcorr_incanopy); *rv = (*rh); find_actual_evaporation_parameters(r,c,alpha, beta, soil_evap_layer, theta, soil, T, psi, P, *r_uc, Ta, Qa, Qgsat, n); /*if(Qg>(*Qs) && n==0){ *rv_ic = (*r_uc) + exp(8.206-4.255*sat); }else{ *rv_ic = *r_uc; }*/ *rb = (*rb) / LSAI; if(Qv<(*Qs)){ //condensation R=1.0; }else{ canopy_evapotranspiration(*rb, Tv, Qa, P, SW, theta, land, soil, froot, &ft, soil_transp_layer); R=fw+(1.0-fw)*ft; } *rc = (*rb) / R; *Ts = (Ta/(*rh) + Tg/(*r_uc) + Tv/(*rb)) / (1./(*rh) + 1./(*r_uc) + 1./(*rb)); *Qs = (Qa/(*rv) + (*alpha)*Qgsat*(*beta)/(*r_uc) + Qv/(*rc)) / (1./(*rv) + (*beta)/(*r_uc) + 1./(*rc)); Hg=air_cp((*Ts+Tg)/2.) * air_density((*Ts+Tg)/2., (*Qs+(*alpha)*Qgsat)/2., P) * (Tg-(*Ts))/(*r_uc); // -u*^3 // Loc = ------------------ Below Canopy Monin-Obukhov length (Niu&Yang) // k(g/T)(Hg/(rho*C)) Loc=-pow(v/rm,1.5)/( ka*(g/(*Ts+tk))*(Hg/(air_density(*Ts,*Qs,P)*air_cp(*Ts))) ); if(Hg==0.0 || Hg!=Hg) Loc=1.E+50; cont++; }while(fabs(Loc0-Loc)>0.01 && cont<=par->maxiter_Loc); /*if(cont==maxiter){ printf("Loc not converging, set at neutrality %ld %ld\n",r,c); }*/ }while(cont2<par->maxiter_Ts && fabs((*Ts)-Ts0)>0.01); /*if(fabs((*Ts)-Ts0)>0.01){ printf("Ts not converging %f %f %ld %ld\n",*Ts,Ts0,r,c); }*/ //CANOPY FLUXES turbulent_fluxes(*rb, *rc, P, *Ts, Tv, *Qs, Qv, dQvdT, H, &dHdT, &E, &dEdT); //Et from transpiration, E-Et condensation or evaporation/sublimation from water on the canopy Lt=Levap(Tv); if(E>0){ //evaporation or sublimation *Esubl=E*fw/R; dEsubldT=dEdT*fw/R; if(fwliq+fwice>0){ Lv=Lt + Lf*fwice/(fwliq+fwice); //linear interpolation to decide if sublimation or condensation occurs }else{ Lv=Lt; } }else{ //condensation *Esubl=E; dEsubldT=dEdT; if(Tv>=0){ Lv=Lt; }else{ Lv=Lt + Lf; } } *Etrans=E-(*Esubl); for(l=1;l<=soil_transp_layer->nh;l++){ soil_transp_layer->co[l] = soil_transp_layer->co[l] * (*Etrans); } *LE=Lt*(*Etrans) + Lv*(*Esubl); *h=(*LWv) - (*H) - (*LE); *dhdT=dLWvdT - dHdT - Lt*(dEdT-dEsubldT) - Lv*dEsubldT; if(*h!=(*h)) printf("No value in canopy fluxes Loc:%e v:%f rm:%e Ts:%f Tv:%f Ta:%f Tg:%f Hg:%f %ld %ld\n", Loc,v,rm,*Ts,Tv,Ta,Tg,Hg,r,c); *Locc=Loc; }
void find_actual_evaporation_parameters(long R, long C, double *alpha, double *beta, DOUBLEVECTOR *evap_layer, double *theta, double **soil, double *T, double psi, double P, double rv, double Ta, double Qa, double Qgsat, long nsnow){ DOUBLEVECTOR *r; double hs, F, A, B, Qs, D, Qsat, rho; long l, n = evap_layer->nh; if(nsnow>0){//snow or ice *alpha = 1.0; *beta = 1.0; initialize_doublevector(evap_layer, 0.); }else{ rho = air_density(0.5*(Ta+T[1]), Qa, P); //from Ye and Pielke, 1993 - bare soil evaporation if(psi > 10.){ //ponding *alpha = 1.0; *beta = 1.0; evap_layer->co[1] = rho * ( Qgsat - Qa ) / rv; }else if(theta[1] >= soil[jsat][1]){ //saturation *alpha = 1.0; *beta = theta[1]; evap_layer->co[1] = theta[1] * rho * ( Qgsat - Qa ) / rv; }else{ //unsaturated A = 0.; B = 0.; r = new_doublevector(n); ////molecular diffusivity resistances [s/m] //calculates water vapor fluxes for(l=1;l<=n;l++){ D = D00 * pow((T[l]+tk)/tk, 2.) * (Pa0/P); //molecular diffusivity water vapor [mm2/s] r->co[l] = (1.E3/D) * soil[jdz][l]; Qsat = SpecHumidity(SatVapPressure(T[l], P), P); if(l>1) r->co[l] += r->co[l-1]; if( theta[l] <= soil[jfc][l] ){ hs = 0.5 * ( 1. - cos( Pi * ( theta[l] - soil[jres][l] ) / ( soil[jfc][l] - soil[jres][l] ) ) ); }else{ hs = 1.; } evap_layer->co[l] = rho*(soil[jsat][l] - theta[l]) * hs * Qsat / r->co[l]; A += ( (soil[jsat][l] - theta[l]) / (soil[jsat][1] - theta[1]) ) * (rv/r->co[l]) * hs * Qsat; B += ( (soil[jsat][l] - theta[l]) / (soil[jsat][1] - theta[1]) ) * (rv/r->co[l]); } Qs = ( Qa + A ) / ( 1. + B ); for(l=1;l<=n;l++){ evap_layer->co[l] -= rho*(soil[jsat][l] - theta[l]) * Qs / r->co[l]; } free_doublevector(r); //calculates evaporation from the surface F = ( soil[jsat][1] ) / ( soil[jsat][1] - soil[jres][1] ); evap_layer->co[1] += rho*( theta[1] - soil[jres][1] ) * F * ( Qgsat - Qa ) / rv; *beta = ( soil[jsat][1] - theta[1] ) + ( theta[1] - soil[jres][1] ) * F - ( soil[jsat][1] - theta[1] ) / ( 1. + B ); *alpha = ( ( theta[1] - soil[jres][1] ) * F + ( soil[jsat][1] - theta[1] ) * A / ( Qgsat * (1. + B ) ) ) / (*beta); } } }