Program Listing for File Iqbal_iswr.cpp#
↰ Return to documentation for file (src/modules/Iqbal_iswr.cpp)
//
// Canadian Hydrological Model - The Canadian Hydrological Model (CHM) is a novel
// modular unstructured mesh based approach for hydrological modelling
// Copyright (C) 2018 Christopher Marsh
//
// This file is part of Canadian Hydrological Model.
//
// Canadian Hydrological Model is free software: you can redistribute it and/or
// modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Canadian Hydrological Model is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Canadian Hydrological Model. If not, see
// <http://www.gnu.org/licenses/>.
//
#include "Iqbal_iswr.hpp"
REGISTER_MODULE_CPP(Iqbal_iswr);
Iqbal_iswr::Iqbal_iswr(config_file cfg)
:module_base("Iqbal_iswr", parallel::data, cfg)
{
depends("t");
depends("rh");
depends("cloud_frac");
depends("solar_el");
provides("iswr_direct_no_slope");
provides("iswr_diffuse_no_slope");
provides("atm_trans");
}
Iqbal_iswr::~Iqbal_iswr()
{
}
void Iqbal_iswr::run(mesh_elem &face)
{
double pressure = mio::Atmosphere::stdAirPressure(face->get_z());//101325.0;
double altitude = face->get_z();
double sun_elevation = (*face)["solar_el"_s];
sun_elevation = sun_elevation < 0? 0. : sun_elevation;
if (sun_elevation < 3)
{
(*face)["iswr_direct_no_slope"_s]=0;
(*face)["iswr_diffuse_no_slope"_s]=0;
return;
}
double ta = (*face)["t"_s]+273.15;
double rh = (*face)["rh"_s]/100.0;
double R_toa = 1375;
double R_direct=0;
double R_diffuse=0;
double ground_albedo = 0.1;
//these pow cost us a lot here, but replacing them by fastPow() has a large impact on accuracy (because of the exp())
const double olt = 0.32; //ozone layer thickness (cm) U.S.standard = 0.34 cm
const double w0 = 0.9; //fraction of energy scattered to total attenuation by aerosols (Bird and Hulstrom(1981))
const double fc = 0.84; //fraction of forward scattering to total scattering (Bird and Hulstrom(1981))
const double alpha = 1.3; //wavelength exponent (Iqbal(1983) p.118). Good average value: 1.3+/-0.5. Related to the size distribution of the particules
const double beta = 0.03; //amount of particules index (Iqbal(1983) p.118). Between 0 & .5 and above.
const double zenith = 90. - sun_elevation; //this is the TRUE zenith because the elevation is the TRUE elevation
const double cos_zenith = cos(zenith*mio::Cst::to_rad); //this uses true zenith angle
// relative optical air mass Young (1994), see http://en.wikipedia.org/wiki/Airmass
//const double mr = 1. / (cos_zenith + 0.50572 * pow( 96.07995-zenith , -1.6364 )); //pbl: this should use apparent zenith angle, and we only get true zenith angle here...
// relative optical air mass, Young, A. T. 1994. Air mass and refraction. Applied Optics. 33:1108–1110.
const double mr = ( 1.002432*cos_zenith*cos_zenith + 0.148386*cos_zenith + 0.0096467) /
( cos_zenith*cos_zenith*cos_zenith + 0.149864*cos_zenith*cos_zenith
+ 0.0102963*cos_zenith +0.000303978);
// actual air mass: because mr is applicable for standard pressure
// it is modified for other pressures (in Iqbal (1983), p.100)
// pressure in Pa
const double ma = mr * (pressure/101325.);
// the equations for all the transmittances of the individual atmospheric constituents
// are from Bird and Hulstrom (1980, 1981) and can be found summarized in Iqbal (1983)
// on the quoted pages
// broadband transmittance by Rayleigh scattering (Iqbal (1983), p.189)
const double taur = exp( -0.0903 * pow(ma,0.84) * (1. + ma - pow(ma,1.01)) );
// broadband transmittance by ozone (Iqbal (1983), p.189)
const double u3 = olt * mr; // ozone relative optical path length
const double alpha_oz = 0.1611 * u3 * pow(1. + 139.48 * u3, -0.3035) -
0.002715 * u3 / ( 1. + 0.044 * u3 + 0.0003 * u3 * u3); //ozone absorbance
const double tauoz = 1. - alpha_oz;
// broadband transmittance by uniformly mixed gases (Iqbal (1983), p.189)
const double taug = exp( -0.0127 * pow(ma, 0.26) );
// saturation vapor pressure in Pa
//const double Ps = exp( 26.23 - 5416./ta ); //as used for the parametrization
const double Ps = mio::Atmosphere::vaporSaturationPressure(ta);
// Leckner (1978) (in Iqbal (1983), p.94), reduced precipitable water
const double w = 0.493 * rh * Ps / ta;
// pressure corrected relative optical path length of precipitable water (Iqbal (1983), p.176)
// pressure and temperature correction not necessary since it is included in its numerical constant
const double u1 = w * mr;
// broadband transmittance by water vapor (in Iqbal (1983), p.189)
const double tauw = 1. - 2.4959 * u1 / (pow(1.0 + 79.034 * u1, 0.6828) + 6.385 * u1);
// broadband total transmittance by aerosols (in Iqbal (1983), pp.189-190)
// using Angstroem's turbidity formula Angstroem (1929, 1930) for the aerosol thickness
// in Iqbal (1983), pp.117-119
// aerosol optical depth at wavelengths 0.38 and 0.5 micrometer
const double ka1 = beta * pow(0.38, -alpha);
const double ka2 = beta * pow(0.5, -alpha);
// broadband aerosol optical depth:
const double ka = 0.2758 * ka1 + 0.35 * ka2;
// total aerosol transmittance function for the two wavelengths 0.38 and 0.5 micrometer:
const double taua = exp( -pow(ka, 0.873) * (1. + ka - pow(ka, 0.7088)) * pow(ma, 0.9108) );
// broadband transmittance by aerosols due to absorption only (Iqbal (1983) p. 190)
const double tauaa = 1. - (1. - w0) * (1. - ma + pow(ma, 1.06)) * (1. - taua);
// broadband transmittance function due to aerosols scattering only
// Iqbal (1983) p. 146 (Bird and Hulstrom (1981))
const double tauas = taua / tauaa;
// direct normal solar irradiance in range 0.3 to 3.0 micrometer (Iqbal (1983) ,p.189)
// 0.9751 is for this wavelength range.
// Bintanja (1996) (see Corripio (2002)) introduced a correction beta_z for increased
// transmittance with altitude that is linear up to 3000 m and than fairly constant up to 5000 - 6000 m
const double beta_z = (altitude<3000.)? 2.2*1.e-5*altitude : 2.2*1.e-5*3000.;
//Now calculating the radiation
//Top of atmosphere radiation (it will always be positive, because we check for sun elevation before)
const double tau_commons = tauoz * taug * tauw * taua;
// Diffuse radiation from the sky
const double factor = 0.79 * R_toa * tau_commons / (1. - ma + pow( ma,1.02 )); //avoid recomputing pow() twice
// Rayleigh-scattered diffuse radiation after the first pass through atmosphere (Iqbal (1983), p.190)
const double Idr = factor * 0.5 * (1. - taur );
// aerosol scattered diffuse radiation after the first pass through atmosphere (Iqbal (1983), p.190)
const double Ida = factor * fc * (1. - tauas);
// cloudless sky albedo Bird and Hulstrom (1980, 1981) (in Iqbal (1983) p. 190)
//in Iqbal, it is recomputed with ma=1.66*pressure/101325.; and alb_sky=0.0685+0.17*(1.-taua_p)*w0;
const double alb_sky = 0.0685 + (1. - fc) * (1. - tauas);
//Now, we compute the direct and diffuse radiation components
//Direct radiation. All transmitances, including Rayleigh scattering (Iqbal (1983), p.189)
R_direct = 0.9751*( taur * tau_commons + beta_z ) * R_toa ;
// multiple reflected diffuse radiation between surface and sky (Iqbal (1983), p.154)
const double Idm = (Idr + Ida + R_direct) * ground_albedo * alb_sky / (1. - ground_albedo * alb_sky);
R_diffuse = (Idr + Ida + Idm)*cos_zenith; //Iqbal always "project" diffuse radiation on the horizontal
double elevation_threshold = 2.0 * mio::Cst::to_rad;
if( sun_elevation < elevation_threshold ) {
//if the Sun is too low on the horizon, we put all the radiation as diffuse
//the splitting calculation that might take place later on will reflect this
//instead point radiation, it becomes the radiation of a horizontal sky above the domain
R_diffuse += R_direct*cos_zenith; //HACK
// R_diffuse =0.0;
R_direct = 0.;
}
double cf = (*face)["cloud_frac"_s];
double dir = R_direct * (0.6 + 0.2*cos_zenith) * (1.0-cf);
dir = std::max(0.0,dir);
R_diffuse = std::max(0.0,R_diffuse);
(*face)["iswr_direct_no_slope"_s]=dir;
(*face)["iswr_diffuse_no_slope"_s]=R_diffuse;
(*face)["atm_trans"_s]=(dir+R_diffuse/1375.);
}