79 const blitz::Array<double, 1>& Model_altitude,
80 const blitz::Array<double, 1>& Model_temperature,
81 const blitz::Array<double, 1>& Ref_altitude,
82 const double Ref_latitude,
84 const double Ref_tropopause_altitude,
85 const double Obs_latitude,
87 : model_pressure(Model_pressure), model_altitude(Model_altitude), model_temperature(Model_temperature),
88 ref_altitude(Ref_altitude), ref_latitude(Ref_latitude), ref_time(Ref_time), ref_tropopause_altitude(Ref_tropopause_altitude),
89 obs_latitude(Obs_latitude), obs_time(Obs_time)
106 blitz::Array<double, 1> lapse_rates(model_altitude.rows());
107 lapse_rates(0) = 0.0;
108 double min_temp_val = max(model_temperature);
109 double min_temp_idx = -1;
110 for (
int i = 1; i < model_altitude.rows(); i++) {
112 lapse_rates(i) = (model_temperature(i) - model_temperature(i - 1)) /
113 (model_altitude(i) - model_altitude(i - 1));
118 model_temperature(i) < min_temp_val ) {
119 min_temp_val = model_temperature(i);
126 for (
int chk_idx = 1; chk_idx < lapse_rates.rows(); chk_idx++) {
127 double lay_p = 0.5 * (model_pressure(chk_idx) + model_pressure(chk_idx - 1));
128 double lay_alt = 0.5 * (model_altitude(chk_idx) + model_altitude(chk_idx - 1));
135 int avg_idx = chk_idx;
139 avg_alt = 0.5 * (model_altitude(avg_idx) + model_altitude(avg_idx - 1));
140 lr_sum += lapse_rates(avg_idx);
143 double lr_mean = lr_sum / (avg_idx - chk_idx);
153 Logger::warning() <<
"Could not determine tropopause altitude using lapse rate, falling back to minimum temperature";
154 lr_idx = min_temp_idx;
158 double last_lr_alt = 0.5 * (model_altitude(lr_idx - 1) + model_altitude(lr_idx - 2));
159 double lr_alt = 0.5 * (model_altitude(lr_idx) + model_altitude(lr_idx - 1));
161 double ztrop = last_lr_alt + (lr_alt - last_lr_alt) * (
lapse_rate_threshold - lapse_rates(lr_idx-1)) /(lapse_rates(lr_idx) - lapse_rates(lr_idx - 1));
162 ztrop = ztrop / (1 - ztrop / radius);
176 Array<double, 1> effective_altitudes(model_altitude.shape());
177 for (
int lev_idx = 0; lev_idx < model_altitude.rows(); lev_idx++) {
179 if(model_altitude(lev_idx) < mod_tropo_alt) {
181 zeff = model_altitude(lev_idx) * ref_tropopause_altitude / mod_tropo_alt;
184 zeff = model_altitude(lev_idx) + (ref_tropopause_altitude - mod_tropo_alt) *
185 exp(-(model_altitude(lev_idx) - mod_tropo_alt) / 10.0);
186 zeff = zeff - exp(-std::pow(obs_latitude / 25.0, 4))
187 * 3.5 * mod_tropo_alt * std::pow(model_altitude(lev_idx) / mod_tropo_alt - 1, 2)
188 * exp(-(model_altitude(lev_idx) - mod_tropo_alt) / 9.0);
192 if(zeff > model_altitude(model_altitude.rows() - 1)) {
193 zeff = model_altitude(model_altitude.rows() - 1);
196 effective_altitudes(lev_idx) = zeff;
198 return effective_altitudes;
210 double fl = obs_latitude / 22;
211 double aoa = 0.313 - 0.085 * exp(-std::pow((obs_latitude - 49) / 18, 2))
212 -0.268 * exp(-1.42 * altitude / (altitude + mod_tropo_alt)) * fl / sqrt(1 + std::pow(fl, 2));
213 if(altitude > mod_tropo_alt)
214 aoa = aoa + 7.0 * (altitude - mod_tropo_alt) / altitude;
226 if(vmr.rows() != ref_altitude.rows()) {
228 err_msg <<
"Altitude size: " << ref_altitude.rows() <<
", does not match " 229 <<
"vmr size: " << vmr.rows();
234 Array<double, 1> mod_grid_vmr(eff_altitude.shape());
235 for(
int lev_idx = 0; lev_idx < eff_altitude.rows(); lev_idx++) {
236 mod_grid_vmr(lev_idx) = mod_grid_interp(eff_altitude(lev_idx));
257 gas_gradient = lat_gradient.at(gas_name);
258 }
catch(
const std::out_of_range& exc) {
259 Logger::warning() <<
"apply_latitude_gradients: No latitude gradient found for: " << gas_name;
262 double xref = gas_gradient * (ref_latitude / 15) / sqrt(1 + std::pow(ref_latitude/15, 2));
263 double xobs = gas_gradient * (obs_latitude / 15) / sqrt(1 + std::pow(obs_latitude/15, 2));
265 Array<double, 1> grad_vmr(vmr.shape());
266 for(
int lev_idx = 0; lev_idx < vmr.rows(); lev_idx++) {
267 double frac = 1.0 / (1.0 + std::pow(model_altitude(lev_idx) / mod_tropo_alt, 2));
268 grad_vmr(lev_idx) = vmr(lev_idx) * (1 + frac * xobs) / (1 + frac * xref);
285 trend = secular_trend.at(gas_name);
286 }
catch(
const std::out_of_range& exc) {
287 Logger::warning() <<
"apply_secular_trend: No secular trend found for: " << gas_name <<
"\n";
291 Array<double, 1> secular_vmr(vmr.shape());
292 for(
int lev_idx = 0; lev_idx < vmr.rows(); lev_idx++) {
293 double aoa =
age_of_air(model_altitude(lev_idx));
294 secular_vmr(lev_idx) = vmr(lev_idx) * (1 + trend * (time_diff - aoa));
296 secular_vmr(lev_idx) = secular_vmr(lev_idx) / (1.0 + exp((-time_diff + aoa - 16) / 5.0));
297 if(gas_name ==
"SF6")
298 secular_vmr(lev_idx) = 1.5 * secular_vmr(lev_idx) / (1.0 + exp((-time_diff + aoa - 4) / 9.0));
314 double obs_year_frac = obs_time.
frac_year() - ptime(obs_time).date().year();
318 amplitude = seasonal_amplitude.at(gas_name);
319 }
catch(
const std::out_of_range& exc) {
320 Logger::warning() <<
"apply_seasonal_cycle: No seasonal cycle amplitude found for: " << gas_name <<
"\n";
324 Array<double, 1> seasonal_vmr(vmr.shape());
325 for(
int lev_idx = 0; lev_idx < vmr.rows(); lev_idx++) {
326 double zobs = model_altitude(lev_idx);
329 if (gas_name ==
"CO2") {
331 double sv = std::sin(twopi * (obs_year_frac - 0.834 - aoa));
333 double svnl = sv + 1.80 * exp(-std::pow((obs_latitude - 74) / 41, 2)) * (0.5 - std::pow(sv, 2));
334 sca = svnl * exp(-aoa/0.20) * (1 + 1.33 * exp(-std::pow((obs_latitude - 76) / 48, 2))*(zobs + 6.0) / (zobs + 1.4));
337 double sv = std::sin(twopi * (obs_year_frac - 0.89));
339 double svl = sv * (obs_latitude / 15) / sqrt(1 + std::pow(obs_latitude / 15, 2));
341 sca = svl * exp(-aoa / 1.60);
343 seasonal_vmr(lev_idx) = vmr(lev_idx) * (1.0 + sca * amplitude);
359 return seasonal_cycle_vmr;
const blitz::Array< double, 1 > resample_to_model_grid(const blitz::Array< double, 1 > &vmr) const
Resamples a VMR to the effective model altitude grid that accounts for the difference in tropopause a...
const blitz::Array< double, 1 > apply_seasonal_cycle(const blitz::Array< double, 1 > &vmr, const std::string &gas_name) const
Modifies the a priori vmr profile to account for the season of the observation/model.
const blitz::Array< double, 1 > apriori_vmr(const blitz::Array< double, 1 > &vmr, const std::string &gas_name) const
Creates the a priori VMR using the various transformation methods of the class.
const double lapse_rate_threshold
double frac_year() const
Calculate the fractional year.
const DoubleWithUnit wgs84_a(6378137.0000, units::m)
Equatorial radius.
DoubleWithUnit model_tropopause_altitude() const
Calculate the tropopause altitude for the model data used to initalize the class. ...
This is the base of the exception hierarchy for Full Physics code.
std::map< std::string, double > secular_trend
const double tropopause_min_press
ReferenceVmrApriori(const blitz::Array< double, 1 > &Model_pressure, const blitz::Array< double, 1 > &Model_altitude, const blitz::Array< double, 1 > &Model_temperature, const blitz::Array< double, 1 > &Ref_altitude, const double Ref_latitude, const Time &Ref_time, const double Ref_tropopause_altitude, const double Obs_latitude, const Time &Obs_time)
Apply value function to a blitz array.
std::map< std::string, double > lat_gradient
Inter-hemispheric gradients Positive values imply more in the NH than the SH than would be expected b...
const double mean_lapse_rate_region_alt
std::map< std::string, double > seasonal_amplitude
We frequently have a double with units associated with it.
DoubleWithUnit convert(const Unit &R) const
Convert to the given units.
This is a simple time class.
const double tropopause_max_press
Contains classes to abstract away details in various Spurr Radiative Transfer software.
const blitz::Array< double, 1 > apply_latitude_gradient(const blitz::Array< double, 1 > &vmr, const std::string &gas_name) const
Modifies the vmr profiles to account for the difference in latitude between the observation latitude ...
const blitz::Array< double, 1 > apply_secular_trend(const blitz::Array< double, 1 > &vmr, const std::string &gas_name) const
Modifies the a priori profiles on a gas-by-gas basis to account for the difference in time between t...
const blitz::Array< double, 1 > effective_altitude() const
Computes an altitude grid for resampling that takes into account a difference in the tropopause altit...
const Unit km("km", 1e3 *m)
static LogHelper warning()
const double age_of_air(const double altitude) const
Computes the Age of Air at particular location (Altitude, Latitude) relative to the surface at 50N...