PISM, A Parallel Ice Sheet Model stable 0.4.1779

src/coupler/localMassBalance.cc

Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines