|
PISM, A Parallel Ice Sheet Model stable 0.4.1779
|
00001 // Copyright (C) 2009, 2010, 2011 Ed Bueler and Constantine Khroulev and Andy Aschwanden 00002 // 00003 // This file is part of PISM. 00004 // 00005 // PISM is free software; you can redistribute it and/or modify it under the 00006 // terms of the GNU General Public License as published by the Free Software 00007 // Foundation; either version 2 of the License, or (at your option) any later 00008 // version. 00009 // 00010 // PISM is distributed in the hope that it will be useful, but WITHOUT ANY 00011 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00012 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 00013 // details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with PISM; if not, write to the Free Software 00017 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00018 00019 #include <petsc.h> 00020 #include <ctime> // for time(), used to initialize random number gen 00021 #include <gsl/gsl_rng.h> 00022 #include <gsl/gsl_randist.h> 00023 #include <gsl/gsl_sf.h> // for erfc() in CalovGreveIntegrand() 00024 #include "pism_const.hh" 00025 #include "NCVariable.hh" 00026 #include "localMassBalance.hh" 00027 00028 PDDMassBalance::PDDMassBalance(const NCConfigVariable& myconfig) : LocalMassBalance(myconfig) { 00029 precip_as_snow = config.get_flag("interpret_precip_as_snow"); 00030 Tmin = config.get("air_temp_all_precip_as_snow"); 00031 Tmax = config.get("air_temp_all_precip_as_rain"); 00032 } 00033 00034 00039 PetscErrorCode PDDMassBalance::getNForTemperatureSeries( 00040 PetscScalar /* t */, PetscScalar dt, PetscInt &N) { 00041 PetscInt NperYear = static_cast<PetscInt>( 00042 config.get("pdd_max_temperature_evals_per_year") ); 00043 PetscScalar dt_years = dt / secpera; 00044 N = (int) ceil((NperYear - 1) * (dt_years) + 1); 00045 if (N < 3) N = 3; 00046 if ((N % 2) == 0) N++; // guarantee N is odd 00047 return 0; 00048 } 00049 00050 00052 00069 PetscScalar PDDMassBalance::CalovGreveIntegrand( 00070 PetscScalar sigma, PetscScalar TacC) { 00071 const PetscScalar Z = TacC / (sqrt(2.0) * sigma); 00072 return (sigma / sqrt(2.0 * pi)) * exp(-Z*Z) + (TacC / 2.0) * gsl_sf_erfc(-Z); 00073 } 00074 00075 00077 PetscScalar PDDMassBalance::getPDDSumFromTemperatureTimeSeries( 00078 PetscScalar pddStdDev, PetscScalar pddThresholdTemp, 00079 PetscScalar /* t */, PetscScalar dt_series, PetscScalar *T, PetscInt N) { 00080 PetscScalar pdd_sum = 0.0; // return value has units K day 00081 const PetscScalar sperd = 8.64e4, // exact seconds per day 00082 h_days = dt_series / sperd; 00083 const PetscInt Nsimp = ((N % 2) == 1) ? N : N-1; // odd N case is pure simpson's 00084 // Simpson's rule is: 00085 // integral \approx (h/3) * sum( [1 4 2 4 2 4 ... 4 1] .* [f(t_0) f(t_1) ... f(t_N-1)] ) 00086 for (PetscInt m = 0; m < Nsimp; ++m) { 00087 PetscScalar coeff = ((m % 2) == 1) ? 4.0 : 2.0; 00088 if ( (m == 0) || (m == (Nsimp-1)) ) coeff = 1.0; 00089 pdd_sum += coeff * CalovGreveIntegrand(pddStdDev,T[m]-pddThresholdTemp); // pass in temp in K 00090 } 00091 pdd_sum = (h_days / 3.0) * pdd_sum; 00092 if (Nsimp < N) { // add one more subinterval by trapezoid 00093 pdd_sum += (h_days / 2.0) * ( CalovGreveIntegrand(pddStdDev,T[N-2]-pddThresholdTemp) 00094 + CalovGreveIntegrand(pddStdDev,T[N-1]-pddThresholdTemp) ); 00095 } 00096 return pdd_sum; 00097 } 00098 00099 00102 00110 PetscScalar PDDMassBalance::getSnowFromPrecipAndTemperatureTimeSeries( 00111 PetscScalar precip_rate, 00112 PetscScalar /*t*/, PetscScalar dt_series, PetscScalar *T, PetscInt N) { 00113 00114 PetscScalar snow = 0.0; 00115 if (precip_as_snow) { 00116 // positive precip_rate: it snowed (precip = snow; never rain) 00117 snow = precip_rate * (N-1) * dt_series; // units: m (ice-equivalent) 00118 } else { 00119 // Following \ref Hock2005b we employ a linear transition from Tmin to Tmax 00120 for (PetscInt i=0; i<N-1; i++) { // go over all N-1 subintervals in interval[t,t+dt_series] 00121 const PetscScalar Tav = (T[i] + T[i+1]) / 2.0; // use midpt of temp series for subinterval 00122 PetscScalar acc_rate = 0.0; // accumulation rate during a sub-interval 00123 00124 if (Tav <= Tmin) { // T <= Tmin, all precip is snow 00125 acc_rate = precip_rate; 00126 } else if (Tav < Tmax) { // linear transition from Tmin to Tmax 00127 acc_rate = ((Tmax-Tav)/(Tmax-Tmin)) * precip_rate; 00128 } else { // T >= Tmax, all precip is rain -- ignore it 00129 acc_rate = 0; 00130 } 00131 snow += acc_rate * dt_series; // units: m (ice-equivalent) 00132 } 00133 } 00134 return snow; 00135 } 00136 00137 00140 00185 PetscErrorCode PDDMassBalance::getMassFluxesFromPDDs(const DegreeDayFactors &ddf, 00186 PetscScalar dt, PetscScalar pddsum, 00187 PetscScalar accumulation, 00188 PetscScalar &accumulation_rate, 00189 PetscScalar &melt_rate, 00190 PetscScalar &runoff_rate, 00191 PetscScalar &smb_rate) { 00192 00193 if (accumulation < 0.0) { // weird, but allowed, case 00194 accumulation_rate = 0.0; 00195 melt_rate = accumulation / dt; 00196 runoff_rate = melt_rate; 00197 smb_rate = melt_rate; 00198 return 0; 00199 } 00200 00201 accumulation_rate = accumulation / dt; 00202 00203 // no melt case: we're done 00204 if (pddsum <= 0.0) { 00205 melt_rate = 0.0; 00206 runoff_rate = 0.0; 00207 smb_rate = accumulation_rate; // = accumulation_rate - runoff_rate 00208 return 0; 00209 } 00210 00211 const PetscScalar snow_max_melted = pddsum * ddf.snow; // units: m (ice-equivalent) 00212 if (snow_max_melted <= accumulation) { 00213 // some of the snow melted and some is left; in any case, all of the energy 00214 // available for melt, namely all of the positive degree days (PDDs) were 00215 // used-up in melting snow; but because the snow does not completely melt, 00216 // we generate 0 modeled runoff, and the surface balance is the accumulation 00217 melt_rate = snow_max_melted / dt; 00218 runoff_rate = 0.0; 00219 smb_rate = accumulation_rate; // = accumulation_rate - runoff_rate 00220 return 0; 00221 } 00222 00223 // at this point: all of the snow melted; some of melt mass is kept as 00224 // refrozen ice; excess PDDs remove some of this ice and perhaps more of the 00225 // underlying ice 00226 const PetscScalar ice_created_by_refreeze = accumulation * ddf.refreezeFrac; // units: m (ice-equivalent) 00227 00228 const PetscScalar 00229 excess_pddsum = pddsum - (accumulation / ddf.snow), // units: K day 00230 ice_melted = excess_pddsum * ddf.ice; // units: m (ice-equivalent) 00231 00232 // all of the snow, plus some of the ice, was melted 00233 melt_rate = (accumulation + ice_melted) / dt; 00234 // all of the snow which melted but which did not refreeze, plus all of the 00235 // ice which melted (ice includes refrozen snow!), combine to give meltwater 00236 // runoff rate 00237 runoff_rate = (accumulation - ice_created_by_refreeze + ice_melted) / dt; 00238 smb_rate = accumulation_rate - runoff_rate; 00239 return 0; 00240 } 00241 00242 00248 PDDrandMassBalance::PDDrandMassBalance(const NCConfigVariable& myconfig, bool repeatable) 00249 : PDDMassBalance(myconfig) { 00250 pddRandGen = gsl_rng_alloc(gsl_rng_default); // so pddRandGen != NULL now 00251 gsl_rng_set(pddRandGen, repeatable ? 0 : time(0)); 00252 } 00253 00254 00255 PDDrandMassBalance::~PDDrandMassBalance() { 00256 if (pddRandGen != NULL) { 00257 gsl_rng_free(pddRandGen); 00258 pddRandGen = NULL; 00259 } 00260 } 00261 00262 00274 PetscErrorCode PDDrandMassBalance::getNForTemperatureSeries( 00275 PetscScalar /* t */, PetscScalar dt, PetscInt &N) { 00276 const PetscScalar sperd = 8.64e4; 00277 N = (int) ceil(dt / sperd); 00278 if (N < 2) N = 2; 00279 return 0; 00280 } 00281 00282 00283 PetscScalar PDDrandMassBalance::getPDDSumFromTemperatureTimeSeries( 00284 PetscScalar pddStdDev, PetscScalar pddThresholdTemp, 00285 PetscScalar /* t */, PetscScalar dt_series, PetscScalar *T, PetscInt N) { 00286 PetscScalar pdd_sum = 0.0; // return value has units K day 00287 const PetscScalar sperd = 8.64e4, // exact seconds per day 00288 h_days = dt_series / sperd; 00289 // there are N-1 intervals [t,t+dt],...,[t+(N-2)dt,t+(N-1)dt] 00290 for (PetscInt m = 0; m < N-1; ++m) { 00291 PetscScalar temp = 0.5*(T[m] + T[m+1]); // av temp in [t+m*dt,t+(m+1)*dt] 00292 temp += gsl_ran_gaussian(pddRandGen, pddStdDev); // add random: N(0,sigma) 00293 if (temp > pddThresholdTemp) 00294 pdd_sum += h_days * (temp - pddThresholdTemp); 00295 } 00296 return pdd_sum; 00297 } 00298 00299 00300 FaustoGrevePDDObject::FaustoGrevePDDObject( 00301 IceGrid &g, const NCConfigVariable &myconfig) 00302 : grid(g), config(myconfig) { 00303 00304 PetscErrorCode ierr; 00305 00306 beta_ice_w = config.get("pdd_fausto_beta_ice_w"); 00307 beta_snow_w = config.get("pdd_fausto_beta_snow_w"); 00308 00309 T_c = config.get("pdd_fausto_T_c"); 00310 T_w = config.get("pdd_fausto_T_w"); 00311 beta_ice_c = config.get("pdd_fausto_beta_ice_c"); 00312 beta_snow_c = config.get("pdd_fausto_beta_snow_c"); 00313 00314 fresh_water_density = config.get("fresh_water_density"); 00315 ice_density = config.get("ice_density"); 00316 pdd_fausto_latitude_beta_w = config.get("pdd_fausto_latitude_beta_w"); 00317 00318 ierr = temp_mj.create(grid, "temp_mj_faustogreve", false); 00319 ierr = temp_mj.set_attrs("internal", 00320 "mean July air temp from Fausto et al (2009) parameterization", 00321 "K", 00322 ""); 00323 } 00324 00325 00326 PetscErrorCode FaustoGrevePDDObject::setDegreeDayFactors( 00327 PetscInt i, PetscInt j, 00328 PetscScalar /* usurf */, PetscScalar lat, PetscScalar /* lon */, 00329 DegreeDayFactors &ddf) { 00330 00331 PetscErrorCode ierr = temp_mj.begin_access(); CHKERRQ(ierr); 00332 const PetscScalar T_mj = temp_mj(i,j); 00333 ierr = temp_mj.end_access(); CHKERRQ(ierr); 00334 00335 if (lat < pdd_fausto_latitude_beta_w) { // case lat < 72 deg N 00336 ddf.ice = beta_ice_w; 00337 ddf.snow = beta_snow_w; 00338 } else { // case > 72 deg N 00339 if (T_mj >= T_w) { 00340 ddf.ice = beta_ice_w; 00341 ddf.snow = beta_snow_w; 00342 } else if (T_mj <= T_c) { 00343 ddf.ice = beta_ice_c; 00344 ddf.snow = beta_snow_c; 00345 } else { // middle case T_c < T_mj < T_w 00346 const PetscScalar 00347 lam_i = pow( (T_w - T_mj) / (T_w - T_c) , 3.0), 00348 lam_s = (T_mj - T_c) / (T_w - T_c); 00349 ddf.ice = beta_ice_w + (beta_ice_c - beta_ice_w) * lam_i; 00350 ddf.snow = beta_snow_w + (beta_snow_c - beta_snow_w) * lam_s; 00351 } 00352 } 00353 00354 // degree-day factors in \ref Faustoetal2009 are water-equivalent 00355 // thickness per degree day; ice-equivalent thickness melted per degree 00356 // day is slightly larger; for example, iwfactor = 1000/910 00357 const PetscScalar iwfactor = fresh_water_density / ice_density; 00358 ddf.snow *= iwfactor; 00359 ddf.ice *= iwfactor; 00360 return 0; 00361 } 00362 00363 00365 00368 PetscErrorCode FaustoGrevePDDObject::update_temp_mj( 00369 IceModelVec2S *surfelev, IceModelVec2S *lat, IceModelVec2S *lon) { 00370 PetscErrorCode ierr; 00371 00372 const PetscReal 00373 d_mj = config.get("snow_temp_fausto_d_mj"), // K 00374 gamma_mj = config.get("snow_temp_fausto_gamma_mj"), // K m-1 00375 c_mj = config.get("snow_temp_fausto_c_mj"), // K (degN)-1 00376 kappa_mj = config.get("snow_temp_fausto_kappa_mj"); // K (degW)-1 00377 00378 PetscScalar **lat_degN, **lon_degE, **h; 00379 ierr = surfelev->get_array(h); CHKERRQ(ierr); 00380 ierr = lat->get_array(lat_degN); CHKERRQ(ierr); 00381 ierr = lon->get_array(lon_degE); CHKERRQ(ierr); 00382 ierr = temp_mj.begin_access(); CHKERRQ(ierr); 00383 00384 for (PetscInt i = grid.xs; i<grid.xs+grid.xm; ++i) { 00385 for (PetscInt j = grid.ys; j<grid.ys+grid.ym; ++j) { 00386 temp_mj(i,j) = d_mj + gamma_mj * h[i][j] + c_mj * lat_degN[i][j] + kappa_mj * (-lon_degE[i][j]); 00387 } 00388 } 00389 00390 ierr = surfelev->end_access(); CHKERRQ(ierr); 00391 ierr = lat->end_access(); CHKERRQ(ierr); 00392 ierr = lon->end_access(); CHKERRQ(ierr); 00393 ierr = temp_mj.end_access(); CHKERRQ(ierr); 00394 00395 return 0; 00396 } 00397
1.7.3